---
title: "Dental presets"
description: "Turn a case's scans into a print-ready .form file for orthodontic models, restorative models, occlusal guards, or surgical guides, set up the way PreForm's dental presets set them up."
url: https://developer.formlabs.com/examples/dental-presets/
---

# Dental presets

This script turns the scans of a dental case into a print-ready `.form` file using one of the [dental presets](https://formlabs.com/support/Preparing-dental-scans-for-printing-with-PreForm-Dental-Workspace-Form-2-Form-3B-Form-3BL/#select-template): orthodontic models, restorative models, occlusal guards, or surgical guides. The script takes in the kind of case and STL files and picks the resin and layer height, orients each arch, adds supports to appliances, and lays everything out on the build platform.

[View on GitHub](https://github.com/Formlabs/formlabs-api-python/tree/v1.0.0-alpha/examples/dental-presets) [Download dental-presets.zip](https://github.com/Formlabs/formlabs-api-python/releases/download/v1.0.0-alpha/dental-presets.zip)

A dental lab can use presets (one of the provided presets, or create your own in PreForm), for example: a night guard that always goes on LT Comfort, tilted and supported, or a crown and bridge model that always goes on Precision Model at 0.05 mm. PreForm
stores those choices as *presets* in its dental workspace, where a technician can pick one and PreForm
sets up the build. This script applies the same presets from a script, so scans from your lab
software or an intraoral scanner become a build ready to send, without a user having to open PreForm.

`presets.toml` holds the four presets PreForm offers for Form 4B, with the same settings:

| Preset | Prints | Resin | Layer height | Tilt | Supports |
| --- | --- | --- | --- | --- | --- |
| `orthodontic-models` | Arch models, such as for thermoforming aligners and retainers | Fast Model V1 | 0.16 mm | 0° | None |
| `restorative-models` | Crown and bridge models | Precision Model V1 | 0.05 mm | 0° | None |
| `occlusal-guards` | Night guards and splints | LT Comfort V1.1 | 0.1 mm | 15° | Full raft, labeled |
| `surgical-guides` | Guides for implant placement | Surgical Guide V1 | 0.1 mm | 15° | Full raft, labeled |

The script uses the PreForm API to orient, support, and lay out the models, and it reads back the
size of each model, whether it fits on the build platform, the layer count, and the resin volume.

![Two arch models without supports, laid out side by side on the build platform](https://developer.formlabs.com/examples/dental-presets-orthodontic-models.png)

![A surgical guide tilted 15 degrees on supports and a full raft labeled with the model name](https://developer.formlabs.com/examples/dental-presets-surgical-guides.png)

This example contains two files and a folder: `apply_preset.py` builds the job through the PreForm API,
`presets.toml` holds the presets, and the `scans` folder holds four sample STL files: an upper and a
lower arch for case 1042, an occlusal guard for case 1043, and a surgical guide for case 1044. The
sample files are simple generated shapes that stand in for real scans.

## Describe each kind of case

A preset is one table in `presets.toml`. The surgical guide preset prints on Surgical Guide V1 at
0.1 mm, tilts the guide 15 degrees, and supports it on a labeled raft:

```toml
[presets.surgical-guides]
material = "Surgical Guide V1"
material_code = "FLSGAM01"
layer_thickness_mm = 0.1
tilt_deg = 15
[presets.surgical-guides.supports]
raft_type = "FULL_RAFT"
raft_label_enabled = true
density = 1.0
touchpoint_size_mm = 0.5
internal_supports_enabled = true
```

The keys under `supports` are the PreForm API's own parameter names. A preset without a
`supports` table prints without supports, as the two model presets do. To add your lab's own kind of
work, such as dentures or dies, add another `[presets.<name>]` table.

## Fill in the defaults

`load_presets` fills each preset from the `[defaults]` table:

```python
def load_presets(path: Path) -> dict[str, dict]:
    """Read the presets, each filled in from the [defaults] table."""
    data = tomllib.loads(path.read_text())
    defaults = data.get("defaults", {})
    return {name: defaults | preset for name, preset in data["presets"].items()}
```

The defaults set the machine type `FORM-4-0`, which covers Form 4B, the `DEFAULT` print setting, a
tilt of 0 degrees, millimeter units, no mesh repair, the `DENTAL` layout, and 1 mm between models.
A preset overrides any of them, as the orthodontic models preset does with `F4_FAST_ARCHES`, the Fast
Arch print setting.

## Connect to PreForm

Make sure [PreForm is ready to accept connections](https://developer.formlabs.com/getting-started/install.md#connect-to-a-running-preform).

`PREFORM_URL` reads the PreForm address from `FL_LOCAL_BASE_URL`, with a default of port 44388:

```python
PREFORM_URL = os.environ.get("FL_LOCAL_BASE_URL", "http://localhost:44388")
```

`main` connects to PreForm at that address, and exits with a hint if nothing answers:

```python
with FormlabsClient() as client:
    try:
        preform = client.preform.connect(base_url=PREFORM_URL)
    except PreFormApiError as exc:
        sys.exit(
            f"{exc}\nNo PreForm at {PREFORM_URL}; open PreForm and start its API server"
        )
```

Set `FL_LOCAL_BASE_URL` to reach PreForm on another port or host.

## Create the scene

`apply_preset` creates a scene with the preset's printer, material, print setting, and layer
thickness:

```python
def apply_preset(
    preform: PreFormApi, preset: dict, models: list[Path], out: Path
) -> Scene:
    """Build a scene from the preset and models, save it as a .form, and return it."""
    scene = preform.create_scene(
        machine_type=preset["machine_type"],
        material_code=preset["material_code"],
        print_setting=preset["print_setting"],
        layer_thickness_mm=preset["layer_thickness_mm"],
    )
```

The four values must be a combination that `/v1/print-setting/list/` offers. For any other
combination, the server returns `INPUT_ERROR` with the message `Scene type not supported`, and the
script stops before it imports a model.

## Import the models

Each STL file becomes one model, named after the file:

```python
for model in models:
    # PreForm resolves paths from its own working directory.
    preform.import_models(
        scene_id=scene.scene_id,
        file=str(model.resolve()),
        name=model.stem,
        units=ImportUnits(preset["units"]),
        repair_behavior=RepairBehavior(preset["repair_behavior"]),
    )
```

The model name carries the case number from the filename, such as `case-1042_upper_arch`. The
presets import in millimeters, the unit that intraoral scanners and dental CAD software export.
`IGNORE` leaves each mesh as it is.

## Orient the models

The `DENTAL` orientation mode puts each arch occlusal side up, then tilts the occlusal plane by the
preset's angle:

```python
# DENTAL puts each arch occlusal side up, then tilts the occlusal plane by tilt degrees.
preform.auto_orient_models(
    scene_id=scene.scene_id, mode=AutoOrientMode.DENTAL, tilt=preset["tilt_deg"]
)
```

Models print flat at 0 degrees, and guards and guides print tilted at 15 degrees, as in PreForm.

## Generate supports

Guards and guides get supports and a raft:

```python
if supports := preset.get("supports"):
    preform.auto_support_models(
        scene_id=scene.scene_id,
        raft_type=RaftType(supports["raft_type"]),
        raft_label_enabled=supports["raft_label_enabled"],
        density=supports["density"],
        touchpoint_size_mm=supports["touchpoint_size_mm"],
        internal_supports_enabled=supports["internal_supports_enabled"],
    )
```

`FULL_RAFT` with `raft_label_enabled` embosses the model name on the raft, so after washing and
curing, a technician can match each appliance to its case by the number on the raft.

## Lay out and save the build

`auto_layout_models` places every model on the build platform, and `save_form_file` writes the job:

```python
preform.auto_layout_models(
    scene_id=scene.scene_id,
    mode=AutoLayoutMode(preset["layout_mode"]),
    model_spacing_mm=preset["model_spacing_mm"],
)
preform.save_form_file(
    file=str(out.resolve()), scene_ids=[scene.scene_id], project_name=out.stem
)
return preform.get_scene(scene_id=scene.scene_id)
```

The `DENTAL` layout mode uses the layout parameters of PreForm's dental workspace. The scene that
`get_scene` returns carries each model's `in_bounds` flag, and the script prints `NO` for any model
that doesn't fit on the build platform.

## Delete the scene

The `finally` block deletes the scene after the file is saved or a call fails:

```python
finally:
    preform.delete_scene(scene_id=scene.scene_id)
```

## Run the example

With pip, [install the Formlabs SDK](https://developer.formlabs.com/getting-started/install.md) first; with [uv](https://docs.astral.sh/uv/getting-started/installation/), `uv run` installs it from the example's `pyproject.toml`.

1. In a terminal, list the presets from the folder you unzipped:

   **pip:**

   ```bash
   python apply_preset.py --list
   ```

   **uv:**

   ```bash
   uv run apply_preset.py --list
   ```
2. Apply a preset to the models of one case:

   **pip:**

   ```bash
   python apply_preset.py orthodontic-models scans/case-1042_*.stl
   ```

   **uv:**

   ```bash
   uv run apply_preset.py orthodontic-models scans/case-1042_*.stl
   ```

The script reads `presets.toml` and the model files you name, and writes `<preset>.form` next to
the first model. Pass `--out` to choose another path and `--presets` to read another presets file.
The two commands print the presets and the build:

```
orthodontic-models   Fast Model V1, 0.16 mm, tilt 0, no supports
restorative-models   Precision Model V1, 0.05 mm, tilt 0, no supports
occlusal-guards      LT Comfort V1.1, 0.1 mm, tilt 15, supports
surgical-guides      Surgical Guide V1, 0.1 mm, tilt 15, supports

orthodontic-models: Fast Model V1 (FLFMGR01) at 0.16 mm, tilt 0 deg, supports off
  model                                 size x*y*z mm  supports  fits
  case-1042_lower_arch                 40.2x53.2x14.7        no   yes
  case-1042_upper_arch                 45.7x58.9x16.4        no   yes
  102 layers, 25.0 mL of resin
-> scans/orthodontic-models.form
```

## What's next

- [Print settings](https://developer.formlabs.com/core-concepts/print-settings.md)
- [Part labels](https://developer.formlabs.com/examples/part-labels.md)
