Packages

Composable, native Elixir CAD with sketches, assemblies, and verified printable exports

Current section

Files

Jump to
smith examples assembly.livemd
Raw

examples/assembly.livemd

# Smith - A named assembly and print pack

```elixir
Mix.install([{:smith, "~> 0.1.0"}, {:kino, "~> 0.19.0"}])
```

## Define a reusable part

Use ordinary functions for features and parameters. Both instances below reuse the
same recipe, which Smith evaluates once within this assembly evaluation. The
rounded sketch sets the footprint; an explicit entry plane fixes the counterbore
at its center. The 3 mm recess leaves 5 mm of material below the screw head.

```elixir
defmodule NotebookParts do
  def foot(width, depth, height) do
    Smith.Sketch.rounded_rectangle(width, depth, 1, align: {:min, :min})
    |> Smith.extrude(height)
    |> Smith.counterbore(on: Smith.Plane.xy(z: height), at: {width / 2, depth / 2},
      diameter: 5, bore_diameter: 8, bore_depth: 3, through: :all)
  end
end
foot = NotebookParts.foot(12, 16, 8)
Smith.Kino.render(foot, label: "1 · Reusable foot")
```

## Place two instances

Installed geometry and printable orientation are independent. References are
available for inspection but excluded from manufactured geometry and print packs.

```elixir
alias Smith.Assembly
pair =
  Assembly.new(:support)
  |> Assembly.part(:left, foot, position: {-20, 0, 0}, print: [on_bed: true])
  |> Assembly.part(:right, foot, position: {20, 0, 0}, print: [on_bed: true],
    exploded_offset: {0, 0, 16})
  |> Assembly.reference(:device, Smith.box(28, 16, 6), position: {-8, 0, 8})
{:ok, result} = Smith.evaluate(pair)
Smith.Kino.render(result, label: "2 · Installed feet")
```

## Inspect the members

Use `Assembly.members/1` to list evaluated leaves without reaching into the
recipe's internal fields. Each member has a path, a kind, an installed flag,
and its evaluated result. References are listed too, but are never manufactured
parts. The same function traverses nested assemblies.

```elixir
{:ok, members} = Assembly.members(result)
2 = Enum.count(members, &(&1.kind == :part))
1 = Enum.count(members, &(&1.kind == :reference))

members
|> Enum.map(fn member ->
  %{path: member.key, kind: member.kind, installed: member.installed}
end)
|> Kino.DataTable.new()
```

## Inspect the exploded view

Create a view from the evaluated assembly. The right foot moves up by its declared
exploded offset; its installed and print placements stay unchanged. The view is
a combined geometry snapshot. Keep the original assembly result for export.

```elixir
{:ok, exploded} = Assembly.view(result, :exploded)
Smith.Kino.render(exploded, label: "3 · Exploded feet")
```

## Inspect a reference

```elixir
{:ok, device} = Assembly.fetch(result, :device)
Smith.Kino.render(device, label: "Reference · Device")
```

## Export the complete assembly

Each manufactured part gets verified STL and 3MF files. The bundle also includes
installed STEP geometry, a separate reference STEP, and a ZIP of printable parts.

```elixir
{:ok, solids} = OCEx.solids(result.shape)
2 = length(solids)
{:ok, files} = Smith.export(result, "output/livebook", name: "support")
files
```

For a reusable group of parts, use `Assembly.subassembly/4`. The [nested assembly notebook](https://github.com/ntodd/smith/blob/main/examples/nested-assemblies.livemd) covers parent/child placement, path lookup, printable extras, and per-leaf exports.