--- jupyter: description: 'A spring clip as a 2D CAD exercise: a dimensioned drawing to model in planex2d or any CAD system, a solution from a center line of tangent lines and arcs offset to both sides into a strip of constant thickness, and the area to check a model against.' --- # Spring clip A spring clip snaps around a tube of 24. Its bend wraps 300° around the tube, and at its opening a small reverse bend at each lip turns the legs outward, so that they guide the tube in. It is a strip of constant thickness, 2, bent along a center line. The clip is drawn and dimensioned on that center line, and `Offset` makes the strip from it. Model the clip from the drawing in planex2d or in any CAD system, then check its area against the value at the end of the page. A solution in planex2d follows. ![The spring clip exercise: a dimensioned drawing of a clip bent 300 degrees around a tube, with two lips that turn its legs outward](../_static/gallery/spring-clip.png){.result} ```python import math import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Row ``` ## The center line The center line is one chain, drawn roughly from the outer end of the left leg. It runs along the leg, the left lip's reverse bend, which turns right, the bend of 300° around the tube, the right lip and the right leg. With `auto_constraint=True`, every joint becomes a tangency. The bend's center is the origin and its radius 13, the tube's 12 and half the strip's thickness. Its middle lies on the vertical axis, so that the clip is symmetric. Each lip turns 90° on a radius of 3, and both legs are 6 long (Figure 1): ```python clip = px.Sketch(auto_constraint=True) clip.Move((-8.4, 20.6), dir=(0.5, -0.866)).Line(6, id="left_leg") clip.Arc(3, -90, id="left_lip") # a negative sweep turns right: the reverse bend clip.Arc(13, 300, id="bend") clip.Arc(3, -90, id="right_lip") clip.Line(6, id="right_leg") clip.Constrain("bend.center", "origin", px.Coincident) clip.Constrain("bend", px.Radius, 13, id="bend_r") clip.Constrain("bend", px.Angle, 300, id="bend_sweep") # an arc's sweep clip.Constrain("bend.mid", "V", px.Coincident) clip.Constrain("left_lip", px.Radius, 3, id="lip_r") clip.Constrain("right_lip", "left_lip", px.Equal) clip.Constrain("left_lip", px.Angle, -90, id="lip_sweep") clip.Constrain("right_lip", px.Angle, -90) clip.Constrain("left_leg", px.Distance, 6, id="leg_l") clip.Constrain("right_leg", "left_leg", px.Equal) print("status", clip.status, "| degrees of freedom", clip.dof) ``` ```python nbsphinx="hide-input" Row((clip, "The center line, every dimension and tie", {"constraints": True}), caption="The center line") ``` ## The strip `Offset` draws the strip around the center line, 1 to each side. An open chain gets both sides and round caps at its ends, and the offset follows the center line when a dimension changes. Its distance is a dimension too, `strip.d`. The center line then becomes construction, so that only the strip's outline bounds the face (Figure 2): ```python clip.Offset(["left_leg", "left_lip", "bend", "right_lip", "right_leg"], 1, id="strip") for line in ("left_leg", "left_lip", "bend", "right_lip", "right_leg"): clip(line).construction = True # the center line stays, as construction print("status", clip.status, "| degrees of freedom", clip.dof) ``` ```python nbsphinx="hide-input" Row((clip, "The strip around the center line, dashed", {"fill": False}), caption="The strip") ``` ## The checks This is the area, to compare with your own model. A strip of constant thickness along a center line, with round ends, is the center line's length times the thickness plus a disc of the thickness. Here the length is the two legs of 6, the two lips' $3 \cdot 90^\circ$ and the bend's $13 \cdot 300^\circ$, with the angles in radians. Times the thickness of 2, plus $\pi \cdot 1^2$, that is the area. The clip is symmetric about the vertical axis, so its centroid lies on it (Figure 3): ```python face = clip.Face() length = 2 * 6 + 2 * 3 * math.radians(90) + 13 * math.radians(300) # the center line exact = length * 2 + math.pi * 1**2 c = face.center print("area", round(face.mass, 4), "mm², exact", round(exact, 4)) print("centroid", tuple(round(v, 4) for v in c[:2])) ``` ```python nbsphinx="hide-input" Row((face, "The spring clip, its centroid marked", {"marks": [c[:2]]}), caption="The spring clip") ``` The clip as DXF in millimeters, for another CAD system or a laser cutter: ```python clip.WriteDXF("spring_clip.dxf", unit=px.mm, scale=1) # scale=1: the numbers as they are ``` The clip as DXF: [spring_clip.dxf](spring_clip.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 center line as a center line, arc by arc; the thickness across the bend at its bottom def center_arc(arc, step=3): (cx, cy), (ax, ay), (bx, by) = (clip.Location(arc + end) for end in (".center", ".start", ".end")) r = math.hypot(ax - cx, ay - cy) t0 = math.atan2(ay - cy, ax - cx) sweep = math.radians(clip.Measure(arc)["angle"]) n = max(2, int(abs(math.degrees(sweep)) / step)) return [(cx + r * math.cos(t0 + sweep * k / n), cy + r * math.sin(t0 + sweep * k / n)) for k in range(n + 1)] line = [p for arc in ("left_lip", "bend", "right_lip") for p in center_arc(arc)] legs = [[clip.Location(f"{leg}.start"), clip.Location(f"{leg}.end")] for leg in ("left_leg", "right_leg")] view = View([(clip, {"bend_r": (220, "inside"), "bend_sweep": 20, "lip_r": (150, "outside", 2), "lip_sweep": 7, "leg_l": 5})], axes=[line] + [[a, a, b, b] for a, b in legs] + [((-18, 0), (18, 0), (0, 0)), ((0, -18), (0, 24), (0, 0))], measures=[((0, -14), (0, -12), -5, 2)]) drawing = SheetFigure([view], margin=12, notes=("planex2d.org", "2D exercise 14")) print("scale", drawing.planex_scale, "| paper", [round(v * 25.4) for v in drawing.get_size_inches()], "mm") save_figure(drawing, "spring-clip", dpi=200) plt.close(drawing) ```