--- jupyter: description: 'A hex standoff as a 3D CAD exercise: a dimensioned drawing with a full section to model in planex2d or any CAD system, a solution from a hexagon padded into the body, a stud padded from its top and a tapped hole pocketed from its bottom, and the volume, mass and center of mass to check a model against.' --- # Hex standoff A standoff holds a circuit board or a panel at a distance from the part below it. Its stud screws into the part, and a screw from above goes into its tapped hole. Its body is a hexagon, so that a spanner can turn it. As a solid, it is a hexagon padded into the body, the stud padded from its top and the tapped hole pocketed from its bottom. The threads are not modeled. The stud is its major diameter of 4, and the tapped hole is the tapping drill of 3.3, as CAD systems model them before a thread is added as a cosmetic feature. Model the standoff from the drawing in planex2d or in any CAD system, then check its volume, mass and center of mass against the values at the end of the page. A solution in planex2d follows. ![The hex standoff exercise: a dimensioned drawing of a hex standoff with an M4 stud and an M4 tapped hole, in a full section, a top view and an isometric](../_static/gallery/hex-standoff.png){.result} ```python import math import netgen.occ as occ import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Row, Views ``` ## The body The body is sketched on the xy plane, its axis the $z$ axis. It is a `Hexagon` 7 across its flats, a profile tool that keeps it regular and makes that width a dimension, `hex.af`. Its center is the origin, and its first side is vertical, so that two of its flats face along $x$. `px.Pad` extrudes it 15 up (Figure 1): ```python base = px.Sketch(auto_constraint=True) base.Hexagon(7, center=(0.5, 0.5), id="hex") # 7 across flats, its center placed roughly base.Constrain("hex.circle.center", "origin", px.Coincident) # the center of the hexagon's construction circle base.Constrain("hex.Line.1", px.Vertical) # the hexagon does not turn: two flats face along x print("status", base.status, "| degrees of freedom", base.dof) body = px.Pad(base, 15) ``` ```python nbsphinx="hide-input" Row((base, "The hexagon, every dimension and tie", {"constraints": True}), (body, "The body, padded 15", {"views": [(body, "shade"), (body, "ink")], "angles": (-60, 0, 30)}), caption="The body") ``` ## The stud The stud, 4 across and 6 long, is sketched on the body's top, its face highest in $z$, around the axis, and padded 6 up from it. `+` fuses it with the body (Figure 2): ```python stud = px.Sketch(body.faces.Max(occ.Z)) stud.Circle(4 / 2, id="stud") stud.Constrain("stud.center", "origin", px.Coincident) stud.Constrain("stud", px.Diameter, 4, id="stud_d") body = body + px.Pad(stud, 6) # + fuses two solids ``` ```python nbsphinx="hide-input" Views(("The stud on the body", [(body, "shade"), (body, "ink")]), angles=(-60, 0, 30), caption="The stud") ``` ## The tapped hole The tapped hole, the tapping drill of 3.3 for the M4 thread, 8 deep, is sketched on the body's bottom, its face lowest in $z$, and pocketed 8 against the face's normal, up into the body (Figure 3): ```python hole = px.Sketch(body.faces.Min(occ.Z)) hole.Circle(3.3 / 2, id="hole") hole.Constrain("hole.center", "origin", px.Coincident) hole.Constrain("hole", px.Diameter, 3.3, id="hole_d") body = px.Pocket(body, hole, 8) # 8 deep, against the face's normal: up into the body ``` ```python nbsphinx="hide-input" Views(("The tapped hole from below", [(body, "shade"), (body, "ink")]), angles=(-60, 0, -40), caption="The tapped hole") ``` ## The checks These are the volume, the mass in brass CuZn39Pb3 (8.47 g/cm³) and the center of mass, with the origin on the axis at the bottom, to compare with your own model. The volume is the hexagon's area, $\sqrt{3} / 2$ times the square of its width across flats, times 15, plus the stud, less the hole. The standoff is symmetric about its axis, so its center of mass lies on it, at the height where the moments of the three parts balance: ```python parts = [(math.sqrt(3) / 2 * 7**2 * 15, 15 / 2), (math.pi * 2**2 * 6, 15 + 6 / 2), (-math.pi * 1.65**2 * 8, 8 / 2)] exact = sum(v for v, z in parts) c = body.center print("volume", round(body.mass, 3), "mm³, exact", round(exact, 3)) print("mass", round(body.mass * 8.47e-6 * 1000, 3), "g") print("center of mass", round(c.z, 4), "above the bottom, exact", round(sum(v * z for v, z in parts) / exact, 4), "| off the axis by", round(math.hypot(c.x, c.y), 6)) ``` The standoff as STEP, for another CAD system: ```python body.WriteStep("hex_standoff.step") ``` The standoff as STEP: [hex_standoff.step](hex_standoff.step) ```python nbsphinx="hidden" import sys sys.path.insert(0, "..") import matplotlib.pyplot as plt from drawings import Pictorial, SheetFigure, View from gallery import save_figure # first angle: the full section through the plane y = 0 in front (the part behind it, its cut faces hatched), the top # view below it; the threads given by their size, as drawn on a part whose threads are not modeled front = px.XZ # x to the right, z up half = body * occ.Box((-10, 0, -1), (10, 10, 25)) cut = [f for f in half.faces if abs(f.center.y) < 1e-6 and f.bounding_box[1].y - f.bounding_box[0].y < 1e-6] section = View([], solid=half, plane=front, hidden_edges=False, hatch=cut, axes=[((0, -3), (0, 24))], measures=[((3.5, 0), (3.5, 15), 10, 15), ((2, 15), (2, 21), 10, 6), ((-2, 21), (2, 21), 25, "M4"), ((-1.65, 0), (1.65, 0), -5, "M4"), ((-1.65, 0), (-1.65, 8), -9, 8)]) top_view = View([(base, {"hex.af": 6})], solid=body, plane=px.XY, at=(0, -22), axes=[((-7, 0), (7, 0)), ((0, -7), (0, 7))]) pictorial = Pictorial(body, at=(24, -8), width=12, angles=(-60, 0, 30)) drawing = SheetFigure([section, top_view, pictorial], margin=10, notes=("planex2d.org", "3D exercise 19"), first_angle=True) print("scale", drawing.planex_scale, "| paper", [round(v * 25.4) for v in drawing.get_size_inches()], "mm") save_figure(drawing, "hex-standoff", dpi=200) plt.close(drawing) ```