Toolpath¶
File Format (.crs)¶
Toolpath files define the laser scanning pattern. Each line is a waypoint:
time x y z laser_flag
0.00000000 0.00050000 0.00200000 0.00069750 0
0.00600000 0.00350000 0.00200000 0.00069750 1
0.00700000 0.00400000 0.00200000 0.00069750 0
| Column | Unit | Description |
|---|---|---|
time |
s | Absolute time of this waypoint |
x |
m | Beam x-position |
y |
m | Beam y-position |
z |
m | Beam z-position (typically top of powder layer) |
laser_flag |
- | 0 = laser off (repositioning), 1 = laser on (scanning) |
The solver interpolates linearly between waypoints to determine beam position and scan velocity at each time step.
Conventions:
- First line is always
0 0 0 0 0(initial state) - Second line moves the beam to the scan start position (laser off)
- Subsequent lines alternate between laser-on (scanning) and laser-off (repositioning) segments
- Scan speed is computed automatically from position change / time change between waypoints
Toolpath Generator¶
A Python script generates rectangular scan patterns:
cd code_base/ToolFiles
python3 toolpath_generator_rectangle.py \
--center_x 0.001 --center_y 0.001 --center_z 0.0006975 \
--size_x 0.001 --size_y 0.001 \
--scan_axis x --bidirectional \
--hatch_spacing 0.0001 --scan_speed 1.2 \
--turnaround_time 0.0005 \
--geo_rotation 0 --scan_rotation 30 \
--output center_geo0_scan30.crs
Geometry placement¶
| Argument | Unit | Description |
|---|---|---|
--start_x/y/z |
m | Starting (lower-left) corner of scan region |
--center_x/y/z |
m | Rectangle center; overrides --start_x/y/z |
--size_x/y |
m | Scan region dimensions |
Scan strategy¶
| Argument | Unit | Description |
|---|---|---|
--scan_axis |
- | Base scan direction (x or y) |
--bidirectional |
- | Alternate scan direction each track (default) |
--unidirectional |
- | Same scan direction each track |
--hatch_spacing |
m | Nominal distance between adjacent tracks; auto-rounded so the first and last tracks touch the rectangle edges |
--scan_speed |
m/s | Laser scan speed |
--turnaround_time |
s | Delay between tracks (laser off) |
Rotations (independent)¶
| Argument | Unit | Description |
|---|---|---|
--geo_rotation |
deg | CCW rotation of the rectangle outline about its center. Scan tracks do not follow this rotation — they remain aligned with the scan-frame defined by --scan_axis + --scan_rotation. |
--scan_rotation |
deg | CCW rotation of the scan-track direction in the global frame, applied on top of --scan_axis. 0 = tracks along the chosen base axis (legacy behaviour). |
--rotation_angle |
deg | DEPRECATED alias for --geo_rotation (kept for backward compatibility; emits a one-line note). |
The two rotations combine like this:
geo_rotation |
scan_rotation |
Effect |
|---|---|---|
| 0 | 0 | Axis-aligned rectangle, axis-aligned tracks (default). |
| θ | 0 | Tilted rectangle, axis-aligned tracks (legacy --rotation_angle θ). |
| 0 | θ | Axis-aligned rectangle, tracks tilted by θ in the global frame. |
| θ | θ | Rigid rotation: rectangle and tracks rotate together. |
| α | β (α ≠ β) | Rectangle at α, tracks at β — fully independent. |
Realistic LPBF strategies often rotate the scan direction between layers (e.g. 67° per layer) on a fixed part outline — that's the --geo_rotation 0 --scan_rotation 67·k pattern.
Plot bounds + simulation domain¶
| Argument | Unit | Description |
|---|---|---|
--input_param |
path | PHOENIX input_param.txt to read X/Y zone-length block from. Defaults to ../inputfile/input_param.txt relative to the script. |
--domain_x/y |
m | Override the auto-read X/Y domain extent for the PNG axis limits. |
--output |
- | Output .crs filename (.png is <output>.png). |
When --input_param resolves to an existing file, the generator reads the X-zone and Y-zone length sums and (a) sets the PNG axis limits to the full simulation domain and (b) outlines the domain as a dashed grey rectangle so the scan footprint is visible inside the simulation volume. Pass --input_param '' (empty) to fall back to autoscale.
Output: .crs file + .png visualization. Stdout reports the parsed domain, the actual hatch (after the round-to-fit adjustment), and the number of tracks.
Note
The .png visualization is only generated when using toolpath_generator_rectangle.py. Manually created .crs files do not have a .png.
Reference toolpaths in code_base/ToolFiles/¶
| File | Geometry | Rotations | Notes |
|---|---|---|---|
center_rot0.crs |
1×1 mm centered, hatch 0.1 mm | 0 / 0 | Baseline. |
center_rot45.crs |
1×1 mm centered, hatch 0.1 mm | 45° / 0 | Tilted rectangle, axis-aligned tracks. |
center_rot0_hatch50.crs |
1×1 mm centered, hatch 0.05 mm | 0 / 0 | Half hatch → 21 tracks, ~28 ms total. |
center_geo0_scan30.crs |
1×1 mm centered, hatch 0.1 mm | 0 / 30° | Axis-aligned rect, tracks tilted 30°. |
center_geo30_scan30.crs |
1×1 mm centered, hatch 0.1 mm | 30° / 30° | Rigid 30° rotation. |
Manual Toolpath Creation¶
For simple patterns (single track, custom shapes), create the .crs file directly:
Example: Single track at 0.5 m/s along x at y=2mm
0.00000000 0.00000000 0.00000000 0.00000000 0
0.00000000 0.00050000 0.00200000 0.00069750 0
0.00600000 0.00350000 0.00200000 0.00069750 1
0.00700000 0.00400000 0.00200000 0.00069750 0
- Line 1: Initial state (origin)
- Line 2: Move to start (x=0.5mm, y=2mm, z=0.6975mm), laser off
- Line 3: Scan to x=3.5mm in 6ms (speed = 3mm/6ms = 0.5 m/s), laser on
- Line 4: Overshoot/cool-down, laser off
Set toolpath_file in input_param.txt:
&output_control ... toolpath_file='./ToolFiles/species_test.crs' /