diff --git a/.claude/skills/orca-profiles/references/extruder-variants.md b/.claude/skills/orca-profiles/references/extruder-variants.md
index 322d79d56f..5059fd5c6b 100644
--- a/.claude/skills/orca-profiles/references/extruder-variants.md
+++ b/.claude/skills/orca-profiles/references/extruder-variants.md
@@ -189,8 +189,7 @@ the presets to a base is in [shared-bases.md](shared-bases.md#variant-arrays-on-
2. Every key in `filament_options_with_variant` ([the full list](#the-four-key-sets)) that reaches the
preset, whether written, included or inherited, is resized to its variant count: write it at exactly
that width, or leave it out. Keys outside the set
- (`filament_type`, plate temperatures, `fan_max_speed`, `slow_down_min_speed`, `pressure_advance`,
- …) are never addressed by variant index; the preset contributes their first value however wide a
+ (`filament_type`, plate temperatures, `fan_max_speed`, `slow_down_min_speed`, …) are never addressed by variant index; the preset contributes their first value however wide a
file writes them.
3. Cover every variant the material is meant to print on across its `compatible_printers`. Leave out
a variant deliberately when the material should not be tuned for it (e.g. a TPU High Flow variant
@@ -455,7 +454,7 @@ no set: the variant-length resize leaves it alone, and only the pair rebuild of
last for contrast; it is not a variant set.
`check` and `fix-variant` read the four sets from `PrintConfig.cpp` on every run, so they follow the
-engine. The lists below are from the 2026-09-25 checkout; regenerate them from the repository root
+engine. The lists below are from the 2026-09-29 checkout; regenerate them from the repository root
before relying on them (the recipe strips comments, since an initializer can carry a commented-out entry):
```bash
@@ -478,7 +477,7 @@ EOF
**`print_options_with_variant`**, process, stride 1 (45): `bridge_acceleration`, `bridge_speed`, `default_acceleration`, `default_jerk`, `default_junction_deviation`, `enable_overhang_speed`, `gap_infill_speed`, `infill_jerk`, `initial_layer_acceleration`, `initial_layer_infill_speed`, `initial_layer_jerk`, `initial_layer_speed`, `initial_layer_travel_acceleration`, `initial_layer_travel_jerk`, `initial_layer_travel_speed`, `inner_wall_acceleration`, `inner_wall_jerk`, `inner_wall_speed`, `internal_bridge_speed`, `internal_solid_infill_acceleration`, `internal_solid_infill_speed`, `outer_wall_acceleration`, `outer_wall_jerk`, `outer_wall_speed`, `overhang_1_4_speed`, `overhang_2_4_speed`, `overhang_3_4_speed`, `overhang_4_4_speed`, `print_extruder_id`, `print_extruder_variant`, `slowdown_for_curled_perimeters`, `small_perimeter_speed`, `small_perimeter_threshold`, `sparse_infill_acceleration`, `sparse_infill_speed`, `support_interface_speed`, `support_speed`, `top_solid_infill_flow_ratio`, `top_surface_acceleration`, `top_surface_jerk`, `top_surface_speed`, `travel_acceleration`, `travel_jerk`, `travel_speed`, `travel_speed_z`
-**`filament_options_with_variant`**, filament, stride 1 (48): `activate_air_filtration`, `activate_air_filtration_during_print`, `activate_air_filtration_on_completion`, `complete_print_exhaust_fan_speed`, `during_print_exhaust_fan_speed`, `filament_adaptive_volumetric_speed`, `filament_cooling_before_tower`, `filament_deretraction_speed`, `filament_extruder_variant`, `filament_flow_ratio`, `filament_flush_temp`, `filament_flush_volumetric_speed`, `filament_ironing_flow`, `filament_ironing_inset`, `filament_ironing_spacing`, `filament_ironing_speed`, `filament_long_retractions_when_cut`, `filament_max_volumetric_speed`, `filament_pre_cooling_temperature`, `filament_pre_cooling_temperature_nc`, `filament_preheat_temperature_delta`, `filament_ramming_travel_time`, `filament_ramming_travel_time_nc`, `filament_ramming_volumetric_speed`, `filament_ramming_volumetric_speed_nc`, `filament_retract_after_wipe`, `filament_retract_before_wipe`, `filament_retract_length_nc`, `filament_retract_length_toolchange`, `filament_retract_lift_above`, `filament_retract_lift_below`, `filament_retract_lift_enforce`, `filament_retract_restart_extra`, `filament_retract_restart_extra_toolchange`, `filament_retract_when_changing_layer`, `filament_retraction_distances_when_cut`, `filament_retraction_length`, `filament_retraction_minimum_travel`, `filament_retraction_speed`, `filament_wipe`, `filament_wipe_distance`, `filament_z_hop`, `filament_z_hop_types`, `long_retractions_when_ec`, `nozzle_temperature`, `nozzle_temperature_initial_layer`, `retraction_distances_when_ec`, `volumetric_speed_coefficients`
+**`filament_options_with_variant`**, filament, stride 1 (54): `activate_air_filtration`, `activate_air_filtration_during_print`, `activate_air_filtration_on_completion`, `adaptive_pressure_advance`, `adaptive_pressure_advance_bridges`, `adaptive_pressure_advance_model`, `adaptive_pressure_advance_overhangs`, `complete_print_exhaust_fan_speed`, `during_print_exhaust_fan_speed`, `enable_pressure_advance`, `filament_adaptive_volumetric_speed`, `filament_cooling_before_tower`, `filament_deretraction_speed`, `filament_extruder_variant`, `filament_flow_ratio`, `filament_flush_temp`, `filament_flush_volumetric_speed`, `filament_ironing_flow`, `filament_ironing_inset`, `filament_ironing_spacing`, `filament_ironing_speed`, `filament_long_retractions_when_cut`, `filament_max_volumetric_speed`, `filament_pre_cooling_temperature`, `filament_pre_cooling_temperature_nc`, `filament_preheat_temperature_delta`, `filament_ramming_travel_time`, `filament_ramming_travel_time_nc`, `filament_ramming_volumetric_speed`, `filament_ramming_volumetric_speed_nc`, `filament_retract_after_wipe`, `filament_retract_before_wipe`, `filament_retract_length_nc`, `filament_retract_length_toolchange`, `filament_retract_lift_above`, `filament_retract_lift_below`, `filament_retract_lift_enforce`, `filament_retract_restart_extra`, `filament_retract_restart_extra_toolchange`, `filament_retract_when_changing_layer`, `filament_retraction_distances_when_cut`, `filament_retraction_length`, `filament_retraction_minimum_travel`, `filament_retraction_speed`, `filament_wipe`, `filament_wipe_distance`, `filament_z_hop`, `filament_z_hop_types`, `long_retractions_when_ec`, `nozzle_temperature`, `nozzle_temperature_initial_layer`, `pressure_advance`, `retraction_distances_when_ec`, `volumetric_speed_coefficients`
**`printer_extruder_options`**, machine, one value per extruder, not a variant set (8):
`default_nozzle_volume_type`, `extruder_max_nozzle_count`, `extruder_printable_area`,
diff --git a/.claude/skills/orca-profiles/references/filament-profiles.md b/.claude/skills/orca-profiles/references/filament-profiles.md
index bc80176eaa..0ec5eca24e 100644
--- a/.claude/skills/orca-profiles/references/filament-profiles.md
+++ b/.claude/skills/orca-profiles/references/filament-profiles.md
@@ -244,10 +244,10 @@ preset for a 0.2 nozzle, explicitly revisit flow limits; do not infer a pressure
required direction of change, from diameter alone.
On a printer with extruder variants, a filament tunes these per variant too:
-`filament_max_volumetric_speed`, `filament_flow_ratio`, `nozzle_temperature` and the retraction
-overrides carry one value per variant of `filament_extruder_variant` (Standard, High Flow, …). The
+`filament_max_volumetric_speed`, `filament_flow_ratio`, `nozzle_temperature`, pressure advance and the
+retraction overrides carry one value per variant of `filament_extruder_variant` (Standard, High Flow, …). The
exact key set is [`filament_options_with_variant`](extruder-variants.md#the-four-key-sets);
-`slow_down_min_speed` and `pressure_advance` are not in it. Keep every such array at exactly that width, even where the
+`slow_down_min_speed` and `fan_max_speed` are not in it. Keep every such array at exactly that width, even where the
setting does not differ per variant, and measure the High Flow variant rather than copying Standard
([extruder-variants.md](extruder-variants.md#filament)).
diff --git a/.github/workflows/ofl-ota-cronjob.yml b/.github/workflows/ofl-ota-cronjob.yml
index 9d027190ae..501508eafa 100644
--- a/.github/workflows/ofl-ota-cronjob.yml
+++ b/.github/workflows/ofl-ota-cronjob.yml
@@ -146,7 +146,7 @@ jobs:
"repos/${{ github.repository }}/actions/workflows/post_merge_profiles.yml/runs" \
-f branch="$branch" -f event=workflow_dispatch -f per_page=100)"
run_id="$(jq -r --arg marker "[OFL cron $dispatch_id]" \
- '[.workflow_runs[] | select((.display_title // "") | contains($marker))] \
+ '[.workflow_runs[] | select((.display_title // "") | contains($marker))]
| sort_by(.created_at) | last | .id // empty' <<< "$runs_json")"
[ -n "$run_id" ] && break
sleep 5
diff --git a/docs/HLSD/preset-cache.md b/docs/HLSD/preset-cache.md
index 666e452f1e..aa39d522a0 100644
--- a/docs/HLSD/preset-cache.md
+++ b/docs/HLSD/preset-cache.md
@@ -28,8 +28,8 @@ Per-vendor granularity is what makes the system practical:
- A vendor whose profile is bumped invalidates only its own cache. The other 60-odd
vendors keep theirs — even when the bumped vendor is the shared Orca filament
library everyone else inherits from.
-- The setup wizard, which loads vendors one at a time, gets the same speedup as
- startup without a second code path.
+- The setup wizard loads its vendors through the same routine as startup, so it
+ gets the same speedup without a second code path.
- A vendor with no cache, or a broken one, costs only that vendor a parse.
A cache holds *system* presets only. User presets, project settings and modified
@@ -162,15 +162,20 @@ cache nothing can invalidate is worse than no cache.
Vendors load in a fixed order, because filament inheritance crosses exactly one
boundary: any vendor's filament may inherit from the shared Orca filament library,
-and nothing else reaches across vendors — an `include` is always vendor-local. The
-library therefore goes first, alone; every other vendor follows in parallel, resolving
-against it; and the results are merged in a stable order:
+and nothing else reaches across vendors — an `include` is always vendor-local. Only
+installing a vendor's presets crosses it; reading the vendor, from its cache or its
+JSONs, needs nothing from the library. So every other vendor is read while the library
+loads, each is installed against it as soon as both are done, and the results are
+merged in a stable order:
```mermaid
flowchart LR
- lib["1 · OrcaFilamentLibrary
loaded first, synchronously"] --> par["2 · every other vendor in parallel,
each into its own bundle, filaments
resolving against the loaded library"] --> merge["3 · bundles merged into one,
sequentially, in stable vendor order"]
+ lib["1 · OrcaFilamentLibrary loaded;
meanwhile every other vendor read
from its cache or its JSONs"] --> par["2 · every other vendor installed
in parallel, each into its own bundle,
filaments resolving against the library"] --> merge["3 · bundles merged into one,
in one pass per collection,
in stable vendor order"]
```
+`PresetBundle::load_vendors` runs these steps for startup and for the setup wizard,
+which hand it the vendors to load and the directory each is installed in.
+
Whether a vendor comes from its cache or from a parse changes nothing in that
order — both produce the same bundle, so cached and parsed vendors mix freely in
one startup.
@@ -211,11 +216,12 @@ shipped cache answered first, so the profile in `/system/` was never p
and its cache was never written back.
Serving from a cache is not a memory-image restore. The entries are deserialized and
-then installed one by one — inheritance resolved against the presets installed before
-them and the currently loaded filament library, includes layered in, configs flattened
-onto the collection defaults, validated and registered — by the same function the JSON
-path calls straight after parsing a sub-file. An `include` layers what the included
-base states, between the parent and the preset's own keys: the base's diff against the
+then installed by `install_vendor`, the routine the JSON path hands the vendor's entries
+to once it has parsed the sub-files: inheritance resolved against the presets installed
+before them and the currently loaded filament library, includes layered in, configs
+flattened onto the collection defaults, validated and registered. An `include` layers
+what the included base states, between the parent and the preset's own keys: the base's
+diff against the
default, taken when the base itself was installed and before the per-variant padding
`inherits` sees, so only what a template sets reaches the presets including it. The two
paths share everything below the parse, which is what makes a cache-loaded bundle
@@ -223,6 +229,16 @@ indistinguishable from a JSON-loaded one by construction rather than by test cov
Installation also rebuilds each preset's file path from the local data directory, so a
shipped cache never carries the generating machine's paths.
+Installing an entry is split in two. `resolve_vendor_preset` flattens it, reading only
+what is registered under the names it inherits and includes, and `commit_vendor_preset`
+registers it, the only step that writes anything shared. Entries resolve across threads
+in runs and commit in the order the vendor lists them. A run ends before an entry that
+inherits or includes one already in it, since that one's commit registers what the
+entry resolves against, so no entry in a run reads what another in it registers. An
+entry's parse messages are held until it commits. The bundle, the log's parse and
+install messages and the error count therefore come out as parsing and installing one
+entry at a time would leave them, whatever the listing order.
+
App upgrades work because a cache normally survives one. Only a deliberate
`CACHE_VERSION` bump makes an installed cache unreadable, and that is handled at
install time rather than at load: a vendor whose cache this build cannot read counts
@@ -254,17 +270,20 @@ the wizard caches the *derived JSON*, not another form of the inputs:
open, the wizard computes the current stamps (one version peek per vendor) and, when
they match, serves the catalog from the file — no bundle built, no preset installed.
Caching bundle inputs instead was tried and measured: rebuilding the bundle from
-per-vendor caches costs ~2 s of preset installation whatever feeds it, so only
-skipping the rebuild entirely wins.
+per-vendor caches costs over a second of preset installation whatever feeds it, so
+only skipping the rebuild entirely wins.
Any change to the set — a vendor added, removed or updated, or its cache-only
`.opc` replaced by a newer one — changes the stamps and retires the whole file;
-the wizard then rebuilds the bundle vendor by vendor (per-vendor caches serving where
-they cover) and writes the catalog back. Selections, region and per-open decorations
-are applied downstream of the cache either way, so a served catalog is
-indistinguishable from a rebuilt one. Nothing ships this file and the updater never
-touches it; it is a locally written artifact, re-derived whenever stale, written
-through a temp file and rename so half a cache is never readable.
+the wizard then rebuilds the bundle with `PresetBundle::load_vendors`, the load
+startup uses (per-vendor caches serving where they cover), and writes the catalog
+back. When a vendor fails to load, the filament library included, that open falls
+back to the wizard's own scan of the vendor JSONs, as when no bundle can be built,
+and writes nothing. Selections, region and per-open decorations are applied
+downstream of the cache either way, so a served catalog is indistinguishable from a
+rebuilt one. Nothing ships this file and the updater never touches it; it is a
+locally written artifact, re-derived whenever stale, written through a temp file and
+rename so half a cache is never readable.
The cache lives under `/cache/`, not beside the vendors: everything that
scans `/system/` treats any `.opc` there as a vendor, so a non-vendor
@@ -378,6 +397,13 @@ enumerates only `*.json` will find no vendors at all in a packaged build.
the `CachedPreset` field list — written and read by `visit_entry` in
`PresetCacheFormat.cpp`, one list for the save, the load and the name peek alike — or
the cache's own layout or stamps, requires bumping `CACHE_VERSION` by hand.
+- **Adding a kind of reference between presets**, as `inherits` and `include` are:
+ parse the names into `CachedPreset` (a field change, so `CACHE_VERSION` is bumped),
+ have `install_vendor_entries` end a run before an entry that names one already in it
+ and retain what the names point at, look them up only in `resolve_vendor_preset`, and
+ register what they point at only in `commit_vendor_preset`. The listing-order test in
+ `test_vendor_cache.cpp` fails for a kind the runs do not check once its fixture uses
+ it.
- **The dictionary indexes with a `uint16`**, so `print_config_def` may hold at most
65535 options and one cache at most 65535 distinct enum value names.
`CacheDictionary::save` throws past that, which surfaces when CI generates the
diff --git a/resources/profiles/OrcaFilamentLibrary.json b/resources/profiles/OrcaFilamentLibrary.json
index 084a12993f..ebf4b7b928 100644
--- a/resources/profiles/OrcaFilamentLibrary.json
+++ b/resources/profiles/OrcaFilamentLibrary.json
@@ -1,6 +1,6 @@
{
"name": "OrcaFilamentLibrary",
- "version": "02.04.00.15",
+ "version": "02.04.00.16",
"force_update": "0",
"description": "Orca Filament Library",
"filament_list": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth ABS rABS.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth ABS rABS.json
index 4d06956b11..b9ee6494c5 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth ABS rABS.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth ABS rABS.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura FDA.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura FDA.json
index 7bc7435c74..8532e514a3 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura FDA.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura FDA.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura.json
index b33951c7ba..c7690e1178 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA Adura.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA-CF Adura X.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA-CF Adura X.json
index 9f938e51e9..7dab608a6f 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA-CF Adura X.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA-CF Adura X.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA6 Addlantis.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA6 Addlantis.json
index 352bd80ac6..6e068507f1 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA6 Addlantis.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PA6 Addlantis.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PC BLend HT LCF.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PC BLend HT LCF.json
index 839c055ebf..de13a67d83 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PC BLend HT LCF.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PC BLend HT LCF.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Base.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Base.json
index f2e5824c14..b1120899d7 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Base.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Base.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG ESD.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG ESD.json
index 1074484973..cc7012c75d 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG ESD.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG ESD.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Economy.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Economy.json
index 3747f7a86f..48119c7b04 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Economy.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Economy.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Flame v0.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Flame v0.json
index 7dea674787..0650202cea 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Flame v0.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG Flame v0.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG PRO Matte.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG PRO Matte.json
index 8dc175d8e1..978db741a7 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG PRO Matte.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG PRO Matte.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"1"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG rPETG Matte.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG rPETG Matte.json
index 0454a13f5c..8514e62315 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG rPETG Matte.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG rPETG Matte.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG-CF Rigid X.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG-CF Rigid X.json
index 024faed63d..d2789e2d15 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG-CF Rigid X.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PETG-CF Rigid X.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"0"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA E-PLA.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA E-PLA.json
index 1f6892b815..b92f8c27b5 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA E-PLA.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA E-PLA.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Economy.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Economy.json
index ccacef16f4..de0cdb9f05 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Economy.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Economy.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA HT-PLA PRO Matte.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA HT-PLA PRO Matte.json
index aa466f3c3e..e8e2fca28a 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA HT-PLA PRO Matte.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA HT-PLA PRO Matte.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Premium Silk.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Premium Silk.json
index f794753c4a..f3cb324a87 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Premium Silk.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Premium Silk.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Textura.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Textura.json
index 73cfea81a4..9b49ebac26 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Textura.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Textura.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Wood.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Wood.json
index 5d64f416b7..e29534a5ad 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Wood.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA Wood.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"0"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA High Speed.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA High Speed.json
index d752b3adaf..98a98c4536 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA High Speed.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA High Speed.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA.json
index 2deaf8d323..def3487467 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA X-PLA.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA rPLA RE-ADD.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA rPLA RE-ADD.json
index 5ef8ccc6b6..ef614b39ae 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA rPLA RE-ADD.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PLA rPLA RE-ADD.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PVDF Adamant S1.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PVDF Adamant S1.json
index 608d67a5bd..e0a99b8b05 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PVDF Adamant S1.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth PVDF Adamant S1.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU EasyFlex.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU EasyFlex.json
index 25f2c310db..a1d9deab44 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU EasyFlex.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU EasyFlex.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 85A.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 85A.json
index a922a91ba1..e944d203a9 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 85A.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 85A.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 95A.json b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 95A.json
index 80c80328e8..d0e272d902 100644
--- a/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 95A.json
+++ b/resources/profiles/OrcaFilamentLibrary/filament/addnorth/BBL/addnorth TPU Pro Matte 95A.json
@@ -111,6 +111,7 @@
"1"
],
"enable_pressure_advance": [
+ "0",
"0"
],
"eng_plate_temp": [
@@ -316,6 +317,7 @@
"2"
],
"pressure_advance": [
+ "0.02",
"0.02"
],
"reduce_fan_stop_start_freq": [
diff --git a/resources/web/model/model.js b/resources/web/model/model.js
index b401bea75b..3d7c2fc8ad 100644
--- a/resources/web/model/model.js
+++ b/resources/web/model/model.js
@@ -214,9 +214,9 @@ function ShowModelInfo( pModel )
SendWXDebugInfo("Model Name: "+sModelName);
- $('#ModelName').html(sModelName);
+ $('#ModelName').text(sModelName);
$('#ModelName').attr('title',sModelName);
- $('#ModelAuthorName').html(sModelAuthor);
+ $('#ModelAuthorName').text(sModelAuthor);
switch(UploadType)
{
@@ -268,7 +268,7 @@ function ShowModelInfo( pModel )
break;
}
- $('#Model_Desc').html( html_decode(sModelDesc) );
+ $('#Model_Desc').empty().append( SanitizeDescHtml( html_decode(sModelDesc) ) );
let ModelPreviewList=pModel.preview_img;
let TotalPreview=ModelPreviewList.length;
@@ -281,16 +281,15 @@ function ShowModelInfo( pModel )
if(TotalPreview>0)
{
- let htmlPreview='';
+ $('#ModelPreviewList').empty();
for(let pn=0;pn
';
+ $('#ModelPreviewList').append( $('').append( $('
').attr('src',FTmpPath) ) );
}
- $('#ModelPreviewList').html(htmlPreview);
$('#Model_Preview_Image').viewer({
title: false,
fullsreen: false,
@@ -410,7 +409,8 @@ function ConstructFileHtml( ID, pItem )
{
let fTotal=pItem.length;
- let strHtml='';
+ let pBoard=$('#'+ID+' .FileListBoard');
+ pBoard.empty();
for( let f=0;f').attr('src',ImgPath);
+ let pMenu=$('');
if( strClass!='ImageIcon' )
{
- strHtml+=''+
- '
'+
- '
'+
- ' '+
- '
';
+ pMenu.on('click', function(){ OnClickOpenFile(tPath); });
}
else
{
ImgID++;
let TmpImgID="AF"+ImgID;
- strHtml+=''+
- '
'+
- '
'+
- ' '+
- '
';
+ pIconImg.attr('id',TmpImgID);
+ pMenu.on('click', function(){ OnClickOpenImage(TmpImgID); });
}
+
+ let pFileItem=$('');
+ pFileItem.append( $('').addClass(strClass).append(pIconImg) );
+ pFileItem.append( $('').append( $('').text(tName).attr('title',tName) ) );
+ pFileItem.append( pMenu );
+ pBoard.append( pFileItem );
}
- $('#'+ID+' .FileListBoard').html(strHtml);
-
if( fTotal>0 )
$('#'+ID).show();
}
+// Descriptions are untrusted 3MF metadata that may carry rich-text HTML (e.g. from MakerWorld).
+// Rebuild them from an inert parse, keeping only plain formatting tags and http(s) links and images.
+var DescAllowedTags=['P','BR','B','STRONG','I','EM','U','S','STRIKE','DEL','INS','SUB','SUP','SMALL','MARK',
+ 'Q','ABBR','KBD','WBR','H1','H2','H3','H4','H5','H6','UL','OL','LI','DL','DT','DD','BLOCKQUOTE','PRE','CODE',
+ 'HR','SPAN','DIV','FIGURE','FIGCAPTION','TABLE','CAPTION','THEAD','TBODY','TFOOT','TR','TH','TD','A','IMG'];
+// Plain attributes kept per tag; the numeric ones must be plain non-negative integers.
+var DescAllowedAttrs={'IMG':['alt','title','width','height'],'TD':['colspan','rowspan'],'TH':['colspan','rowspan'],'OL':['start']};
+var DescNumericAttrs=['width','height','colspan','rowspan','start'];
+// Dropped together with their content; any other unknown tag is unwrapped to its children.
+var DescDroppedTags=['SCRIPT','STYLE','TEMPLATE','NOSCRIPT','TEXTAREA','TITLE','IFRAME','FRAME','OBJECT','EMBED','SVG','MATH'];
+
+function IsHttpUrl( strUrl )
+{
+ // The scheme must be written as is, so nothing the URL parser would strip can precede or split it.
+ if( typeof strUrl!='string' || !/^https?:/i.test(strUrl) )
+ return false;
+ try
+ {
+ let sProtocol=new URL(strUrl).protocol;
+ return sProtocol=='http:' || sProtocol=='https:';
+ }
+ catch(e)
+ {
+ return false;
+ }
+}
+
+// Images load as soon as the page opens, so only https sources are kept: no plain-http requests to the local network.
+function IsHttpsUrl( strUrl )
+{
+ return IsHttpUrl(strUrl) && new URL(strUrl).protocol=='https:';
+}
+
+// Embedded YouTube players become a plain link to the video.
+function GetYouTubeEmbedUrl( pNode )
+{
+ let sSrc=pNode.getAttribute('src');
+ if( !IsHttpUrl(sSrc) )
+ return null;
+ let pUrl=new URL(sSrc);
+ return ( pUrl.origin=='https://www.youtube.com' && pUrl.pathname.indexOf('/embed/')==0 ) ? pUrl.href : null;
+}
+
+function CopyDescNodes( pSrc, pDst )
+{
+ for( let pNode=pSrc.firstChild;pNode!=null;pNode=pNode.nextSibling )
+ {
+ if( pNode.nodeType==Node.TEXT_NODE )
+ {
+ pDst.appendChild( document.createTextNode(pNode.nodeValue) );
+ continue;
+ }
+ if( pNode.nodeType!=Node.ELEMENT_NODE )
+ continue;
+
+ let sTag=pNode.nodeName.toUpperCase();
+ if( sTag=='IFRAME' )
+ {
+ let sVideoUrl=GetYouTubeEmbedUrl(pNode);
+ if( sVideoUrl!=null )
+ {
+ let pLink=document.createElement('A');
+ pLink.setAttribute('href',sVideoUrl);
+ pLink.textContent=sVideoUrl;
+ pDst.appendChild(pLink);
+ }
+ continue;
+ }
+ if( $.inArray(sTag,DescDroppedTags)>=0 )
+ continue;
+ if( $.inArray(sTag,DescAllowedTags)<0 )
+ {
+ CopyDescNodes(pNode,pDst);
+ continue;
+ }
+
+ let pElem=document.createElement(sTag);
+ if( sTag=='A' && IsHttpUrl(pNode.getAttribute('href')) )
+ pElem.setAttribute('href',pNode.getAttribute('href'));
+ else if( sTag=='IMG' )
+ {
+ if( !IsHttpsUrl(pNode.getAttribute('src')) )
+ continue;
+ pElem.setAttribute('src',pNode.getAttribute('src'));
+ }
+ $.each( DescAllowedAttrs[sTag]||[], function(i,sAttr){
+ let sValue=pNode.getAttribute(sAttr);
+ if( sValue!=null && ( $.inArray(sAttr,DescNumericAttrs)<0 || /^\d+$/.test(sValue) ) )
+ pElem.setAttribute(sAttr,sValue);
+ });
+ CopyDescNodes(pNode,pElem);
+ pDst.appendChild(pElem);
+ }
+}
+
+function SanitizeDescHtml( strHtml )
+{
+ let pFragment=document.createDocumentFragment();
+ // A DOMParser document is inert: it runs no scripts and loads no resources.
+ let pDoc=new DOMParser().parseFromString(strHtml,'text/html');
+ if( pDoc && pDoc.body )
+ CopyDescNodes(pDoc.body,pFragment);
+ return pFragment;
+}
+
function ShowProfilelInfo( pProfile )
{
//==========Profile Info==========
@@ -483,10 +583,10 @@ function ShowProfilelInfo( pProfile )
let sProfileAuthor=decodeURIComponent(pProfile.author);
let sProfileDesc=decodeURIComponent(pProfile.description);
- $('#ProfileName').html(sProfileName);
- $('#ProfileAuthor').html(sProfileAuthor);
+ $('#ProfileName').text(sProfileName);
+ $('#ProfileAuthor').text(sProfileAuthor);
- $('#Profile_Desc').html( html_decode(sProfileDesc) );
+ $('#Profile_Desc').empty().append( SanitizeDescHtml( html_decode(sProfileDesc) ) );
let ProfilePreviewList=pProfile.preview_img;
let TotalPreview=ProfilePreviewList.length;
@@ -499,15 +599,14 @@ function ShowProfilelInfo( pProfile )
if(TotalPreview>0)
{
- let htmlPreview='';
+ $('#ProfilePreviewList').empty();
for(let pn=0;pn
';
+ $('#ProfilePreviewList').append( $('').append( $('
').attr('src',FTmpPath) ) );
}
- $('#ProfilePreviewList').html(htmlPreview);
$('#Profile_Preview_Image').viewer({
title: false,
fullsreen: false,
diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp
index 33411e2f0d..1939988261 100644
--- a/src/OrcaSlicer.cpp
+++ b/src/OrcaSlicer.cpp
@@ -3476,6 +3476,14 @@ int CLI::run(int argc, char **argv)
}
new_variant_counts = old_variant_counts;
//filament_variant_count = old_variant_counts;
+ //ORCA: lay the per-variant options out one value per variant of the current filaments before each
+ // loaded filament replaces its own variants, including an option only a loaded filament
+ // defines, which otherwise starts as a single default value and never reaches the others.
+ for (const DynamicPrintConfig &config : load_filaments_config)
+ for (const std::string &opt_key : filament_options_with_variant)
+ if (opt_key != "filament_extruder_variant" && config.has(opt_key))
+ m_print_config.option(opt_key, true);
+ normalize_filament_values_to_variants(m_print_config);
for (int index = 0; index < load_filaments_config.size(); index++) {
DynamicPrintConfig& config = load_filaments_config[index];
int filament_index = load_filaments_index[index];
@@ -3647,6 +3655,25 @@ int CLI::run(int argc, char **argv)
}
}
+ //ORCA: a per-variant option the loaded filament does not define keeps the values of the
+ // variants the filament already had, and a variant new to it takes its first one's.
+ const int old_start = old_start_indice[filament_index - 1];
+ std::vector kept_variant_indice = new_variant_indice;
+ for (int &i : kept_variant_indice)
+ if (i < 0)
+ i = old_start;
+ for (const std::string &opt_key : filament_options_with_variant) {
+ if (config.has(opt_key))
+ continue;
+ auto *opt_vec_dst = dynamic_cast(m_print_config.option(opt_key));
+ if (opt_vec_dst == nullptr || opt_vec_dst->size() < size_t(old_start + old_variant_count))
+ continue;
+ // set_with_restore_2() pads its source in place
+ std::unique_ptr old_values(opt_vec_dst->clone());
+ opt_vec_dst->set_with_restore_2(static_cast(old_values.get()), kept_variant_indice, old_start,
+ old_variant_count, true);
+ }
+
//update the old index
if (old_variant_count != new_variant_count)
{
@@ -4081,6 +4108,9 @@ int CLI::run(int argc, char **argv)
if (printer_technology == ptFFF) {
fff_print_config.apply(m_print_config, true);
m_print_config.apply(fff_print_config, true);
+ //ORCA: an option no preset or project defines has just come in as its single default value, and a
+ // command line override may hold one value per filament.
+ normalize_filament_values_to_variants(m_print_config);
} else {
boost::nowide::cerr << "invalid printer_technology " << std::endl;
record_exit_reson(outfile_dir, CLI_INVALID_PRINTER_TECH, 0, cli_errors[CLI_INVALID_PRINTER_TECH], sliced_info);
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index cdcd9b4dec..d67b48c981 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -511,6 +511,10 @@ void AppConfig::set_defaults()
set_bool("enable_high_low_temp_mixed_printing", false);
}
+ if (get("remember_print_action").empty()) {
+ set_bool("remember_print_action", false);
+ }
+
if (get("ignore_ext_filament_in_filament_map").empty()){
set_bool("ignore_ext_filament_in_filament_map", false);
}
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index b27f58e699..1857412466 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -355,6 +355,7 @@ set(lisbslic3r_sources
Optimize/Optimizer.hpp
Orient.cpp
Orient.hpp
+ ParallelResolve.hpp
ParameterUtils.cpp
ParameterUtils.hpp
pchheader.cpp
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index 42d6970ad1..427f6e66c9 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -849,6 +849,19 @@ ConfigSubstitutions ConfigBase::load_from_json(const std::string &file, ForwardC
return std::move(substitutions_ctxt.substitutions);
}
+// Case-insensitive compare of a JSON key against a fixed ASCII one, without
+// boost::iequals, whose std::locale() takes a lock the whole process shares in the
+// MSVC runtime.
+static bool ascii_iequals(const std::string &key, const char *literal)
+{
+ auto lower = [](char c) { return (c >= 'A' && c <= 'Z') ? char(c - 'A' + 'a') : c; };
+ size_t i = 0;
+ for (; i < key.size() && literal[i] != '\0'; ++ i)
+ if (lower(key[i]) != lower(literal[i]))
+ return false;
+ return i == key.size() && literal[i] == '\0';
+}
+
int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContext& substitution_context, bool load_inherits_to_config, std::map& key_values, std::string& reason)
{
json j;
@@ -916,44 +929,44 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
}
//parse the json elements
for (auto it = j.begin(); it != j.end(); it++) {
- if (boost::iequals(it.key(),BBL_JSON_KEY_VERSION)) {
+ if (ascii_iequals(it.key(), BBL_JSON_KEY_VERSION)) {
key_values.emplace(BBL_JSON_KEY_VERSION, it.value());
}
- else if (boost::iequals(it.key(), BBL_JSON_KEY_IS_CUSTOM)) {
+ else if (ascii_iequals(it.key(), BBL_JSON_KEY_IS_CUSTOM)) {
//skip it
}
- else if (boost::iequals(it.key(), BBL_JSON_KEY_NAME)) {
+ else if (ascii_iequals(it.key(), BBL_JSON_KEY_NAME)) {
key_values.emplace(BBL_JSON_KEY_NAME, it.value());
if (it.value() == "project_settings")
is_project_settings = true;
}
- else if (boost::iequals(it.key(), BBL_JSON_KEY_URL)) {
+ else if (ascii_iequals(it.key(), BBL_JSON_KEY_URL)) {
key_values.emplace(BBL_JSON_KEY_URL, it.value());
}
- else if (boost::iequals(it.key(), BBL_JSON_KEY_TYPE)) {
+ else if (ascii_iequals(it.key(), BBL_JSON_KEY_TYPE)) {
key_values.emplace(BBL_JSON_KEY_TYPE, it.value());
}
- else if (boost::iequals(it.key(), BBL_JSON_KEY_SETTING_ID)) {
+ else if (ascii_iequals(it.key(), BBL_JSON_KEY_SETTING_ID)) {
key_values.emplace(BBL_JSON_KEY_SETTING_ID, it.value());
}
- else if (boost::iequals(it.key(), BBL_JSON_KEY_FILAMENT_ID)) {
+ else if (ascii_iequals(it.key(), BBL_JSON_KEY_FILAMENT_ID)) {
key_values.emplace(BBL_JSON_KEY_FILAMENT_ID, it.value());
}
- else if (boost::iequals(it.key(), BBL_JSON_KEY_FROM)) {
+ else if (ascii_iequals(it.key(), BBL_JSON_KEY_FROM)) {
key_values.emplace(BBL_JSON_KEY_FROM, it.value());
}
- else if (boost::iequals(it.key(), BBL_JSON_KEY_DESCRIPTION)) {
+ else if (ascii_iequals(it.key(), BBL_JSON_KEY_DESCRIPTION)) {
key_values.emplace(BBL_JSON_KEY_DESCRIPTION, it.value());
}
- else if (boost::iequals(it.key(), BBL_JSON_KEY_INSTANTIATION)) {
+ else if (ascii_iequals(it.key(), BBL_JSON_KEY_INSTANTIATION)) {
key_values.emplace(BBL_JSON_KEY_INSTANTIATION, it.value());
}
- else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INHERITS)) {
+ else if (!load_inherits_to_config && ascii_iequals(it.key(), BBL_JSON_KEY_INHERITS)) {
key_values.emplace(BBL_JSON_KEY_INHERITS, it.value());
}
- else if (!load_inherits_to_config && boost::iequals(it.key(), BBL_JSON_KEY_INCLUDES)) {
+ else if (!load_inherits_to_config && ascii_iequals(it.key(), BBL_JSON_KEY_INCLUDES)) {
key_values.emplace(BBL_JSON_KEY_INCLUDES, it.value().dump());
- } else if (boost::iequals(it.key(), ORCA_JSON_KEY_RENAMED_FROM)) {
+ } else if (ascii_iequals(it.key(), ORCA_JSON_KEY_RENAMED_FROM)) {
key_values.emplace(ORCA_JSON_KEY_RENAMED_FROM, it.value());
} else {
t_config_option_key opt_key = it.key();
diff --git a/src/libslic3r/Format/OBJ.cpp b/src/libslic3r/Format/OBJ.cpp
index de91efb64d..8242f2c0c9 100644
--- a/src/libslic3r/Format/OBJ.cpp
+++ b/src/libslic3r/Format/OBJ.cpp
@@ -141,7 +141,7 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
its.indices.emplace_back(indices[0], indices[1], indices[2]);
int face_index =its.indices.size() - 1;
RGBA face_color;
- auto set_face_color = [&uvs, &data, &mtl_data, &obj_info, &face_color](int face_index, const std::string mtl_name) {
+ auto set_face_color = [&uvs, &data, &mtl_data, &obj_info, &face_color](int face_index, const std::string mtl_name, const std::array &corners) {
if (mtl_data.new_mtl_unmap.find(mtl_name) != mtl_data.new_mtl_unmap.end()) {
bool is_merge_ka_kd = true;
for (size_t n = 0; n < 3; n++) {
@@ -174,7 +174,7 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
return Vec2f::Zero();
return Vec2f(data.textureCoordinates[idx * OBJ_TEXCOORD_LENGTH], data.textureCoordinates[idx * OBJ_TEXCOORD_LENGTH + 1]);
};
- std::array uv_array{uv_at(uvs[0]), uv_at(uvs[1]), uv_at(uvs[2])};
+ std::array uv_array{uv_at(uvs[corners[0]]), uv_at(uvs[corners[1]]), uv_at(uvs[corners[2]])};
obj_info.uvs.emplace_back(uv_array);
}
obj_info.face_colors.emplace_back(face_color);
@@ -185,27 +185,27 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
}
}
};
- auto set_face_color_by_mtl = [&data, &set_face_color](int face_index) {
+ auto set_face_color_by_mtl = [&data, &set_face_color](int face_index, const std::array &corners) {
if (data.usemtls.size() == 1) {
- set_face_color(face_index, data.usemtls[0].name);
+ set_face_color(face_index, data.usemtls[0].name, corners);
} else {
for (size_t k = 0; k < data.usemtls.size(); k++) {
auto mtl = data.usemtls[k];
if (face_index >= mtl.face_start && face_index <= mtl.face_end) {
- set_face_color(face_index, data.usemtls[k].name);
+ set_face_color(face_index, data.usemtls[k].name, corners);
break;
}
}
}
};
if (exist_mtl) {
- set_face_color_by_mtl(face_index);
+ set_face_color_by_mtl(face_index, {0, 1, 2});
}
if (cnt == 4) {
its.indices.emplace_back(indices[0], indices[2], indices[3]);
int face_index = its.indices.size() - 1;
if (exist_mtl) {
- set_face_color_by_mtl(face_index);
+ set_face_color_by_mtl(face_index, {0, 2, 3});
}
}
}
@@ -217,8 +217,12 @@ bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::s
message = _L("This OBJ file couldn't be read because it's empty.");
return false;
}
- if (meshptr->volume() < 0)
+ if (meshptr->volume() < 0) {
meshptr->flip_triangles();
+ // Flipping swaps corners 1 and 2 of every face, so the UVs have to follow.
+ for (std::array &uv : obj_info.uvs)
+ std::swap(uv[1], uv[2]);
+ }
// Hand the parsed material table back so callers can build a TexturedMesh from it.
if (out_mtl)
*out_mtl = mtl_data;
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 8bbeb05514..90cca3f084 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1497,12 +1497,7 @@ static std::vector get_path_of_change_filament(const Print& print)
check_add_eol(toolchange_gcode_str);
// SoftFever: set new PA for new filament
- if (gcodegen.config().enable_pressure_advance.get_at(new_filament_id)) {
- gcode += gcodegen.writer().set_pressure_advance(gcodegen.config().pressure_advance.get_at(new_filament_id));
- // Orca: Adaptive PA
- // Reset Adaptive PA processor last PA value
- gcodegen.m_pa_processor->resetPreviousPA(gcodegen.config().pressure_advance.get_at(new_filament_id));
- }
+ gcode += gcodegen.set_filament_pressure_advance(new_filament_id);
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy((end_pos + plate_origin_2d).cast());
@@ -1826,12 +1821,8 @@ static std::vector get_path_of_change_filament(const Print& print)
check_add_eol(toolchange_gcode_str);
// SoftFever: set new PA for new filament
- if (new_extruder_id != -1 && gcodegen.config().enable_pressure_advance.get_at(new_extruder_id)) {
- gcode += gcodegen.writer().set_pressure_advance(gcodegen.config().pressure_advance.get_at(new_extruder_id));
- // Orca: Adaptive PA
- // Reset Adaptive PA processor last PA value
- gcodegen.m_pa_processor->resetPreviousPA(gcodegen.config().pressure_advance.get_at(new_extruder_id));
- }
+ if (new_extruder_id != -1)
+ gcode += gcodegen.set_filament_pressure_advance(new_extruder_id);
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy((end_pos + plate_origin_2d).cast());
@@ -3232,7 +3223,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
m_cooling_buffer->set_current_extruder(initial_extruder_id, extruder_id);
// Orca: Initialise AdaptivePA processor filter
- m_pa_processor = std::make_unique(*this, tool_ordering.all_extruders());
+ m_pa_processor = std::make_unique(*this);
// Update output variables after the extruders were initialized.
m_placeholder_parser_integration.init(m_writer);
@@ -3797,12 +3788,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
}
}
// Orca: add missing PA settings for initial filament
- if (m_config.enable_pressure_advance.get_at(initial_non_support_extruder_id)) {
- file.write(m_writer.set_pressure_advance(m_config.pressure_advance.get_at(initial_non_support_extruder_id)));
- // Orca: Adaptive PA
- // Reset Adaptive PA processor last PA value
- m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(initial_non_support_extruder_id));
- }
+ file.write(set_filament_pressure_advance(initial_non_support_extruder_id));
}
//flush FanMover buffer to avoid modifying the start gcode if it's manual.
@@ -4188,6 +4174,28 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
print.throw_if_canceled();
+ // Some firmwares only scan the last N lines for the time estimate, so the stats are written
+ // after the config block.
+ if (!is_bbl_printers && !skip_config_block) {
+ file.write("; CONFIG_BLOCK_START\n");
+ std::string full_config;
+ append_full_config(print, full_config);
+ if (!full_config.empty())
+ file.write(full_config);
+
+ // SoftFever: write compatiple info
+ int first_layer_bed_temperature = get_bed_temperature(0, true, print.config().curr_bed_type);
+ file.write_format("; first_layer_bed_temperature = %d\n", first_layer_bed_temperature);
+ file.write_format("; bed_shape = %s\n", print.full_print_config().opt_serialize("printable_area").c_str());
+ file.write_format("; first_layer_temperature = %d\n", print.config().nozzle_temperature_initial_layer.get_at(0));
+ file.write_format("; first_layer_height = %.3f\n", print.config().initial_layer_print_height.value);
+
+ //SF TODO
+// file.write_format("; variable_layer_height = %d\n", print.ad.adaptive_layer_height ? 1 : 0);
+
+ file.write("; CONFIG_BLOCK_END\n\n");
+ }
+
// Get filament stats.
file.write(DoExport::update_print_stats_and_format_filament_stats(
// Const inputs
@@ -4214,26 +4222,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder)
.c_str());
file.write("\n");
- if (!skip_config_block) {
- file.write("; CONFIG_BLOCK_START\n");
- std::string full_config;
- append_full_config(print, full_config);
- if (!full_config.empty())
- file.write(full_config);
-
- // SoftFever: write compatiple info
- int first_layer_bed_temperature = get_bed_temperature(0, true, print.config().curr_bed_type);
- file.write_format("; first_layer_bed_temperature = %d\n", first_layer_bed_temperature);
- file.write_format("; bed_shape = %s\n", print.full_print_config().opt_serialize("printable_area").c_str());
- file.write_format("; first_layer_temperature = %d\n", print.config().nozzle_temperature_initial_layer.get_at(0));
- file.write_format("; first_layer_height = %.3f\n", print.config().initial_layer_print_height.value);
-
- //SF TODO
-// file.write_format("; variable_layer_height = %d\n", print.ad.adaptive_layer_height ? 1 : 0);
-
- file.write("; CONFIG_BLOCK_END\n\n");
- } // !skip_config_block
-
}
file.write("\n");
@@ -8390,12 +8378,16 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// Orca: Dynamic PA
// If adaptive PA is enabled, by default evaluate PA on all extrusion moves
+ // The PA settings and the PA_CHANGE tags use the filament config index of the filament's extruder variant
+ const unsigned int pa_config_index = (unsigned int) get_filament_config_index(m_writer.filament()->id());
+ const bool adaptive_pa = m_config.adaptive_pressure_advance.get_at(pa_config_index) && m_config.enable_pressure_advance.get_at(pa_config_index);
+ const bool adaptive_pa_overhangs = adaptive_pa && m_config.adaptive_pressure_advance_overhangs.get_at(pa_config_index);
bool is_pa_calib = m_curr_print->calib_mode() == CalibMode::Calib_PA_Line ||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Pattern ||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower;
bool evaluate_adaptive_pa = false;
bool role_change = (m_last_extrusion_role != path.role());
- if (!is_pa_calib && FILAMENT_CONFIG(adaptive_pressure_advance) && FILAMENT_CONFIG(enable_pressure_advance)) {
+ if (!is_pa_calib && adaptive_pa) {
evaluate_adaptive_pa = true;
// If we have already emmited a PA change because the m_multi_flow_segment_path_pa_set is set
// skip re-issuing the PA change tag.
@@ -8478,7 +8470,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// Post processor flag generation code segment when option to emit only at role changes is enabled
// Variables published to the post processor:
// 1) Tag to trigger a PA evaluation (because a role change was identified and the user has requested dynamic PA adjustments)
- // 2) Current extruder ID (to identify the PA model for the currently used extruder)
+ // 2) Current filament config index (to identify the PA model for the currently used filament and its extruder variant)
// 3) mm3_per_mm value (to then multiply by the final model print speed after slowdown for cooling is applied)
// 4) the current acceleration (to pass to the model for evaluation)
// 5) whether this is an external perimeter (for future use)
@@ -8489,7 +8481,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (m_multi_flow_segment_path_average_mm3_per_mm > 0) {
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
- m_writer.filament()->id(),
+ pa_config_index,
m_multi_flow_segment_path_average_mm3_per_mm,
acceleration_i,
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
@@ -8502,7 +8494,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// to issue a zero flow PA change command for this
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
- m_writer.filament()->id(),
+ pa_config_index,
_mm3_per_mm,
acceleration_i,
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
@@ -8610,11 +8602,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// or a flow change, so emit the flag to evaluate PA for the upcomming extrusion
// Emit tag before new speed is set so the post processor reads the next speed immediately and uses it.
// Dont emit tag if it has just already been emitted from a role change above
- if(_mm3_per_mm >0 &&
- FILAMENT_CONFIG(adaptive_pressure_advance) &&
- FILAMENT_CONFIG(enable_pressure_advance) &&
- FILAMENT_CONFIG(adaptive_pressure_advance_overhangs) &&
- !evaluate_adaptive_pa){
+ if(_mm3_per_mm >0 && adaptive_pa_overhangs && !evaluate_adaptive_pa){
if(writer().get_current_speed() > F){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion
if(m_config.gcode_comments){
sprintf(buf, "; Ramp down-non-variable\n");
@@ -8622,7 +8610,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
- m_writer.filament()->id(),
+ pa_config_index,
_mm3_per_mm,
acceleration_i,
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
@@ -8637,7 +8625,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
- m_writer.filament()->id(),
+ pa_config_index,
_mm3_per_mm,
acceleration_i,
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
@@ -8843,10 +8831,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// ORCA: Adaptive PA code segment when adjusting PA within the same feature
// There is a speed change or flow change so emit the flag to evaluate PA for the upcomming extrusion
// Emit tag before new speed is set so the post processor reads the next speed immediately and uses it.
- if(_mm3_per_mm >0 &&
- EXTRUDER_CONFIG(adaptive_pressure_advance) &&
- EXTRUDER_CONFIG(enable_pressure_advance) &&
- EXTRUDER_CONFIG(adaptive_pressure_advance_overhangs) ){
+ if(_mm3_per_mm >0 && adaptive_pa_overhangs){
if(last_set_speed > new_speed){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion
if(m_config.gcode_comments) {
sprintf(buf, "; Ramp up-variable\n");
@@ -8854,7 +8839,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
- m_writer.filament()->id(),
+ pa_config_index,
_mm3_per_mm,
acceleration_i,
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
@@ -8869,7 +8854,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(),
- m_writer.filament()->id(),
+ pa_config_index,
_mm3_per_mm,
acceleration_i,
((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
@@ -9422,33 +9407,29 @@ void GCode::update_placeholder_parser_with_variant_params()
if (num_filaments == 0)
return;
- // Helpers: remap config arrays from variant index space to filament_id index space.
+ // Helper: remap config arrays from variant index space to filament_id index space.
// After remapping, gcode templates can use param[filament_id] directly.
- auto remap_floats_by_filament = [&](const auto &src) {
- std::vector dst(num_filaments);
- for (size_t i = 0; i < num_filaments; ++i)
- dst[i] = src.get_at(get_filament_config_index(i));
- return dst;
- };
- auto remap_ints_by_filament = [&](const auto &src) {
- std::vector dst(num_filaments);
+ auto remap_by_filament = [&](const auto &src) {
+ std::decay_t dst(num_filaments);
for (size_t i = 0; i < num_filaments; ++i)
dst[i] = src.get_at(get_filament_config_index(i));
return dst;
};
// --- filament_options_with_variant: gcode indexes by filament_id ---
- this->placeholder_parser().set("filament_max_volumetric_speed", new ConfigOptionFloats(remap_floats_by_filament(m_config.filament_max_volumetric_speed)));
- this->placeholder_parser().set("filament_pre_cooling_temperature", new ConfigOptionInts(remap_ints_by_filament(m_config.filament_pre_cooling_temperature)));
- this->placeholder_parser().set("filament_pre_cooling_temperature_nc", new ConfigOptionInts(remap_ints_by_filament(m_config.filament_pre_cooling_temperature_nc)));
- this->placeholder_parser().set("filament_cooling_before_tower", new ConfigOptionFloats(remap_floats_by_filament(m_config.filament_cooling_before_tower)));
- this->placeholder_parser().set("nozzle_temperature_initial_layer", new ConfigOptionInts(remap_ints_by_filament(m_config.nozzle_temperature_initial_layer)));
- this->placeholder_parser().set("nozzle_temperature", new ConfigOptionInts(remap_ints_by_filament(m_config.nozzle_temperature)));
+ this->placeholder_parser().set("filament_max_volumetric_speed", new ConfigOptionFloats(remap_by_filament(m_config.filament_max_volumetric_speed)));
+ this->placeholder_parser().set("filament_pre_cooling_temperature", new ConfigOptionInts(remap_by_filament(m_config.filament_pre_cooling_temperature)));
+ this->placeholder_parser().set("filament_pre_cooling_temperature_nc", new ConfigOptionInts(remap_by_filament(m_config.filament_pre_cooling_temperature_nc)));
+ this->placeholder_parser().set("filament_cooling_before_tower", new ConfigOptionFloats(remap_by_filament(m_config.filament_cooling_before_tower)));
+ this->placeholder_parser().set("nozzle_temperature_initial_layer", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_initial_layer)));
+ this->placeholder_parser().set("nozzle_temperature", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature)));
// first_layer_temperature is a legacy alias of nozzle_temperature_initial_layer
- this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(remap_ints_by_filament(m_config.nozzle_temperature_initial_layer)));
+ this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_initial_layer)));
+ this->placeholder_parser().set("pressure_advance", new ConfigOptionFloats(remap_by_filament(m_config.pressure_advance)));
+ this->placeholder_parser().set("enable_pressure_advance", new ConfigOptionBools(remap_by_filament(m_config.enable_pressure_advance)));
// --- printer_options_with_variant_1: in m_config these are already merged as filament-indexed ---
- this->placeholder_parser().set("retraction_distances_when_cut", new ConfigOptionFloats(remap_floats_by_filament(m_config.retraction_distances_when_cut)));
+ this->placeholder_parser().set("retraction_distances_when_cut", new ConfigOptionFloats(remap_by_filament(m_config.retraction_distances_when_cut)));
// hotend_cooling_rate / hotend_heating_rate: gcode uses [filament_map[x]-1] (extruder_id), no remap needed
// --- filament_map: per-layer dynamic, sync from m_config to placeholder_parser ---
@@ -9458,10 +9439,10 @@ void GCode::update_placeholder_parser_with_variant_params()
{
// Fast purge mode uses filament_flush_temp_fast; Default is inert.
bool use_fast_flush = m_config.prime_volume_mode == PrimeVolumeMode::pvmFast;
- auto flush_v_speed = remap_floats_by_filament(m_config.filament_flush_volumetric_speed);
- auto filament_max_v = remap_floats_by_filament(m_config.filament_max_volumetric_speed);
- auto flush_temps = remap_ints_by_filament(use_fast_flush ? m_config.filament_flush_temp_fast
- : m_config.filament_flush_temp);
+ auto flush_v_speed = remap_by_filament(m_config.filament_flush_volumetric_speed);
+ auto filament_max_v = remap_by_filament(m_config.filament_max_volumetric_speed);
+ auto flush_temps = remap_by_filament(use_fast_flush ? m_config.filament_flush_temp_fast
+ : m_config.filament_flush_temp);
for (size_t i = 0; i < num_filaments; ++i) {
if (flush_v_speed[i] == 0)
flush_v_speed[i] = filament_max_v[i];
@@ -9513,12 +9494,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
gcode += this->placeholder_parser_process("filament_start_gcode", filament_start_gcode, new_filament_id, &config);
check_add_eol(gcode);
}
- if (m_config.enable_pressure_advance.get_at(new_filament_id)) {
- gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id));
- // Orca: Adaptive PA
- // Reset Adaptive PA processor last PA value
- m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(new_filament_id));
- }
+ gcode += set_filament_pressure_advance(new_filament_id);
gcode += m_writer.toolchange(new_filament_id, new_extruder_id);
if (Extruder *fil = m_writer.filament())
@@ -9909,18 +9885,24 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
if (m_ooze_prevention.enable && !defer_temp_wait)
gcode += m_ooze_prevention.post_toolchange(*this);
- if (m_config.enable_pressure_advance.get_at(new_filament_id)) {
- gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id));
- // Orca: Adaptive PA
- // Reset Adaptive PA processor last PA value
- m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(new_filament_id));
- }
+ gcode += set_filament_pressure_advance(new_filament_id);
//Orca: tool changer or IDEX's firmware may change Z position, so we set it to unknown/undefined
m_last_pos_defined = false;
return gcode;
}
+std::string GCode::set_filament_pressure_advance(unsigned int filament_id)
+{
+ const size_t fi = get_filament_config_index(filament_id);
+ if (!m_config.enable_pressure_advance.get_at(fi))
+ return {};
+ // Orca: Adaptive PA
+ // Reset Adaptive PA processor last PA value
+ m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(fi));
+ return m_writer.set_pressure_advance(m_config.pressure_advance.get_at(fi));
+}
+
inline std::string polygon_to_string(const Polygon &polygon, Print *print, bool is_print_space = false) {
std::ostringstream gcode;
gcode << "[";
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 41986cc1d6..bded77d9d1 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -281,6 +281,8 @@ public:
// extra_retract forwards a PETG pre-extrusion over-extrusion; default 0 -> identical to the plain deretract.
std::string unretract(float extra_retract = 0.f) { return m_writer.unlift() + m_writer.unretract(extra_retract); }
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override = -1, bool defer_temp_wait = false);
+ // Sets the pressure advance of the filament's extruder variant, if enabled for it.
+ std::string set_filament_pressure_advance(unsigned int filament_id);
bool is_BBL_Printer();
WipeTowerType wipe_tower_type();
diff --git a/src/libslic3r/GCode/AdaptivePAProcessor.cpp b/src/libslic3r/GCode/AdaptivePAProcessor.cpp
index 80d6b034ad..8145db3b9f 100644
--- a/src/libslic3r/GCode/AdaptivePAProcessor.cpp
+++ b/src/libslic3r/GCode/AdaptivePAProcessor.cpp
@@ -21,38 +21,30 @@ namespace Slic3r {
*
* @param gcodegen A reference to the GCode object that generates the G-code.
*/
-AdaptivePAProcessor::AdaptivePAProcessor(GCode &gcodegen, const std::vector &tools_used)
+AdaptivePAProcessor::AdaptivePAProcessor(GCode &gcodegen)
: m_gcodegen(gcodegen),
m_config(gcodegen.config()),
m_last_predicted_pa(0.0),
m_max_next_feedrate(0.0),
m_next_feedrate(0.0),
m_current_feedrate(0.0),
- m_last_extruder_id(-1),
+ m_last_config_index(-1),
m_pa_change_pattern(R"(; PA_CHANGE:T(\d+) MM3MM:([0-9]*\.[0-9]+) ACCEL:(\d+) BR:(\d+) RC:(\d+) OV:(\d+))"),
m_g1_f_pattern(R"(G1 F([0-9]+))")
{
- // Constructor body can be used for further initialization if necessary
- for (unsigned int tool : tools_used) {
- // Only enable model for the tool if both PA and adaptive PA options are enabled
- if(m_config.adaptive_pressure_advance.get_at(tool) && m_config.enable_pressure_advance.get_at(tool)){
- auto interpolator = std::make_unique();
- // Get calibration values from extruder
- std::string pa_calibration_values = m_config.adaptive_pressure_advance_model.get_at(tool);
- // Setup the model and store it in the tool-interpolation model map
- interpolator->parseAndSetData(pa_calibration_values);
- m_AdaptivePAInterpolators[tool] = std::move(interpolator);
- }
- }
}
-// Method to get the interpolator for a specific tool ID
-AdaptivePAInterpolator* AdaptivePAProcessor::getInterpolator(unsigned int tool_id) {
- auto it = m_AdaptivePAInterpolators.find(tool_id);
- if (it != m_AdaptivePAInterpolators.end()) {
- return it->second.get();
+// Method to get the interpolator for a specific filament config index.
+// The model is built the first time an index is requested, as the indices in use depend on
+// the extruder variant each filament prints with.
+AdaptivePAInterpolator* AdaptivePAProcessor::getInterpolator(unsigned int config_index) {
+ auto [it, inserted] = m_AdaptivePAInterpolators.try_emplace(config_index);
+ // Only enable model for the index if both PA and adaptive PA options are enabled
+ if (inserted && m_config.adaptive_pressure_advance.get_at(config_index) && m_config.enable_pressure_advance.get_at(config_index)) {
+ it->second = std::make_unique();
+ it->second->parseAndSetData(m_config.adaptive_pressure_advance_model.get_at(config_index));
}
- return nullptr; // Handle the case where the tool_id was not found
+ return it->second.get();
}
/**
@@ -108,16 +100,17 @@ std::string AdaptivePAProcessor::process_layer(std::string &&gcode) {
// the PA for that material is set. As no tag below will be found for this extruder, the original PA is retained.
if (line.find("; PA_CHANGE") == 0) { // prune lines quickly before running regex check as regex is more expensive to run
if (std::regex_search(line, m_match, m_pa_change_pattern)) {
- int extruder_id = std::stoi(m_match[1].str());
+ // The tag carries the filament config index, which selects the PA settings of the filament's extruder variant
+ int config_index = std::stoi(m_match[1].str());
mm3mm_value = std::stod(m_match[2].str());
accel_value = std::stod(m_match[3].str());
int isBridge = std::stoi(m_match[4].str());
int roleChange = std::stoi(m_match[5].str());
int isOverhang = std::stoi(m_match[6].str());
- // Check if the extruder ID has changed
- bool extruder_changed = (extruder_id != m_last_extruder_id);
- m_last_extruder_id = extruder_id;
+ // Check if the filament config index has changed
+ bool config_index_changed = (config_index != m_last_config_index);
+ m_last_config_index = config_index;
// Save the PA_CHANGE line to output later after finding feedrate
pa_change_line = line;
@@ -212,23 +205,23 @@ std::string AdaptivePAProcessor::process_layer(std::string &&gcode) {
// Calculate the predicted PA using the upcomming feature maximum feedrate
// Get the interpolator for the active tool
- AdaptivePAInterpolator* interpolator = getInterpolator(m_last_extruder_id);
+ AdaptivePAInterpolator* interpolator = getInterpolator(m_last_config_index);
double predicted_pa = 0;
double adaptive_PA_speed = 0;
if(!interpolator){ // Tool not found in the interpolator map
// Tool not found in the PA interpolator to tool map
- predicted_pa = m_config.enable_pressure_advance.get_at(m_last_extruder_id) ? m_config.pressure_advance.get_at(m_last_extruder_id) : 0;
+ predicted_pa = m_config.enable_pressure_advance.get_at(m_last_config_index) ? m_config.pressure_advance.get_at(m_last_config_index) : 0;
if(m_config.gcode_comments) output << "; APA: Tool doesnt have APA enabled\n";
- } else if (!interpolator->isInitialised() || (!m_config.adaptive_pressure_advance.get_at(m_last_extruder_id)) )
+ } else if (!interpolator->isInitialised() || (!m_config.adaptive_pressure_advance.get_at(m_last_config_index)) )
// Check if the model is not initialised by the constructor for the active extruder
// Also check that adaptive PA is enabled for that extruder. This should not be needed
// as the PA change flag should not be set upstream (in the GCode.cpp file) if adaptive PA is disabled
// however check for robustness sake.
{
// Model failed or adaptive pressure advance not enabled - use default value from m_config
- predicted_pa = m_config.enable_pressure_advance.get_at(m_last_extruder_id) ? m_config.pressure_advance.get_at(m_last_extruder_id) : 0;
+ predicted_pa = m_config.enable_pressure_advance.get_at(m_last_config_index) ? m_config.pressure_advance.get_at(m_last_config_index) : 0;
if(m_config.gcode_comments) output << "; APA: Interpolator setup failed, using default pressure advance\n";
} else { // Model setup succeeded
// Proceed to identify the print speed to use to calculate the adaptive PA value
@@ -249,18 +242,18 @@ std::string AdaptivePAProcessor::process_layer(std::string &&gcode) {
predicted_pa = (*interpolator)(mm3mm_value * adaptive_PA_speed, accel_value);
// This is a bridge, use the dedicated PA setting.
- if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_extruder_id) > EPSILON)
- predicted_pa = m_config.adaptive_pressure_advance_bridges.get_at(m_last_extruder_id);
+ if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_config_index) > EPSILON)
+ predicted_pa = m_config.adaptive_pressure_advance_bridges.get_at(m_last_config_index);
if (predicted_pa < 0) { // If extrapolation fails, fall back to the default PA for the extruder.
- predicted_pa = m_config.enable_pressure_advance.get_at(m_last_extruder_id) ? m_config.pressure_advance.get_at(m_last_extruder_id) : 0;
+ predicted_pa = m_config.enable_pressure_advance.get_at(m_last_config_index) ? m_config.pressure_advance.get_at(m_last_config_index) : 0;
if(m_config.gcode_comments) output << "; APA: Interpolation failed, using fallback pressure advance value\n";
}
}
if(m_config.gcode_comments) {
// Output debug GCode comments
output << pa_change_line << '\n'; // Output PA change command tag
- if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_extruder_id) > EPSILON)
+ if(isBridge && m_config.adaptive_pressure_advance_bridges.get_at(m_last_config_index) > EPSILON)
output << "; APA Model Override (bridge)\n";
output << "; APA Current Speed: " << std::to_string(m_current_feedrate) << "\n";
output << "; APA Next Speed: " << std::to_string(m_next_feedrate) << "\n";
@@ -269,7 +262,7 @@ std::string AdaptivePAProcessor::process_layer(std::string &&gcode) {
output << "; APA Flow rate: " << std::to_string(mm3mm_value * m_max_next_feedrate) << "\n";
output << "; APA Prev PA: " << std::to_string(m_last_predicted_pa) << " New PA: " << std::to_string(predicted_pa) << "\n";
}
- if (extruder_changed || std::fabs(predicted_pa - m_last_predicted_pa) > EPSILON) {
+ if (config_index_changed || std::fabs(predicted_pa - m_last_predicted_pa) > EPSILON) {
output << m_gcodegen.writer().set_pressure_advance(predicted_pa); // Use m_writer to set pressure advance
m_last_predicted_pa = predicted_pa; // Update the last predicted PA value
}
diff --git a/src/libslic3r/GCode/AdaptivePAProcessor.hpp b/src/libslic3r/GCode/AdaptivePAProcessor.hpp
index 33d4c26d24..01aa2f1104 100644
--- a/src/libslic3r/GCode/AdaptivePAProcessor.hpp
+++ b/src/libslic3r/GCode/AdaptivePAProcessor.hpp
@@ -11,7 +11,6 @@
#include
#include
#include