MichaelRKessler commited on
Commit
57b46fb
·
1 Parent(s): 71c658a

Committing previous changes to README and other files

Browse files
README.md CHANGED
@@ -1,16 +1,22 @@
1
  ---
2
- title: STL to TIFF Slicer
3
  sdk: gradio
4
  sdk_version: 6.10.0
5
  python_version: "3.12"
6
  app_file: app.py
7
  fullWidth: true
8
- short_description: Upload an STL and export a TIFF slice.
9
  ---
10
 
11
- # STL to TIFF Gradio App
12
 
13
- This project provides a Gradio app that takes up to three uploaded STL files, shows interactive 3D viewers, slices each model along the Z axis, saves slices as TIFF images, and lets you browse each stack inside the UI.
 
 
 
 
 
 
14
 
15
  ## Run
16
 
@@ -27,17 +33,22 @@ uv run gradio app.py
27
 
28
  When `app.py` changes, Gradio will automatically rerun the file and refresh the demo.
29
 
30
- Then open the local Gradio URL in your browser, upload an STL file, and generate the TIFF stack.
31
 
32
  ## What the app does
33
 
34
  - Uploads up to three `.stl` files
 
35
  - Shows interactive 3D viewers for rotating each model
 
36
  - Lets you choose layer height and XY pixel size
37
  - Produces one `.tif` image per slice
38
  - Encodes material as black (`0`) and empty space as white (`255`) in each TIFF slice
39
  - Lets you step through the slice stack in the browser
40
  - Exports a ZIP containing the generated TIFF images
 
 
 
41
 
42
  ## Behavior and Implementation Notes
43
 
@@ -55,6 +66,27 @@ When you click **Generate Reference TIFF Stack**, the app combines available TIF
55
 
56
  - G-code generation uses the slicer's `Pixel Size/Fill Width` for XY step distance by passing `fil_width=pixel_size` into `generate_snake_path_gcode()`.
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  ## Test
59
 
60
  ```powershell
 
1
  ---
2
+ title: STL to G-Code Slicer
3
  sdk: gradio
4
  sdk_version: 6.10.0
5
  python_version: "3.12"
6
  app_file: app.py
7
  fullWidth: true
8
+ short_description: Upload STLs, export TIFF stacks, and generate G-code.
9
  ---
10
 
11
+ # STL to G-Code Gradio App
12
 
13
+ This project provides a Gradio app that takes up to three uploaded STL files, shows interactive 3D viewers, slices each model along the Z axis, saves slices as TIFF images, generates G-code from those TIFF stacks, and lets you preview the resulting tool path.
14
+
15
+ ## Prerequisites
16
+
17
+ - Python 3.11 or newer for local development
18
+ - `uv` for dependency management and script execution
19
+ - Git LFS for the bundled `.stl` sample files
20
 
21
  ## Run
22
 
 
33
 
34
  When `app.py` changes, Gradio will automatically rerun the file and refresh the demo.
35
 
36
+ Then open the local Gradio URL in your browser, upload STL files or load the bundled samples, and generate the TIFF stacks.
37
 
38
  ## What the app does
39
 
40
  - Uploads up to three `.stl` files
41
+ - Loads bundled sample STL files
42
  - Shows interactive 3D viewers for rotating each model
43
+ - Shows model extents, face count, vertex count, and watertight status
44
  - Lets you choose layer height and XY pixel size
45
  - Produces one `.tif` image per slice
46
  - Encodes material as black (`0`) and empty space as white (`255`) in each TIFF slice
47
  - Lets you step through the slice stack in the browser
48
  - Exports a ZIP containing the generated TIFF images
49
+ - Combines generated stacks into a reference TIFF stack
50
+ - Converts generated TIFF ZIPs into G-code files with pressure, valve, and port settings per shape
51
+ - Visualizes generated or uploaded G-code tool paths
52
 
53
  ## Behavior and Implementation Notes
54
 
 
66
 
67
  - G-code generation uses the slicer's `Pixel Size/Fill Width` for XY step distance by passing `fil_width=pixel_size` into `generate_snake_path_gcode()`.
68
 
69
+ ### G-code Output
70
+
71
+ - Generated G-code starts in relative coordinate mode (`G91`).
72
+ - `G0` is travel and `G1` is print/feed.
73
+ - The app generates print/feed moves from material pixels and travel moves between material regions.
74
+ - Generated files include pressure preset commands and WAGO valve commands based on the selected pressure, valve, and port.
75
+ - Pressure increases by `0.1` psi per layer by default.
76
+
77
+ ### G-code Visualization
78
+
79
+ The G-code visualization tab can render either the generated Shape 1 G-code or an uploaded `.txt`, `.gcode`, or `.nc` file. It parses `G0` and `G1` movement lines, supports relative (`G91`) and absolute (`G90`) positioning, and displays travel and print segments with separate colors and opacity controls.
80
+
81
+ ## Dependency Updates
82
+
83
+ When dependencies change, update the lockfile and refresh the Hugging Face `requirements.txt` export:
84
+
85
+ ```powershell
86
+ uv sync --all-groups
87
+ uv export --format requirements.txt --no-hashes --no-dev --frozen --output-file requirements.txt
88
+ ```
89
+
90
  ## Test
91
 
92
  ```powershell
