--- jupyter: description: 'A wall hanger as a 2D CAD exercise: a dimensioned drawing to model in planex2d or any CAD system, a solution from a rectangle with rounded corners and two keyholes, the second a linear pattern of the first, and the area to check a model against.' --- # Wall hanger A wall hanger hangs a part on two screws in a wall. Their heads pass through the round holes of its keyholes, and the plate drops onto their shanks in the narrow slots above. The plate is a rectangle with rounded corners, and the keyholes are a profile tool of the sketch. Model the hanger from the drawing in planex2d or in any CAD system, then check its area and centroid against the values at the end of the page. A solution in planex2d follows. ![The wall hanger exercise: a dimensioned drawing of a rectangular plate with rounded corners and two keyholes](../_static/gallery/wall-hanger.png){.result} ```python import math import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Row ``` ## The plate The plate is a `Rectangle` 100 by 60 from the pen at the origin. Its lines are `plate.Line.1` to `plate.Line.4`, counterclockwise from the bottom edge, and its right angles are constraints already. Its corner sits at the origin, and with `auto_constraint={"horizontal": True, "vertical": True}` its bottom edge, drawn level, stays horizontal. `Fillet` rounds the four corners with a radius of 6 (Figure 1): ```python hanger = px.Sketch(auto_constraint={"horizontal": True, "vertical": True}) hanger.Rectangle(100, 60, id="plate") hanger.Constrain("plate.Line.1.start", "origin", px.Coincident) hanger.Constrain("plate.Line.1", px.Distance, 100, id="length") hanger.Constrain("plate.Line.2", px.Distance, 60, id="width") hanger.Fillet(["plate.Line.1.start", "plate.Line.2.start", "plate.Line.3.start", "plate.Line.4.start"], 6, id=["corner_1", "corner_2", "corner_3", "corner_4"]) print("status", hanger.status, "| degrees of freedom", hanger.dof) ``` ```python nbsphinx="hide-input" Row((hanger, "The rectangle with its rounded corners, every dimension and tie", {"constraints": True}), caption="The plate") ``` ## The keyholes `Keyhole` draws a straight slot from the pen that starts in a round hole, here a slot 12 long and 5 wide for the screw's shank, from a hole of radius 5 for its head, drawn upward. It makes the slot's width and the hole's radius dimensions, `key.w` and `key.r`. The hole's center, the start of the keyhole's spine, is 20 from the left edge and 30 from the bottom edge. `LinearPattern` repeats the whole keyhole once, 60 to the right (Figure 2): ```python hanger.Move((22, 28), dir=(0, 1)).Keyhole(12, 5, 5, id="key") # 12 long, 5 wide, a hole of radius 5, pointing up hanger.Constrain("key.spine", px.Distance, 12, id="key_l") hanger.Constrain("key.spine.start", "plate.Line.4", px.Distance, 20, id="key_x") hanger.Constrain("key.spine.start", "plate.Line.1", px.Distance, 30, id="key_y") hanger.LinearPattern("key", px.H, 2, 60, id="keys") # the keyhole and one copy, 60 apart print("status", hanger.status, "| degrees of freedom", hanger.dof) ``` ```python nbsphinx="hide-input" Row((hanger, "The plate and its two keyholes", {"fill": False}), caption="The keyholes") ``` ## The checks These are the area and the centroid, with the bottom left corner at the origin, to compare with your own model. The area is the rectangle less what the corners round off, $(4 - \pi) \cdot 6^2$, less the two keyholes. A keyhole is its hole of radius 5, the slot's 5 by 12 above the hole's center less the part of the hole under it, and the half disc of the slot's end. The hanger is symmetric about its vertical middle line, so its centroid lies on it (Figure 3): ```python face = hanger.Face() under = 2.5 * math.sqrt(5**2 - 2.5**2) + 5**2 * math.asin(2.5 / 5) # the hole's area under the slot, |x| < 2.5 keyhole = math.pi * 5**2 + (5 * 12 - under) + math.pi * 2.5**2 / 2 exact = 100 * 60 - (4 - math.pi) * 6**2 - 2 * keyhole c = face.center print("area", round(face.mass, 2), "mm², exact", round(exact, 2)) print("centroid", tuple(round(v, 2) for v in c[:2])) ``` ```python nbsphinx="hide-input" Row((face, "The wall hanger, its centroid marked", {"marks": [c[:2]]}), caption="The wall hanger") ``` The hanger as DXF in millimeters, for another CAD system or a laser cutter: ```python hanger.WriteDXF("wall_hanger.dxf", unit=px.mm, scale=1) # scale=1: the numbers as they are ``` The hanger as DXF: [wall_hanger.dxf](wall_hanger.dxf) ```python nbsphinx="hidden" import sys sys.path.insert(0, "..") import matplotlib.pyplot as plt from drawings import SheetFigure, View from gallery import save_figure # the holes' diameters and the slots' width from the geometry, the keyholes' spacing between the holes' centers (hx, hy), (sx, sy) = hanger.Location("key.spine.start"), hanger.Location("key.spine.end") view = View([(hanger, {"length": -14, "width": 114, "corner_3.r": (45, "outside", 4), "key_x": 70, "key_y": -10, "key_l": hx - 12})], axes=[((hx, hy - 9), (hx, sy + 6)), ((hx + 60, hy - 9), (hx + 60, sy + 6)), ((hx - 9, hy), (hx + 69, hy))], diameters=[((hx + 60, hy), 5, (315, "leader", 2))], measures=[((hx, hy), (hx + 60, hy), 70, 60), ((sx - 2.5, sy), (sx + 2.5, sy), 80, 5)]) drawing = SheetFigure([view], margin=24, notes=("planex2d.org", "2D exercise 12")) print("scale", drawing.planex_scale, "| paper", [round(v * 25.4) for v in drawing.get_size_inches()], "mm") save_figure(drawing, "wall-hanger", dpi=200) plt.close(drawing) ```