autoforge 2.1.1__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {autoforge-2.1.1 → autoforge-2.2.0}/PKG-INFO +4 -7
- {autoforge-2.1.1 → autoforge-2.2.0}/README.md +3 -6
- {autoforge-2.1.1 → autoforge-2.2.0}/pyproject.toml +2 -2
- {autoforge-2.1.1 → autoforge-2.2.0}/src/AutoForge.egg-info/PKG-INFO +4 -7
- {autoforge-2.1.1 → autoforge-2.2.0}/src/AutoForge.egg-info/SOURCES.txt +14 -1
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/AmpUtils.py +8 -3
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/DeterministicOps.py +5 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/DeviceUtils.py +19 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/FilamentHelper.py +71 -16
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/FusedComposite.py +166 -17
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/HeightAssign.py +6 -1
- autoforge-2.2.0/src/autoforge/Helper/HeightGuidance.py +617 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/HeightfieldMesh.py +13 -2
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/Heightmaps/ChristofidesHeightMap.py +5 -2
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/Heightmaps/DepthEstimateHeightMap.py +13 -10
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/Heightmaps/FastTSPHeightMap.py +44 -2
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/Heightmaps/_cluster.py +59 -20
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/ImageHelper.py +55 -3
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/OptimizerHelper.py +44 -5
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/OtherHelper.py +9 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/OutputHelper.py +76 -23
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/PixelHeightRefine.py +214 -44
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/PruningHelper.py +165 -144
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Init_comparer/auto_forge_init_check.py +1 -1
- autoforge-2.2.0/src/autoforge/Init_comparer/plot_results.py +124 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Modules/Optimizer.py +317 -120
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/auto_forge.py +196 -60
- autoforge-2.2.0/src/autoforge/default_materials.csv +15 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/priority_mask_editor.py +23 -15
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/filaments.py +4 -1
- autoforge-2.2.0/src/autoforge/webui/api/guidance.py +105 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/images.py +22 -3
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/init.py +50 -1
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/optimization.py +76 -8
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/outputs.py +69 -4
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/preview.py +58 -29
- autoforge-2.2.0/src/autoforge/webui/api/project.py +221 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/pruning.py +359 -87
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/sliders.py +24 -6
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/system.py +31 -4
- autoforge-2.2.0/src/autoforge/webui/api/workers.py +63 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/ws.py +44 -4
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/config.py +13 -1
- autoforge-2.2.0/src/autoforge/webui/helpers/auto_prune.py +186 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/helpers/colored_mesh.py +7 -7
- autoforge-2.2.0/src/autoforge/webui/helpers/edited_export.py +147 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/helpers/gpu_memory.py +13 -2
- autoforge-2.2.0/src/autoforge/webui/helpers/guidance_views.py +224 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/helpers/mesh_persist.py +5 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/helpers/pipeline_runner.py +188 -124
- autoforge-2.2.0/src/autoforge/webui/helpers/project_heightmap.py +134 -0
- autoforge-2.2.0/src/autoforge/webui/helpers/result_store.py +444 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/helpers/slider_render.py +41 -28
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/helpers/sliders.py +19 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/helpers/telemetry.py +20 -6
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/models.py +95 -7
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/server.py +94 -11
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/services/catalog_service.py +1 -1
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/services/filament_service.py +51 -12
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/services/image_service.py +15 -5
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/services/optimization_service.py +28 -2
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/services/project_service.py +19 -3
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_amp_utils.py +14 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_auto_forge_cli.py +1 -1
- autoforge-2.2.0/tests/test_bugreport_2026_10_01.py +635 -0
- autoforge-2.2.0/tests/test_bugreport_2026_10_03.py +418 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_device_utils.py +75 -14
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_fasttsp_heightmap.py +23 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_filamentcolors_library.py +5 -2
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_graph_cache_eviction.py +47 -0
- autoforge-2.2.0/tests/test_height_guidance.py +590 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_prune_repeatability.py +4 -2
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_remove_height_spikes.py +63 -0
- autoforge-2.2.0/tests/test_worktree_bugs_2026_10_01.py +376 -0
- autoforge-2.1.1/src/autoforge/Init_comparer/plot_results.py +0 -116
- autoforge-2.1.1/src/autoforge/webui/api/project.py +0 -43
- {autoforge-2.1.1 → autoforge-2.2.0}/setup.cfg +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/AutoForge.egg-info/dependency_links.txt +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/AutoForge.egg-info/entry_points.txt +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/AutoForge.egg-info/requires.txt +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/AutoForge.egg-info/top_level.txt +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/CAdamW.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/ConstraintHelper.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/Heightmaps/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Helper/filamentcolors_library.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Init_comparer/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Loss/LossFunctions.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Loss/PerceptionLoss.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Loss/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Misc/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Misc/color_fit_all.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/Modules/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/__main__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/data/filamentcolors_catalog.json +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/settings.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/api/state.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/helpers/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/src/autoforge/webui/services/__init__.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_bug_regressions.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_bugfixes_2026_09_26.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_cadamw.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_christofides_heightmap.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_composite_image.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_constrained_opt.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_depth_adjusted_height_map.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_depth_estimate_height_map.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_edge_bleed.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_end_to_end_optimizer.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_filament_helper.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_flatforge.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_graph_capture_threads.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_gumbel_nan.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_image_helper.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_loss_functions.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_optimizer_module.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_optimizer_training.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_other_helper.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_output_helper.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_perception_loss.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_pipeline_smoke.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_property_based.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_pruning_helper.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_webui_backend.py +0 -0
- {autoforge-2.1.1 → autoforge-2.2.0}/tests/test_webui_regressions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AutoForge
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: AutoForge - 3D color prints made easy
|
|
5
5
|
Author-email: Hendric Voss <hvoss@techfak.uni-bielefeld.de>
|
|
6
6
|
License: CC BY-NC-SA 4.0
|
|
@@ -93,6 +93,7 @@ The easiest way to use AutoForge is the web UI, a local app with drag-and-drop i
|
|
|
93
93
|
- Windows: double-click `run_webui.bat`
|
|
94
94
|
|
|
95
95
|
This starts the server and opens the web UI in your browser automatically (usually at `http://localhost:8000`).
|
|
96
|
+
It only listens on this computer by default, since the web UI has no login. To open it from other devices on your network, start it with `WEBUI_HOST=0.0.0.0 ./run_webui.sh` (Windows: `set WEBUI_HOST=0.0.0.0` before `run_webui.bat`). Open it by IP address or by this computer's name; any other name (for example one from a reverse proxy) has to be listed in `AUTOFORGE_WEBUI_ALLOWED_HOSTS` (comma separated).
|
|
96
97
|
|
|
97
98
|
To make some parts of the picture come out closer than the rest (a face, the eyes, lettering), click **Focus** in the image panel and paint over them; a slider sets how much more they count (2× to 100×, default 10×). After a run, the **Differences** view shows where the print strays furthest from the picture.
|
|
98
99
|
|
|
@@ -100,7 +101,7 @@ The easiest way to use AutoForge is the web UI, a local app with drag-and-drop i
|
|
|
100
101
|
|
|
101
102
|
To find a filament you own without measuring it yourself, click **Catalog** in the filament library. It searches every filament on [filamentcolors.xyz](https://filamentcolors.xyz) that has a measured TD (search by brand, color name, type or hex code; filter by type, brand or color family; or pick a color to see the closest matches first), and **Add** puts it straight into your library.
|
|
102
103
|
|
|
103
|
-
The web UI sends anonymous usage telemetry to the project via [PostHog](https://posthog.com/) by default, to notify me of problems and any bugs. This includes crash reports (unhandled errors from both the browser frontend and the backend server, with the error type, message, and stack trace) so bugs can get fixed faster. No image data, filament data
|
|
104
|
+
The web UI sends anonymous usage telemetry to the project via [PostHog](https://posthog.com/) by default, to notify me of problems and any bugs. This includes crash reports (unhandled errors from both the browser frontend and the backend server, with the error type, message, and stack trace) so bugs can get fixed faster. No image data, filament data or other personal information is sent, except that error messages and stack traces can contain file paths (the backend replaces your home folder in them with `~`, so your user name isn't sent from there; the browser's own stack traces only name the app's script files). To disable it, pass `--no-telemetry` (e.g. `./run_webui.sh --no-telemetry` / `run_webui.bat --no-telemetry`), or set `AUTOFORGE_WEBUI_TELEMETRY_ENABLED=false` permanently in your environment.
|
|
104
105
|
|
|
105
106
|
4. **Update to the latest release** whenever you want, from the project folder:
|
|
106
107
|
- Linux/macOS: `./update.sh`
|
|
@@ -193,7 +194,7 @@ This will generate separate STL files for each color, allowing you to print face
|
|
|
193
194
|
- `--processing_reduction_factor` Reduction factor for the processing size compared to the output size (default: 2 - half resolution).
|
|
194
195
|
- `--nozzle_diameter` Diameter of the printer nozzle in millimeters (default: 0.4).
|
|
195
196
|
**Note:** Details smaller than half this value will be ignored.
|
|
196
|
-
- `--early_stopping` Number of steps without improvement before stopping (default:
|
|
197
|
+
- `--early_stopping` Number of steps without improvement before stopping (default: 3000).
|
|
197
198
|
- `--priority_mask` *(Optional)* Path to a greyscale image the size of the input that marks the parts that matter most: white areas are matched more closely, black areas still count, just less.
|
|
198
199
|
- `--priority_mask_strength` How many times more a white pixel of `--priority_mask` counts than a black one (default: 10; must be at least 1). Raise it if the marked areas still come out off, lower it for a gentler nudge.
|
|
199
200
|
|
|
@@ -334,10 +335,6 @@ docker run --rm -v "$PWD:/work" -w /work autoforge-webui:cpu \
|
|
|
334
335
|
```
|
|
335
336
|
The container runs as root, so on Linux the output files belong to root; `sudo chown -R "$USER" output` makes them yours again.
|
|
336
337
|
|
|
337
|
-
## You can now run Autoforge for free in your browser thanks to [Huggingface space support](https://huggingface.co/spaces/hvoss-techfak/Autoforge).
|
|
338
|
-
This includes the option to run it locally if you have a powerful pc and don't want to limit yourself to the Huggingface computing limits. \
|
|
339
|
-
For this simply go to the [Huggingface](https://huggingface.co/spaces/hvoss-techfak/Autoforge) space and pull the docker container for this project (upper right corner -> three dots -> "run locally")
|
|
340
|
-
|
|
341
338
|
## Development
|
|
342
339
|
|
|
343
340
|
To have a "nightly" version of the repository or have live updating changes during development please do the following:
|
|
@@ -56,6 +56,7 @@ The easiest way to use AutoForge is the web UI, a local app with drag-and-drop i
|
|
|
56
56
|
- Windows: double-click `run_webui.bat`
|
|
57
57
|
|
|
58
58
|
This starts the server and opens the web UI in your browser automatically (usually at `http://localhost:8000`).
|
|
59
|
+
It only listens on this computer by default, since the web UI has no login. To open it from other devices on your network, start it with `WEBUI_HOST=0.0.0.0 ./run_webui.sh` (Windows: `set WEBUI_HOST=0.0.0.0` before `run_webui.bat`). Open it by IP address or by this computer's name; any other name (for example one from a reverse proxy) has to be listed in `AUTOFORGE_WEBUI_ALLOWED_HOSTS` (comma separated).
|
|
59
60
|
|
|
60
61
|
To make some parts of the picture come out closer than the rest (a face, the eyes, lettering), click **Focus** in the image panel and paint over them; a slider sets how much more they count (2× to 100×, default 10×). After a run, the **Differences** view shows where the print strays furthest from the picture.
|
|
61
62
|
|
|
@@ -63,7 +64,7 @@ The easiest way to use AutoForge is the web UI, a local app with drag-and-drop i
|
|
|
63
64
|
|
|
64
65
|
To find a filament you own without measuring it yourself, click **Catalog** in the filament library. It searches every filament on [filamentcolors.xyz](https://filamentcolors.xyz) that has a measured TD (search by brand, color name, type or hex code; filter by type, brand or color family; or pick a color to see the closest matches first), and **Add** puts it straight into your library.
|
|
65
66
|
|
|
66
|
-
The web UI sends anonymous usage telemetry to the project via [PostHog](https://posthog.com/) by default, to notify me of problems and any bugs. This includes crash reports (unhandled errors from both the browser frontend and the backend server, with the error type, message, and stack trace) so bugs can get fixed faster. No image data, filament data
|
|
67
|
+
The web UI sends anonymous usage telemetry to the project via [PostHog](https://posthog.com/) by default, to notify me of problems and any bugs. This includes crash reports (unhandled errors from both the browser frontend and the backend server, with the error type, message, and stack trace) so bugs can get fixed faster. No image data, filament data or other personal information is sent, except that error messages and stack traces can contain file paths (the backend replaces your home folder in them with `~`, so your user name isn't sent from there; the browser's own stack traces only name the app's script files). To disable it, pass `--no-telemetry` (e.g. `./run_webui.sh --no-telemetry` / `run_webui.bat --no-telemetry`), or set `AUTOFORGE_WEBUI_TELEMETRY_ENABLED=false` permanently in your environment.
|
|
67
68
|
|
|
68
69
|
4. **Update to the latest release** whenever you want, from the project folder:
|
|
69
70
|
- Linux/macOS: `./update.sh`
|
|
@@ -156,7 +157,7 @@ This will generate separate STL files for each color, allowing you to print face
|
|
|
156
157
|
- `--processing_reduction_factor` Reduction factor for the processing size compared to the output size (default: 2 - half resolution).
|
|
157
158
|
- `--nozzle_diameter` Diameter of the printer nozzle in millimeters (default: 0.4).
|
|
158
159
|
**Note:** Details smaller than half this value will be ignored.
|
|
159
|
-
- `--early_stopping` Number of steps without improvement before stopping (default:
|
|
160
|
+
- `--early_stopping` Number of steps without improvement before stopping (default: 3000).
|
|
160
161
|
- `--priority_mask` *(Optional)* Path to a greyscale image the size of the input that marks the parts that matter most: white areas are matched more closely, black areas still count, just less.
|
|
161
162
|
- `--priority_mask_strength` How many times more a white pixel of `--priority_mask` counts than a black one (default: 10; must be at least 1). Raise it if the marked areas still come out off, lower it for a gentler nudge.
|
|
162
163
|
|
|
@@ -297,10 +298,6 @@ docker run --rm -v "$PWD:/work" -w /work autoforge-webui:cpu \
|
|
|
297
298
|
```
|
|
298
299
|
The container runs as root, so on Linux the output files belong to root; `sudo chown -R "$USER" output` makes them yours again.
|
|
299
300
|
|
|
300
|
-
## You can now run Autoforge for free in your browser thanks to [Huggingface space support](https://huggingface.co/spaces/hvoss-techfak/Autoforge).
|
|
301
|
-
This includes the option to run it locally if you have a powerful pc and don't want to limit yourself to the Huggingface computing limits. \
|
|
302
|
-
For this simply go to the [Huggingface](https://huggingface.co/spaces/hvoss-techfak/Autoforge) space and pull the docker container for this project (upper right corner -> three dots -> "run locally")
|
|
303
|
-
|
|
304
301
|
## Development
|
|
305
302
|
|
|
306
303
|
To have a "nightly" version of the repository or have live updating changes during development please do the following:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "AutoForge"
|
|
7
|
-
version="2.
|
|
7
|
+
version="2.2.0"
|
|
8
8
|
description = "AutoForge - 3D color prints made easy"
|
|
9
9
|
authors = [{ name = "Hendric Voss", email = "hvoss@techfak.uni-bielefeld.de" }]
|
|
10
10
|
readme = "README.md"
|
|
@@ -51,7 +51,7 @@ test = [
|
|
|
51
51
|
package-dir = {"" = "src"}
|
|
52
52
|
|
|
53
53
|
[tool.setuptools.package-data]
|
|
54
|
-
autoforge = ["data/*.json"]
|
|
54
|
+
autoforge = ["data/*.json", "default_materials.csv"]
|
|
55
55
|
|
|
56
56
|
[project.scripts]
|
|
57
57
|
autoforge = "autoforge.auto_forge:main"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AutoForge
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: AutoForge - 3D color prints made easy
|
|
5
5
|
Author-email: Hendric Voss <hvoss@techfak.uni-bielefeld.de>
|
|
6
6
|
License: CC BY-NC-SA 4.0
|
|
@@ -93,6 +93,7 @@ The easiest way to use AutoForge is the web UI, a local app with drag-and-drop i
|
|
|
93
93
|
- Windows: double-click `run_webui.bat`
|
|
94
94
|
|
|
95
95
|
This starts the server and opens the web UI in your browser automatically (usually at `http://localhost:8000`).
|
|
96
|
+
It only listens on this computer by default, since the web UI has no login. To open it from other devices on your network, start it with `WEBUI_HOST=0.0.0.0 ./run_webui.sh` (Windows: `set WEBUI_HOST=0.0.0.0` before `run_webui.bat`). Open it by IP address or by this computer's name; any other name (for example one from a reverse proxy) has to be listed in `AUTOFORGE_WEBUI_ALLOWED_HOSTS` (comma separated).
|
|
96
97
|
|
|
97
98
|
To make some parts of the picture come out closer than the rest (a face, the eyes, lettering), click **Focus** in the image panel and paint over them; a slider sets how much more they count (2× to 100×, default 10×). After a run, the **Differences** view shows where the print strays furthest from the picture.
|
|
98
99
|
|
|
@@ -100,7 +101,7 @@ The easiest way to use AutoForge is the web UI, a local app with drag-and-drop i
|
|
|
100
101
|
|
|
101
102
|
To find a filament you own without measuring it yourself, click **Catalog** in the filament library. It searches every filament on [filamentcolors.xyz](https://filamentcolors.xyz) that has a measured TD (search by brand, color name, type or hex code; filter by type, brand or color family; or pick a color to see the closest matches first), and **Add** puts it straight into your library.
|
|
102
103
|
|
|
103
|
-
The web UI sends anonymous usage telemetry to the project via [PostHog](https://posthog.com/) by default, to notify me of problems and any bugs. This includes crash reports (unhandled errors from both the browser frontend and the backend server, with the error type, message, and stack trace) so bugs can get fixed faster. No image data, filament data
|
|
104
|
+
The web UI sends anonymous usage telemetry to the project via [PostHog](https://posthog.com/) by default, to notify me of problems and any bugs. This includes crash reports (unhandled errors from both the browser frontend and the backend server, with the error type, message, and stack trace) so bugs can get fixed faster. No image data, filament data or other personal information is sent, except that error messages and stack traces can contain file paths (the backend replaces your home folder in them with `~`, so your user name isn't sent from there; the browser's own stack traces only name the app's script files). To disable it, pass `--no-telemetry` (e.g. `./run_webui.sh --no-telemetry` / `run_webui.bat --no-telemetry`), or set `AUTOFORGE_WEBUI_TELEMETRY_ENABLED=false` permanently in your environment.
|
|
104
105
|
|
|
105
106
|
4. **Update to the latest release** whenever you want, from the project folder:
|
|
106
107
|
- Linux/macOS: `./update.sh`
|
|
@@ -193,7 +194,7 @@ This will generate separate STL files for each color, allowing you to print face
|
|
|
193
194
|
- `--processing_reduction_factor` Reduction factor for the processing size compared to the output size (default: 2 - half resolution).
|
|
194
195
|
- `--nozzle_diameter` Diameter of the printer nozzle in millimeters (default: 0.4).
|
|
195
196
|
**Note:** Details smaller than half this value will be ignored.
|
|
196
|
-
- `--early_stopping` Number of steps without improvement before stopping (default:
|
|
197
|
+
- `--early_stopping` Number of steps without improvement before stopping (default: 3000).
|
|
197
198
|
- `--priority_mask` *(Optional)* Path to a greyscale image the size of the input that marks the parts that matter most: white areas are matched more closely, black areas still count, just less.
|
|
198
199
|
- `--priority_mask_strength` How many times more a white pixel of `--priority_mask` counts than a black one (default: 10; must be at least 1). Raise it if the marked areas still come out off, lower it for a gentler nudge.
|
|
199
200
|
|
|
@@ -334,10 +335,6 @@ docker run --rm -v "$PWD:/work" -w /work autoforge-webui:cpu \
|
|
|
334
335
|
```
|
|
335
336
|
The container runs as root, so on Linux the output files belong to root; `sudo chown -R "$USER" output` makes them yours again.
|
|
336
337
|
|
|
337
|
-
## You can now run Autoforge for free in your browser thanks to [Huggingface space support](https://huggingface.co/spaces/hvoss-techfak/Autoforge).
|
|
338
|
-
This includes the option to run it locally if you have a powerful pc and don't want to limit yourself to the Huggingface computing limits. \
|
|
339
|
-
For this simply go to the [Huggingface](https://huggingface.co/spaces/hvoss-techfak/Autoforge) space and pull the docker container for this project (upper right corner -> three dots -> "run locally")
|
|
340
|
-
|
|
341
338
|
## Development
|
|
342
339
|
|
|
343
340
|
To have a "nightly" version of the repository or have live updating changes during development please do the following:
|
|
@@ -9,6 +9,7 @@ src/AutoForge.egg-info/top_level.txt
|
|
|
9
9
|
src/autoforge/__init__.py
|
|
10
10
|
src/autoforge/__main__.py
|
|
11
11
|
src/autoforge/auto_forge.py
|
|
12
|
+
src/autoforge/default_materials.csv
|
|
12
13
|
src/autoforge/priority_mask_editor.py
|
|
13
14
|
src/autoforge/Helper/AmpUtils.py
|
|
14
15
|
src/autoforge/Helper/CAdamW.py
|
|
@@ -18,6 +19,7 @@ src/autoforge/Helper/DeviceUtils.py
|
|
|
18
19
|
src/autoforge/Helper/FilamentHelper.py
|
|
19
20
|
src/autoforge/Helper/FusedComposite.py
|
|
20
21
|
src/autoforge/Helper/HeightAssign.py
|
|
22
|
+
src/autoforge/Helper/HeightGuidance.py
|
|
21
23
|
src/autoforge/Helper/HeightfieldMesh.py
|
|
22
24
|
src/autoforge/Helper/ImageHelper.py
|
|
23
25
|
src/autoforge/Helper/OptimizerHelper.py
|
|
@@ -49,6 +51,7 @@ src/autoforge/webui/models.py
|
|
|
49
51
|
src/autoforge/webui/server.py
|
|
50
52
|
src/autoforge/webui/api/__init__.py
|
|
51
53
|
src/autoforge/webui/api/filaments.py
|
|
54
|
+
src/autoforge/webui/api/guidance.py
|
|
52
55
|
src/autoforge/webui/api/images.py
|
|
53
56
|
src/autoforge/webui/api/init.py
|
|
54
57
|
src/autoforge/webui/api/optimization.py
|
|
@@ -60,12 +63,18 @@ src/autoforge/webui/api/settings.py
|
|
|
60
63
|
src/autoforge/webui/api/sliders.py
|
|
61
64
|
src/autoforge/webui/api/state.py
|
|
62
65
|
src/autoforge/webui/api/system.py
|
|
66
|
+
src/autoforge/webui/api/workers.py
|
|
63
67
|
src/autoforge/webui/api/ws.py
|
|
64
68
|
src/autoforge/webui/helpers/__init__.py
|
|
69
|
+
src/autoforge/webui/helpers/auto_prune.py
|
|
65
70
|
src/autoforge/webui/helpers/colored_mesh.py
|
|
71
|
+
src/autoforge/webui/helpers/edited_export.py
|
|
66
72
|
src/autoforge/webui/helpers/gpu_memory.py
|
|
73
|
+
src/autoforge/webui/helpers/guidance_views.py
|
|
67
74
|
src/autoforge/webui/helpers/mesh_persist.py
|
|
68
75
|
src/autoforge/webui/helpers/pipeline_runner.py
|
|
76
|
+
src/autoforge/webui/helpers/project_heightmap.py
|
|
77
|
+
src/autoforge/webui/helpers/result_store.py
|
|
69
78
|
src/autoforge/webui/helpers/slider_render.py
|
|
70
79
|
src/autoforge/webui/helpers/sliders.py
|
|
71
80
|
src/autoforge/webui/helpers/telemetry.py
|
|
@@ -79,6 +88,8 @@ tests/test_amp_utils.py
|
|
|
79
88
|
tests/test_auto_forge_cli.py
|
|
80
89
|
tests/test_bug_regressions.py
|
|
81
90
|
tests/test_bugfixes_2026_09_26.py
|
|
91
|
+
tests/test_bugreport_2026_10_01.py
|
|
92
|
+
tests/test_bugreport_2026_10_03.py
|
|
82
93
|
tests/test_cadamw.py
|
|
83
94
|
tests/test_christofides_heightmap.py
|
|
84
95
|
tests/test_composite_image.py
|
|
@@ -95,6 +106,7 @@ tests/test_flatforge.py
|
|
|
95
106
|
tests/test_graph_cache_eviction.py
|
|
96
107
|
tests/test_graph_capture_threads.py
|
|
97
108
|
tests/test_gumbel_nan.py
|
|
109
|
+
tests/test_height_guidance.py
|
|
98
110
|
tests/test_image_helper.py
|
|
99
111
|
tests/test_loss_functions.py
|
|
100
112
|
tests/test_optimizer_module.py
|
|
@@ -108,4 +120,5 @@ tests/test_prune_repeatability.py
|
|
|
108
120
|
tests/test_pruning_helper.py
|
|
109
121
|
tests/test_remove_height_spikes.py
|
|
110
122
|
tests/test_webui_backend.py
|
|
111
|
-
tests/test_webui_regressions.py
|
|
123
|
+
tests/test_webui_regressions.py
|
|
124
|
+
tests/test_worktree_bugs_2026_10_01.py
|
|
@@ -30,9 +30,14 @@ def _probe_backward(device: torch.device, dtype: torch.dtype) -> bool:
|
|
|
30
30
|
here.
|
|
31
31
|
"""
|
|
32
32
|
try:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
# Fixed inputs, not torch.randn: the probe runs once per process, and
|
|
34
|
+
# drawing from the device's generator there shifted the random
|
|
35
|
+
# stream of only the first run - the same seed gave a different
|
|
36
|
+
# result in the first webui job than in every later one.
|
|
37
|
+
base = torch.linspace(-1.0, 1.0, 64, device=device, dtype=torch.float32).view(8, 8)
|
|
38
|
+
x = base.clone()
|
|
39
|
+
w = base.t().contiguous().requires_grad_(True)
|
|
40
|
+
target = base.flip(0)
|
|
36
41
|
with torch.autocast(device_type=device.type, dtype=dtype):
|
|
37
42
|
y = x @ w
|
|
38
43
|
loss = torch.nn.functional.mse_loss(y, target)
|
|
@@ -40,6 +40,11 @@ def gather_plan(labels: torch.Tensor, n: int):
|
|
|
40
40
|
|
|
41
41
|
def segment_gather(values: torch.Tensor, labels: torch.Tensor, plan) -> torch.Tensor:
|
|
42
42
|
"""``values[labels]`` with a deterministic backward (see gather_plan)."""
|
|
43
|
+
from autoforge.Helper import DeviceUtils
|
|
44
|
+
|
|
45
|
+
if not DeviceUtils.has_float64(values.device):
|
|
46
|
+
# Its backward sums in float64, which MPS doesn't have.
|
|
47
|
+
return values[labels]
|
|
43
48
|
return _SegmentGather.apply(values, labels, *plan)
|
|
44
49
|
|
|
45
50
|
|
|
@@ -74,6 +74,12 @@ def backend_of(device: torch.device) -> str:
|
|
|
74
74
|
return device.type
|
|
75
75
|
|
|
76
76
|
|
|
77
|
+
def has_float64(device: torch.device) -> bool:
|
|
78
|
+
"""False on MPS, which has no float64: creating or casting to a float64
|
|
79
|
+
tensor there raises, so float64 work has to stay on the host."""
|
|
80
|
+
return device.type != "mps"
|
|
81
|
+
|
|
82
|
+
|
|
77
83
|
def is_accelerator(device: torch.device) -> bool:
|
|
78
84
|
return device.type in ("cuda", "mps")
|
|
79
85
|
|
|
@@ -133,6 +139,19 @@ def resolve_device(spec: Optional[str] = None, args=None) -> torch.device:
|
|
|
133
139
|
return torch.device("cpu")
|
|
134
140
|
|
|
135
141
|
|
|
142
|
+
def activate_device(device: Optional[torch.device]) -> None:
|
|
143
|
+
"""Make ``device`` the current CUDA device of the calling thread.
|
|
144
|
+
|
|
145
|
+
Triton kernels and CUDA graph capture run on the *current* device, not
|
|
146
|
+
on the device of their tensors, so ``--device cuda:1`` without this
|
|
147
|
+
launched the fused kernels on GPU 0 with pointers into GPU 1. The
|
|
148
|
+
current device is per thread: every thread that runs GPU work for a
|
|
149
|
+
run (the webui's job, pruning and render threads) has to call this."""
|
|
150
|
+
# A bare "cuda" means the current device already; set_device needs an index.
|
|
151
|
+
if device is not None and device.type == "cuda" and device.index is not None and cuda_is_available():
|
|
152
|
+
torch.cuda.set_device(device)
|
|
153
|
+
|
|
154
|
+
|
|
136
155
|
def _device_is_usable(device: torch.device) -> bool:
|
|
137
156
|
if device.type == "cuda":
|
|
138
157
|
if not cuda_is_available():
|
|
@@ -23,12 +23,14 @@ def load_materials(args):
|
|
|
23
23
|
- colors_list (list): List of color hex strings.
|
|
24
24
|
"""
|
|
25
25
|
df = load_materials_pandas(args)
|
|
26
|
+
_check_material_table(df)
|
|
27
|
+
# An empty CSV cell is NaN in pandas; str() turned it into "nan".
|
|
26
28
|
material_names = [
|
|
27
|
-
|
|
29
|
+
" - ".join(part for part in (_text(brand), _text(name)) if part)
|
|
28
30
|
for brand, name in zip(df["Brand"].tolist(), df["Name"].tolist())
|
|
29
31
|
]
|
|
30
32
|
material_TDs = (df["Transmissivity"].astype(float)).to_numpy()
|
|
31
|
-
colors_list = df["Color"].tolist()
|
|
33
|
+
colors_list = [normalize_hex(color) for color in df["Color"].tolist()]
|
|
32
34
|
# Use float64 for material colors.
|
|
33
35
|
material_colors = np.array(
|
|
34
36
|
[hex_to_rgb(color) for color in colors_list], dtype=np.float64
|
|
@@ -36,6 +38,34 @@ def load_materials(args):
|
|
|
36
38
|
material_TDs = np.array(material_TDs, dtype=np.float64)
|
|
37
39
|
return material_colors, material_TDs, material_names, colors_list
|
|
38
40
|
|
|
41
|
+
def _check_material_table(df) -> None:
|
|
42
|
+
"""Exit with a message naming the problem instead of failing later: a
|
|
43
|
+
missing TD became NaN and turned the whole loss into NaN, and an empty
|
|
44
|
+
or malformed colour only failed once the run had started."""
|
|
45
|
+
problems = []
|
|
46
|
+
for column in ("Brand", "Name", "Color", "Transmissivity"):
|
|
47
|
+
if column not in df.columns:
|
|
48
|
+
problems.append(f"missing column '{column}'")
|
|
49
|
+
if not problems:
|
|
50
|
+
for row, (color, td) in enumerate(zip(df["Color"].tolist(), df["Transmissivity"].tolist()), start=1):
|
|
51
|
+
label = f"material {row} ({_text(df['Brand'].iloc[row - 1])} {_text(df['Name'].iloc[row - 1])})".strip()
|
|
52
|
+
try:
|
|
53
|
+
normalize_hex(color)
|
|
54
|
+
except ValueError:
|
|
55
|
+
problems.append(f"{label}: color {color!r} is not a hex color like #RRGGBB")
|
|
56
|
+
try:
|
|
57
|
+
td_value = float(td)
|
|
58
|
+
except (TypeError, ValueError):
|
|
59
|
+
td_value = float("nan")
|
|
60
|
+
if not np.isfinite(td_value) or td_value <= 0:
|
|
61
|
+
problems.append(f"{label}: transmissivity (TD) {td!r} must be a positive number")
|
|
62
|
+
if len(df) == 0:
|
|
63
|
+
problems.append("no materials")
|
|
64
|
+
if problems:
|
|
65
|
+
print("Error: invalid material file:\n " + "\n ".join(problems), file=sys.stderr)
|
|
66
|
+
sys.exit(1)
|
|
67
|
+
|
|
68
|
+
|
|
39
69
|
def load_materials_pandas(args):
|
|
40
70
|
csv_filename = args.csv_file
|
|
41
71
|
json_filename = args.json_file
|
|
@@ -47,16 +77,15 @@ def load_materials_pandas(args):
|
|
|
47
77
|
traceback.print_exc()
|
|
48
78
|
print("Error reading filament CSV file:", e)
|
|
49
79
|
sys.exit(1)
|
|
50
|
-
# rename all columns that start with a whitespace
|
|
51
|
-
df.columns = [col.strip() for col in df.columns]
|
|
52
|
-
# if TD in columns rename to Transmissivity
|
|
53
|
-
if "TD" in df.columns:
|
|
54
|
-
df.rename(columns={"TD": "Transmissivity"}, inplace=True)
|
|
55
80
|
else:
|
|
56
81
|
# read json
|
|
57
|
-
with open(json_filename, "r") as f:
|
|
82
|
+
with open(json_filename, "r", encoding="utf-8-sig") as f:
|
|
58
83
|
data = json.load(f)
|
|
59
|
-
if
|
|
84
|
+
if isinstance(data, list):
|
|
85
|
+
# A bare list of filaments (our own export) works as well as
|
|
86
|
+
# HueForge's {"Filaments": [...]}.
|
|
87
|
+
pass
|
|
88
|
+
elif isinstance(data, dict) and "Filaments" in data:
|
|
60
89
|
data = data["Filaments"]
|
|
61
90
|
else:
|
|
62
91
|
print(
|
|
@@ -65,6 +94,11 @@ def load_materials_pandas(args):
|
|
|
65
94
|
sys.exit(1)
|
|
66
95
|
# list to dataframe
|
|
67
96
|
df = pd.DataFrame(data)
|
|
97
|
+
# Same column spellings for both sources: a JSON library using "TD" (or
|
|
98
|
+
# padded keys) used to fail with KeyError 'Transmissivity'.
|
|
99
|
+
df.columns = [str(col).strip() for col in df.columns]
|
|
100
|
+
if "TD" in df.columns and "Transmissivity" not in df.columns:
|
|
101
|
+
df.rename(columns={"TD": "Transmissivity"}, inplace=True)
|
|
68
102
|
return df
|
|
69
103
|
|
|
70
104
|
def load_materials_data(args):
|
|
@@ -84,17 +118,38 @@ def load_materials_data(args):
|
|
|
84
118
|
return records
|
|
85
119
|
|
|
86
120
|
|
|
121
|
+
def _text(value) -> str:
|
|
122
|
+
"""A table cell as text; missing / NaN cells are empty."""
|
|
123
|
+
if value is None or (isinstance(value, float) and np.isnan(value)):
|
|
124
|
+
return ""
|
|
125
|
+
return str(value).strip()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def normalize_hex(hex_str) -> str:
|
|
129
|
+
"""``#RRGGBB`` for the hex spellings libraries use (``RRGGBB``, ``#RGB``,
|
|
130
|
+
``#RRGGBBAA``); ValueError for anything else."""
|
|
131
|
+
text = str(hex_str).strip().lstrip("#")
|
|
132
|
+
if len(text) == 3:
|
|
133
|
+
text = "".join(c * 2 for c in text)
|
|
134
|
+
elif len(text) == 8:
|
|
135
|
+
text = text[:6]
|
|
136
|
+
if len(text) != 6 or any(c not in "0123456789abcdefABCDEF" for c in text):
|
|
137
|
+
raise ValueError(f"{hex_str!r} is not a hex color like #RRGGBB")
|
|
138
|
+
return "#" + text.upper()
|
|
139
|
+
|
|
140
|
+
|
|
87
141
|
def hex_to_rgb(hex_str):
|
|
88
142
|
"""
|
|
89
143
|
Convert a hex color string to a normalized RGB list.
|
|
90
144
|
|
|
91
145
|
Args:
|
|
92
|
-
hex_str (str): The hex color string (
|
|
146
|
+
hex_str (str): The hex color string ('#RRGGBB'; '#RGB', '#RRGGBBAA'
|
|
147
|
+
and a missing '#' are accepted too).
|
|
93
148
|
|
|
94
149
|
Returns:
|
|
95
150
|
list: A list of three floats representing the RGB values normalized to [0, 1].
|
|
96
151
|
"""
|
|
97
|
-
hex_str = hex_str.lstrip("#")
|
|
152
|
+
hex_str = normalize_hex(hex_str).lstrip("#")
|
|
98
153
|
return [int(hex_str[i : i + 2], 16) / 255.0 for i in (0, 2, 4)]
|
|
99
154
|
|
|
100
155
|
|
|
@@ -113,11 +168,11 @@ def extract_colors_from_swatches(swatch_data):
|
|
|
113
168
|
|
|
114
169
|
# convert to the same format as the hueforge csv files
|
|
115
170
|
material_names = [str(brand) + " - " + str(name) for (brand, name) in out.keys()]
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)
|
|
171
|
+
# normalize_hex: the swatch's hex may or may not carry the "#" ("#" +
|
|
172
|
+
# "#ff0000" failed), and colors_list is "#RRGGBB" like load_materials'.
|
|
173
|
+
colors_list = [normalize_hex(color) for color, _ in out.values()]
|
|
174
|
+
material_colors = np.array([hex_to_rgb(color) for color in colors_list], dtype=np.float64)
|
|
119
175
|
material_TDs = np.array([td for _, td in out.values()], dtype=np.float64)
|
|
120
|
-
colors_list = [color for color, _ in out.values()]
|
|
121
176
|
|
|
122
177
|
return material_colors, material_TDs, material_names, colors_list
|
|
123
178
|
|
|
@@ -140,7 +195,7 @@ def swatch_data_to_table(swatch_data):
|
|
|
140
195
|
"Brand": brand,
|
|
141
196
|
"Name": name,
|
|
142
197
|
"TD": td,
|
|
143
|
-
"HexColor":
|
|
198
|
+
"HexColor": "#" + str(hex_color).lstrip("#"),
|
|
144
199
|
"Uuid": str(uuid.uuid4()),
|
|
145
200
|
}
|
|
146
201
|
)
|