gcode_viewer.py CHANGED
@@ -70,7 +70,7 @@ def parse_gcode_path(gcode_text: str) -> dict:
70
  if dz_match is not None:
71
  z = dz
72
 
73
- kind = "travel" if gcmd == "G1" else "print"
74
 
75
  if kind != current_kind:
76
  flush_segment()
 
70
  if dz_match is not None:
71
  z = dz
72
 
73
+ kind = "print" if gcmd == "G1" else "travel"
74
 
75
  if kind != current_kind:
76
  flush_segment()
tests/test_gcode_viewer.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from gcode_viewer import parse_gcode_path
4
+
5
+
6
+ def test_parse_gcode_classifies_g0_as_travel_and_g1_as_print() -> None:
7
+ parsed = parse_gcode_path(
8
+ "\n".join(
9
+ [
10
+ "G91",
11
+ "G0 X1 Y0 ; travel",
12
+ "G1 X0 Y1 ; print",
13
+ ]
14
+ )
15
+ )
16
+
17
+ assert len(parsed["travel_segments"]) == 1
18
+ assert len(parsed["print_segments"]) == 1
tests/test_tiff_to_gcode.py CHANGED
@@ -34,3 +34,30 @@ def test_gcode_header_writes_presets_before_initial_aux_commands(tmp_path) -> No
34
  assert lines[2].startswith("{preset}serialPort3.write(")
35
  assert lines[3].startswith("{aux_command}WAGO_ValveCommands(")
36
  assert lines[4].startswith("{aux_command}WAGO_ValveCommands(")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  assert lines[2].startswith("{preset}serialPort3.write(")
35
  assert lines[3].startswith("{aux_command}WAGO_ValveCommands(")
36
  assert lines[4].startswith("{aux_command}WAGO_ValveCommands(")
37
+
38
+
39
+ def test_gcode_uses_g1_for_print_and_g0_for_travel(tmp_path) -> None:
40
+ tiff_path = tmp_path / "slice_0000.tif"
41
+ Image.new("L", (1, 1), 0).save(tiff_path)
42
+
43
+ zip_path = tmp_path / "slices.zip"
44
+ with zipfile.ZipFile(zip_path, mode="w") as archive:
45
+ archive.write(tiff_path, arcname=tiff_path.name)
46
+
47
+ gcode_path = generate_snake_path_gcode(
48
+ zip_path,
49
+ shape_name="move_types",
50
+ pressure=25,
51
+ valve=7,
52
+ port=3,
53
+ )
54
+
55
+ move_lines = [
56
+ line.strip()
57
+ for line in gcode_path.read_text().splitlines()
58
+ if line.startswith(("G0", "G1"))
59
+ ]
60
+
61
+ assert any(line.startswith("G1") and "; Color 255" in line for line in move_lines)
62
+ assert all(not line.startswith("G0") for line in move_lines if "; Color 255" in line)
63
+ assert all(not line.startswith("G1") for line in move_lines if "; Color 0" in line)
tiff_to_gcode.py CHANGED
@@ -306,7 +306,7 @@ def generate_snake_path_gcode(
306
  f.write(_valve_cmd(color_dict[cur_color], 1))
307
  f.write(_valve_cmd(color_dict[prev_color], 0))
308
 
309
- move_type = "G0" if cur_color != off_color else "G1"
310
  if "Z" in move:
311
  line = (
312
  f"{move_type} X{move['X']} Y{move['Y']} Z{move['Z']} "
 
306
  f.write(_valve_cmd(color_dict[cur_color], 1))
307
  f.write(_valve_cmd(color_dict[prev_color], 0))
308
 
309
+ move_type = "G1" if cur_color != off_color else "G0"
310
  if "Z" in move:
311
  line = (
312
  f"{move_type} X{move['X']} Y{move['Y']} Z{move['Z']} "