--- jupyter: description: 'An electronics enclosure as a 3D CAD exercise: a dimensioned drawing with a full section to model in planex2d or any CAD system, a solution from a rounded outline padded, its inward offset pocketed as the inside, screw bosses padded from the floor and a rebate for the lid, and the exact volume, the mass and center of mass to check a model against.' --- # Electronics enclosure An enclosure holds a circuit board on four screw bosses and closes with a lid that sits in a rebate around its rim. Its wall has the same thickness all around, so the inside is the outline offset inward by the wall. That one offset of the sketch, pocketed down to the floor, keeps the wall's thickness and rounds the inside corners by itself. The rebate is a ring, the inside offset outward less the inside, pocketed from the top. Model the enclosure 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 electronics enclosure exercise: a dimensioned drawing of an open box with rounded corners, four screw bosses inside and a rebate around its rim, in a full section, a top view and an isometric](../_static/gallery/enclosure.png){.result} ```python import math import netgen.occ as occ import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Row, Views ``` ## The outline The enclosure is 100 by 60 outside and 35 high, its corners rounded with a radius of 8. The outline is a `RectangleC` around the origin on the xy plane, its four corners filleted, and each fillet makes its radius a dimension, `corner_1.r` to `corner_4.r`. `px.Pad` extrudes it 35 up (Figure 1): ```python outline = px.Sketch(auto_constraint=True) outline.RectangleC(100, 60, id="box") outline.Constrain("box.center", "origin", px.Coincident) outline.Constrain("box.Line.1", px.Horizontal) outline.Constrain("box.Line.1", px.Distance, 100, id="length") outline.Constrain("box.Line.2", px.Distance, 60, id="width") outline.Fillet(["box.Line.1.start", "box.Line.2.start", "box.Line.3.start", "box.Line.4.start"], 8, id=["corner_1", "corner_2", "corner_3", "corner_4"]) print("status", outline.status, "| degrees of freedom", outline.dof) box = px.Pad(outline, 35) ``` ```python nbsphinx="hide-input" Row((outline, "The outline, every dimension and tie", {"constraints": True}), (box, "Padded 35", {"views": [(box, "shade"), (box, "ink")], "angles": (-60, 0, 30)}), caption="The outline") ``` ## The inside The wall is 2.5 thick all around, and so is the floor. The inside is the outline's face offset by -2.5, inward, a rectangle of 95 by 55 whose corners have the radius $8 - 2.5$, around the same centers. It is pocketed from the top, $35 - 2.5$ deep, down to the floor (Figure 2): ```python inside = outline.Face().Offset(-2.5, inplace=False) inside_top = inside.Translated((0, 0, 35)) box = px.Pocket(box, inside_top, 35 - 2.5) print("inside", round(inside.mass, 4), "mm², exact", round(95 * 55 - (4 - math.pi) * 5.5**2, 4)) ``` ```python nbsphinx="hide-input" Views(("The inside pocketed down to the floor", [(box, "shade"), (box, "ink")]), angles=(-60, 0, 30), caption="The inside") ``` ## The bosses The four screw bosses stand on the floor, around the centers of the corners' arcs, 42 and 22 from the middle. They are rings of 8 outside with holes of 2.5 for self-tapping screws, 1.5 clear of the inside corners. Two circles nested in one sketch are a ring. They are sketched on the floor, the xy plane lifted by 2.5, and padded 27.5 up, so that they end 5 below the rim (Figure 3): ```python bosses = px.QuickSketch(px.Plane(px.XY, 2.5)) for x in (-42, 42): for y in (-22, 22): bosses.Circle(8 / 2, center=(x, y)).Circle(2.5 / 2, center=(x, y)) # a ring: the hole nested in the boss box = box + px.Pad(bosses, 27.5) # + fuses the bosses with the box ``` ```python nbsphinx="hide-input" Views(("The bosses on the floor", [(box, "shade"), (box, "ink")]), angles=(-60, 0, 60), caption="The bosses") ``` ## The rebate The lid sits in a rebate around the rim, 1.2 wide and 2 deep. The rebate's face is a ring between the inside and the inside offset outward by 1.2, their difference `-`, and it is pocketed 2 deep from the top (Figure 4): ```python rebate = inside.Offset(1.2, inplace=False) - inside box = px.Pocket(box, rebate.Translated((0, 0, 35)), 2) print("rebate", round(rebate.mass, 4), "mm²") ``` ```python nbsphinx="hide-input" Row((rebate, "The rebate's ring", {}), (box, "The rebate around the rim", {"views": [(box, "shade"), (box, "ink")], "angles": (-60, 0, 30)}), caption="The rebate") ``` ## The checks These are the volume, the mass in aluminum EN AW-6082 (2.70 g/cm³) and the center of mass, with the origin in the middle of the bottom, to compare with your own model. Every part has an exact area. A rectangle $a$ by $b$ whose corners are rounded with a radius $r$ has the area $a b - (4 - \pi) r^2$, and the volume is the outline times 35, less the inside times 32.5, plus the four bosses' rings times 27.5, less the rebate's ring times 2. The enclosure is symmetric about two planes, so its center of mass lies on the vertical axis: ```python def rounded(a, b, r): return a * b - (4 - math.pi) * r**2 ring = rounded(97.4, 57.4, 6.7) - rounded(95, 55, 5.5) # the rebate exact = (rounded(100, 60, 8) * 35 - rounded(95, 55, 5.5) * 32.5 + 4 * math.pi * (4**2 - 1.25**2) * 27.5 - ring * 2) c = box.center print("volume", round(box.mass, 2), "mm³, exact", round(exact, 2)) print("mass", round(box.mass * 2.70e-6, 4), "kg") print("center of mass", round(c.z, 3), "above the bottom, off the axis by", round(math.hypot(c.x, c.y), 6)) ``` The enclosure as STEP, for another CAD system: ```python box.WriteStep("enclosure.step") ``` The enclosure as STEP: [enclosure.step](enclosure.step) ```python nbsphinx="hidden" import sys sys.path.insert(0, "..") import matplotlib.pyplot as plt from drawings import LANDSCAPE, Pictorial, SheetFigure, Table, 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 # bosses behind the plane seen), the top view below it front = px.XZ # x to the right, z up behind = box * occ.Box((-55, 0, -1), (55, 35, 40)) cut = [f for f in behind.faces if abs(f.center.y) < 1e-6 and f.bounding_box[1].y - f.bounding_box[0].y < 1e-6] section = View([], solid=behind, plane=front, hidden_edges=False, hatch=cut, axes=[((0, -3), (0, 38))], measures=[((-50, 0), (50, 0), -10, 100), ((50, 0), (50, 35), 58, 35), ((42, 2.5), (42, 30), 30, 27.5)]) top = View([(outline, {"width": 68, "corner_1.r": (225, "outside")})], solid=box, plane=px.XY, at=(0, -58), hidden_edges=False, axes=[((-56, 0), (56, 0)), ((0, -36), (0, 36))] + [((x - 6, y), (x + 6, y)) for x in (-42, 42) for y in (-22, 22)] + [((x, y - 6), (x, y + 6)) for x in (-42, 42) for y in (-22, 22)], measures=[((-42, -22), (42, -22), -38, 84), ((42, -22), (42, 22), 57, 44)], diameters=[((42, 22), 4, (45, "leader", 4)), ((-42, 22), 1.25, (135, "leader", 4))]) # the wall and the rebate, too small to dimension at 1:1, in a table data = Table([("Wall and floor", 2.5), ("Rebate for the lid", "1,2 wide, 2 deep")], at=(74, 36), widths=(40, 36)) pictorial = Pictorial(box, at=(105, -30), width=56, angles=(-60, 0, 30)) drawing = SheetFigure([section, top, data, pictorial], margin=14, notes=("planex2d.org", "3D exercise 22"), first_angle=True, sheet=LANDSCAPE) print("scale", drawing.planex_scale, "| paper", [round(v * 25.4) for v in drawing.get_size_inches()], "mm") save_figure(drawing, "enclosure", dpi=200) plt.close(drawing) ```