--- jupyter: description: 'A rocker as a 3D CAD exercise: a dimensioned drawing with a section through its middle plane to model in planex2d or any CAD system, a solution from one chain of tangent arcs and lines, bosses on both faces and a fork pocketed partway into the thickness, and the volume, mass and center of mass to check a model against.' --- # Rocker The rocker, or bell crank, of a small race car's pushrod suspension turns the travel of the pushrod into the stroke of the damper. It pivots on a bearing in its hub, the rod end of the pushrod sits in the fork of its lower arm, and the damper is bolted to its upper boss. Its outline is one chain of tangent arcs and lines around the hub, the boss and the eye. The bosses stand proud of both faces, and the fork is a slot through the middle of the thickness. Model the rocker 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 rocker exercise: a dimensioned drawing of an L-shaped rocker with a hub, an upper boss and a forked eye, arcs of large radius along its lower arm, in a front view, a section through its middle plane and an isometric](../_static/gallery/rocker.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 outline is the rocker's plan, sketched in the xy plane, which becomes the middle plane of its thickness. It is one chain, drawn roughly from the hub out. It runs along the lower arm's underside, a concave arc that turns right, then around the eye and back along the lower arm's top. A convex arc of large radius follows, a small arc turning right into the inner corner, the upper arm's right flank, the boss, the left flank, and the hub back to the start. With `auto_constraint=True`, every joint of the chain becomes a tangency, and where the chain closes, a coincidence and a tangency are added. The arcs and lines carry ids for the dimensions, which the interactive plot shows under the mouse (Figure 1). ```python rocker = px.Sketch(auto_constraint=True) rocker.Move((14, -17), dir=(1, 0.8)).Arc(50, -80, id="lower") # a negative sweep turns right: a concave arc rocker.Arc(11, 185, id="eye") rocker.Arc(110, 33, id="upper") rocker.Arc(10, -72, id="corner") rocker.Line(42, id="right_flank") rocker.Arc(14, 162, id="boss") rocker.Line(65, id="left_flank") rocker.Arc(22, 132, id="hub") rocker.Constrain("hub.end", "lower.start", px.Coincident) rocker.Constrain("hub", "lower", px.Tangent) rocker.Plot(backend="plotly") ``` ## The dimensions The hub's center is the origin. The boss, the eye and the center of the inner corner's arc are placed by their distances from the hub's center lines, and the radii give the arcs their sizes. The convex arc of 110 and the corner's arc of 10 could still slide along the eye and the boss together. The corner's center, 28 across and 32 up from the hub's center, holds them. With these, nothing in the sketch is left free (Figure 1): ```python rocker.Constrain("hub.center", "origin", px.Coincident) rocker.Constrain("boss.center", "V", px.Distance, 5, id="boss_x") rocker.Constrain("boss.center", "H", px.Distance, 70, id="boss_y") rocker.Constrain("eye.center", "V", px.Distance, 85, id="eye_x") rocker.Constrain("eye.center", "H", px.Distance, 10, id="eye_y") rocker.Constrain("corner.center", "V", px.Distance, 28, id="corner_x") rocker.Constrain("corner.center", "H", px.Distance, 32, id="corner_y") rocker.Constrain("hub", px.Radius, 22, id="hub_r") rocker.Constrain("boss", px.Radius, 14, id="boss_r") rocker.Constrain("eye", px.Radius, 11, id="eye_r") rocker.Constrain("lower", px.Radius, 50, id="lower_r") rocker.Constrain("upper", px.Radius, 110, id="upper_r") rocker.Constrain("corner", px.Radius, 10, id="corner_r") print("status", rocker.status, "| degrees of freedom", rocker.dof) ``` ```python nbsphinx="hide-input" Row((rocker, "Every dimension and tie of the outline", {"constraints": True}), caption="The outline") ``` ## The plate and the bosses `px.Pad` extrudes the outline 10 to both sides of the xy plane, 20 in all. The bosses, the hub's of 44 and the upper one of 28, are circles in the same middle plane at the solved centers of the hub's and the boss's arcs, which `Location` gives. `px.Fix` holds each where it is drawn, with its size. Padded 15 to both sides, they are 30 long and stand 5 proud of each face, and `+` fuses them with the plate (Figure 2): ```python block = px.Pad(rocker, 10, length2=10) bosses = px.Sketch() bosses.Circle(44 / 2, px.Fix, center=rocker.Location("hub.center"), id="hub_boss") bosses.Circle(28 / 2, px.Fix, center=rocker.Location("boss.center"), id="upper_boss") block = block + px.Pad(bosses, 15, length2=15) ``` ```python nbsphinx="hide-input" Views(("The plate with its bosses", [(block, "shade"), (block, "ink")]), angles=(-60, 0, 30), caption="The bosses") ``` ## The fork The fork's slot, 8 wide, runs through the middle of the thickness from the eye's end to an arc of radius 60 around the hub, clear of the inner corner. Its profile is a region in the middle plane, a rectangle over the lower arm's end less a disc of radius 60 around the hub. `px.Pocket` with `length2` cuts 4 to each side of the plane, only where the region meets the rocker (Figure 3): ```python region = px.QuickSketch().Move((30, -40)).Rectangle(90, 80).Face() - px.Face([px.Circle((0, 0), 60)]) block = px.Pocket(block, region, 4, length2=4) # 4 to each side of the middle plane: a slot 8 wide ``` ```python nbsphinx="hide-input" Row((region, "The region of the slot in the middle plane", {"dashed": [rocker]}), (block, "The slot through the middle of the lower arm", {"views": [(block, "shade"), (block, "ink")], "angles": (-60, 0, 60)}), caption="The fork") ``` ## The holes The bearing seat of 26 in the hub, the hole of 10 for the damper's bolt in the upper boss and the hole of 8 for the rod end's bolt in the eye are circles in the middle plane too. Like the bosses, they are fixed at the arcs' centers. Pocketed 20 to both sides, they go through everything, the eye's hole through both cheeks of the fork (Figure 4): ```python holes = px.Sketch() holes.Circle(26 / 2, px.Fix, center=rocker.Location("hub.center"), id="bearing_seat") holes.Circle(10 / 2, px.Fix, center=rocker.Location("boss.center"), id="damper_hole") holes.Circle(8 / 2, px.Fix, center=rocker.Location("eye.center"), id="eye_hole") block = px.Pocket(block, holes, 20, length2=20) ``` ```python nbsphinx="hide-input" Views(("The holes through the bosses and the fork", [(block, "shade"), (block, "ink")]), angles=(-60, 0, 30), caption="The holes") ``` ## The checks These are the volume, the mass in aluminum EN AW-7075 (2.81 g/cm³) and the center of mass, with the origin at the hub's center in the middle plane, to compare with your own model. The volume is the outline's area times 20, plus the bosses' 10 of length beyond the plate, less the holes and the slot. The eye's hole runs through the two cheeks, 12 of the thickness, and the slot's area in plan is where the region and the outline overlap, their intersection `*`. The rocker is symmetric about its middle plane, so its center of mass lies in it: ```python outline = rocker.Face() slot = (outline * region).mass # the slot's area in plan exact = (outline.mass * 20 + math.pi * (22**2 + 14**2) * 10 - math.pi * (13**2 + 5**2) * 30 - math.pi * 4**2 * 12 - slot * 8) c = block.center print("volume", round(block.mass, 1), "mm³, exact", round(exact, 1)) print("mass", round(block.mass * 2.81e-6, 3), "kg") print("center of mass", tuple(round(v, 2) for v in (c.x, c.y)), "in the middle plane, off it by", round(abs(c.z), 6)) ``` The rocker as STEP, for another CAD system: ```python block.WriteStep("rocker.step") ``` The rocker as STEP: [rocker.step](rocker.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 # the plan as a section through the middle plane z = 0 (the symmetry plane of the thickness: no designation needed), # the part below the plane seen from above, its cut faces hatched; above it the front view from the solid front = px.XZ # x to the right, z up below = block * occ.Box((-40, -40, -20), (110, 100, 0)) cut = [f for f in below.faces if abs(f.center.z) < 1e-6 and f.bounding_box[1].z - f.bounding_box[0].z < 1e-6] (ex, ey), (bx, by) = rocker.Location("eye.center"), rocker.Location("boss.center") end = ex + rocker.Measure("eye")["radius"] front_view = View([], solid=block, plane=front, at=(0, 60), axes=[((0, -18), (0, 18)), ((bx, -18), (bx, 18)), ((ex, -14), (ex, 14))], measures=[((-22, -10), (-22, 10), -36, 20), ((-22, -15), (-22, 15), -48, 30), ((end, -4), (end, 4), end + 10, 8)]) plan = View([(rocker, {"boss_x": by + 22, "boss_y": -34, "eye_x": -50, "eye_y": ex + 32, "corner_x": 52, "corner_y": -46, "hub_r": (225, "outside"), "boss_r": (135, "outside"), "eye_r": (45, "outside"), "lower_r": (70, "inside"), "upper_r": (82, "inside"), "corner_r": (225, "inside")}), (holes, {}, {})], solid=below, plane=px.XY, hidden_edges=False, hatch=cut, at=(0, -60), axes=[((-28, 0), (ex + 16, 0), (0, 0)), ((0, -28), (0, by + 20), (0, 0)), ((bx - 20, by), (bx + 20, by)), ((ex, ey - 16), (ex, ey + 16))], radii=[((0, 0), 60, (10, "inside"))], diameters=[((0, 0), 13, (300, "leader")), ((bx, by), 5, (45, "leader")), ((ex, ey), 4, (300, "leader"))]) pictorial = Pictorial(block, at=(165, -15), width=75, angles=(-60, 0, 30)) drawing = SheetFigure([front_view, plan, pictorial], margin=30, notes=("planex2d.org", "3D exercise 11"), first_angle=True) print("scale", drawing.planex_scale, "| paper", [round(v * 25.4) for v in drawing.get_size_inches()], "mm") save_figure(drawing, "rocker", dpi=200) plt.close(drawing) ```