Mix.install([{:smith, "~> 0.4.0"}, {:kino, "~> 0.19.0"}])Before you start
After sketches and solid forms, build a mounting plate and its mirrored partner. Add slots and three types of screw or locating holes, then inspect the selected features.
Run cells in order. Lengths are millimeters and modeling angles are degrees. Each 3D preview supports orbit, zoom, and fullscreen.
Round the outline
The outline is centered in XY. Corner rounding happens before sketch cutouts. Extrusion starts at Z=0 and finishes at Z=8.
alias Smith.{Assembly, Plane, Selector, Sketch}
outline = Sketch.rounded_rectangle(60, 40, 4)
blank = Smith.extrude(outline, 8)
Smith.Kino.render(blank, label: "1 · Rounded plate blank")Add a slot
Slot length is the overall dimension, including the two semicircular ends. A 20 × 6 slot therefore has 14 mm straight sides and 3 mm end radii. The formula below checks the extruded volume independently of the kernel construction.
slotted = outline |> Sketch.cut(Sketch.slot(20, 6)) |> Smith.extrude(8)
{:ok, slotted_result} = Smith.evaluate(slotted)
outer_area = 60 * 40 - (4 - :math.pi()) * 4 * 4
slot_area = (20 - 6) * 6 + :math.pi() * 3 * 3
{:ok, volume} = OCEx.volume(slotted_result.shape)
true = abs(volume - (outer_area - slot_area) * 8) < 1.0e-5
Smith.Kino.render(slotted_result, label: "2 · Through slot")Countersink a fixed pattern
An explicit entry plane keeps the fastener positions fixed as cuts change the top face's centroid. The 90° included sink angle and diameters of 8 and 4 mm produce a 2 mm deep recess. through: :all drills the pilot through the full body.
entry = Plane.xy(z: 8)
fastened =
for x <- [-22, 22], y <- [-12, 12], reduce: slotted do
plate ->
Smith.countersink(plate,
on: entry,
at: {x, y},
diameter: 4,
sink_diameter: 8,
angle: 90,
through: :all
)
end
Smith.Kino.render(fastened, label: "3 · Four countersunk fasteners")Add a counterbore and blind locating hole
The counterbore has an 8 mm diameter recess, 3 mm deep, and a 4 mm through pilot. The opposite locating hole is 3 mm across and stops 4 mm below the entry plane. Blind depth runs along the negative plane normal and includes any recess. These are geometric features; choose tolerances and fastener dimensions for your own hardware.
plate =
fastened
|> Smith.counterbore(on: entry, at: {0, -12}, diameter: 4,
bore_diameter: 8, bore_depth: 3, through: :all)
|> Smith.hole(on: entry, at: {0, 12}, diameter: 3, depth: 4)
{:ok, part} = Smith.evaluate(plate)
{:ok, volume} = OCEx.volume(part.shape)
sink_volume = :math.pi() * (2 * 2 * 6 + 2 / 3 * (4 * 4 + 4 * 2 + 2 * 2))
bore_volume = :math.pi() * (2 * 2 * 5 + 4 * 4 * 3)
blind_volume = :math.pi() * 1.5 * 1.5 * 4
expected = (outer_area - slot_area) * 8 - 4 * sink_volume - bore_volume - blind_volume
true = abs(volume - expected) < 1.0e-5
Smith.Kino.render(part, label: "4 · Finished plate")Inspect the topology
Measured filters accept an exact target with a tolerance, or an inclusive range. sort_by is stable, and take deliberately limits its result. The returned metadata uses world coordinates. Native handles identify this evaluated revision, so reuse selectors rather than old handles when the design changes.
rims = Selector.type(:circle) |> Selector.radius({1.5, 4})
{:ok, edge_info} = Smith.inspect_edges(part, rims)
true = edge_info != []
largest = Selector.type(:plane) |> Selector.sort_by(:area, :desc) |> Selector.take(2)
{:ok, face_info} = Smith.inspect_faces(part, largest)
Enum.map(face_info, &Map.take(&1, [:area, :center, :normal]))Use Selector.any_of([a, b]) to retain either query's matches, or Selector.exclude(query) to remove them. Each branch starts with the preceding selection. Union removes duplicates and preserves incoming order. Extrema retain all coordinate ties; take can discard them.
Mirror across a positioned plane
The plane at X=40 reflects the plate into a separate part at X=80. Mirroring returns just that reflected recipe; the original remains reusable. Finishing operations can follow a mirror because the reflected solid retains outward orientation.
partner = part |> Smith.from_result() |> Smith.mirror(Plane.yz(x: 40))
Smith.Kino.render(partner, label: "5 · Mirrored partner")Assemble and preview the finished parts
The two plates remain independent parts. Their installed positions show the mirrored arrangement; print placement centers each part on the bed.
assembly =
Assembly.new(:mechanical_parts)
|> Assembly.part(:left, Smith.from_result(part), print: [on_bed: true])
|> Assembly.part(:right, partner, print: [on_bed: true])
{:ok, assembled} = Smith.evaluate(assembly)
Smith.Kino.render(assembled, label: "6 · Mechanical sample parts")Export printable files
Export writes verified STL and 3MF for each manufactured part, the installed assembly STEP, and a ZIP containing the printable pairs. The path returned below is on the Livebook runtime's machine. A 0.1 rad angular tolerance keeps the small circular features well resolved.
{:ok, files} = Smith.export(assembled, "output/models",
name: "mechanical-parts", tolerance: 0.03, angular_tolerance: 0.1)
files.print_packThe formulas above check material volume, and export checks mesh connectivity/winding and STEP volume agreement. These checks do not establish strength, fit, or a print strategy. Inspect the blind floor and countersink walls in your slicer.
Try changing a hole's depth, moving the entry plane to a side face, or selecting circular rims by radius. Keep the volume formulas consistent with dimensional changes. Giving both depth: and through: must fail rather than silently choosing one.