Idler plate: a drafting exercise in 3D

The example is the idler plate of AutoCAD practice exercise 7a, modeled in 3D. The long hole, the top boss and the curved slot are raised parts. The plate is one sketch that projects the three parts into its plane, connects them with tangents and fillets as in the drawing, and trims the projected pieces that end up inside. Dimensions are in inches.

The idler plate of a drafting exercise, modeled in 3D

[1]:
import math
import netgen.occ as occ
from ngsolve_webgpu.jupyter import Draw
import planex as px

The dimensions

The dimensions of the drawing, with the center of the main hole at the origin:

Parameter

slot_pitch, R_slot, r_slot

The long hole: the distance of its end centers, and its outer and inner end radius

span

From the long hole’s left end center to the lower end of the curved slot

R_curve, sweep, R_cap, r_cap

The curved slot: the radius of its center line around the main hole, its angle, and its outer and inner end radius

boss_center, D_boss, d_boss

The top boss: its center, and its outer and hole diameter

d_main, R_bottom

The main hole and the arc around it

R_blend, R_fillet

The blend between the long hole and the boss, and the fillets at the curved slot

[3]:
slot_pitch = 1.000
R_slot = 0.750
r_slot = 0.437
span = 5.804
R_curve = 2.312
sweep = 40.0
R_cap = 0.875
r_cap = 0.438
boss_center = (-0.750, 1.875)
D_boss = 1.625
d_boss = 0.750
d_main = 1.125
R_bottom = 1.375
R_blend = 1.750
R_fillet = 0.625

The long hole’s left end center lies span to the left of the curved slot’s lower end, at x0:

[4]:
x0 = R_curve - span

The long hole

The long hole is built from the center line between the two slot centers (Figure 1), offset outward for the outside and the inside, and their difference is extruded to 1.

[5]:
slot_line = px.QuickSketch((x0, 0)).Line(slot_pitch)
slot = slot_line.Wire().Offset(R_slot) - slot_line.Wire().Offset(r_slot)
slot_part = slot.ToOCCShape().Extrude(1)
The center line
(a) The center line
Offset outside minus offset inside
(b) Offset outside minus offset inside
Figure 1: The long hole

The long hole in 3D:

[7]:
Draw(slot_part)
3D view
▶ Click to interact

The top boss

The boss has both circles in one sketch, and the inner one becomes the hole (Figure 2).

[8]:
boss_sketch = px.QuickSketch().Circle(*boss_center, D_boss / 2, id="boss_outer")
boss_sketch.Circle(*boss_center, d_boss / 2, id="boss_inner")
boss_part = boss_sketch.Face().ToOCCFace().Extrude(1)
The boss sketch
Figure 2: The top boss

The boss next to the long hole:

[10]:
Draw(occ.Compound([slot_part, boss_part]))
3D view
▶ Click to interact

The curved slot

The curved slot is a single arc around the main hole, offset once for the outside and once for the inside, and extruded to 0.5 (Figure 3).

[11]:
arc = px.QuickSketch((R_curve, 0)).Arc(center=(0, 0), sweep=sweep)
curved = arc.Wire().Offset(R_cap) - arc.Wire().Offset(r_cap)
curve_part = curved.ToOCCShape().Extrude(0.5)
The center arc
(a) The center arc
Offset outside minus offset inside
(b) Offset outside minus offset inside
Figure 3: The curved slot

The three raised parts:

[13]:
Draw(occ.Compound([slot_part, boss_part, curve_part]))
3D view
▶ Click to interact

The plate sketch

The plate sketch contains the three parts projected into the plane as drawn edges, the R1.375 circle around the main hole as construction, and the main hole:

[14]:
s = px.QuickSketch()
s.Project(slot_part, construction=False, name="slot")
s.Project(boss_part, construction=False, name="boss")
s.Project(curve_part, construction=False, name="curve")
s.Circle(0, 0, R_bottom, id="r1375", construction=True)
s.Circle(0, 0, d_main / 2, id="main hole")
projected = s.Copy()

Tangents and fillets

The tangents and fillets touch single projected edges, each selected by a point near it. s(name="slot") selects the edges projected from the slot, and of those, the one nearest to the point is the element that the drawing calls take in place of an id:

  • the bottom line touches the slot’s left end and the R1.375 circle from below;

  • the R1.750 blend touches the slot’s top edge and the boss;

  • the R.625 fillets connect the boss and the R1.375 circle to the ends of the curved slot;

  • the bottom arc follows the R1.375 circle from the bottom line to the lower fillet.

