Guide block

A guide block carries a T-slot for the bolts or sliding nuts of a fixture and is screwed down through two counterbored holes. As a solid, it is one profile with the T-slot, padded through, and two stepped holes pocketed from the top. Model the block 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 guide block exercise: a dimensioned drawing of a block with a T-slot and two counterbored holes in a front view, a top view and an isometric

[1]:
import math
import netgen.occ as occ
import planex as px

The profile

The profile is the front view, sketched in the front plane, the plane with its normal toward the viewer, \(-y\), with \(x\) to the right and \(z\) up and the origin in the middle of the bottom. The outline runs from the bottom left corner around the block and down into the T-slot, over its neck, the ledge out to the wide part, its flank and its bottom, and the same back up on the other side. With auto_constraint=True, the flags px.Horizontal and px.Vertical in the drawing calls become constraints.

The profile is symmetric about the vertical axis. The left neck, flank, ledge and top mirror the lines of the right ones, and the mirror sets their directions too, so they carry no flags. With the sizes of the front view, nothing in the sketch is left free (Figure 1):

[3]:
front = px.XZ
profile = px.Sketch(front, auto_constraint=True)
profile.Move((-50, 0)).Line(100, px.Horizontal, id="bottom")
profile.Turn(90).Line(40, px.Vertical, id="right_side")  # Turn(90) turns the pen left, Turn(-90) right
profile.Turn(90).Line(40, px.Horizontal, id="right_top")
profile.Turn(90).Line(12, px.Vertical, id="right_neck")
profile.Turn(90).Line(5, px.Horizontal, id="right_ledge")
profile.Turn(-90).Line(8, px.Vertical, id="right_flank")
profile.Turn(-90).Line(24, px.Horizontal, id="slot_bottom")
profile.Turn(-90).Line(8, id="left_flank")
profile.Turn(-90).Line(5, id="left_ledge")
profile.Turn(90).Line(12, id="left_neck")
profile.Turn(90).Line(40, id="left_top")
profile.Close(px.Vertical, id="left_side")
profile.Constrain("bottom.mid", "origin", px.Coincident)
profile.Constrain("bottom", px.Distance, 100, id="length")
profile.Constrain("right_side", px.Distance, 40, id="height")
profile.Constrain("right_neck", "left_neck", px.Distance, 14, id="opening")
profile.Constrain("right_neck", px.Distance, 12, id="neck")
profile.Constrain("right_flank", "left_flank", px.Distance, 24, id="slot_w")
profile.Constrain("right_flank", px.Distance, 8, id="slot_h")
profile.Constrain("left_neck", "right_neck", "V", px.SymmetricCarriers)
profile.Constrain("left_flank", "right_flank", "V", px.SymmetricCarriers)
profile.Constrain("left_ledge", "right_ledge", "V", px.SymmetricCarriers)
profile.Constrain("left_top", "right_top", "V", px.SymmetricCarriers)
print("status", profile.status, "| degrees of freedom", profile.dof)
status full | degrees of freedom 0
Every dimension and tie of the profile
Figure 1: The fully constrained profile

The block

px.Pad extrudes the profile 30 to both sides of the front plane, 60 in all, so that the T-slot runs through the block from front to back. Its volume is the area of the profile times 60 (Figure 2):

[5]:
block = px.Pad(profile, 30, length2=30)
print("volume", block.mass, "area × 60", profile.Face().mass * 60)
volume 218399.99999999997 area × 60 218400.0
The profile extruded 60 deep
Figure 2: The block

The holes

The counterbores, 15 across and 9 deep, are sketched on the top of the block, its face highest in \(z\). The T-slot splits the top in two, but both halves lie in the plane of the sketch. They are placed roughly, 60 apart, and the spacing of 70 moves them to their place (Figure 3a). The block shows them pocketed (Figure 3b):

[7]:
top = block.faces.Max(occ.Z)  # one half of the top; the sketch takes its whole plane
counterbores = px.Sketch(top)
counterbores.Circle(15 / 2, center=(-30, 0), id="left_counterbore")
counterbores.Circle(15 / 2, center=(30, 0), id="right_counterbore")
counterbores.Constrain("left_counterbore", px.Diameter, 15, id="counterbore_d")
counterbores.Constrain("left_counterbore", "right_counterbore", px.Equal)
counterbores.Constrain("left_counterbore.center", "right_counterbore.center", px.Distance, 70, id="spacing")
counterbores.Constrain("left_counterbore.center", "H", px.Coincident)
counterbores.Constrain("left_counterbore.center", "right_counterbore.center", "V", px.Symmetric)
block = px.Pocket(block, counterbores, 9)  # 9 deep, against the face's normal: down
The sketch on the top, the block under it
(a) The sketch on the top, the block under it
Both counterbores in the top
(b) Both counterbores in the top
Figure 3: The counterbores

The through holes, 9 across, go through the counterbores’ centers, sketched on the same top, and until=px.last cuts them down through the bottom (Figure 4):

[9]:
holes = px.Sketch(top)
for side in ("left", "right"):
    holes.Circle(9 / 2, center=counterbores.Location(side + "_counterbore.center"), id=side + "_hole")  # the solved center
holes.Constrain("left_hole", px.Diameter, 9, id="hole_d")
holes.Constrain("left_hole", "right_hole", px.Equal)
block = px.Pocket(block, holes, until=px.last)  # through to the last flat face it crosses
The sketch of the holes on the top, the block under it
(a) The sketch of the holes on the top, the block under it
The through holes from the counterbores down
(b) The through holes from the counterbores down
Figure 4: The through holes

The checks

These are the volume, the mass in steel S235 (7.85 g/cm³) and the center of mass, with the origin in the middle of the bottom, to compare with your own model. The block is symmetric about two planes, so its center of mass lies on the vertical axis:

[11]:
c = block.center
print("volume", round(block.mass, 1), "mm³")
print("mass", round(block.mass * 7.85e-6, 3), "kg")
print("center of mass", round(c.z, 2), "above the bottom, off the axis by", round(math.hypot(c.x, c.y), 6))
volume 211274.9 mm³
mass 1.659 kg
center of mass 18.96 above the bottom, off the axis by 0.0

The block as STEP, for another CAD system:

[12]:
block.WriteStep("guide_block.step")

The block as STEP: guide_block.step