[15]:
def edge(name, point):
    return min(s(name=name), key=lambda e: e.Distance(point))


slot_left = edge("slot", (x0 - 1, 0))
slot_top = edge("slot", (x0 + 0.5, 1))
boss_outer = edge("boss", (boss_center[0], boss_center[1] + 1))
cap_top = edge("curve", (R_curve * math.cos(math.radians(sweep)) - 0.6, R_curve * math.sin(math.radians(sweep)) + 0.6))
cap_low = edge("curve", (R_curve, -1))

s.Line(tangent=((slot_left, (x0, -2)), ("r1375", (0, -2))), id="bottom")
blend = ((slot_top, (-3.2, 2.5)), (boss_outer, (-3.2, 2.5)))
s.Arc(*blend, R_blend, tangent=blend, id="blend")
top = ((boss_outer, (0.6, 2.4)), (cap_top, (0.6, 2.4)))
s.Arc(*top, R_fillet, tangent=top, id="fillet top")
low = ((cap_low, (1.5, -1.3)), ("r1375", (1.5, -1.3)))
s.Arc(*low, R_fillet, tangent=low, id="fillet bottom")
s.Arc("bottom", "fillet bottom", congruent="r1375", id="bottom arc")
joined = s.Copy()

Trimming

Every projected piece that lies inside the plate is trimmed:

  • on the slot, the bottom, the right end, the top right of the blend and a sliver of the left end;

  • on the boss, everything below the blend and the fillet;

  • on the curved slot, the inside arc and the inner parts of both ends.

A point on each piece selects the edge and the piece. Trim cuts the edge where it meets the rest of the sketch and makes the piece nearest the point construction: it stays in the sketch, dashed, and is no longer part of the outline. The R1.375 construction circle also cuts, because it crosses the lower half of the boss, which therefore comes in three pieces. Figure 4 shows the sketch at each stage.

[16]:
inside = {
    "slot": [(-3.0, -0.75), (-1.74, 0), (-2.7, 0.75), (-3.55, -0.75)],
    "boss": [(-1.37, 1.35), (-0.45, 1.12), (0.01, 1.6), (-1.56, 1.97), (0.05, 2.02)],
    "curve": [(1.35, 0.49), (0.95, 1.19), (1.55, -0.44)],
}
for name, points in inside.items():
    for point in points:
        s.Trim((edge(name, point), point))
The projected parts
(a) The projected parts
Tangents and fillets
(b) Tangents and fillets
Trimmed
(c) Trimmed
Figure 4: The plate sketch

The plate

The outline and the holes of the four parts form one face, which is extruded to 0.5 and united with the raised parts.

[18]:
plate_part = s.ToOCCFace().Extrude(0.5)
idler = slot_part + boss_part + curve_part + plate_part
Draw(idler)
3D view
▶ Click to interact

Reading the drawing back

Check: the sketch gives back the dimensions of the drawing. The arcs have their radii, and the bottom line lies R_slot from the slot’s end center and R_bottom from the main hole:

[19]:
for name, r in (("blend", R_blend), ("fillet top", R_fillet), ("fillet bottom", R_fillet), ("bottom arc", R_bottom)):
    print(name, "radius", s.Measure(name)["radius"], "drawing", r)
bottom = s.Edge("bottom").edges[0]
print("bottom line from the slot end", bottom.Distance((x0, 0), on_carrier=True), "drawing", R_slot)
print("bottom line from the main hole", bottom.Distance((0, 0), on_carrier=True), "drawing", R_bottom)
blend radius 1.7500000000000004 drawing 1.75
fillet top radius 0.625 drawing 0.625
fillet bottom radius 0.6249999999999999 drawing 0.625
bottom arc radius 1.3749999999999998 drawing 1.375
bottom line from the slot end 0.7499999999999998 drawing 0.75
bottom line from the main hole 1.375 drawing 1.375

Mesh

netgen meshes the part with elements of 0.2 at most, curved to order 3:

[20]:
mesh = occ.OCCGeometry(idler).GenerateMesh(maxh=0.2)
mesh.Curve(3)
print(mesh.ne, "elements")
Draw(mesh)
4784 elements
3D view
▶ Click to interact