diff --git a/README.md b/README.md
index 408efb57b7..cf1b084e4c 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,8 @@ If you come across any of these in search results, please report them as
Supports a broad range of printers: Bambu Lab, Prusa, Creality, Voron, and more.
- **[Belt Printer Support](https://www.orcaslicer.com/wiki/belt_printing)**
Slice for belt / conveyor (infinite-Z) printers, with belt-aware supports and a tilted-bed preview. Contributed by [Joseph Robertson (@HarrierPigeon)](https://github.com/HarrierPigeon).
+- **[IDEX/IQEX Parallel Printing Support](https://www.orcaslicer.com/wiki/idex_iqex_parallel_printing)**
+ Print copies or mirror images of a part on every carriage of an IDEX or IQEX printer at once, with the mode chosen per plate and nozzle clearance zones shown on the bed. Contributed by [Clifford (@cgarwood82)](https://github.com/cgarwood82).
- Additional features can be found in the [change notes](https://github.com/OrcaSlicer/OrcaSlicer/releases/).
# Wiki
diff --git a/localization/i18n/list.txt b/localization/i18n/list.txt
index b1ebb54f5e..1d41680deb 100644
--- a/localization/i18n/list.txt
+++ b/localization/i18n/list.txt
@@ -164,6 +164,7 @@ src/slic3r/GUI/OptionsGroup.cpp
src/slic3r/GUI/PrintOptionsDialog.cpp
src/slic3r/GUI/SafetyOptionsDialog.cpp
src/slic3r/GUI/ParamsPanel.cpp
+src/slic3r/GUI/IMEXModesCtrl.cpp
src/slic3r/GUI/PartPlate.cpp
src/slic3r/GUI/Plater.cpp
src/slic3r/GUI/Preferences.cpp
@@ -212,6 +213,7 @@ src/libslic3r/ExtrusionEntity.cpp
src/libslic3r/Flow.cpp
src/libslic3r/Format/AMF.cpp
src/libslic3r/miniz_extension.cpp
+src/libslic3r/IMEXHelpers.cpp
src/libslic3r/Preset.cpp
src/libslic3r/Print.cpp
src/libslic3r/PrintBase.cpp
diff --git a/resources/images/plate_imex_copy_mode.svg b/resources/images/plate_imex_copy_mode.svg
new file mode 100644
index 0000000000..b938e909a1
--- /dev/null
+++ b/resources/images/plate_imex_copy_mode.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_copy_mode_dark.svg b/resources/images/plate_imex_copy_mode_dark.svg
new file mode 100644
index 0000000000..c87b0e0ffe
--- /dev/null
+++ b/resources/images/plate_imex_copy_mode_dark.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_copy_mode_hover.svg b/resources/images/plate_imex_copy_mode_hover.svg
new file mode 100644
index 0000000000..4238552426
--- /dev/null
+++ b/resources/images/plate_imex_copy_mode_hover.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_copy_mode_hover_dark.svg b/resources/images/plate_imex_copy_mode_hover_dark.svg
new file mode 100644
index 0000000000..4633b747fe
--- /dev/null
+++ b/resources/images/plate_imex_copy_mode_hover_dark.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_custom_mode.svg b/resources/images/plate_imex_custom_mode.svg
new file mode 100644
index 0000000000..99bc534382
--- /dev/null
+++ b/resources/images/plate_imex_custom_mode.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_custom_mode_dark.svg b/resources/images/plate_imex_custom_mode_dark.svg
new file mode 100644
index 0000000000..d31da3bb3a
--- /dev/null
+++ b/resources/images/plate_imex_custom_mode_dark.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_custom_mode_hover.svg b/resources/images/plate_imex_custom_mode_hover.svg
new file mode 100644
index 0000000000..c0cc65a2a4
--- /dev/null
+++ b/resources/images/plate_imex_custom_mode_hover.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_custom_mode_hover_dark.svg b/resources/images/plate_imex_custom_mode_hover_dark.svg
new file mode 100644
index 0000000000..4b5682dbb2
--- /dev/null
+++ b/resources/images/plate_imex_custom_mode_hover_dark.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_mirror_mode.svg b/resources/images/plate_imex_mirror_mode.svg
new file mode 100644
index 0000000000..92f57755b3
--- /dev/null
+++ b/resources/images/plate_imex_mirror_mode.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_mirror_mode_dark.svg b/resources/images/plate_imex_mirror_mode_dark.svg
new file mode 100644
index 0000000000..b0ab1ee257
--- /dev/null
+++ b/resources/images/plate_imex_mirror_mode_dark.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_mirror_mode_hover.svg b/resources/images/plate_imex_mirror_mode_hover.svg
new file mode 100644
index 0000000000..1772d2c7fb
--- /dev/null
+++ b/resources/images/plate_imex_mirror_mode_hover.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_mirror_mode_hover_dark.svg b/resources/images/plate_imex_mirror_mode_hover_dark.svg
new file mode 100644
index 0000000000..d50131ed4b
--- /dev/null
+++ b/resources/images/plate_imex_mirror_mode_hover_dark.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_normal_mode.svg b/resources/images/plate_imex_normal_mode.svg
new file mode 100644
index 0000000000..2d2324236f
--- /dev/null
+++ b/resources/images/plate_imex_normal_mode.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_normal_mode_dark.svg b/resources/images/plate_imex_normal_mode_dark.svg
new file mode 100644
index 0000000000..86d4b2ec0e
--- /dev/null
+++ b/resources/images/plate_imex_normal_mode_dark.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_normal_mode_hover.svg b/resources/images/plate_imex_normal_mode_hover.svg
new file mode 100644
index 0000000000..3cdfb350fa
--- /dev/null
+++ b/resources/images/plate_imex_normal_mode_hover.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/resources/images/plate_imex_normal_mode_hover_dark.svg b/resources/images/plate_imex_normal_mode_hover_dark.svg
new file mode 100644
index 0000000000..70b26ed81b
--- /dev/null
+++ b/resources/images/plate_imex_normal_mode_hover_dark.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/resources/profiles/Vivedino.json b/resources/profiles/Vivedino.json
index c8f6b65c3e..11b384aabb 100644
--- a/resources/profiles/Vivedino.json
+++ b/resources/profiles/Vivedino.json
@@ -1,6 +1,6 @@
{
"name": "Vivedino",
- "version": "02.04.00.02",
+ "version": "02.04.00.13",
"force_update": "0",
"description": "Vivedino configurations",
"machine_model_list": [
@@ -11,6 +11,22 @@
{
"name": "Troodon 2.0 - RRF",
"sub_path": "machine/Troodon2RRF.json"
+ },
+ {
+ "name": "Xplorer Dual Gantry",
+ "sub_path": "machine/XplorerDualGantry.json"
+ },
+ {
+ "name": "Xplorer IDEX",
+ "sub_path": "machine/XplorerIDEX.json"
+ },
+ {
+ "name": "Xplorer IQEX",
+ "sub_path": "machine/XplorerIQEX.json"
+ },
+ {
+ "name": "Xplorer Single",
+ "sub_path": "machine/XplorerSingle.json"
}
],
"process_list": [
@@ -45,6 +61,86 @@
{
"name": "0.28mm Extra Draft @Troodon2",
"sub_path": "process/0.28mm Extra Draft @Troodon2.json"
+ },
+ {
+ "name": "fdm_process_xplorer_common",
+ "sub_path": "process/fdm_process_xplorer_common.json"
+ },
+ {
+ "name": "0.04mm Ultra Fine @Xplorer 0.4",
+ "sub_path": "process/0.04mm Ultra Fine @Xplorer 0.4.json"
+ },
+ {
+ "name": "0.08mm Extra Fine @Xplorer 0.4",
+ "sub_path": "process/0.08mm Extra Fine @Xplorer 0.4.json"
+ },
+ {
+ "name": "0.12mm Fine @Xplorer 0.4",
+ "sub_path": "process/0.12mm Fine @Xplorer 0.4.json"
+ },
+ {
+ "name": "0.16mm Optimal @Xplorer 0.4",
+ "sub_path": "process/0.16mm Optimal @Xplorer 0.4.json"
+ },
+ {
+ "name": "0.20mm Standard @Xplorer 0.4",
+ "sub_path": "process/0.20mm Standard @Xplorer 0.4.json"
+ },
+ {
+ "name": "0.28mm Draft @Xplorer 0.4",
+ "sub_path": "process/0.28mm Draft @Xplorer 0.4.json"
+ },
+ {
+ "name": "0.32mm Extra Draft @Xplorer 0.4",
+ "sub_path": "process/0.32mm Extra Draft @Xplorer 0.4.json"
+ },
+ {
+ "name": "fdm_process_xplorer_common_0_6",
+ "sub_path": "process/fdm_process_xplorer_common_0_6.json"
+ },
+ {
+ "name": "fdm_process_xplorer_common_0_8",
+ "sub_path": "process/fdm_process_xplorer_common_0_8.json"
+ },
+ {
+ "name": "0.18mm Fine @Xplorer 0.6",
+ "sub_path": "process/0.18mm Fine @Xplorer 0.6.json"
+ },
+ {
+ "name": "0.24mm Optimal @Xplorer 0.6",
+ "sub_path": "process/0.24mm Optimal @Xplorer 0.6.json"
+ },
+ {
+ "name": "0.30mm Standard @Xplorer 0.6",
+ "sub_path": "process/0.30mm Standard @Xplorer 0.6.json"
+ },
+ {
+ "name": "0.36mm Draft @Xplorer 0.6",
+ "sub_path": "process/0.36mm Draft @Xplorer 0.6.json"
+ },
+ {
+ "name": "0.42mm Extra Draft @Xplorer 0.6",
+ "sub_path": "process/0.42mm Extra Draft @Xplorer 0.6.json"
+ },
+ {
+ "name": "0.24mm Fine @Xplorer 0.8",
+ "sub_path": "process/0.24mm Fine @Xplorer 0.8.json"
+ },
+ {
+ "name": "0.32mm Optimal @Xplorer 0.8",
+ "sub_path": "process/0.32mm Optimal @Xplorer 0.8.json"
+ },
+ {
+ "name": "0.40mm Standard @Xplorer 0.8",
+ "sub_path": "process/0.40mm Standard @Xplorer 0.8.json"
+ },
+ {
+ "name": "0.48mm Draft @Xplorer 0.8",
+ "sub_path": "process/0.48mm Draft @Xplorer 0.8.json"
+ },
+ {
+ "name": "0.56mm Extra Draft @Xplorer 0.8",
+ "sub_path": "process/0.56mm Extra Draft @Xplorer 0.8.json"
}
],
"filament_list": [],
@@ -65,9 +161,61 @@
"name": "Troodon 2.0 Klipper 0.4 nozzle",
"sub_path": "machine/Troodon 2.0 Klipper 0.4 nozzle.json"
},
+ {
+ "name": "Xplorer common",
+ "sub_path": "machine/Xplorer common.json"
+ },
{
"name": "Troodon 2.0 RRF 0.4 nozzle",
"sub_path": "machine/Troodon 2.0 RRF 0.4 nozzle.json"
+ },
+ {
+ "name": "Xplorer Dual Gantry 0.4 nozzle",
+ "sub_path": "machine/Xplorer Dual Gantry 0.4 nozzle.json"
+ },
+ {
+ "name": "Xplorer Dual Gantry 0.6 nozzle",
+ "sub_path": "machine/Xplorer Dual Gantry 0.6 nozzle.json"
+ },
+ {
+ "name": "Xplorer Dual Gantry 0.8 nozzle",
+ "sub_path": "machine/Xplorer Dual Gantry 0.8 nozzle.json"
+ },
+ {
+ "name": "Xplorer IDEX 0.4 nozzle",
+ "sub_path": "machine/Xplorer IDEX 0.4 nozzle.json"
+ },
+ {
+ "name": "Xplorer IDEX 0.6 nozzle",
+ "sub_path": "machine/Xplorer IDEX 0.6 nozzle.json"
+ },
+ {
+ "name": "Xplorer IDEX 0.8 nozzle",
+ "sub_path": "machine/Xplorer IDEX 0.8 nozzle.json"
+ },
+ {
+ "name": "Xplorer IQEX 0.4 nozzle",
+ "sub_path": "machine/Xplorer IQEX 0.4 nozzle.json"
+ },
+ {
+ "name": "Xplorer IQEX 0.6 nozzle",
+ "sub_path": "machine/Xplorer IQEX 0.6 nozzle.json"
+ },
+ {
+ "name": "Xplorer IQEX 0.8 nozzle",
+ "sub_path": "machine/Xplorer IQEX 0.8 nozzle.json"
+ },
+ {
+ "name": "Xplorer Single 0.4 nozzle",
+ "sub_path": "machine/Xplorer Single 0.4 nozzle.json"
+ },
+ {
+ "name": "Xplorer Single 0.6 nozzle",
+ "sub_path": "machine/Xplorer Single 0.6 nozzle.json"
+ },
+ {
+ "name": "Xplorer Single 0.8 nozzle",
+ "sub_path": "machine/Xplorer Single 0.8 nozzle.json"
}
]
}
diff --git a/resources/profiles/Vivedino/Xplorer Dual Gantry_cover.png b/resources/profiles/Vivedino/Xplorer Dual Gantry_cover.png
new file mode 100644
index 0000000000..cc82af425a
Binary files /dev/null and b/resources/profiles/Vivedino/Xplorer Dual Gantry_cover.png differ
diff --git a/resources/profiles/Vivedino/Xplorer IDEX_cover.png b/resources/profiles/Vivedino/Xplorer IDEX_cover.png
new file mode 100644
index 0000000000..2462c32015
Binary files /dev/null and b/resources/profiles/Vivedino/Xplorer IDEX_cover.png differ
diff --git a/resources/profiles/Vivedino/Xplorer IQEX_cover.png b/resources/profiles/Vivedino/Xplorer IQEX_cover.png
new file mode 100644
index 0000000000..89c6092e5d
Binary files /dev/null and b/resources/profiles/Vivedino/Xplorer IQEX_cover.png differ
diff --git a/resources/profiles/Vivedino/Xplorer Single_cover.png b/resources/profiles/Vivedino/Xplorer Single_cover.png
new file mode 100644
index 0000000000..9a86cf6f9f
Binary files /dev/null and b/resources/profiles/Vivedino/Xplorer Single_cover.png differ
diff --git a/resources/profiles/Vivedino/Xplorer_bed_texture_dualgantry.svg b/resources/profiles/Vivedino/Xplorer_bed_texture_dualgantry.svg
new file mode 100644
index 0000000000..b73c3c529f
--- /dev/null
+++ b/resources/profiles/Vivedino/Xplorer_bed_texture_dualgantry.svg
@@ -0,0 +1,74 @@
+
+
+
diff --git a/resources/profiles/Vivedino/Xplorer_bed_texture_idex.svg b/resources/profiles/Vivedino/Xplorer_bed_texture_idex.svg
new file mode 100644
index 0000000000..ed0d3182f8
--- /dev/null
+++ b/resources/profiles/Vivedino/Xplorer_bed_texture_idex.svg
@@ -0,0 +1,77 @@
+
+
+
diff --git a/resources/profiles/Vivedino/Xplorer_bed_texture_iqex.svg b/resources/profiles/Vivedino/Xplorer_bed_texture_iqex.svg
new file mode 100644
index 0000000000..768653f848
--- /dev/null
+++ b/resources/profiles/Vivedino/Xplorer_bed_texture_iqex.svg
@@ -0,0 +1,154 @@
+
+
+
diff --git a/resources/profiles/Vivedino/Xplorer_buildplate_model.stl b/resources/profiles/Vivedino/Xplorer_buildplate_model.stl
new file mode 100644
index 0000000000..0f4f99f052
Binary files /dev/null and b/resources/profiles/Vivedino/Xplorer_buildplate_model.stl differ
diff --git a/resources/profiles/Vivedino/machine/Xplorer Dual Gantry 0.4 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer Dual Gantry 0.4 nozzle.json
new file mode 100644
index 0000000000..1f8ea0fd3a
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer Dual Gantry 0.4 nozzle.json
@@ -0,0 +1,261 @@
+{
+ "type": "machine",
+ "name": "Xplorer Dual Gantry 0.4 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "DgYk8X6L1R0MKcfg",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}\n{if layer_num <= 0}\nG91\nG1 Z+10 F1200\nG90\n{endif}",
+ "change_filament_gcode": ";CHANGE_FILAMENT - Start\n;Layer#=[layer_num] - Z=[layer_z]\n{if layer_z >first_layer_height}\nG91\nG1 Z+2 F1200\nG90\n{endif}\n{if layer_num != -1 || next_extruder != 0}\n Tool{next_extruder}\n{endif}\n; <----\nG1 Z[layer_z] F1200\n{if layer_num == -1 || layer_num == 0}\n M109 S[first_layer_temperature[next_extruder]] T[next_extruder]\n{else}\n M104 S[temperature[next_extruder]] T[next_extruder]\n{endif}\n; ---->\nM83\n;Layer#=[layer_num] - Z=[layer_z]\nLOG_EXTR\n;CHANGE_FILAMENT - End",
+ "default_nozzle_volume_type": [
+ "High Flow",
+ "High Flow"
+ ],
+ "default_print_profile": "0.20mm Standard @Xplorer 0.4",
+ "deretraction_speed": [
+ "120",
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F",
+ "#729FCF"
+ ],
+ "extruder_offset": [
+ "0x0",
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive",
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "imex_gantry_count": "2",
+ "imex_mode_active_tools": [
+ "0:P",
+ "0:P,1:C",
+ "0:P,1:M"
+ ],
+ "imex_mode_gcodes": [
+ "SET_PRINT_MODE MODE=PRIMARY",
+ "SET_PRINT_MODE MODE=COPY",
+ "SET_PRINT_MODE MODE=MIRROR"
+ ],
+ "imex_mode_names": [
+ "primary",
+ "copy",
+ "mirror"
+ ],
+ "imex_tool_layout": "rear-left",
+ "imex_tools_per_gantry": "1",
+ "is_imex": "1",
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0",
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000",
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000",
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50",
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60",
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20",
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S{first_layer_temperature[0]} T0\nM104 S{first_layer_temperature[1]} T1\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S{first_layer_temperature[0]} T0\nM109 S{first_layer_temperature[1]} T1\nstart_print\nG1 F5000\n{if first_layer_temperature[1] !=0 }\nTool1\nbrush_Tool1\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nbrush_Tool0\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nG1 Y280 X10.0 Z0.38 F5000.0 \nG1 Y100 X10.0 Z0.38 F1500.0 E15 \nG1 Y100 X10.4 Z0.38 F5000.0 \nG1 Y280 X10.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\n{if first_layer_temperature[0] !=0 }\nTool0\nG1 Y280 X11.0 Z0.38 F5000.0 \nG1 Y100 X11.0 Z0.38 F1500.0 E15 \nG1 Y100 X11.4 Z0.38 F5000.0 \nG1 Y280 X11.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\nM400\nSKEW_PROFILE LOAD=SKEW1\nG28 X\nG28 Y\nTool{initial_extruder}\nSTART_PRINTING_MODE\n\n",
+ "max_layer_height": [
+ "0.32",
+ "0.32"
+ ],
+ "min_layer_height": [
+ "0.04",
+ "0.04"
+ ],
+ "nozzle_diameter": [
+ "0.4",
+ "0.4"
+ ],
+ "nozzle_type": [
+ "hardened_steel",
+ "hardened_steel"
+ ],
+ "nozzle_volume": [
+ "0",
+ "0"
+ ],
+ "printable_area": [
+ "10x0",
+ "410x0",
+ "410x342.5",
+ "10x342.5"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1",
+ "2"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer Dual Gantry",
+ "printer_variant": "0.4",
+ "retract_before_wipe": [
+ "70%",
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2",
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2",
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400",
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces",
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0",
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0",
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1",
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18",
+ "18"
+ ],
+ "retraction_length": [
+ "1.35",
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2",
+ "2"
+ ],
+ "retraction_speed": [
+ "120",
+ "120"
+ ],
+ "travel_slope": [
+ "1",
+ "1"
+ ],
+ "wipe": [
+ "1",
+ "1"
+ ],
+ "wipe_distance": [
+ "1",
+ "1"
+ ],
+ "z_hop": [
+ "1",
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift",
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer Dual Gantry 0.6 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer Dual Gantry 0.6 nozzle.json
new file mode 100644
index 0000000000..39e9ecead3
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer Dual Gantry 0.6 nozzle.json
@@ -0,0 +1,261 @@
+{
+ "type": "machine",
+ "name": "Xplorer Dual Gantry 0.6 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "M1ZyjMxGz0TpRDcv",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}\n{if layer_num <= 0}\nG91\nG1 Z+10 F1200\nG90\n{endif}",
+ "change_filament_gcode": ";CHANGE_FILAMENT - Start\n;Layer#=[layer_num] - Z=[layer_z]\n{if layer_z >first_layer_height}\nG91\nG1 Z+2 F1200\nG90\n{endif}\n{if layer_num != -1 || next_extruder != 0}\n Tool{next_extruder}\n{endif}\n; <----\nG1 Z[layer_z] F1200\n{if layer_num == -1 || layer_num == 0}\n M109 S[first_layer_temperature[next_extruder]] T[next_extruder]\n{else}\n M104 S[temperature[next_extruder]] T[next_extruder]\n{endif}\n; ---->\nM83\n;Layer#=[layer_num] - Z=[layer_z]\nLOG_EXTR\n;CHANGE_FILAMENT - End",
+ "default_nozzle_volume_type": [
+ "High Flow",
+ "High Flow"
+ ],
+ "default_print_profile": "0.30mm Standard @Xplorer 0.6",
+ "deretraction_speed": [
+ "120",
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F",
+ "#729FCF"
+ ],
+ "extruder_offset": [
+ "0x0",
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive",
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "imex_gantry_count": "2",
+ "imex_mode_active_tools": [
+ "0:P",
+ "0:P,1:C",
+ "0:P,1:M"
+ ],
+ "imex_mode_gcodes": [
+ "SET_PRINT_MODE MODE=PRIMARY",
+ "SET_PRINT_MODE MODE=COPY",
+ "SET_PRINT_MODE MODE=MIRROR"
+ ],
+ "imex_mode_names": [
+ "primary",
+ "copy",
+ "mirror"
+ ],
+ "imex_tool_layout": "rear-left",
+ "imex_tools_per_gantry": "1",
+ "is_imex": "1",
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0",
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000",
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000",
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50",
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60",
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20",
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S{first_layer_temperature[0]} T0\nM104 S{first_layer_temperature[1]} T1\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S{first_layer_temperature[0]} T0\nM109 S{first_layer_temperature[1]} T1\nstart_print\nG1 F5000\n{if first_layer_temperature[1] !=0 }\nTool1\nbrush_Tool1\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nbrush_Tool0\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nG1 Y280 X10.0 Z0.38 F5000.0 \nG1 Y100 X10.0 Z0.38 F1500.0 E15 \nG1 Y100 X10.4 Z0.38 F5000.0 \nG1 Y280 X10.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\n{if first_layer_temperature[0] !=0 }\nTool0\nG1 Y280 X11.0 Z0.38 F5000.0 \nG1 Y100 X11.0 Z0.38 F1500.0 E15 \nG1 Y100 X11.4 Z0.38 F5000.0 \nG1 Y280 X11.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\nM400\nSKEW_PROFILE LOAD=SKEW1\nG28 X\nG28 Y\nTool{initial_extruder}\nSTART_PRINTING_MODE\n\n",
+ "max_layer_height": [
+ "0.45",
+ "0.45"
+ ],
+ "min_layer_height": [
+ "0.12",
+ "0.12"
+ ],
+ "nozzle_diameter": [
+ "0.6",
+ "0.6"
+ ],
+ "nozzle_type": [
+ "hardened_steel",
+ "hardened_steel"
+ ],
+ "nozzle_volume": [
+ "0",
+ "0"
+ ],
+ "printable_area": [
+ "10x0",
+ "410x0",
+ "410x342.5",
+ "10x342.5"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1",
+ "2"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer Dual Gantry",
+ "printer_variant": "0.6",
+ "retract_before_wipe": [
+ "70%",
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2",
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2",
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400",
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces",
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0",
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0",
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1",
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18",
+ "18"
+ ],
+ "retraction_length": [
+ "1.35",
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2",
+ "2"
+ ],
+ "retraction_speed": [
+ "120",
+ "120"
+ ],
+ "travel_slope": [
+ "1",
+ "1"
+ ],
+ "wipe": [
+ "1",
+ "1"
+ ],
+ "wipe_distance": [
+ "1",
+ "1"
+ ],
+ "z_hop": [
+ "1",
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift",
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer Dual Gantry 0.8 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer Dual Gantry 0.8 nozzle.json
new file mode 100644
index 0000000000..4b6e2ed54d
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer Dual Gantry 0.8 nozzle.json
@@ -0,0 +1,261 @@
+{
+ "type": "machine",
+ "name": "Xplorer Dual Gantry 0.8 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "fWRcMcf9H5NP6zrX",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}\n{if layer_num <= 0}\nG91\nG1 Z+10 F1200\nG90\n{endif}",
+ "change_filament_gcode": ";CHANGE_FILAMENT - Start\n;Layer#=[layer_num] - Z=[layer_z]\n{if layer_z >first_layer_height}\nG91\nG1 Z+2 F1200\nG90\n{endif}\n{if layer_num != -1 || next_extruder != 0}\n Tool{next_extruder}\n{endif}\n; <----\nG1 Z[layer_z] F1200\n{if layer_num == -1 || layer_num == 0}\n M109 S[first_layer_temperature[next_extruder]] T[next_extruder]\n{else}\n M104 S[temperature[next_extruder]] T[next_extruder]\n{endif}\n; ---->\nM83\n;Layer#=[layer_num] - Z=[layer_z]\nLOG_EXTR\n;CHANGE_FILAMENT - End",
+ "default_nozzle_volume_type": [
+ "High Flow",
+ "High Flow"
+ ],
+ "default_print_profile": "0.40mm Standard @Xplorer 0.8",
+ "deretraction_speed": [
+ "120",
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F",
+ "#729FCF"
+ ],
+ "extruder_offset": [
+ "0x0",
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive",
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "imex_gantry_count": "2",
+ "imex_mode_active_tools": [
+ "0:P",
+ "0:P,1:C",
+ "0:P,1:M"
+ ],
+ "imex_mode_gcodes": [
+ "SET_PRINT_MODE MODE=PRIMARY",
+ "SET_PRINT_MODE MODE=COPY",
+ "SET_PRINT_MODE MODE=MIRROR"
+ ],
+ "imex_mode_names": [
+ "primary",
+ "copy",
+ "mirror"
+ ],
+ "imex_tool_layout": "rear-left",
+ "imex_tools_per_gantry": "1",
+ "is_imex": "1",
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0",
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000",
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000",
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50",
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60",
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20",
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S{first_layer_temperature[0]} T0\nM104 S{first_layer_temperature[1]} T1\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S{first_layer_temperature[0]} T0\nM109 S{first_layer_temperature[1]} T1\nstart_print\nG1 F5000\n{if first_layer_temperature[1] !=0 }\nTool1\nbrush_Tool1\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nbrush_Tool0\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nG1 Y280 X10.0 Z0.38 F5000.0 \nG1 Y100 X10.0 Z0.38 F1500.0 E15 \nG1 Y100 X10.4 Z0.38 F5000.0 \nG1 Y280 X10.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\n{if first_layer_temperature[0] !=0 }\nTool0\nG1 Y280 X11.0 Z0.38 F5000.0 \nG1 Y100 X11.0 Z0.38 F1500.0 E15 \nG1 Y100 X11.4 Z0.38 F5000.0 \nG1 Y280 X11.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\nM400\nSKEW_PROFILE LOAD=SKEW1\nG28 X\nG28 Y\nTool{initial_extruder}\nSTART_PRINTING_MODE\n\n",
+ "max_layer_height": [
+ "0.64",
+ "0.64"
+ ],
+ "min_layer_height": [
+ "0.2",
+ "0.2"
+ ],
+ "nozzle_diameter": [
+ "0.8",
+ "0.8"
+ ],
+ "nozzle_type": [
+ "hardened_steel",
+ "hardened_steel"
+ ],
+ "nozzle_volume": [
+ "0",
+ "0"
+ ],
+ "printable_area": [
+ "10x0",
+ "410x0",
+ "410x342.5",
+ "10x342.5"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1",
+ "2"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer Dual Gantry",
+ "printer_variant": "0.8",
+ "retract_before_wipe": [
+ "70%",
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2",
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2",
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400",
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces",
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0",
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0",
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1",
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18",
+ "18"
+ ],
+ "retraction_length": [
+ "1.35",
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2",
+ "2"
+ ],
+ "retraction_speed": [
+ "120",
+ "120"
+ ],
+ "travel_slope": [
+ "1",
+ "1"
+ ],
+ "wipe": [
+ "1",
+ "1"
+ ],
+ "wipe_distance": [
+ "1",
+ "1"
+ ],
+ "z_hop": [
+ "1",
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift",
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer IDEX 0.4 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer IDEX 0.4 nozzle.json
new file mode 100644
index 0000000000..1529f22b4b
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer IDEX 0.4 nozzle.json
@@ -0,0 +1,254 @@
+{
+ "type": "machine",
+ "name": "Xplorer IDEX 0.4 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "lOZ2qJY7SsNRW03z",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}\n{if layer_num <= 0}\nG91\nG1 Z+10 F1200\nG90\n{endif}",
+ "change_filament_gcode": ";CHANGE_FILAMENT - Start\n;Layer#=[layer_num] - Z=[layer_z]\n{if layer_z >first_layer_height}\nG91\nG1 Z+2 F1200\nG90\n{endif}\n{if layer_num != -1 || next_extruder != 0}\n Tool{next_extruder}\n{endif}\n; <----\nG1 Z[layer_z] F1200\n{if layer_num == -1 || layer_num == 0}\n M109 S[first_layer_temperature[next_extruder]] T[next_extruder]\n{else}\n M104 S[temperature[next_extruder]] T[next_extruder]\n{endif}\n; ---->\nM83\n;Layer#=[layer_num] - Z=[layer_z]\nLOG_EXTR\n;CHANGE_FILAMENT - End",
+ "default_nozzle_volume_type": [
+ "High Flow",
+ "High Flow"
+ ],
+ "default_print_profile": "0.20mm Standard @Xplorer 0.4",
+ "deretraction_speed": [
+ "120",
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F",
+ "#729FCF"
+ ],
+ "extruder_offset": [
+ "0x0",
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive",
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "imex_mode_active_tools": [
+ "0:P",
+ "0:P,1:C",
+ "0:P,1:M"
+ ],
+ "imex_mode_gcodes": [
+ "SET_PRINT_MODE MODE=PRIMARY",
+ "SET_PRINT_MODE MODE=COPY",
+ "SET_PRINT_MODE MODE=MIRROR"
+ ],
+ "imex_mode_names": [
+ "primary",
+ "copy",
+ "mirror"
+ ],
+ "imex_nozzle_clearance_x": "40",
+ "imex_tool_layout": "front-left",
+ "is_imex": "1",
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0",
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000",
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000",
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50",
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60",
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20",
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S{first_layer_temperature[0]} T0\nM104 S{first_layer_temperature[1]} T1\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S{first_layer_temperature[0]} T0\nM109 S{first_layer_temperature[1]} T1\nstart_print\nG1 F5000\n{if first_layer_temperature[1] !=0 }\nTool1\nbrush_Tool1\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nbrush_Tool0\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nG1 X280.1 Y3.0 Z0.38 F5000.0 \nG1 X100.1 Y3.0 Z0.38 F1500.0 E15 \nG1 X100.1 Y3.4 Z0.38 F5000.0 \nG1 X280.1 Y3.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nG1 X100.1 Y2.0 Z0.38 F5000.0 \nG1 X280.1 Y2.0 Z0.38 F1500.0 E15 \nG1 X280.1 Y2.4 Z0.38 F5000.0 \nG1 X100.1 Y2.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nM400\nSKEW_PROFILE LOAD=SKEW1\nG28 X\nTool{initial_extruder}\nSTART_PRINTING_MODE\n\n",
+ "max_layer_height": [
+ "0.32",
+ "0.32"
+ ],
+ "min_layer_height": [
+ "0.04",
+ "0.04"
+ ],
+ "nozzle_diameter": [
+ "0.4",
+ "0.4"
+ ],
+ "nozzle_type": [
+ "hardened_steel",
+ "hardened_steel"
+ ],
+ "nozzle_volume": [
+ "0",
+ "0"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1",
+ "2"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer IDEX",
+ "printer_variant": "0.4",
+ "retract_before_wipe": [
+ "70%",
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2",
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2",
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400",
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces",
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0",
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0",
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1",
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18",
+ "18"
+ ],
+ "retraction_length": [
+ "1.35",
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2",
+ "2"
+ ],
+ "retraction_speed": [
+ "120",
+ "120"
+ ],
+ "travel_slope": [
+ "1",
+ "1"
+ ],
+ "wipe": [
+ "1",
+ "1"
+ ],
+ "wipe_distance": [
+ "1",
+ "1"
+ ],
+ "z_hop": [
+ "1",
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift",
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer IDEX 0.6 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer IDEX 0.6 nozzle.json
new file mode 100644
index 0000000000..b6cfd97d54
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer IDEX 0.6 nozzle.json
@@ -0,0 +1,254 @@
+{
+ "type": "machine",
+ "name": "Xplorer IDEX 0.6 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "S4ZLGZuPtR3yC4IZ",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}\n{if layer_num <= 0}\nG91\nG1 Z+10 F1200\nG90\n{endif}",
+ "change_filament_gcode": ";CHANGE_FILAMENT - Start\n;Layer#=[layer_num] - Z=[layer_z]\n{if layer_z >first_layer_height}\nG91\nG1 Z+2 F1200\nG90\n{endif}\n{if layer_num != -1 || next_extruder != 0}\n Tool{next_extruder}\n{endif}\n; <----\nG1 Z[layer_z] F1200\n{if layer_num == -1 || layer_num == 0}\n M109 S[first_layer_temperature[next_extruder]] T[next_extruder]\n{else}\n M104 S[temperature[next_extruder]] T[next_extruder]\n{endif}\n; ---->\nM83\n;Layer#=[layer_num] - Z=[layer_z]\nLOG_EXTR\n;CHANGE_FILAMENT - End",
+ "default_nozzle_volume_type": [
+ "High Flow",
+ "High Flow"
+ ],
+ "default_print_profile": "0.30mm Standard @Xplorer 0.6",
+ "deretraction_speed": [
+ "120",
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F",
+ "#729FCF"
+ ],
+ "extruder_offset": [
+ "0x0",
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive",
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "imex_mode_active_tools": [
+ "0:P",
+ "0:P,1:C",
+ "0:P,1:M"
+ ],
+ "imex_mode_gcodes": [
+ "SET_PRINT_MODE MODE=PRIMARY",
+ "SET_PRINT_MODE MODE=COPY",
+ "SET_PRINT_MODE MODE=MIRROR"
+ ],
+ "imex_mode_names": [
+ "primary",
+ "copy",
+ "mirror"
+ ],
+ "imex_nozzle_clearance_x": "40",
+ "imex_tool_layout": "front-left",
+ "is_imex": "1",
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0",
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000",
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000",
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50",
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60",
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20",
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S{first_layer_temperature[0]} T0\nM104 S{first_layer_temperature[1]} T1\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S{first_layer_temperature[0]} T0\nM109 S{first_layer_temperature[1]} T1\nstart_print\nG1 F5000\n{if first_layer_temperature[1] !=0 }\nTool1\nbrush_Tool1\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nbrush_Tool0\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nG1 X280.1 Y3.0 Z0.38 F5000.0 \nG1 X100.1 Y3.0 Z0.38 F1500.0 E15 \nG1 X100.1 Y3.4 Z0.38 F5000.0 \nG1 X280.1 Y3.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nG1 X100.1 Y2.0 Z0.38 F5000.0 \nG1 X280.1 Y2.0 Z0.38 F1500.0 E15 \nG1 X280.1 Y2.4 Z0.38 F5000.0 \nG1 X100.1 Y2.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nM400\nSKEW_PROFILE LOAD=SKEW1\nG28 X\nTool{initial_extruder}\nSTART_PRINTING_MODE\n\n",
+ "max_layer_height": [
+ "0.45",
+ "0.45"
+ ],
+ "min_layer_height": [
+ "0.12",
+ "0.12"
+ ],
+ "nozzle_diameter": [
+ "0.6",
+ "0.6"
+ ],
+ "nozzle_type": [
+ "hardened_steel",
+ "hardened_steel"
+ ],
+ "nozzle_volume": [
+ "0",
+ "0"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1",
+ "2"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer IDEX",
+ "printer_variant": "0.6",
+ "retract_before_wipe": [
+ "70%",
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2",
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2",
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400",
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces",
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0",
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0",
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1",
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18",
+ "18"
+ ],
+ "retraction_length": [
+ "1.35",
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2",
+ "2"
+ ],
+ "retraction_speed": [
+ "120",
+ "120"
+ ],
+ "travel_slope": [
+ "1",
+ "1"
+ ],
+ "wipe": [
+ "1",
+ "1"
+ ],
+ "wipe_distance": [
+ "1",
+ "1"
+ ],
+ "z_hop": [
+ "1",
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift",
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer IDEX 0.8 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer IDEX 0.8 nozzle.json
new file mode 100644
index 0000000000..599717aba4
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer IDEX 0.8 nozzle.json
@@ -0,0 +1,254 @@
+{
+ "type": "machine",
+ "name": "Xplorer IDEX 0.8 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "U50ftmONKAXI4OQ9",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}\n{if layer_num <= 0}\nG91\nG1 Z+10 F1200\nG90\n{endif}",
+ "change_filament_gcode": ";CHANGE_FILAMENT - Start\n;Layer#=[layer_num] - Z=[layer_z]\n{if layer_z >first_layer_height}\nG91\nG1 Z+2 F1200\nG90\n{endif}\n{if layer_num != -1 || next_extruder != 0}\n Tool{next_extruder}\n{endif}\n; <----\nG1 Z[layer_z] F1200\n{if layer_num == -1 || layer_num == 0}\n M109 S[first_layer_temperature[next_extruder]] T[next_extruder]\n{else}\n M104 S[temperature[next_extruder]] T[next_extruder]\n{endif}\n; ---->\nM83\n;Layer#=[layer_num] - Z=[layer_z]\nLOG_EXTR\n;CHANGE_FILAMENT - End",
+ "default_nozzle_volume_type": [
+ "High Flow",
+ "High Flow"
+ ],
+ "default_print_profile": "0.40mm Standard @Xplorer 0.8",
+ "deretraction_speed": [
+ "120",
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F",
+ "#729FCF"
+ ],
+ "extruder_offset": [
+ "0x0",
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive",
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "imex_mode_active_tools": [
+ "0:P",
+ "0:P,1:C",
+ "0:P,1:M"
+ ],
+ "imex_mode_gcodes": [
+ "SET_PRINT_MODE MODE=PRIMARY",
+ "SET_PRINT_MODE MODE=COPY",
+ "SET_PRINT_MODE MODE=MIRROR"
+ ],
+ "imex_mode_names": [
+ "primary",
+ "copy",
+ "mirror"
+ ],
+ "imex_nozzle_clearance_x": "40",
+ "imex_tool_layout": "front-left",
+ "is_imex": "1",
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0",
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000",
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000",
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50",
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60",
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20",
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S{first_layer_temperature[0]} T0\nM104 S{first_layer_temperature[1]} T1\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S{first_layer_temperature[0]} T0\nM109 S{first_layer_temperature[1]} T1\nstart_print\nG1 F5000\n{if first_layer_temperature[1] !=0 }\nTool1\nbrush_Tool1\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nbrush_Tool0\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nG1 X280.1 Y3.0 Z0.38 F5000.0 \nG1 X100.1 Y3.0 Z0.38 F1500.0 E15 \nG1 X100.1 Y3.4 Z0.38 F5000.0 \nG1 X280.1 Y3.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nG1 X100.1 Y2.0 Z0.38 F5000.0 \nG1 X280.1 Y2.0 Z0.38 F1500.0 E15 \nG1 X280.1 Y2.4 Z0.38 F5000.0 \nG1 X100.1 Y2.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nM400\nSKEW_PROFILE LOAD=SKEW1\nG28 X\nTool{initial_extruder}\nSTART_PRINTING_MODE\n\n",
+ "max_layer_height": [
+ "0.64",
+ "0.64"
+ ],
+ "min_layer_height": [
+ "0.2",
+ "0.2"
+ ],
+ "nozzle_diameter": [
+ "0.8",
+ "0.8"
+ ],
+ "nozzle_type": [
+ "hardened_steel",
+ "hardened_steel"
+ ],
+ "nozzle_volume": [
+ "0",
+ "0"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1",
+ "2"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer IDEX",
+ "printer_variant": "0.8",
+ "retract_before_wipe": [
+ "70%",
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2",
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2",
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400",
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces",
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0",
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0",
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1",
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18",
+ "18"
+ ],
+ "retraction_length": [
+ "1.35",
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2",
+ "2"
+ ],
+ "retraction_speed": [
+ "120",
+ "120"
+ ],
+ "travel_slope": [
+ "1",
+ "1"
+ ],
+ "wipe": [
+ "1",
+ "1"
+ ],
+ "wipe_distance": [
+ "1",
+ "1"
+ ],
+ "z_hop": [
+ "1",
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift",
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer IQEX 0.4 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer IQEX 0.4 nozzle.json
new file mode 100644
index 0000000000..d98f63e703
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer IQEX 0.4 nozzle.json
@@ -0,0 +1,399 @@
+{
+ "type": "machine",
+ "name": "Xplorer IQEX 0.4 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "fbUJR4RMoMczxSkH",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}\n{if layer_num <= 0}\nG91\nG1 Z+10 F1200\nG90\n{endif}",
+ "change_filament_gcode": ";CHANGE_FILAMENT - Start\n;Layer#=[layer_num] - Z=[layer_z]\n{if layer_z >first_layer_height}\nG91\nG1 Z+2 F1200\nG90\n{endif}\n{if layer_num != -1 || next_extruder != 0}\n Tool{next_extruder}\n{endif}\n; <----\nG1 Z[layer_z] F1200\n{if layer_num == -1 || layer_num == 0}\n M109 S[first_layer_temperature[next_extruder]] T[next_extruder]\n{else}\n M104 S[temperature[next_extruder]] T[next_extruder]\n{endif}\n; ---->\nM83\n;Layer#=[layer_num] - Z=[layer_z]\nLOG_EXTR\n;CHANGE_FILAMENT - End",
+ "default_nozzle_volume_type": [
+ "High Flow",
+ "High Flow",
+ "High Flow",
+ "High Flow"
+ ],
+ "default_print_profile": "0.20mm Standard @Xplorer 0.4",
+ "deretraction_speed": [
+ "120",
+ "120",
+ "120",
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F",
+ "#729FCF",
+ "#AD7FA8",
+ "#F57900"
+ ],
+ "extruder_offset": [
+ "0x0",
+ "0x0",
+ "0x0",
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive",
+ "Direct Drive",
+ "Direct Drive",
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "imex_gantry_count": "2",
+ "imex_mode_active_tools": [
+ "0:P",
+ "0:P,1:C",
+ "0:P,1:M",
+ "0:P,1:C,2:C,3:C",
+ "0:P,1:C,2:M,3:M",
+ "0:P,1:S,2:C,3:C",
+ "0:P,1:S,2:M,3:M"
+ ],
+ "imex_mode_gcodes": [
+ "SET_PRINT_MODE MODE=PRIMARY",
+ "SET_PRINT_MODE MODE=COPY01",
+ "SET_PRINT_MODE MODE=MIRROR01",
+ "SET_PRINT_MODE MODE=COPY0123",
+ "SET_PRINT_MODE MODE=MIRROR0123",
+ "SET_PRINT_MODE MODE=MULTICOLOR_COPY01",
+ "SET_PRINT_MODE MODE=MULTICOLOR_MIRROR01"
+ ],
+ "imex_mode_names": [
+ "primary",
+ "copy",
+ "mirror",
+ "iq-copy",
+ "iq-mirror",
+ "mc-copy",
+ "mc-mirror"
+ ],
+ "imex_nozzle_clearance_x": "40",
+ "imex_tool_layout": "rear-left",
+ "is_imex": "1",
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000",
+ "6000",
+ "5000",
+ "6000",
+ "5000",
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50",
+ "100",
+ "50",
+ "100",
+ "50",
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0",
+ "0",
+ "0",
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60",
+ "120",
+ "60",
+ "120",
+ "60",
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20",
+ "40",
+ "20",
+ "40",
+ "20",
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S{first_layer_temperature[0]} T0\nM104 S{first_layer_temperature[1]} T1\nM104 S{first_layer_temperature[2]} T2\nM104 S{first_layer_temperature[3]} T3\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S{first_layer_temperature[0]} T0\nM109 S{first_layer_temperature[1]} T1\nM109 S{first_layer_temperature[2]} T2\nM109 S{first_layer_temperature[3]} T3\nstart_print\nG1 F5000\n{if first_layer_temperature[3] !=0 }\nTool3\nbrush_Tool3\n{endif}\n{if first_layer_temperature[2] !=0 }\nTool2\nbrush_Tool2\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nbrush_Tool1\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nbrush_Tool0\n{endif}\n{if first_layer_temperature[3] !=0 }\nTool3\nG1 Y280 X407.0 Z0.38 F5000.0 \nG1 Y175 X407.0 Z0.38 F1500.0 E15 \nG1 Y175 X407.4 Z0.38 F5000.0 \nG1 Y280 X407.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\n{if first_layer_temperature[2] !=0 }\nTool2\nG1 Y280 X12.0 Z0.38 F5000.0 \nG1 Y175 X12.0 Z0.38 F1500.0 E15 \nG1 Y175 X12.4 Z0.38 F5000.0 \nG1 Y280 X12.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nG1 Y280 X408.0 Z0.38 F5000.0 \nG1 Y175 X408.0 Z0.38 F1500.0 E15 \nG1 Y175 X408.4 Z0.38 F5000.0 \nG1 Y280 X408.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\n{if first_layer_temperature[0] !=0 }\nTool0\nG1 Y280 X11.0 Z0.38 F5000.0 \nG1 Y175 X11.0 Z0.38 F1500.0 E15 \nG1 Y175 X11.4 Z0.38 F5000.0 \nG1 Y280 X11.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\nM400\nSKEW_PROFILE LOAD=SKEW1\nG28 X\nG28 Y\nTool{initial_extruder}\nSTART_PRINTING_MODE\n\n",
+ "max_layer_height": [
+ "0.32",
+ "0.32",
+ "0.32",
+ "0.32"
+ ],
+ "min_layer_height": [
+ "0.04",
+ "0.04",
+ "0.04",
+ "0.04"
+ ],
+ "nozzle_diameter": [
+ "0.4",
+ "0.4",
+ "0.4",
+ "0.4"
+ ],
+ "nozzle_type": [
+ "hardened_steel",
+ "hardened_steel",
+ "hardened_steel",
+ "hardened_steel"
+ ],
+ "nozzle_volume": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "printable_area": [
+ "10x0",
+ "410x0",
+ "410x342.5",
+ "10x342.5"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1",
+ "2",
+ "3",
+ "4"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer IQEX",
+ "printer_variant": "0.4",
+ "retract_before_wipe": [
+ "70%",
+ "70%",
+ "70%",
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2",
+ "2",
+ "2",
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2",
+ "0.2",
+ "0.2",
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400",
+ "400",
+ "400",
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces",
+ "All Surfaces",
+ "All Surfaces",
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18",
+ "18",
+ "18",
+ "18"
+ ],
+ "retraction_length": [
+ "1.35",
+ "1.35",
+ "1.35",
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2",
+ "2",
+ "2",
+ "2"
+ ],
+ "retraction_speed": [
+ "120",
+ "120",
+ "120",
+ "120"
+ ],
+ "travel_slope": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "wipe": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "wipe_distance": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "z_hop": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift",
+ "Normal Lift",
+ "Normal Lift",
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer IQEX 0.6 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer IQEX 0.6 nozzle.json
new file mode 100644
index 0000000000..97ab9406c5
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer IQEX 0.6 nozzle.json
@@ -0,0 +1,399 @@
+{
+ "type": "machine",
+ "name": "Xplorer IQEX 0.6 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "hOYAHBik6rxU1B0d",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}\n{if layer_num <= 0}\nG91\nG1 Z+10 F1200\nG90\n{endif}",
+ "change_filament_gcode": ";CHANGE_FILAMENT - Start\n;Layer#=[layer_num] - Z=[layer_z]\n{if layer_z >first_layer_height}\nG91\nG1 Z+2 F1200\nG90\n{endif}\n{if layer_num != -1 || next_extruder != 0}\n Tool{next_extruder}\n{endif}\n; <----\nG1 Z[layer_z] F1200\n{if layer_num == -1 || layer_num == 0}\n M109 S[first_layer_temperature[next_extruder]] T[next_extruder]\n{else}\n M104 S[temperature[next_extruder]] T[next_extruder]\n{endif}\n; ---->\nM83\n;Layer#=[layer_num] - Z=[layer_z]\nLOG_EXTR\n;CHANGE_FILAMENT - End",
+ "default_nozzle_volume_type": [
+ "High Flow",
+ "High Flow",
+ "High Flow",
+ "High Flow"
+ ],
+ "default_print_profile": "0.30mm Standard @Xplorer 0.6",
+ "deretraction_speed": [
+ "120",
+ "120",
+ "120",
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F",
+ "#729FCF",
+ "#AD7FA8",
+ "#F57900"
+ ],
+ "extruder_offset": [
+ "0x0",
+ "0x0",
+ "0x0",
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive",
+ "Direct Drive",
+ "Direct Drive",
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "imex_gantry_count": "2",
+ "imex_mode_active_tools": [
+ "0:P",
+ "0:P,1:C",
+ "0:P,1:M",
+ "0:P,1:C,2:C,3:C",
+ "0:P,1:C,2:M,3:M",
+ "0:P,1:S,2:C,3:C",
+ "0:P,1:S,2:M,3:M"
+ ],
+ "imex_mode_gcodes": [
+ "SET_PRINT_MODE MODE=PRIMARY",
+ "SET_PRINT_MODE MODE=COPY01",
+ "SET_PRINT_MODE MODE=MIRROR01",
+ "SET_PRINT_MODE MODE=COPY0123",
+ "SET_PRINT_MODE MODE=MIRROR0123",
+ "SET_PRINT_MODE MODE=MULTICOLOR_COPY01",
+ "SET_PRINT_MODE MODE=MULTICOLOR_MIRROR01"
+ ],
+ "imex_mode_names": [
+ "primary",
+ "copy",
+ "mirror",
+ "iq-copy",
+ "iq-mirror",
+ "mc-copy",
+ "mc-mirror"
+ ],
+ "imex_nozzle_clearance_x": "40",
+ "imex_tool_layout": "rear-left",
+ "is_imex": "1",
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000",
+ "6000",
+ "5000",
+ "6000",
+ "5000",
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50",
+ "100",
+ "50",
+ "100",
+ "50",
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0",
+ "0",
+ "0",
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60",
+ "120",
+ "60",
+ "120",
+ "60",
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20",
+ "40",
+ "20",
+ "40",
+ "20",
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S{first_layer_temperature[0]} T0\nM104 S{first_layer_temperature[1]} T1\nM104 S{first_layer_temperature[2]} T2\nM104 S{first_layer_temperature[3]} T3\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S{first_layer_temperature[0]} T0\nM109 S{first_layer_temperature[1]} T1\nM109 S{first_layer_temperature[2]} T2\nM109 S{first_layer_temperature[3]} T3\nstart_print\nG1 F5000\n{if first_layer_temperature[3] !=0 }\nTool3\nbrush_Tool3\n{endif}\n{if first_layer_temperature[2] !=0 }\nTool2\nbrush_Tool2\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nbrush_Tool1\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nbrush_Tool0\n{endif}\n{if first_layer_temperature[3] !=0 }\nTool3\nG1 Y280 X407.0 Z0.38 F5000.0 \nG1 Y175 X407.0 Z0.38 F1500.0 E15 \nG1 Y175 X407.4 Z0.38 F5000.0 \nG1 Y280 X407.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\n{if first_layer_temperature[2] !=0 }\nTool2\nG1 Y280 X12.0 Z0.38 F5000.0 \nG1 Y175 X12.0 Z0.38 F1500.0 E15 \nG1 Y175 X12.4 Z0.38 F5000.0 \nG1 Y280 X12.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nG1 Y280 X408.0 Z0.38 F5000.0 \nG1 Y175 X408.0 Z0.38 F1500.0 E15 \nG1 Y175 X408.4 Z0.38 F5000.0 \nG1 Y280 X408.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\n{if first_layer_temperature[0] !=0 }\nTool0\nG1 Y280 X11.0 Z0.38 F5000.0 \nG1 Y175 X11.0 Z0.38 F1500.0 E15 \nG1 Y175 X11.4 Z0.38 F5000.0 \nG1 Y280 X11.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\nM400\nSKEW_PROFILE LOAD=SKEW1\nG28 X\nG28 Y\nTool{initial_extruder}\nSTART_PRINTING_MODE\n\n",
+ "max_layer_height": [
+ "0.45",
+ "0.45",
+ "0.45",
+ "0.45"
+ ],
+ "min_layer_height": [
+ "0.12",
+ "0.12",
+ "0.12",
+ "0.12"
+ ],
+ "nozzle_diameter": [
+ "0.6",
+ "0.6",
+ "0.6",
+ "0.6"
+ ],
+ "nozzle_type": [
+ "hardened_steel",
+ "hardened_steel",
+ "hardened_steel",
+ "hardened_steel"
+ ],
+ "nozzle_volume": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "printable_area": [
+ "10x0",
+ "410x0",
+ "410x342.5",
+ "10x342.5"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1",
+ "2",
+ "3",
+ "4"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer IQEX",
+ "printer_variant": "0.6",
+ "retract_before_wipe": [
+ "70%",
+ "70%",
+ "70%",
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2",
+ "2",
+ "2",
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2",
+ "0.2",
+ "0.2",
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400",
+ "400",
+ "400",
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces",
+ "All Surfaces",
+ "All Surfaces",
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18",
+ "18",
+ "18",
+ "18"
+ ],
+ "retraction_length": [
+ "1.35",
+ "1.35",
+ "1.35",
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2",
+ "2",
+ "2",
+ "2"
+ ],
+ "retraction_speed": [
+ "120",
+ "120",
+ "120",
+ "120"
+ ],
+ "travel_slope": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "wipe": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "wipe_distance": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "z_hop": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift",
+ "Normal Lift",
+ "Normal Lift",
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer IQEX 0.8 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer IQEX 0.8 nozzle.json
new file mode 100644
index 0000000000..68a22685ed
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer IQEX 0.8 nozzle.json
@@ -0,0 +1,399 @@
+{
+ "type": "machine",
+ "name": "Xplorer IQEX 0.8 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "e048HgBDJy6XnaaO",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}\n{if layer_num <= 0}\nG91\nG1 Z+10 F1200\nG90\n{endif}",
+ "change_filament_gcode": ";CHANGE_FILAMENT - Start\n;Layer#=[layer_num] - Z=[layer_z]\n{if layer_z >first_layer_height}\nG91\nG1 Z+2 F1200\nG90\n{endif}\n{if layer_num != -1 || next_extruder != 0}\n Tool{next_extruder}\n{endif}\n; <----\nG1 Z[layer_z] F1200\n{if layer_num == -1 || layer_num == 0}\n M109 S[first_layer_temperature[next_extruder]] T[next_extruder]\n{else}\n M104 S[temperature[next_extruder]] T[next_extruder]\n{endif}\n; ---->\nM83\n;Layer#=[layer_num] - Z=[layer_z]\nLOG_EXTR\n;CHANGE_FILAMENT - End",
+ "default_nozzle_volume_type": [
+ "High Flow",
+ "High Flow",
+ "High Flow",
+ "High Flow"
+ ],
+ "default_print_profile": "0.40mm Standard @Xplorer 0.8",
+ "deretraction_speed": [
+ "120",
+ "120",
+ "120",
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F",
+ "#729FCF",
+ "#AD7FA8",
+ "#F57900"
+ ],
+ "extruder_offset": [
+ "0x0",
+ "0x0",
+ "0x0",
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive",
+ "Direct Drive",
+ "Direct Drive",
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "imex_gantry_count": "2",
+ "imex_mode_active_tools": [
+ "0:P",
+ "0:P,1:C",
+ "0:P,1:M",
+ "0:P,1:C,2:C,3:C",
+ "0:P,1:C,2:M,3:M",
+ "0:P,1:S,2:C,3:C",
+ "0:P,1:S,2:M,3:M"
+ ],
+ "imex_mode_gcodes": [
+ "SET_PRINT_MODE MODE=PRIMARY",
+ "SET_PRINT_MODE MODE=COPY01",
+ "SET_PRINT_MODE MODE=MIRROR01",
+ "SET_PRINT_MODE MODE=COPY0123",
+ "SET_PRINT_MODE MODE=MIRROR0123",
+ "SET_PRINT_MODE MODE=MULTICOLOR_COPY01",
+ "SET_PRINT_MODE MODE=MULTICOLOR_MIRROR01"
+ ],
+ "imex_mode_names": [
+ "primary",
+ "copy",
+ "mirror",
+ "iq-copy",
+ "iq-mirror",
+ "mc-copy",
+ "mc-mirror"
+ ],
+ "imex_nozzle_clearance_x": "40",
+ "imex_tool_layout": "rear-left",
+ "is_imex": "1",
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000",
+ "6000",
+ "5000",
+ "6000",
+ "5000",
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500",
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50",
+ "100",
+ "50",
+ "100",
+ "50",
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25",
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5",
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2",
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0",
+ "0",
+ "0",
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60",
+ "120",
+ "60",
+ "120",
+ "60",
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150",
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20",
+ "40",
+ "20",
+ "40",
+ "20",
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S{first_layer_temperature[0]} T0\nM104 S{first_layer_temperature[1]} T1\nM104 S{first_layer_temperature[2]} T2\nM104 S{first_layer_temperature[3]} T3\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S{first_layer_temperature[0]} T0\nM109 S{first_layer_temperature[1]} T1\nM109 S{first_layer_temperature[2]} T2\nM109 S{first_layer_temperature[3]} T3\nstart_print\nG1 F5000\n{if first_layer_temperature[3] !=0 }\nTool3\nbrush_Tool3\n{endif}\n{if first_layer_temperature[2] !=0 }\nTool2\nbrush_Tool2\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nbrush_Tool1\n{endif}\n{if first_layer_temperature[0] !=0 }\nTool0\nbrush_Tool0\n{endif}\n{if first_layer_temperature[3] !=0 }\nTool3\nG1 Y280 X407.0 Z0.38 F5000.0 \nG1 Y175 X407.0 Z0.38 F1500.0 E15 \nG1 Y175 X407.4 Z0.38 F5000.0 \nG1 Y280 X407.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\n{if first_layer_temperature[2] !=0 }\nTool2\nG1 Y280 X12.0 Z0.38 F5000.0 \nG1 Y175 X12.0 Z0.38 F1500.0 E15 \nG1 Y175 X12.4 Z0.38 F5000.0 \nG1 Y280 X12.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\n{if first_layer_temperature[1] !=0 }\nTool1\nG1 Y280 X408.0 Z0.38 F5000.0 \nG1 Y175 X408.0 Z0.38 F1500.0 E15 \nG1 Y175 X408.4 Z0.38 F5000.0 \nG1 Y280 X408.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\n{if first_layer_temperature[0] !=0 }\nTool0\nG1 Y280 X11.0 Z0.38 F5000.0 \nG1 Y175 X11.0 Z0.38 F1500.0 E15 \nG1 Y175 X11.4 Z0.38 F5000.0 \nG1 Y280 X11.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z2 F2000\n{endif}\nG1 F5000\nM400\nSKEW_PROFILE LOAD=SKEW1\nG28 X\nG28 Y\nTool{initial_extruder}\nSTART_PRINTING_MODE\n\n",
+ "max_layer_height": [
+ "0.64",
+ "0.64",
+ "0.64",
+ "0.64"
+ ],
+ "min_layer_height": [
+ "0.2",
+ "0.2",
+ "0.2",
+ "0.2"
+ ],
+ "nozzle_diameter": [
+ "0.8",
+ "0.8",
+ "0.8",
+ "0.8"
+ ],
+ "nozzle_type": [
+ "hardened_steel",
+ "hardened_steel",
+ "hardened_steel",
+ "hardened_steel"
+ ],
+ "nozzle_volume": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "printable_area": [
+ "10x0",
+ "410x0",
+ "410x342.5",
+ "10x342.5"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1",
+ "2",
+ "3",
+ "4"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow",
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer IQEX",
+ "printer_variant": "0.8",
+ "retract_before_wipe": [
+ "70%",
+ "70%",
+ "70%",
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2",
+ "2",
+ "2",
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2",
+ "0.2",
+ "0.2",
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400",
+ "400",
+ "400",
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces",
+ "All Surfaces",
+ "All Surfaces",
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0",
+ "0",
+ "0",
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18",
+ "18",
+ "18",
+ "18"
+ ],
+ "retraction_length": [
+ "1.35",
+ "1.35",
+ "1.35",
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2",
+ "2",
+ "2",
+ "2"
+ ],
+ "retraction_speed": [
+ "120",
+ "120",
+ "120",
+ "120"
+ ],
+ "travel_slope": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "wipe": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "wipe_distance": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "z_hop": [
+ "1",
+ "1",
+ "1",
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift",
+ "Normal Lift",
+ "Normal Lift",
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer Single 0.4 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer Single 0.4 nozzle.json
new file mode 100644
index 0000000000..2d629ad93a
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer Single 0.4 nozzle.json
@@ -0,0 +1,167 @@
+{
+ "type": "machine",
+ "name": "Xplorer Single 0.4 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "P4sDniCHVUne9JJF",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}",
+ "change_filament_gcode": "M600",
+ "default_nozzle_volume_type": [
+ "High Flow"
+ ],
+ "default_print_profile": "0.20mm Standard @Xplorer 0.4",
+ "deretraction_speed": [
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F"
+ ],
+ "extruder_offset": [
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow"
+ ],
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S[first_layer_temperature] T[initial_tool]\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG4 P10000 ; wait for temp stabilisation\nstart_print \nTool[initial_tool]\nG90\nM109 S[first_layer_temperature] T[initial_tool]\nBrush_Tool[initial_tool]\nG1 Z5.0 F3000 \nG1 X100 Y1.0 Z0.38 F5000.0 \nG1 X280 Y1.0 Z0.38 F1500.0 E15 \nG1 X280 Y1.4 Z0.38 F5000.0 \nG1 X100 Y1.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z5.0 F3000\nSKEW_PROFILE LOAD=SKEW1\n",
+ "max_layer_height": [
+ "0.32"
+ ],
+ "min_layer_height": [
+ "0.04"
+ ],
+ "nozzle_diameter": [
+ "0.4"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer Single",
+ "printer_variant": "0.4",
+ "retract_before_wipe": [
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18"
+ ],
+ "retraction_length": [
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2"
+ ],
+ "retraction_speed": [
+ "120"
+ ],
+ "travel_slope": [
+ "1"
+ ],
+ "wipe": [
+ "1"
+ ],
+ "wipe_distance": [
+ "1"
+ ],
+ "z_hop": [
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer Single 0.6 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer Single 0.6 nozzle.json
new file mode 100644
index 0000000000..da3f85454a
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer Single 0.6 nozzle.json
@@ -0,0 +1,167 @@
+{
+ "type": "machine",
+ "name": "Xplorer Single 0.6 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "AN4SjJRQBVxztQoT",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}",
+ "change_filament_gcode": "M600",
+ "default_nozzle_volume_type": [
+ "High Flow"
+ ],
+ "default_print_profile": "0.30mm Standard @Xplorer 0.6",
+ "deretraction_speed": [
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F"
+ ],
+ "extruder_offset": [
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow"
+ ],
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S[first_layer_temperature] T[initial_tool]\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG4 P10000 ; wait for temp stabilisation\nstart_print \nTool[initial_tool]\nG90\nM109 S[first_layer_temperature] T[initial_tool]\nBrush_Tool[initial_tool]\nG1 Z5.0 F3000 \nG1 X100 Y1.0 Z0.38 F5000.0 \nG1 X280 Y1.0 Z0.38 F1500.0 E15 \nG1 X280 Y1.4 Z0.38 F5000.0 \nG1 X100 Y1.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z5.0 F3000\nSKEW_PROFILE LOAD=SKEW1\n",
+ "max_layer_height": [
+ "0.45"
+ ],
+ "min_layer_height": [
+ "0.12"
+ ],
+ "nozzle_diameter": [
+ "0.6"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer Single",
+ "printer_variant": "0.6",
+ "retract_before_wipe": [
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18"
+ ],
+ "retraction_length": [
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2"
+ ],
+ "retraction_speed": [
+ "120"
+ ],
+ "travel_slope": [
+ "1"
+ ],
+ "wipe": [
+ "1"
+ ],
+ "wipe_distance": [
+ "1"
+ ],
+ "z_hop": [
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer Single 0.8 nozzle.json b/resources/profiles/Vivedino/machine/Xplorer Single 0.8 nozzle.json
new file mode 100644
index 0000000000..76e26d31bf
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer Single 0.8 nozzle.json
@@ -0,0 +1,167 @@
+{
+ "type": "machine",
+ "name": "Xplorer Single 0.8 nozzle",
+ "inherits": "Xplorer common",
+ "from": "system",
+ "setting_id": "rGCuBhLqBfV0zFLJ",
+ "instantiation": "true",
+ "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\n{if layer_num != 0}\nG92 E0\n{endif}",
+ "change_filament_gcode": "M600",
+ "default_nozzle_volume_type": [
+ "High Flow"
+ ],
+ "default_print_profile": "0.40mm Standard @Xplorer 0.8",
+ "deretraction_speed": [
+ "120"
+ ],
+ "extruder_colour": [
+ "#FCE94F"
+ ],
+ "extruder_offset": [
+ "0x0"
+ ],
+ "extruder_type": [
+ "Direct Drive"
+ ],
+ "extruder_variant_list": [
+ "Direct Drive High Flow"
+ ],
+ "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nLOG_RESUME\nLOG_EXTR",
+ "long_retractions_when_cut": [
+ "0"
+ ],
+ "machine_max_acceleration_e": [
+ "6000",
+ "5000"
+ ],
+ "machine_max_acceleration_extruding": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_retracting": [
+ "5000",
+ "5000"
+ ],
+ "machine_max_acceleration_travel": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_x": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_y": [
+ "5000",
+ "2500"
+ ],
+ "machine_max_acceleration_z": [
+ "100",
+ "50"
+ ],
+ "machine_max_jerk_e": [
+ "2.5",
+ "1.25"
+ ],
+ "machine_max_jerk_x": [
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_y": [
+ "5",
+ "2.5"
+ ],
+ "machine_max_jerk_z": [
+ "0.4",
+ "0.2"
+ ],
+ "machine_max_junction_deviation": [
+ "0",
+ "0"
+ ],
+ "machine_max_speed_e": [
+ "120",
+ "60"
+ ],
+ "machine_max_speed_x": [
+ "300",
+ "150"
+ ],
+ "machine_max_speed_y": [
+ "300",
+ "150"
+ ],
+ "machine_max_speed_z": [
+ "40",
+ "20"
+ ],
+ "machine_start_gcode": "M104 S[first_layer_temperature] T[initial_tool]\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG4 P10000 ; wait for temp stabilisation\nstart_print \nTool[initial_tool]\nG90\nM109 S[first_layer_temperature] T[initial_tool]\nBrush_Tool[initial_tool]\nG1 Z5.0 F3000 \nG1 X100 Y1.0 Z0.38 F5000.0 \nG1 X280 Y1.0 Z0.38 F1500.0 E15 \nG1 X280 Y1.4 Z0.38 F5000.0 \nG1 X100 Y1.4 Z0.38 F1500.0 E30\nG92 E0\nG1 Z5.0 F3000\nSKEW_PROFILE LOAD=SKEW1\n",
+ "max_layer_height": [
+ "0.64"
+ ],
+ "min_layer_height": [
+ "0.2"
+ ],
+ "nozzle_diameter": [
+ "0.8"
+ ],
+ "printable_height": "401",
+ "printer_extruder_id": [
+ "1"
+ ],
+ "printer_extruder_variant": [
+ "Direct Drive High Flow"
+ ],
+ "printer_model": "Xplorer Single",
+ "printer_variant": "0.8",
+ "retract_before_wipe": [
+ "70%"
+ ],
+ "retract_length_toolchange": [
+ "2"
+ ],
+ "retract_lift_above": [
+ "0.2"
+ ],
+ "retract_lift_below": [
+ "400"
+ ],
+ "retract_lift_enforce": [
+ "All Surfaces"
+ ],
+ "retract_restart_extra": [
+ "0"
+ ],
+ "retract_restart_extra_toolchange": [
+ "0"
+ ],
+ "retract_when_changing_layer": [
+ "1"
+ ],
+ "retraction_distances_when_cut": [
+ "18"
+ ],
+ "retraction_length": [
+ "1.35"
+ ],
+ "retraction_minimum_travel": [
+ "2"
+ ],
+ "retraction_speed": [
+ "120"
+ ],
+ "travel_slope": [
+ "1"
+ ],
+ "wipe": [
+ "1"
+ ],
+ "wipe_distance": [
+ "1"
+ ],
+ "z_hop": [
+ "1"
+ ],
+ "z_hop_types": [
+ "Normal Lift"
+ ]
+}
diff --git a/resources/profiles/Vivedino/machine/Xplorer common.json b/resources/profiles/Vivedino/machine/Xplorer common.json
new file mode 100644
index 0000000000..c7b2054c7b
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/Xplorer common.json
@@ -0,0 +1,51 @@
+{
+ "type": "machine",
+ "name": "Xplorer common",
+ "inherits": "fdm_klipper_common",
+ "from": "system",
+ "instantiation": "false",
+ "default_filament_profile": [
+ "Generic PLA @System"
+ ],
+ "adaptive_bed_mesh_margin": "0",
+ "bed_mesh_max": "99999,99999",
+ "bed_mesh_min": "-99999,-99999",
+ "bed_mesh_probe_distance": "50,50",
+ "best_object_pos": "0.5,0.5",
+ "disable_m73": "0",
+ "emit_machine_limits_to_gcode": "0",
+ "enable_long_retraction_when_cut": "0",
+ "extruder_clearance_height_to_rod": "43",
+ "extruder_clearance_radius": "70",
+ "fan_kickstart": "0",
+ "fan_speedup_overhangs": "1",
+ "fan_speedup_time": "0",
+ "head_wrap_detect_zone": [],
+ "machine_end_gcode": "end_print",
+ "max_resonance_avoidance_speed": "120",
+ "min_resonance_avoidance_speed": "70",
+ "nozzle_height": "2.5",
+ "nozzle_hrc": "0",
+ "nozzle_type": "hardened_steel",
+ "nozzle_volume": "0",
+ "pellet_modded_printer": "0",
+ "preferred_orientation": "0",
+ "printable_area": [
+ "10x0",
+ "410x0",
+ "410x400",
+ "10x400"
+ ],
+ "purge_in_prime_tower": "1",
+ "resonance_avoidance": "0",
+ "single_extruder_multi_material": "0",
+ "support_air_filtration": "1",
+ "support_chamber_temp_control": "1",
+ "support_multi_bed_types": "0",
+ "thumbnails": "48x48/PNG, 300x300/PNG",
+ "thumbnails_format": "PNG",
+ "time_cost": "0",
+ "use_firmware_retraction": "0",
+ "use_relative_e_distances": "1",
+ "z_offset": "0"
+}
diff --git a/resources/profiles/Vivedino/machine/XplorerDualGantry.json b/resources/profiles/Vivedino/machine/XplorerDualGantry.json
new file mode 100644
index 0000000000..bf57b9137b
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/XplorerDualGantry.json
@@ -0,0 +1,12 @@
+{
+ "type": "machine_model",
+ "name": "Xplorer Dual Gantry",
+ "model_id": "XplorerDualGantry",
+ "nozzle_diameter": "0.4;0.6;0.8",
+ "machine_tech": "FFF",
+ "family": "Vivedino",
+ "bed_model": "Xplorer_buildplate_model.stl",
+ "bed_texture": "Xplorer_bed_texture_dualgantry.svg",
+ "hotend_model": "",
+ "default_materials": "Generic ABS @System;Generic PLA @System;Generic PLA-CF @System;Generic PETG @System;Generic TPU @System;Generic ASA @System;Generic PC @System;Generic PVA @System;Generic PA @System;Generic PA-CF @System"
+}
diff --git a/resources/profiles/Vivedino/machine/XplorerIDEX.json b/resources/profiles/Vivedino/machine/XplorerIDEX.json
new file mode 100644
index 0000000000..ffa5eee149
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/XplorerIDEX.json
@@ -0,0 +1,12 @@
+{
+ "type": "machine_model",
+ "name": "Xplorer IDEX",
+ "model_id": "XplorerIDEX",
+ "nozzle_diameter": "0.4;0.6;0.8",
+ "machine_tech": "FFF",
+ "family": "Vivedino",
+ "bed_model": "Xplorer_buildplate_model.stl",
+ "bed_texture": "Xplorer_bed_texture_idex.svg",
+ "hotend_model": "",
+ "default_materials": "Generic ABS @System;Generic PLA @System;Generic PLA-CF @System;Generic PETG @System;Generic TPU @System;Generic ASA @System;Generic PC @System;Generic PVA @System;Generic PA @System;Generic PA-CF @System"
+}
diff --git a/resources/profiles/Vivedino/machine/XplorerIQEX.json b/resources/profiles/Vivedino/machine/XplorerIQEX.json
new file mode 100644
index 0000000000..95a45c221d
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/XplorerIQEX.json
@@ -0,0 +1,12 @@
+{
+ "type": "machine_model",
+ "name": "Xplorer IQEX",
+ "model_id": "XplorerIQEX",
+ "nozzle_diameter": "0.4;0.6;0.8",
+ "machine_tech": "FFF",
+ "family": "Vivedino",
+ "bed_model": "Xplorer_buildplate_model.stl",
+ "bed_texture": "Xplorer_bed_texture_iqex.svg",
+ "hotend_model": "",
+ "default_materials": "Generic ABS @System;Generic PLA @System;Generic PLA-CF @System;Generic PETG @System;Generic TPU @System;Generic ASA @System;Generic PC @System;Generic PVA @System;Generic PA @System;Generic PA-CF @System"
+}
diff --git a/resources/profiles/Vivedino/machine/XplorerSingle.json b/resources/profiles/Vivedino/machine/XplorerSingle.json
new file mode 100644
index 0000000000..d3ad22af7a
--- /dev/null
+++ b/resources/profiles/Vivedino/machine/XplorerSingle.json
@@ -0,0 +1,12 @@
+{
+ "type": "machine_model",
+ "name": "Xplorer Single",
+ "model_id": "XplorerSingle",
+ "nozzle_diameter": "0.4;0.6;0.8",
+ "machine_tech": "FFF",
+ "family": "Vivedino",
+ "bed_model": "Xplorer_buildplate_model.stl",
+ "bed_texture": "",
+ "hotend_model": "",
+ "default_materials": "Generic ABS @System;Generic PLA @System;Generic PLA-CF @System;Generic PETG @System;Generic TPU @System;Generic ASA @System;Generic PC @System;Generic PVA @System;Generic PA @System;Generic PA-CF @System"
+}
diff --git a/resources/profiles/Vivedino/process/0.04mm Ultra Fine @Xplorer 0.4.json b/resources/profiles/Vivedino/process/0.04mm Ultra Fine @Xplorer 0.4.json
new file mode 100644
index 0000000000..22e6bd1a33
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.04mm Ultra Fine @Xplorer 0.4.json
@@ -0,0 +1,28 @@
+{
+ "type": "process",
+ "name": "0.04mm Ultra Fine @Xplorer 0.4",
+ "inherits": "fdm_process_xplorer_common",
+ "from": "system",
+ "setting_id": "7SZOHFM1nfZsGYHe",
+ "instantiation": "true",
+ "bottom_shell_layers": "7",
+ "initial_layer_print_height": "0.3",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_distance": "3",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "1",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "9",
+ "top_solid_infill_flow_ratio": "1",
+ "layer_height": "0.04",
+ "compatible_printers": [
+ "Xplorer Single 0.4 nozzle",
+ "Xplorer IDEX 0.4 nozzle",
+ "Xplorer Dual Gantry 0.4 nozzle",
+ "Xplorer IQEX 0.4 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.08mm Extra Fine @Xplorer 0.4.json b/resources/profiles/Vivedino/process/0.08mm Extra Fine @Xplorer 0.4.json
new file mode 100644
index 0000000000..f272186218
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.08mm Extra Fine @Xplorer 0.4.json
@@ -0,0 +1,28 @@
+{
+ "type": "process",
+ "name": "0.08mm Extra Fine @Xplorer 0.4",
+ "inherits": "fdm_process_xplorer_common",
+ "from": "system",
+ "setting_id": "OvD7mwbTHKOUsgiT",
+ "instantiation": "true",
+ "bottom_shell_layers": "4",
+ "initial_layer_print_height": "0.3",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_distance": "3",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "1",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "5",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.08",
+ "compatible_printers": [
+ "Xplorer Single 0.4 nozzle",
+ "Xplorer IDEX 0.4 nozzle",
+ "Xplorer Dual Gantry 0.4 nozzle",
+ "Xplorer IQEX 0.4 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.12mm Fine @Xplorer 0.4.json b/resources/profiles/Vivedino/process/0.12mm Fine @Xplorer 0.4.json
new file mode 100644
index 0000000000..fc742230b6
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.12mm Fine @Xplorer 0.4.json
@@ -0,0 +1,28 @@
+{
+ "type": "process",
+ "name": "0.12mm Fine @Xplorer 0.4",
+ "inherits": "fdm_process_xplorer_common",
+ "from": "system",
+ "setting_id": "9iNid6X4xIPh267R",
+ "instantiation": "true",
+ "bottom_shell_layers": "4",
+ "initial_layer_print_height": "0.3",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_distance": "3",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "1",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "5",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.12",
+ "compatible_printers": [
+ "Xplorer Single 0.4 nozzle",
+ "Xplorer IDEX 0.4 nozzle",
+ "Xplorer Dual Gantry 0.4 nozzle",
+ "Xplorer IQEX 0.4 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.16mm Optimal @Xplorer 0.4.json b/resources/profiles/Vivedino/process/0.16mm Optimal @Xplorer 0.4.json
new file mode 100644
index 0000000000..27a67f5a48
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.16mm Optimal @Xplorer 0.4.json
@@ -0,0 +1,28 @@
+{
+ "type": "process",
+ "name": "0.16mm Optimal @Xplorer 0.4",
+ "inherits": "fdm_process_xplorer_common",
+ "from": "system",
+ "setting_id": "6eUjAhfHVuxRSCQr",
+ "instantiation": "true",
+ "bottom_shell_layers": "4",
+ "initial_layer_print_height": "0.3",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_distance": "3",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "1",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "5",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.16",
+ "compatible_printers": [
+ "Xplorer Single 0.4 nozzle",
+ "Xplorer IDEX 0.4 nozzle",
+ "Xplorer Dual Gantry 0.4 nozzle",
+ "Xplorer IQEX 0.4 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.18mm Fine @Xplorer 0.6.json b/resources/profiles/Vivedino/process/0.18mm Fine @Xplorer 0.6.json
new file mode 100644
index 0000000000..8013e9a901
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.18mm Fine @Xplorer 0.6.json
@@ -0,0 +1,28 @@
+{
+ "type": "process",
+ "name": "0.18mm Fine @Xplorer 0.6",
+ "inherits": "fdm_process_xplorer_common_0_6",
+ "from": "system",
+ "setting_id": "tQHKzSbBNyoPQkup",
+ "instantiation": "true",
+ "bottom_shell_layers": "4",
+ "initial_layer_print_height": "0.3",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_distance": "3",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "1",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "5",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.18",
+ "compatible_printers": [
+ "Xplorer Single 0.6 nozzle",
+ "Xplorer IDEX 0.6 nozzle",
+ "Xplorer Dual Gantry 0.6 nozzle",
+ "Xplorer IQEX 0.6 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.20mm Standard @Xplorer 0.4.json b/resources/profiles/Vivedino/process/0.20mm Standard @Xplorer 0.4.json
new file mode 100644
index 0000000000..d880d2ba5a
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.20mm Standard @Xplorer 0.4.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.20mm Standard @Xplorer 0.4",
+ "inherits": "fdm_process_xplorer_common",
+ "from": "system",
+ "setting_id": "CiJAkwpq5pR6MfYD",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.3",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "10",
+ "outer_wall_speed": "130",
+ "skirt_loops": "2",
+ "slowdown_for_curled_perimeters": "1",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.20",
+ "compatible_printers": [
+ "Xplorer Single 0.4 nozzle",
+ "Xplorer IDEX 0.4 nozzle",
+ "Xplorer Dual Gantry 0.4 nozzle",
+ "Xplorer IQEX 0.4 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.24mm Fine @Xplorer 0.8.json b/resources/profiles/Vivedino/process/0.24mm Fine @Xplorer 0.8.json
new file mode 100644
index 0000000000..cd2be9355d
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.24mm Fine @Xplorer 0.8.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.24mm Fine @Xplorer 0.8",
+ "inherits": "fdm_process_xplorer_common_0_8",
+ "from": "system",
+ "setting_id": "yv9TMduh7bgD1IjO",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.3",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "10",
+ "outer_wall_speed": "130",
+ "skirt_loops": "2",
+ "slowdown_for_curled_perimeters": "1",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.24",
+ "compatible_printers": [
+ "Xplorer Single 0.8 nozzle",
+ "Xplorer IDEX 0.8 nozzle",
+ "Xplorer Dual Gantry 0.8 nozzle",
+ "Xplorer IQEX 0.8 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.24mm Optimal @Xplorer 0.6.json b/resources/profiles/Vivedino/process/0.24mm Optimal @Xplorer 0.6.json
new file mode 100644
index 0000000000..d628e1a121
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.24mm Optimal @Xplorer 0.6.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.24mm Optimal @Xplorer 0.6",
+ "inherits": "fdm_process_xplorer_common_0_6",
+ "from": "system",
+ "setting_id": "WKjOeNEZndeHrbQw",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.3",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "10",
+ "outer_wall_speed": "130",
+ "skirt_loops": "2",
+ "slowdown_for_curled_perimeters": "1",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.24",
+ "compatible_printers": [
+ "Xplorer Single 0.6 nozzle",
+ "Xplorer IDEX 0.6 nozzle",
+ "Xplorer Dual Gantry 0.6 nozzle",
+ "Xplorer IQEX 0.6 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.28mm Draft @Xplorer 0.4.json b/resources/profiles/Vivedino/process/0.28mm Draft @Xplorer 0.4.json
new file mode 100644
index 0000000000..eaad4d5f45
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.28mm Draft @Xplorer 0.4.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.28mm Draft @Xplorer 0.4",
+ "inherits": "fdm_process_xplorer_common",
+ "from": "system",
+ "setting_id": "zjvcZ59oVnh73fij",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.32",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "0",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.28",
+ "compatible_printers": [
+ "Xplorer Single 0.4 nozzle",
+ "Xplorer IDEX 0.4 nozzle",
+ "Xplorer Dual Gantry 0.4 nozzle",
+ "Xplorer IQEX 0.4 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.30mm Standard @Xplorer 0.6.json b/resources/profiles/Vivedino/process/0.30mm Standard @Xplorer 0.6.json
new file mode 100644
index 0000000000..4afd997862
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.30mm Standard @Xplorer 0.6.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.30mm Standard @Xplorer 0.6",
+ "inherits": "fdm_process_xplorer_common_0_6",
+ "from": "system",
+ "setting_id": "1NEAZrryKhhZNWoB",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.32",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "0",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.30",
+ "compatible_printers": [
+ "Xplorer Single 0.6 nozzle",
+ "Xplorer IDEX 0.6 nozzle",
+ "Xplorer Dual Gantry 0.6 nozzle",
+ "Xplorer IQEX 0.6 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.32mm Extra Draft @Xplorer 0.4.json b/resources/profiles/Vivedino/process/0.32mm Extra Draft @Xplorer 0.4.json
new file mode 100644
index 0000000000..21ac5b0f55
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.32mm Extra Draft @Xplorer 0.4.json
@@ -0,0 +1,27 @@
+{
+ "type": "process",
+ "name": "0.32mm Extra Draft @Xplorer 0.4",
+ "inherits": "fdm_process_xplorer_common",
+ "from": "system",
+ "setting_id": "IrvXoa4zZ1b2Y5cX",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.32",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "0",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "top_surface_line_width": "0.45",
+ "layer_height": "0.32",
+ "compatible_printers": [
+ "Xplorer Single 0.4 nozzle",
+ "Xplorer IDEX 0.4 nozzle",
+ "Xplorer Dual Gantry 0.4 nozzle",
+ "Xplorer IQEX 0.4 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.32mm Optimal @Xplorer 0.8.json b/resources/profiles/Vivedino/process/0.32mm Optimal @Xplorer 0.8.json
new file mode 100644
index 0000000000..57571e080b
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.32mm Optimal @Xplorer 0.8.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.32mm Optimal @Xplorer 0.8",
+ "inherits": "fdm_process_xplorer_common_0_8",
+ "from": "system",
+ "setting_id": "N3z9IBRaaeXwVLbl",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.32",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "0",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.32",
+ "compatible_printers": [
+ "Xplorer Single 0.8 nozzle",
+ "Xplorer IDEX 0.8 nozzle",
+ "Xplorer Dual Gantry 0.8 nozzle",
+ "Xplorer IQEX 0.8 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.36mm Draft @Xplorer 0.6.json b/resources/profiles/Vivedino/process/0.36mm Draft @Xplorer 0.6.json
new file mode 100644
index 0000000000..73783a0e5f
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.36mm Draft @Xplorer 0.6.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.36mm Draft @Xplorer 0.6",
+ "inherits": "fdm_process_xplorer_common_0_6",
+ "from": "system",
+ "setting_id": "uv3jUBdJRkJ4dWVd",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.32",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "0",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.36",
+ "compatible_printers": [
+ "Xplorer Single 0.6 nozzle",
+ "Xplorer IDEX 0.6 nozzle",
+ "Xplorer Dual Gantry 0.6 nozzle",
+ "Xplorer IQEX 0.6 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.40mm Standard @Xplorer 0.8.json b/resources/profiles/Vivedino/process/0.40mm Standard @Xplorer 0.8.json
new file mode 100644
index 0000000000..2082554b0c
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.40mm Standard @Xplorer 0.8.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.40mm Standard @Xplorer 0.8",
+ "inherits": "fdm_process_xplorer_common_0_8",
+ "from": "system",
+ "setting_id": "ZP9WXfiKDvSAjqU2",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.32",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "0",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.40",
+ "compatible_printers": [
+ "Xplorer Single 0.8 nozzle",
+ "Xplorer IDEX 0.8 nozzle",
+ "Xplorer Dual Gantry 0.8 nozzle",
+ "Xplorer IQEX 0.8 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.42mm Extra Draft @Xplorer 0.6.json b/resources/profiles/Vivedino/process/0.42mm Extra Draft @Xplorer 0.6.json
new file mode 100644
index 0000000000..9641ffb5ef
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.42mm Extra Draft @Xplorer 0.6.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.42mm Extra Draft @Xplorer 0.6",
+ "inherits": "fdm_process_xplorer_common_0_6",
+ "from": "system",
+ "setting_id": "JMbldCAibS7WnG1D",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.32",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "0",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.42",
+ "compatible_printers": [
+ "Xplorer Single 0.6 nozzle",
+ "Xplorer IDEX 0.6 nozzle",
+ "Xplorer Dual Gantry 0.6 nozzle",
+ "Xplorer IQEX 0.6 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.48mm Draft @Xplorer 0.8.json b/resources/profiles/Vivedino/process/0.48mm Draft @Xplorer 0.8.json
new file mode 100644
index 0000000000..a7942c6387
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.48mm Draft @Xplorer 0.8.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.48mm Draft @Xplorer 0.8",
+ "inherits": "fdm_process_xplorer_common_0_8",
+ "from": "system",
+ "setting_id": "OhseWRzfSjom4gC9",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.32",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "0",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.48",
+ "compatible_printers": [
+ "Xplorer Single 0.8 nozzle",
+ "Xplorer IDEX 0.8 nozzle",
+ "Xplorer Dual Gantry 0.8 nozzle",
+ "Xplorer IQEX 0.8 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/0.56mm Extra Draft @Xplorer 0.8.json b/resources/profiles/Vivedino/process/0.56mm Extra Draft @Xplorer 0.8.json
new file mode 100644
index 0000000000..065cbf975d
--- /dev/null
+++ b/resources/profiles/Vivedino/process/0.56mm Extra Draft @Xplorer 0.8.json
@@ -0,0 +1,26 @@
+{
+ "type": "process",
+ "name": "0.56mm Extra Draft @Xplorer 0.8",
+ "inherits": "fdm_process_xplorer_common_0_8",
+ "from": "system",
+ "setting_id": "EXEeYQRkvcwYeLDA",
+ "instantiation": "true",
+ "initial_layer_print_height": "0.32",
+ "inner_wall_speed": "190",
+ "internal_solid_infill_acceleration": "100%",
+ "min_skirt_length": "0",
+ "outer_wall_speed": "130",
+ "skirt_loops": "1",
+ "slowdown_for_curled_perimeters": "0",
+ "small_perimeter_speed": "50%",
+ "sparse_infill_acceleration": "100%",
+ "top_shell_layers": "4",
+ "top_solid_infill_flow_ratio": "0.87",
+ "layer_height": "0.56",
+ "compatible_printers": [
+ "Xplorer Single 0.8 nozzle",
+ "Xplorer IDEX 0.8 nozzle",
+ "Xplorer Dual Gantry 0.8 nozzle",
+ "Xplorer IQEX 0.8 nozzle"
+ ]
+}
diff --git a/resources/profiles/Vivedino/process/fdm_process_xplorer_common.json b/resources/profiles/Vivedino/process/fdm_process_xplorer_common.json
new file mode 100644
index 0000000000..c8f4cc55a7
--- /dev/null
+++ b/resources/profiles/Vivedino/process/fdm_process_xplorer_common.json
@@ -0,0 +1,265 @@
+{
+ "type": "process",
+ "name": "fdm_process_xplorer_common",
+ "inherits": "fdm_process_klipper_common",
+ "from": "system",
+ "instantiation": "false",
+ "accel_to_decel_enable": "1",
+ "accel_to_decel_factor": "50%",
+ "align_infill_direction_to_model": "0",
+ "alternate_extra_wall": "0",
+ "bottom_shell_thickness": "1",
+ "bottom_solid_infill_flow_ratio": "1",
+ "bottom_surface_density": "100%",
+ "bottom_surface_pattern": "monotonicline",
+ "bridge_acceleration": "1500",
+ "bridge_angle": "0",
+ "bridge_density": "100%",
+ "bridge_flow": "1.5",
+ "brim_ears_detection_length": "1",
+ "brim_ears_max_angle": "125",
+ "brim_type": "no_brim",
+ "calib_flowrate_topinfill_special_order": "0",
+ "counterbore_hole_bridging": "none",
+ "default_acceleration": "5000",
+ "default_jerk": "5",
+ "default_junction_deviation": "0",
+ "detect_narrow_internal_solid_infill": "0",
+ "dont_filter_internal_bridges": "limited",
+ "elefant_foot_compensation": "0.1",
+ "elefant_foot_compensation_layers": "2",
+ "enable_extra_bridge_layer": "disabled",
+ "enable_overhang_speed": "1",
+ "enable_prime_tower": "0",
+ "enforce_support_layers": "0",
+ "ensure_vertical_shell_thickness": "ensure_moderate",
+ "extra_perimeters_on_overhangs": "1",
+ "extra_solid_infills": "",
+ "extrusion_rate_smoothing_external_perimeter_only": "0",
+ "fill_multiline": "1",
+ "filter_out_gap_fill": "2",
+ "flush_into_infill": "0",
+ "flush_into_objects": "0",
+ "flush_into_support": "1",
+ "fuzzy_skin": "none",
+ "fuzzy_skin_first_layer": "0",
+ "fuzzy_skin_mode": "displacement",
+ "fuzzy_skin_noise_type": "classic",
+ "fuzzy_skin_octaves": "4",
+ "fuzzy_skin_persistence": "0.5",
+ "fuzzy_skin_point_distance": "0.8",
+ "fuzzy_skin_scale": "1",
+ "fuzzy_skin_thickness": "0.3",
+ "gap_fill_target": "nowhere",
+ "gap_infill_speed": "10",
+ "gcode_add_line_number": "0",
+ "gcode_comments": "0",
+ "gcode_label_objects": "1",
+ "hole_to_polyhole": "0",
+ "hole_to_polyhole_threshold": "0.01",
+ "hole_to_polyhole_twisted": "1",
+ "independent_support_layer_height": "1",
+ "infill_anchor": "10",
+ "infill_anchor_max": "10",
+ "infill_combination_max_layer_height": "100%",
+ "infill_jerk": "0",
+ "infill_lock_depth": "1",
+ "infill_overhang_angle": "60",
+ "infill_shift_step": "0.4",
+ "infill_wall_overlap": "20%",
+ "initial_layer_acceleration": "1000",
+ "initial_layer_jerk": "0",
+ "initial_layer_line_width": "120%",
+ "initial_layer_min_bead_width": "85%",
+ "initial_layer_travel_speed": "200",
+ "inner_wall_acceleration": "3000",
+ "inner_wall_jerk": "0",
+ "inner_wall_line_width": "110%",
+ "interlocking_beam": "0",
+ "interlocking_beam_layer_count": "2",
+ "interlocking_beam_width": "0.8",
+ "interlocking_boundary_avoidance": "2",
+ "interlocking_depth": "2",
+ "interlocking_orientation": "22.5",
+ "internal_bridge_angle": "0",
+ "internal_bridge_density": "100%",
+ "internal_bridge_flow": "1.5",
+ "internal_bridge_speed": "150%",
+ "internal_solid_infill_line_width": "120%",
+ "internal_solid_infill_pattern": "monotonicline",
+ "ironing_angle": "-1",
+ "ironing_inset": "0",
+ "ironing_pattern": "rectilinear",
+ "is_infill_first": "0",
+ "lateral_lattice_angle_1": "-45",
+ "lateral_lattice_angle_2": "45",
+ "line_width": "110%",
+ "make_overhang_printable": "0",
+ "make_overhang_printable_angle": "55",
+ "make_overhang_printable_hole_size": "0",
+ "max_bridge_length": "10",
+ "max_volumetric_extrusion_rate_slope": "0",
+ "max_volumetric_extrusion_rate_slope_segment_length": "1",
+ "min_bead_width": "85%",
+ "min_feature_size": "25%",
+ "min_length_factor": "0.5",
+ "min_width_top_surface": "0",
+ "mmu_segmented_region_interlocking_depth": "0",
+ "mmu_segmented_region_max_width": "0",
+ "notes": "",
+ "only_one_wall_first_layer": "0",
+ "only_one_wall_top": "1",
+ "ooze_prevention": "0",
+ "outer_wall_acceleration": "1000",
+ "outer_wall_jerk": "0",
+ "outer_wall_line_width": "100%",
+ "overhang_1_4_speed": "100",
+ "overhang_reverse": "0",
+ "overhang_reverse_internal_only": "0",
+ "overhang_reverse_threshold": "50%",
+ "post_process": [],
+ "precise_outer_wall": "1",
+ "precise_z_height": "0",
+ "preheat_steps": "1",
+ "preheat_time": "10",
+ "prime_tower_brim_width": "10",
+ "prime_tower_width": "30",
+ "prime_volume": "10",
+ "print_flow_ratio": "1",
+ "print_order": "default",
+ "raft_contact_distance": "0.1",
+ "raft_expansion": "1.5",
+ "raft_first_layer_density": "90%",
+ "raft_first_layer_expansion": "2",
+ "reduce_crossing_wall": "1",
+ "role_based_wipe_speed": "1",
+ "scarf_angle_threshold": "155",
+ "scarf_joint_flow_ratio": "1",
+ "scarf_joint_speed": "100%",
+ "scarf_overhang_threshold": "40%",
+ "seam_gap": "10%",
+ "seam_slope_conditional": "0",
+ "seam_slope_entire_loop": "0",
+ "seam_slope_inner_walls": "0",
+ "seam_slope_min_length": "20",
+ "seam_slope_start_height": "0",
+ "seam_slope_steps": "10",
+ "seam_slope_type": "none",
+ "single_extruder_multi_material_priming": "0",
+ "single_loop_draft_shield": "0",
+ "skeleton_infill_density": "25%",
+ "skeleton_infill_line_width": "100%",
+ "skin_infill_density": "25%",
+ "skin_infill_depth": "2",
+ "skin_infill_line_width": "100%",
+ "skirt_speed": "50",
+ "skirt_start_angle": "-135",
+ "skirt_type": "combined",
+ "slice_closing_radius": "0.049",
+ "slicing_mode": "regular",
+ "slow_down_layers": "0",
+ "small_area_infill_flow_compensation": "1",
+ "small_area_infill_flow_compensation_model": [
+ "0,0",
+ "\n0.2,0.4444",
+ "\n0.4,0.6145",
+ "\n0.6,0.7059",
+ "\n0.8,0.7619",
+ "\n1.5,0.8571",
+ "\n2,0.8889",
+ "\n3,0.9231",
+ "\n5,0.9520",
+ "\n10,1"
+ ],
+ "small_perimeter_threshold": "0",
+ "solid_infill_direction": "45",
+ "solid_infill_filament": "1",
+ "solid_infill_rotate_template": "45,135",
+ "sparse_infill_filament": "1",
+ "sparse_infill_line_width": "110%",
+ "sparse_infill_pattern": "gyroid",
+ "sparse_infill_rotate_template": "",
+ "spiral_finishing_flow_ratio": "0",
+ "spiral_mode_max_xy_smoothing": "200%",
+ "spiral_mode_smooth": "0",
+ "spiral_starting_flow_ratio": "0",
+ "staggered_inner_seams": "1",
+ "support_angle": "0",
+ "support_bottom_interface_spacing": "0.5",
+ "support_bottom_z_distance": "0.2",
+ "support_critical_regions_only": "0",
+ "support_expansion": "0",
+ "support_interface_not_for_body": "1",
+ "support_interface_pattern": "auto",
+ "support_ironing": "0",
+ "support_ironing_flow": "10%",
+ "support_ironing_pattern": "rectilinear",
+ "support_ironing_spacing": "0.1",
+ "support_line_width": "100%",
+ "support_object_first_layer_gap": "0.2",
+ "support_on_build_plate_only": "1",
+ "support_remove_small_overhang": "1",
+ "support_threshold_overlap": "50%",
+ "support_type": "tree(auto)",
+ "symmetric_infill_y_axis": "0",
+ "thick_bridges": "1",
+ "thick_internal_bridges": "1",
+ "timelapse_type": "0",
+ "top_bottom_infill_wall_overlap": "15%",
+ "top_surface_acceleration": "1000",
+ "top_surface_density": "100%",
+ "top_surface_jerk": "0",
+ "top_surface_line_width": "80%",
+ "top_surface_speed": "50",
+ "travel_acceleration": "5000",
+ "travel_jerk": "0",
+ "travel_speed": "250",
+ "travel_speed_z": "0",
+ "tree_support_adaptive_layer_height": "1",
+ "tree_support_angle_slow": "25",
+ "tree_support_auto_brim": "1",
+ "tree_support_branch_angle_organic": "40",
+ "tree_support_branch_diameter": "5",
+ "tree_support_branch_diameter_angle": "5",
+ "tree_support_branch_diameter_organic": "2",
+ "tree_support_branch_distance": "5",
+ "tree_support_branch_distance_organic": "1",
+ "tree_support_brim_width": "3",
+ "tree_support_tip_diameter": "0.8",
+ "tree_support_top_rate": "30%",
+ "wall_direction": "auto",
+ "wall_distribution_count": "1",
+ "wall_filament": "1",
+ "wall_generator": "arachne",
+ "wall_sequence": "inner wall/outer wall",
+ "wall_transition_angle": "10",
+ "wall_transition_filter_deviation": "25%",
+ "wall_transition_length": "100%",
+ "wipe_before_external_loop": "0",
+ "wipe_on_loops": "1",
+ "wipe_speed": "80%",
+ "wipe_tower_bridging": "10",
+ "wipe_tower_cone_angle": "10",
+ "wipe_tower_extra_flow": "100%",
+ "wipe_tower_extra_rib_length": "0",
+ "wipe_tower_extra_spacing": "100%",
+ "wipe_tower_filament": "0",
+ "wipe_tower_fillet_wall": "1",
+ "wipe_tower_max_purge_speed": "90",
+ "wipe_tower_rib_width": "8",
+ "wipe_tower_rotation_angle": "0",
+ "wipe_tower_wall_type": "rectangle",
+ "wiping_volumes_extruders": [
+ "70",
+ "70",
+ "70",
+ "70",
+ "70",
+ "70",
+ "70",
+ "70",
+ "70",
+ "70"
+ ],
+ "xy_hole_compensation": "0.05"
+}
diff --git a/resources/profiles/Vivedino/process/fdm_process_xplorer_common_0_6.json b/resources/profiles/Vivedino/process/fdm_process_xplorer_common_0_6.json
new file mode 100644
index 0000000000..3158e4f186
--- /dev/null
+++ b/resources/profiles/Vivedino/process/fdm_process_xplorer_common_0_6.json
@@ -0,0 +1,8 @@
+{
+ "type": "process",
+ "name": "fdm_process_xplorer_common_0_6",
+ "inherits": "fdm_process_xplorer_common",
+ "from": "system",
+ "instantiation": "false",
+ "initial_layer_print_height": "0.3"
+}
diff --git a/resources/profiles/Vivedino/process/fdm_process_xplorer_common_0_8.json b/resources/profiles/Vivedino/process/fdm_process_xplorer_common_0_8.json
new file mode 100644
index 0000000000..a6b36e2947
--- /dev/null
+++ b/resources/profiles/Vivedino/process/fdm_process_xplorer_common_0_8.json
@@ -0,0 +1,8 @@
+{
+ "type": "process",
+ "name": "fdm_process_xplorer_common_0_8",
+ "inherits": "fdm_process_xplorer_common",
+ "from": "system",
+ "instantiation": "false",
+ "initial_layer_print_height": "0.4"
+}
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 1f9d4b7c53..16f08e6a56 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -325,6 +325,12 @@ set(lisbslic3r_sources
Geometry/VoronoiUtils.cpp
Geometry/VoronoiUtils.hpp
Geometry/VoronoiVisualUtils.hpp
+ IMEXArrange.cpp
+ IMEXArrange.hpp
+ IMEXHelpers.cpp
+ IMEXHelpers.hpp
+ IMEXZones.cpp
+ IMEXZones.hpp
InstanceLock.cpp
InstanceLock.hpp
Int128.hpp
diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp
index 4ae316a055..8a22201c51 100644
--- a/src/libslic3r/Format/bbs_3mf.cpp
+++ b/src/libslic3r/Format/bbs_3mf.cpp
@@ -1,5 +1,6 @@
#include "../libslic3r.h"
#include "../Exception.hpp"
+#include "../IMEXHelpers.hpp"
#include "../Model.hpp"
#include "../Preset.hpp"
#include "../Utils.hpp"
@@ -368,6 +369,12 @@ static constexpr const char* FIRST_LAYER_PRINT_SEQUENCE_ATTR = "first_layer_prin
static constexpr const char* OTHER_LAYERS_PRINT_SEQUENCE_ATTR = "other_layers_print_sequence";
static constexpr const char* OTHER_LAYERS_PRINT_SEQUENCE_NUMS_ATTR = "other_layers_print_sequence_nums";
static constexpr const char* SPIRAL_VASE_MODE = "spiral_mode";
+static constexpr const char* IMEX_PARALLEL_MODE_ATTR = "imex_parallel_mode";
+// The same attribute before the feature was renamed IMEX. Plate metadata is matched by exact
+// string and written with set_key_value, so it never passes through handle_legacy: without this
+// a project saved in that window loads every plate back on the Primary mode, silently.
+static constexpr const char* IXEX_PARALLEL_MODE_ATTR_LEGACY = "ixex_parallel_mode";
+static constexpr const char* IMEX_HEAD_FILAMENT_MAP_ATTR = "imex_head_filament_map";
static constexpr const char* FILAMENT_MAP_MODE_ATTR = "filament_map_mode";
static constexpr const char* FILAMENT_MAP_ATTR = "filament_maps";
static constexpr const char* FILAMENT_VOL_MAP_ATTR = "filament_volume_maps";
@@ -4576,6 +4583,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
std::istringstream(value) >> std::boolalpha >> spiral_mode;
m_curr_plater->config.set_key_value("spiral_mode", new ConfigOptionBool(spiral_mode));
}
+ else if (key == IMEX_PARALLEL_MODE_ATTR || key == IXEX_PARALLEL_MODE_ATTR_LEGACY) {
+ m_curr_plater->config.set_key_value("imex_parallel_mode", new ConfigOptionString(value));
+ }
+ else if (key == IMEX_HEAD_FILAMENT_MAP_ATTR) {
+ m_curr_plater->config.set_key_value("imex_head_filament_map", new ConfigOptionString(value));
+ }
else if (key == FILAMENT_MAP_MODE_ATTR)
{
FilamentMapMode map_mode = FilamentMapMode::fmmAutoForFlush;
@@ -8277,6 +8290,23 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
if (spiral_mode_opt)
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SPIRAL_VASE_MODE << "\" " << VALUE_ATTR << "=\"" << spiral_mode_opt->getBool() << "\"/>\n";
+ {
+ // Mode names are user supplied free text, so the value has to be escaped for an
+ // attribute. xml_escape_double_quotes_attribute_value() is used rather than
+ // xml_escape() because it also emits tab/CR/LF as numeric character references:
+ // XML normalizes literal whitespace in attribute values on read, which would
+ // silently rename the mode. The reader takes the value straight from expat,
+ // which resolves both entities and character references, so this round-trips.
+ auto* imex_mode_opt = plate_data->config.option("imex_parallel_mode");
+ if (imex_mode_opt && !imex_mode_opt->value.empty() && imex_mode_opt->value != kImexPrimaryMode)
+ stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << IMEX_PARALLEL_MODE_ATTR << "\" " << VALUE_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(imex_mode_opt->value) << "\"/>\n";
+ }
+ {
+ auto* imex_hfm_opt = plate_data->config.option("imex_head_filament_map");
+ if (imex_hfm_opt && !imex_hfm_opt->value.empty())
+ stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << IMEX_HEAD_FILAMENT_MAP_ATTR << "\" " << VALUE_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(imex_hfm_opt->value) << "\"/>\n";
+ }
+
//filament map related
ConfigOption* filament_map_mode_opt = plate_data->config.option("filament_map_mode");
t_config_enum_names filament_map_mode_names = ConfigOptionEnum::get_enum_names();
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index accb67b3da..3c6fcee856 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -29,6 +29,7 @@
#include "enum_bitmask.hpp"
#include "libslic3r.h"
#include "I18N.hpp"
+#include "IMEXHelpers.hpp"
#include "GCode.hpp"
#include
#include "Exception.hpp"
@@ -87,6 +88,7 @@
#include
#include
#include
+#include
#include
#include
@@ -1710,7 +1712,16 @@ static std::vector get_path_of_change_filament(const Print& print)
: gcodegen.config().nozzle_temperature.get_at(new_fi);
if (std::abs(tcr.print_z) < EPSILON)
base_temp = gcodegen.config().nozzle_temperature_initial_layer.get_at(new_fi);
- const std::string t_token = " T" + std::to_string(new_extruder_id);
+ // This pass strips the M109 that OozePrevention::post_toolchange just emitted into
+ // toolchange_gcode_str, so it must look for the tool index that emission actually
+ // wrote -- physical, translated by GCodeWriter::set_temperature -- not the logical
+ // id the temperature lookups above use. Matching on the logical id would leave the
+ // blocking M109 in place and silently defeat the tower interface temperature.
+ // (The sibling scan further down reads WipeTower2 output, whose set_extruder_temp
+ // emits no T at all, so it is deliberately left alone.)
+ const int heater_id = imex_physical_heater_for(
+ gcodegen.config().is_imex.value, gcodegen.config().physical_extruder_map, new_extruder_id);
+ const std::string t_token = " T" + std::to_string(heater_id);
std::string out;
out.reserve(toolchange_gcode_str.size());
size_t pos = 0;
@@ -1731,7 +1742,7 @@ static std::vector get_path_of_change_filament(const Print& print)
const std::string t_val = trimmed.substr(t_pos + 1, t_end == std::string::npos ? std::string::npos : t_end - (t_pos + 1));
if (!t_val.empty()) {
try {
- matches_extruder = std::stoi(t_val) == new_extruder_id;
+ matches_extruder = std::stoi(t_val) == heater_id;
} catch (...) {
matches_extruder = false;
}
@@ -1763,44 +1774,6 @@ static std::vector get_path_of_change_filament(const Print& print)
toolchange_gcode_str.swap(out);
}
- if (toolchange_temp_override > 0) {
- const std::string preheat_token = "preheat T" + std::to_string(new_extruder_id);
- const int preheat_temp = interface_temp > 0 ? interface_temp : toolchange_temp_override;
- std::string out;
- out.reserve(tcr_rotated_gcode.size());
- size_t pos = 0;
- while (pos < tcr_rotated_gcode.size()) {
- size_t line_end = tcr_rotated_gcode.find('\n', pos);
- if (line_end == std::string::npos)
- line_end = tcr_rotated_gcode.size();
- std::string line = tcr_rotated_gcode.substr(pos, line_end - pos);
- std::string trimmed = line;
- trimmed.erase(0, trimmed.find_first_not_of(" \t"));
- const bool is_preheat_line = (trimmed.find(preheat_token) != std::string::npos);
- if (is_preheat_line) {
- // Preserve early-preheat timing while forcing interface temp for contact toolchanges.
- size_t s_pos = trimmed.find('S');
- if (s_pos != std::string::npos) {
- size_t s_end = trimmed.find_first_not_of("0123456789", s_pos + 1);
- trimmed.replace(s_pos + 1,
- (s_end == std::string::npos ? trimmed.size() : s_end) - (s_pos + 1),
- std::to_string(preheat_temp));
- // Reapply left indentation from the original line.
- size_t line_prefix = line.find_first_not_of(" \t");
- if (line_prefix != std::string::npos)
- line = line.substr(0, line_prefix) + trimmed;
- else
- line = trimmed;
- }
- }
- out.append(line);
- if (line_end < tcr_rotated_gcode.size())
- out.push_back('\n');
- pos = line_end + 1;
- }
- tcr_rotated_gcode.swap(out);
- }
-
if (toolchange_temp_override > 0 && interface_temp > 0) {
const std::string t_token = " T" + std::to_string(new_extruder_id);
std::string out;
@@ -3251,6 +3224,38 @@ static BambuBedType to_bambu_bed_type(BedType type)
return bambu_bed_type;
}
+// Orca IMEX: Returns the PHYSICAL tool indices the active IMEX mode drives, or an EMPTY
+// vector in primary mode -- a single tool, so there are no parallel carriages to list.
+// The mode's Primary head IS included in parallel modes; callers handle it themselves.
+// In copy/mirror parallel modes, secondary carriages never receive tool-change
+// commands — the firmware mirrors the primary's moves — so they don't appear in
+// tool_ordering.all_extruders(). This helper extracts the active mode's tool string
+// from the Print and delegates parsing to IMEXHelpers::parse_imex_active_tools so the
+// "phys[:role]" tokenization matches every other IMEX consumer (PartPlate zones,
+// GCodeViewer legend, Plater warnings).
+// Indices are PHYSICAL T-indices. Callers that need a filament-slot (for PA / temp
+// lookups) must resolve via first_filament_for_physical_head or resolve_filament_for_head.
+static std::vector get_imex_active_tools(const Print& print)
+{
+ std::vector active_tools;
+
+ if (!print.config().is_imex.value || print.objects().empty())
+ return active_tools;
+
+ const std::string& raw_mode = print.objects().front()->config().imex_parallel_mode.value;
+ const std::string active_mode = raw_mode.empty() ? kImexPrimaryMode : raw_mode;
+
+ // Primary mode drives a single tool, so there are no parallel carriages to list.
+ if (active_mode == kImexPrimaryMode)
+ return active_tools;
+
+ // An unresolved mode, and a mode the tools array is too short to cover, both hand back an
+ // empty tools string, which parses to no tools.
+ for (const auto& [phys, role] : parse_imex_active_tools(find_imex_mode(print.config(), active_mode).active_tools))
+ active_tools.push_back(phys);
+ return active_tools;
+}
+
void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb)
{
PROFILE_FUNC();
@@ -3660,6 +3665,51 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
std::vector is_extruder_used(std::max(size_t(MAXIMUM_EXTRUDER_NUMBER), print.config().filament_diameter.size()), 0);
for (unsigned int extruder : tool_ordering.all_extruders())
is_extruder_used[extruder] = true;
+
+ // Orca IMEX: mark the LOGICAL filament slot each active SECONDARY carriage
+ // will load during the print. is_extruder_used is logical-slot indexed
+ // (consumed as `is_extruder_used[N]` in machine_start_gcode templates), but
+ // get_imex_active_tools returns PHYSICAL extruder indices. On any printer
+ // with physical_extruder_map size > 1 (MMU/AFC), writing the physical index
+ // into a logical-indexed array marks the wrong slot.
+ //
+ // The primary is intentionally SKIPPED here — its filament is already
+ // covered by tool_ordering.all_extruders() above (which lists the slots the
+ // objects on the plate are actually assigned to). Marking the primary
+ // again via the pem `first_filament_for_physical_head` fallback would
+ // pollute is_extruder_used with the *first* slot routed to the primary's
+ // physical extruder, which is generally not the slot the user assigned to
+ // the printing object. Same skip-primary pattern as the IMEX PA emission
+ // path in _do_export().
+ //
+ // For secondaries: translate physical -> logical via the per-plate
+ // imex_head_filament_map (set by the IMEX ghost picker), with
+ // first_filament_for_physical_head as the fallback when no override is set.
+ if (print.config().is_imex.value && !print.objects().empty()
+ && initial_extruder_id != (unsigned int)-1) {
+ const auto plate_head_map = parse_imex_head_filament_map(
+ print.objects().front()->config().imex_head_filament_map.value);
+ const ConfigOptionInts& pem = print.config().physical_extruder_map;
+ // Bounds-checked, not get_at(): a clamp would suppress whichever head sits at pem[0].
+ // A miss stays -1 and matches no head, so nothing is skipped -- see IMEXHelpers.hpp.
+ const int primary_physical =
+ ((int) initial_extruder_id >= 0 &&
+ (int) initial_extruder_id < (int) pem.values.size())
+ ? pem.values[(int) initial_extruder_id]
+ : -1;
+ // Bound by the array, not by the filament count. The array is padded to
+ // MAXIMUM_EXTRUDER_NUMBER on purpose: start G-code addresses HEADS through it
+ // (fdm_toolchanger_common.json gates M104 T0..T5 on it), and a parallel copy print has
+ // more heads than filaments by definition. PlaceholderParser clamps an out-of-range
+ // first_layer_temperature read to filament 0, which is the right temperature when every
+ // head is printing the same filament. Narrowing this to the filament count leaves the
+ // secondary carriages unheated.
+ for (int logical : imex_secondary_logical_slots(
+ get_imex_active_tools(print), primary_physical, plate_head_map, pem))
+ if (logical >= 0 && logical < (int) is_extruder_used.size())
+ is_extruder_used[logical] = true;
+ }
+
this->placeholder_parser().set("is_extruder_used", new ConfigOptionBools(is_extruder_used));
{
@@ -3900,6 +3950,103 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Sync variant-mapped params into placeholder_parser before processing start gcode
update_placeholder_parser_with_variant_params();
+ // IDEX/IQEX: set {imex_mode}, {imex_mode_index}, {imex_mode_gcode} placeholders before
+ // any G-code script is processed so they are available in machine_start_gcode,
+ // filament_start_gcode, filament_end_gcode, change_filament_gcode, etc.
+ // {imex_mode_names} and {imex_mode_gcodes} array placeholders are already accessible
+ // via the printer config (e.g. {imex_mode_names[0]}, {imex_mode_gcodes[1]}).
+ std::string imex_active_mode;
+ int imex_active_mode_index = 0;
+ std::string imex_active_mode_gcode;
+ m_imex_parallel_mode.clear();
+ m_imex_head_filament_map.clear();
+ if (print.config().is_imex.value && !print.objects().empty()) {
+ const std::string& raw = print.objects().front()->config().imex_parallel_mode.value;
+ imex_active_mode = raw.empty() ? kImexPrimaryMode : raw;
+ m_imex_head_filament_map = parse_imex_head_filament_map(
+ print.objects().front()->config().imex_head_filament_map.value);
+ // Look `name` up in the printer's mode table, filling in its index, script and
+ // tool roster. False means the name matches no row, i.e. the mode is unresolved.
+ // find_imex_mode() owns the "which row, and what if a sibling array is short"
+ // rule for every IMEX consumer; see IMEXHelpers.hpp.
+ std::string imex_active_mode_tools;
+ auto resolve_mode = [&](const std::string& name) {
+ const ImexMode mode = find_imex_mode(print.config(), name);
+ if (!mode.found())
+ return false;
+ imex_active_mode_index = mode.index;
+ imex_active_mode_gcode = mode.gcode;
+ imex_active_mode_tools = mode.active_tools;
+ return true;
+ };
+ // An unresolved mode name falls back to Primary rather than being carried into the
+ // parallel-mode paths. `imex_parallel_mode` is a plain string stored on the plate and
+ // matched against the printer's `imex_mode_names` by value, so it goes stale whenever
+ // the two drift apart -- a mode renamed or deleted after a plate was set to it, or a
+ // project shared between printer presets that name their modes differently.
+ //
+ // Without the fallback the exporter took every "not Primary" branch while every lookup
+ // keyed on the mode name came back empty, and wrote a file that is wrong rather than
+ // merely unconfigured: the 1st->2nd layer temperature branch is mutually exclusive with
+ // the standard one, so an empty active-tool roster meant NO head got its transition and
+ // all of them held nozzle_temperature_initial_layer for the whole print; and
+ // imex_suppresses_bare_toolchange() dropped the initial T on the assumption that a
+ // mode script would select the tool, while the mode script -- also looked up by name --
+ // did not exist. Print::validate()'s IMEX rules did not catch it either: they resolve
+ // the same name to an empty tools string, which yields no declared primary and skips
+ // the guard. Primary is the one interpretation that is always well-formed: single
+ // carriage, ordinary temperatures, ordinary tool changes.
+ //
+ // Warned rather than silent, and warned rather than blocked. Silent is not an option:
+ // the plate still labels itself with the stale mode and draws no zones (PartPlate's
+ // zone builder returns early on the same unresolved state), so a user who asked for two
+ // parts in copy mode would get one with nothing anywhere saying why. Blocking is not an
+ // option either -- opening someone else's 3MF on a differently-named preset is a
+ // legitimate way to arrive here, and the Primary interpretation prints correctly, so
+ // refusing to slice would be a regression for a case that has a good answer.
+ //
+ // The second trigger below is the ragged-table case, and it lands here rather than
+ // anywhere else because the damage is identical: the mode's name resolves, but the
+ // printer's `imex_mode_active_tools` is too short to reach its row (or the row is
+ // explicitly empty), so the roster every branch above keys on comes back empty and
+ // the export takes the parallel path with nothing in it. Nothing used to report
+ // that -- half the lookup sites in the tree turned a short tools array into "mode
+ // not found" and the other half did not, and this one did not. One rule now, in
+ // find_imex_mode(), and one warning, here.
+ const bool imex_mode_resolved = resolve_mode(imex_active_mode);
+ if (imex_active_mode != kImexPrimaryMode && (!imex_mode_resolved || imex_active_mode_tools.empty())) {
+ print.active_step_add_warning(
+ PrintStateBase::WarningLevel::NON_CRITICAL,
+ imex_mode_resolved
+ ? Slic3r::format(_(L("The IDEX/IQEX mode \"%1%\" has no tools assigned on the selected printer. "
+ "Printing in Primary mode instead. Assign the mode's tools in Printer "
+ "Settings, or pick another mode from the plate's IDEX/IQEX button.")),
+ imex_active_mode)
+ : Slic3r::format(_(L("This plate is set to the IDEX/IQEX mode \"%1%\", which the selected printer "
+ "does not define. Printing in Primary mode instead. Pick a mode from the "
+ "plate's IDEX/IQEX button, or restore the mode in Printer Settings.")),
+ imex_active_mode));
+ imex_active_mode = kImexPrimaryMode;
+ imex_active_mode_index = 0;
+ imex_active_mode_gcode.clear();
+ imex_active_mode_tools.clear();
+ // Primary is a real row in the table (always the first one), so it may carry its own
+ // setup script; resolve it the same way any other selected mode would be.
+ resolve_mode(imex_active_mode);
+ }
+ m_imex_parallel_mode = imex_active_mode;
+ }
+ this->placeholder_parser().set("imex_mode", imex_active_mode);
+ this->placeholder_parser().set("imex_mode_index", imex_active_mode_index);
+ this->placeholder_parser().set("imex_mode_gcode", imex_active_mode_gcode);
+
+ // IDEX/IQEX: process mode G-code BEFORE machine_start_gcode so that any
+ // {global abc = 1} declarations in the mode script are visible to machine_start_gcode.
+ // The processed result is buffered here and written to the file after temp setup below.
+ std::string imex_processed_gcode;
+ if (!imex_active_mode_gcode.empty())
+ imex_processed_gcode = this->placeholder_parser_process(
+ "imex_mode_gcode", imex_active_mode_gcode, initial_extruder_id);
// Expand the file header only after the start-up placeholders and writer state are ready.
// Use the regular custom G-code path so invalid placeholders report the template name.
if (!print.config().file_start_gcode.value.empty())
@@ -4073,6 +4220,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write(m_writer.set_chamber_temperature(max_chamber_temp, true)); // set chamber_temperature
}
+ // Write IMEX mode G-code first (processed above), then machine_start_gcode.
+ if (!imex_processed_gcode.empty())
+ file.writeln(imex_processed_gcode);
+
// Write the custom start G-code
file.writeln(machine_start_gcode);
// Mark the end of the machine start g-code so the GCodeProcessor usage-block builder knows where user
@@ -4189,6 +4340,49 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Set initial extruder only after custom start G-code.
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
file.write(this->set_extruder(initial_extruder_id, 0.));
+
+ // IMEX parallel modes only: emit PA for all secondary active tools.
+ // In primary mode, regular tool-change PA handles each tool as it becomes active.
+ // In parallel modes no tool changes occur, so every carriage must be addressed
+ // explicitly here before printing starts.
+ if (!m_imex_parallel_mode.empty() && m_imex_parallel_mode != kImexPrimaryMode
+ && !m_config.physical_extruder_map.values.empty()) {
+ // initial_physical: pem-translate the print's initial logical extruder so the
+ // loop can skip the primary head (which emitted PA via the normal path).
+ // Then pem-invert each active physical head back to its first routed filament
+ // for the PA setting lookup. Guarded on non-empty pem above.
+ // Bounds-checked, not get_at() -- see the note on imex_pem_tool_for in
+ // IMEXHelpers.hpp. A clamped initial_physical would skip whichever active head
+ // equals pem[0], leaving it with no PA at all.
+ const int initial_physical =
+ ((int) initial_extruder_id >= 0 &&
+ (int) initial_extruder_id < (int) m_config.physical_extruder_map.values.size())
+ ? m_config.physical_extruder_map.values[(int) initial_extruder_id]
+ : -1;
+ // enable_pressure_advance and pressure_advance are indexed by COLUMN, not by
+ // filament slot. filament_diameter is one entry per slot and is never
+ // variant-expanded, so it is the slot-space bound; translate the slot to its column
+ // with get_filament_config_index(), as the second-layer temperature loop below does.
+ const int num_pa_filament_slots = (int) print.config().filament_diameter.values.size();
+ for (int tool_idx : get_imex_active_tools(print)) {
+ // Unlike the second-layer temperature loop, the primary is skipped here:
+ // set_extruder() above already emitted its PA with the pem tool qualifier.
+ if (tool_idx == initial_physical) continue;
+ const int logical = resolve_filament_for_head(
+ m_imex_head_filament_map, m_config.physical_extruder_map, tool_idx);
+ // resolve_filament_for_head() answers in pem's index space -- one entry per
+ // NOZZLE -- while these two options are indexed per FILAMENT SLOT. The spaces
+ // diverge when the printer has more nozzles than the project has filaments, and
+ // get_at() would clamp a past-the-end index to filament 0 and pin its PA onto a
+ // secondary carriage. Same guard the second-layer temperature loop uses.
+ if (logical < 0 || logical >= num_pa_filament_slots) continue;
+ const size_t pa_fi = get_filament_config_index(logical);
+ if (!print.config().enable_pressure_advance.get_at(pa_fi)) continue;
+ file.write(m_writer.set_pressure_advance(
+ print.config().pressure_advance.get_at(pa_fi),
+ tool_idx));
+ }
+ }
}
this->m_objsWithBrim.clear();
@@ -6142,6 +6336,7 @@ std::string GCode::generate_timelapse_gcode(const Print &print, coordf_t print_z
// In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
// For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths
// and performing the extruder specific extrusions together.
+
LayerResult GCode::process_layer(
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
@@ -6343,6 +6538,7 @@ LayerResult GCode::process_layer(
+ "\n";
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
}
+
//BBS: set layer time fan speed after layer change gcode
gcode += ";_SET_FAN_SPEED_CHANGING_LAYER\n";
@@ -6506,17 +6702,56 @@ LayerResult GCode::process_layer(
gcode += m_writer.set_jerk_xy(NOZZLE_CONFIG(default_jerk));
}
- // Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
- // nozzle_temperature_initial_layer vs. temperature settings.
- for (const Extruder& extruder : m_writer.extruders()) {
- if ((print.config().single_extruder_multi_material.value || m_ooze_prevention.enable) &&
- extruder.id() != m_writer.filament()->id())
- // In single extruder multi material mode, set the temperature for the current extruder only.
- continue;
- size_t fi = get_filament_config_index((int)extruder.id());
- int temperature = print.config().nozzle_temperature.get_at(fi);
- if (temperature > 0 && temperature != print.config().nozzle_temperature_initial_layer.get_at(fi))
- gcode += m_writer.set_temperature(temperature, false, extruder.id());
+ // Transition from 1st to 2nd layer: set non-initial-layer nozzle temperatures.
+ // IMEX parallel modes: secondary carriages (T1-T3) never receive tool-change commands,
+ // so they're not in m_writer.extruders() and multiple_extruders==false (max id==0).
+ // Use the static set_temperature path for IMEX so the T index is always emitted.
+ // In primary mode, fall through to the standard path — tool changes handle temps normally.
+ if (!m_imex_parallel_mode.empty() && m_imex_parallel_mode != kImexPrimaryMode) {
+ // All active tools need explicit temps — none receive tool-change commands,
+ // so we can't rely on the condition used for non-IMEX (temp != initial_layer_temp).
+ // A tool whose initial and regular temps are the same still needs to be set here.
+ // Mutually exclusive with the `else` below, so a head skipped here gets no
+ // transition at all. `tool_idx` is physical; the printing head uses this layer's
+ // own filament, the parallel carriages resolve through the head map.
+ // nozzle_temperature is indexed by column: bound in slot space, or an out-of-slot
+ // logical reaches get_filament_config_index and comes back as filament 0.
+ // filament_diameter is the slot-space yardstick -- one entry per filament slot,
+ // never variant-expanded -- and is the bound the pressure advance loop uses too.
+ const int num_filament_slots = (int) print.config().filament_diameter.values.size();
+ // Bounds-checked, not get_at() -- see IMEXHelpers.hpp. A clamp would hand the
+ // "initial" branch below to whichever secondary sits on pem[0], giving it the wrong
+ // filament's transition temperature and never its own.
+ const int initial_physical =
+ ((int) first_extruder_id >= 0 &&
+ (int) first_extruder_id < (int) m_config.physical_extruder_map.values.size())
+ ? m_config.physical_extruder_map.values[(int) first_extruder_id]
+ : -1;
+ for (int tool_idx : get_imex_active_tools(print)) {
+ const int logical = (tool_idx == initial_physical)
+ ? (int)first_extruder_id
+ : resolve_filament_for_head(
+ m_imex_head_filament_map, m_config.physical_extruder_map, tool_idx);
+ if (logical < 0 || logical >= num_filament_slots) continue;
+ // Variant-expanded printers column each filament; index as the `else` does.
+ int temperature = print.config().nozzle_temperature.get_at(get_filament_config_index(logical));
+ if (temperature > 0)
+ gcode += GCodeWriter::set_temperature(temperature, m_writer.config.gcode_flavor, false,
+ tool_idx, "set IMEX tool temperature");
+ }
+ } else {
+ // Adjust nozzle temperatures as prescribed by the nozzle dependent
+ // nozzle_temperature_initial_layer vs. temperature settings.
+ for (const Extruder& extruder : m_writer.extruders()) {
+ if ((print.config().single_extruder_multi_material.value || m_ooze_prevention.enable) &&
+ extruder.id() != m_writer.filament()->id())
+ // In single extruder multi material mode, set the temperature for the current extruder only.
+ continue;
+ size_t fi = get_filament_config_index((int)extruder.id());
+ int temperature = print.config().nozzle_temperature.get_at(fi);
+ if (temperature > 0 && temperature != print.config().nozzle_temperature_initial_layer.get_at(fi))
+ gcode += m_writer.set_temperature(temperature, false, extruder.id());
+ }
}
// BBS
@@ -7898,6 +8133,31 @@ void GCode::append_full_config(const Print &print, std::string &str)
// banning it from the dump has no effect on the feature; the only H2C/H2D delta is the M9711/M971
// snapshot reposition.
"farthest_point_timelapse"sv,
+ // The IMEX (IDEX/IQEX parallel printing) keys are newly-registered printer/process keys whose
+ // defaults are non-nil, so leaving them in the dump would add fourteen `; imex_* = `
+ // lines to every printer's config block — an ordinary single-nozzle machine included. Excluding
+ // them keeps the config-dump byte-identical for the whole shipping fleet; the IMEX printers pay
+ // the same price the timelapse/prime-volume keys above already pay, and the active mode is still
+ // visible in the body of their g-code through the injected imex_mode_gcodes macro. Every key is
+ // read at slice time from m_config (get_imex_active_tools / find_imex_mode / the zone layout
+ // helpers) and never parsed back out of the dump, so banning them costs the feature nothing:
+ // the g-code viewer's overlay reads the loaded presets, not the config block, and the two
+ // per-plate process keys round-trip through the 3MF's model_settings.config plate metadata
+ // (bbs_3mf.cpp IMEX_PARALLEL_MODE_ATTR / IMEX_HEAD_FILAMENT_MAP_ATTR), not through this dump.
+ "is_imex"sv,
+ "imex_firmware_managed_zones"sv,
+ "imex_gantry_count"sv,
+ "imex_tools_per_gantry"sv,
+ "imex_tool_layout"sv,
+ "imex_nozzle_clearance_x"sv,
+ "imex_nozzle_clearance_y"sv,
+ "imex_carriage_margin"sv,
+ "imex_viz_theme"sv,
+ "imex_mode_names"sv,
+ "imex_mode_active_tools"sv,
+ "imex_mode_gcodes"sv,
+ "imex_parallel_mode"sv,
+ "imex_head_filament_map"sv,
"compatible_printers"sv,
"compatible_prints"sv,
"filament_colour_type"sv,
@@ -10304,9 +10564,21 @@ 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);
}
- gcode += set_filament_pressure_advance(new_filament_id);
+ // In IMEX parallel modes each carriage needs an explicit tool address. In primary
+ // mode, and on a non-IMEX printer, imex_pem_tool_for() returns -1 and the qualifier
+ // is omitted, because regular tool changes already handle PA there.
+ gcode += set_filament_pressure_advance(new_filament_id,
+ imex_pem_tool_for((int) new_filament_id, m_imex_parallel_mode, m_config.physical_extruder_map));
- gcode += m_writer.toolchange(new_filament_id, new_extruder_id);
+ // Same suppression as the long multi-extruder path: at print-start in IMEX
+ // parallel modes the user's mode_gcode + machine_start_gcode is responsible
+ // for tool selection, so the slicer's bare T would be a duplicate. Mid-
+ // print toolchanges (count > 1) emit normally — Print::validate() blocks
+ // multi-color in configurations where mid-print T wouldn't make sense
+ // (see imex_multicolor_block_reason).
+ const std::string toolchange_command = m_writer.toolchange(new_filament_id, new_extruder_id);
+ if (!imex_suppresses_bare_toolchange(m_imex_parallel_mode, m_toolchange_count))
+ gcode += toolchange_command;
if (Extruder *fil = m_writer.filament())
fil->set_config_index((int)get_filament_config_index((int)fil->id()));
return gcode;
@@ -10350,7 +10622,27 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
// If ooze prevention is enabled, park current extruder in the nearest
// standby point and set it to the standby temperature.
- if (m_ooze_prevention.enable && m_writer.filament() != nullptr)
+ //
+ // Same-physical short-circuit: on an IMEX printer, skip the standby cool-down when the
+ // old and new filament both route to the same physical extruder via physical_extruder_map
+ // (AFC/MMU lane swaps where the active heater stays selected and only the lane changes).
+ // The cool-down → re-heat round trip is pointless there — same nozzle, same heater, just a
+ // different filament feeding it. post_toolchange below still emits M109 to the new
+ // filament's print temp, so per-lane temperature differences are still handled.
+ //
+ // Gated on is_imex so ooze prevention behaves exactly as upstream on every other printer.
+ // 73 shipping profiles enable it by default (Snapmaker, Flashforge, Prusa and others) and
+ // none of them are asking for this optimisation.
+ auto same_physical_extruder = [&](int a, int b) {
+ if (!m_config.is_imex.value)
+ return false;
+ const auto& pem = m_config.physical_extruder_map.values;
+ return !pem.empty() && a >= 0 && b >= 0
+ && a < (int)pem.size() && b < (int)pem.size()
+ && pem[a] == pem[b];
+ };
+ if (m_ooze_prevention.enable && m_writer.filament() != nullptr
+ && !same_physical_extruder(m_writer.filament()->id(), (int)new_filament_id))
gcode += m_ooze_prevention.pre_toolchange(*this);
// BBS
@@ -10620,7 +10912,13 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
std::string toolchange_command = m_writer.toolchange(new_filament_id, next_nozzle_id);
if (Extruder *fil = m_writer.filament())
fil->set_config_index((int)get_filament_config_index((int)fil->id()));
- if (!custom_gcode_changes_tool(toolchange_gcode_parsed, m_writer.toolchange_prefix(), new_filament_id))
+ // See imex_suppresses_bare_toolchange() — only suppress the print-start initial
+ // T in parallel modes (the user's imex_mode_gcode + machine_start_gcode owns
+ // tool activation there). Mid-print T emits normally; Print::validate blocks
+ // multi-color setups where it wouldn't make sense.
+ const bool suppress_imex_bare = imex_suppresses_bare_toolchange(m_imex_parallel_mode, m_toolchange_count);
+ if (!custom_gcode_changes_tool(toolchange_gcode_parsed, m_writer.toolchange_prefix(), new_filament_id)
+ && !suppress_imex_bare)
gcode += toolchange_command;
else {
// user provided his own toolchange gcode, no need to do anything
@@ -10693,14 +10991,16 @@ 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);
- gcode += set_filament_pressure_advance(new_filament_id);
+ // See the note on the single-extruder path above: -1 outside IMEX parallel modes.
+ gcode += set_filament_pressure_advance(new_filament_id,
+ imex_pem_tool_for((int) new_filament_id, m_imex_parallel_mode, m_config.physical_extruder_map));
//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)
+std::string GCode::set_filament_pressure_advance(unsigned int filament_id, int tool)
{
const size_t fi = get_filament_config_index(filament_id);
if (!m_config.enable_pressure_advance.get_at(fi))
@@ -10708,7 +11008,9 @@ std::string GCode::set_filament_pressure_advance(unsigned int filament_id)
// 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));
+ // tool >= 0 addresses one IMEX carriage explicitly; -1 omits the qualifier, so every
+ // caller that does not pass one emits exactly what it did before.
+ return m_writer.set_pressure_advance(m_config.pressure_advance.get_at(fi), tool);
}
inline std::string polygon_to_string(const Polygon &polygon, Print *print, bool is_print_space = false) {
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index da78aa0a35..c89b646ae2 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -262,6 +262,15 @@ public:
void export_layer_filaments(GCodeProcessorResult* result);
//BBS: set offset for gcode writer
void set_gcode_offset(double x, double y) { m_writer.set_xy_offset(x, y); m_processor.set_xy_offset(x, y);}
+ // IMEX firmware-managed slice: writer offset is augmented by the IMEX shift so the
+ // emitted gcode is centered at bed origin (firmware fans copies/mirrors out from there).
+ // Processor offset stays at plate_origin only so the gcode-preview visualizer renders
+ // the centered toolpath at bed center, not at the prepare-view zone placement. When
+ // imex_x/imex_y are zero this is byte-identical to set_gcode_offset(x, y).
+ void set_gcode_offset_with_imex_shift(double x, double y, double imex_x, double imex_y) {
+ m_writer.set_xy_offset(x + imex_x, y + imex_y);
+ m_processor.set_xy_offset(x, y);
+ }
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
const Vec2d& origin() const { return m_origin; }
@@ -302,7 +311,11 @@ public:
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);
+ // tool addresses one IMEX carriage explicitly. -1 means no carriage: it omits the tool
+ // qualifier on Klipper, Marlin and BBL, and keeps RepRapFirmware's historical `D0`, since a
+ // bare M572 there applies to whatever tool is selected and errors when none is. That is
+ // what every non-IMEX caller wants, and what imex_pem_tool_for() returns off IMEX.
+ std::string set_filament_pressure_advance(unsigned int filament_id, int tool = -1);
bool is_BBL_Printer();
WipeTowerType wipe_tower_type();
@@ -836,6 +849,14 @@ protected:
std::unique_ptr m_pa_processor;
+ // IMEX: active parallel mode name ("primary", "copy", "iq-copy", etc.).
+ // Set at the start of export. Empty string means non-IMEX or not yet set.
+ // PA and temperature tool-qualification is only applied when this is not "primary".
+ std::string m_imex_parallel_mode;
+ // IMEX: parsed per-plate head→filament overrides (physical T-index → 1-based filament slot).
+ // Cached from imex_head_filament_map at print start. Empty map means "fall back to pem".
+ std::map m_imex_head_filament_map;
+
std::unique_ptr m_wipe_tower;
std::unique_ptr m_small_area_infill_flow_compensator;
diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp
index bd68ec1abd..b2ddc75a34 100644
--- a/src/libslic3r/GCode/WipeTower.cpp
+++ b/src/libslic3r/GCode/WipeTower.cpp
@@ -1472,6 +1472,10 @@ public:
void set_multi_nozzle_group_result(const MultiNozzleUtils::LayeredNozzleGroupResult *multi_nozzle_group_result) { m_multi_nozzle_group_result = multi_nozzle_group_result; }
void set_physical_extruder_map(const std::vector &physical_extruder_map) { m_physical_extruder_map = physical_extruder_map; }
+ // physical_extruder_map defaults to the single element {0} and is only widened to one entry
+ // per extruder on IMEX printers, so indexing it by tool is out of range on any other
+ // multi-extruder machine. Fall back to the tool's own index, matching the bounds-checked
+ // form GCodeProcessor uses for the same map.
private:
std::string set_normal_acceleration() {
std::vector accelerations = m_is_first_layer ? m_first_layer_normal_accelerations : m_normal_accelerations;
diff --git a/src/libslic3r/GCode/WipeTowerEstimate.cpp b/src/libslic3r/GCode/WipeTowerEstimate.cpp
index c421689b6d..9b6c7882dd 100644
--- a/src/libslic3r/GCode/WipeTowerEstimate.cpp
+++ b/src/libslic3r/GCode/WipeTowerEstimate.cpp
@@ -28,6 +28,28 @@ static const ConfigOption *option_of(const ConfigBase &config, const char *key)
return nullptr;
}
+bool prime_tower_is_printed(const ConfigBase &config, int used_filaments, int num_objects, bool has_mixed_filament)
+{
+ const auto *ept = config.option("enable_prime_tower");
+ if (ept == nullptr || !ept->value)
+ return false;
+ // normalize_fdm_2 only reconsiders the option when the plate uses a filament; below that it
+ // leaves the user's choice alone, and there is no tower to reason about either way.
+ if (used_filaments <= 0)
+ return true;
+
+ const ConfigOption *timelapse = option_of(config, "timelapse_type");
+ const bool smooth_timelapse = timelapse != nullptr && timelapse->getInt() == int(TimelapseType::tlSmooth);
+ const auto *wrapping = config.option("enable_wrapping_detection");
+ const bool enable_wrapping = wrapping != nullptr && wrapping->value;
+ if (smooth_timelapse || enable_wrapping)
+ return true;
+
+ const ConfigOption *sequence = option_of(config, "print_sequence");
+ const bool by_object = sequence != nullptr && sequence->getInt() == int(PrintSequence::ByObject);
+ return !((used_filaments == 1 && !has_mixed_filament) || (by_object && num_objects > 1));
+}
+
WipeTowerType resolve_wipe_tower_type(const ConfigBase &config)
{
// printer_model is what the CLI keys its Bambu Lab detection on; the GUI's vendor flag
diff --git a/src/libslic3r/GCode/WipeTowerEstimate.hpp b/src/libslic3r/GCode/WipeTowerEstimate.hpp
index 387028649b..1f0b674a11 100644
--- a/src/libslic3r/GCode/WipeTowerEstimate.hpp
+++ b/src/libslic3r/GCode/WipeTowerEstimate.hpp
@@ -44,4 +44,17 @@ WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase
double layer_height,
double max_object_height);
+// Whether a prime tower is PRINTED for a plate. The footprint estimate above answers how big a
+// tower is and never reads enable_prime_tower or print_sequence, so it reports a size for a plate
+// that prints no tower at all; the authority is DynamicPrintConfig::normalize_fdm_2(), which clears
+// enable_prime_tower before the plate is sliced. This mirrors that rule so pre-slice consumers can
+// ask it without mutating a config, and a test pins the two together.
+//
+// used_filaments: the plate's filament SLOTS as authored - a mixed slot counts once, matching
+// Print::extruders(), which is what normalize_fdm_2 is handed.
+// num_objects: objects printed on the plate; only ByObject sequencing looks at it.
+// has_mixed_filament: filament_is_mixed is a PROJECT option, so it is passed rather than read -
+// `config` only has to carry the print preset's own keys.
+bool prime_tower_is_printed(const ConfigBase &config, int used_filaments, int num_objects, bool has_mixed_filament);
+
} // namespace Slic3r
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index 6176b6693c..62382f8214 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -6,6 +6,7 @@
#include "Point.hpp"
#include "Polygon.hpp"
#include "PrintConfig.hpp"
+#include "IMEXHelpers.hpp"
#include "ClipperUtils.hpp"
#include "Geometry/ArcWelder.hpp"
#include "Line.hpp"
@@ -358,8 +359,16 @@ std::string GCodeWriter::set_temperature(unsigned int temperature, GCodeFlavor f
std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool) const
{
// set tool to -1 to make sure we won't emit T parameter for single extruder or SEMM
- if (!this->multiple_extruders || m_single_extruder_multi_material)
+ if (!this->multiple_extruders || m_single_extruder_multi_material) {
tool = -1;
+ } else {
+ // Every caller of this overload addresses filaments by LOGICAL id, but M104/M109
+ // name a physical heater -- so translate at the one point they all pass through.
+ // The static overload below is already physical-in (GCode.cpp:5952,
+ // GCode/GCodeProcessor.cpp:1410) and must not be remapped, which is why the
+ // translation lives here and not there.
+ tool = imex_physical_heater_for(this->config.is_imex.value, this->config.physical_extruder_map, tool);
+ }
return set_temperature(temperature, this->config.gcode_flavor, wait, tool);
}
@@ -582,26 +591,43 @@ std::string GCodeWriter::set_junction_deviation(double junction_deviation){
return gcode.str();
}
-std::string GCodeWriter::set_pressure_advance(double pa) const
+std::string GCodeWriter::set_pressure_advance(double pa, int tool) const
{
std::ostringstream gcode;
if (pa < 0)
return gcode.str();
- if(m_is_bbl_printers){
- //SoftFever: set L1000 to use linear model
- gcode << "M900 K" < targets a specific extruder; a bare M572 applies to whatever tool is
+ // currently selected and errors when there isn't one. Callers with no tool index keep
+ // the historical D0 rather than the bare form: PA would otherwise depend on
+ // tool-selection state for every RRF user, none of whom are asking for IMEX.
+ gcode << "M572 D" << (tool >= 0 ? tool : 0)
+ << " S" << std::setprecision(4) << pa << "; Override pressure advance value\n";
+ } else if (FLAVOR_IS(gcfRepetier)) {
+ // Repetier M233: X is quadratic (K), Y is linear (L).
+ // Applying the value to both parameters simultaneously.
+ gcode << "M233 X" << std::setprecision(4) << pa << " Y" << std::setprecision(4) << pa << " ; Override pressure advance value\n";
+ } else if (FLAVOR_IS(gcfMarlinFirmware)) {
+ // Marlin 2.x supports T parameter for per-extruder LA
+ gcode << "M900 K" << std::setprecision(4) << pa;
+ if (tool >= 0)
+ gcode << " T" << tool;
+ gcode << "; Override pressure advance value\n";
+ } else {
+ // Marlin Legacy and everything else: single-extruder M900, no tool parameter
+ gcode << "M900 K" << std::setprecision(4) << pa << "; Override pressure advance value\n";
}
return gcode.str();
}
diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp
index 12a56dacdc..3c90a4e9e1 100644
--- a/src/libslic3r/GCodeWriter.hpp
+++ b/src/libslic3r/GCodeWriter.hpp
@@ -72,7 +72,7 @@ public:
// Orca: set acceleration and jerk in one command for Klipper
std::string set_accel_and_jerk(unsigned int acceleration, double jerk);
std::string set_junction_deviation(double junction_deviation);
- std::string set_pressure_advance(double pa) const;
+ std::string set_pressure_advance(double pa, int tool = -1) const;
std::string set_input_shaping(char axis, float damp, float freq, std::string type) const;
std::string reset_e(bool force = false);
std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const;
diff --git a/src/libslic3r/IMEXArrange.cpp b/src/libslic3r/IMEXArrange.cpp
new file mode 100644
index 0000000000..ff4007f98a
--- /dev/null
+++ b/src/libslic3r/IMEXArrange.cpp
@@ -0,0 +1,220 @@
+#include "libslic3r/IMEXArrange.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "libslic3r/Arrange.hpp"
+#include "libslic3r/BoundingBox.hpp"
+#include "libslic3r/ClipperUtils.hpp"
+#include "libslic3r/Config.hpp"
+#include "libslic3r/IMEXHelpers.hpp"
+#include "libslic3r/IMEXZones.hpp"
+#include "libslic3r/Point.hpp"
+#include "libslic3r/Polygon.hpp"
+#include "libslic3r/PrintConfig.hpp"
+#include "libslic3r/libslic3r.h"
+
+namespace Slic3r {
+
+namespace {
+
+// The arranger's names for the keep-outs. finish() leaves the fences out when it arranges a
+// plate inside its zone, where the zone itself is the bed shape.
+constexpr const char* kFenceName = "IMEXOutsidePrimaryZone";
+constexpr const char* kStripName = "IMEXCollisionZone";
+
+// Each plate computes its zones from where it sits, so the same zone differs in the last bits.
+bool same_zone(const BoundingBoxf& a, const BoundingBoxf& b)
+{
+ return is_approx(a.min, b.min) && is_approx(a.max, b.max);
+}
+
+BoundingBox inset(BoundingBox box, const Point& by)
+{
+ box.min += by;
+ box.max -= by;
+ return box;
+}
+
+// The parts of `bed` outside `zone`: full-depth boxes left and right of it, and the pieces in
+// front of and behind it. Empty pieces are left out.
+std::vector boxes_around(const BoundingBox& bed, const BoundingBox& zone)
+{
+ std::vector out;
+ auto add = [&out](coord_t x0, coord_t y0, coord_t x1, coord_t y1) {
+ if (x1 > x0 && y1 > y0)
+ out.emplace_back(Point(x0, y0), Point(x1, y1));
+ };
+ const coord_t x0 = std::max(zone.min.x(), bed.min.x());
+ const coord_t x1 = std::min(zone.max.x(), bed.max.x());
+ add(bed.min.x(), bed.min.y(), zone.min.x(), bed.max.y());
+ add(zone.max.x(), bed.min.y(), bed.max.x(), bed.max.y());
+ add(x0, bed.min.y(), x1, zone.min.y());
+ add(x0, zone.max.y(), x1, bed.max.y());
+ return out;
+}
+
+} // namespace
+
+std::optional imex_arrange_zones(const ImexZoneLayout& layout)
+{
+ if (!layout.primary_zone_box)
+ return std::nullopt;
+ ImexArrangeZones zones{*layout.primary_zone_box, {}};
+ for (const BoundingBoxf3& strip : layout.collision_zones)
+ zones.collision_zones.emplace_back(Vec2d(strip.min.head<2>()), Vec2d(strip.max.head<2>()));
+ return zones;
+}
+
+void ImexArrangeInput::read_config(const DynamicPrintConfig& full_config)
+{
+ // A plate the arrange adds has no mode of its own, so it takes the process preset's.
+ added_plate.reset();
+ if (adds_plates) {
+ const auto* mode = full_config.option("imex_parallel_mode");
+ added_plate = imex_arrange_zones(compute_imex_zone_layout(full_config, kImexPrimaryMode, mode ? mode->value : std::string(),
+ get_extents(full_config.opt("printable_area")->values)));
+ }
+
+ // An estimate: auto brim picks its own width, which brim_width stands in for.
+ const auto* brim_type = full_config.option>("brim_type");
+ brim = !brim_type || brim_type->value == btNoBrim || brim_type->value == btInnerOnly ?
+ 0. :
+ full_config.opt_float("brim_width") + full_config.opt_float("brim_object_gap");
+}
+
+ImexArranger::ImexArranger(const ImexArrangeInput& input, const arrangement::ArrangeParams& params, const Points& bed)
+ : m_bed(bed)
+ , m_bed_bb(bed)
+ , m_margin(scaled(std::max(0.f, params.bed_shrink_x)), scaled(std::max(0.f, params.bed_shrink_y)))
+ , m_adds_plates(input.adds_plates)
+{
+ for (size_t i = 0; i < input.beds.size(); ++i)
+ if (input.beds[i])
+ m_beds.push_back({int(i), *input.beds[i]});
+
+ // The bed shape can be a zone only when every plate being arranged has it, and so does every
+ // plate the arrange may add.
+ const auto& first = input.beds.empty() ? std::nullopt : input.beds.front();
+ if (first &&
+ std::all_of(input.beds.begin(), input.beds.end(),
+ [&](const std::optional& b) { return b && same_zone(b->primary_zone, first->primary_zone); }) &&
+ (!input.adds_plates || (input.added_plate && same_zone(input.added_plate->primary_zone, first->primary_zone))))
+ m_shared_room = room_in(first->primary_zone);
+
+ // The beds past the plates being arranged become new plates.
+ if (input.adds_plates && input.added_plate)
+ for (int bed_idx = int(input.beds.size()); bed_idx < MAX_NUM_PLATES; ++bed_idx)
+ m_beds.push_back({bed_idx, *input.added_plate});
+
+ // A zone narrower than its margins leaves no room, and its fences then fill the whole bed.
+ const Point strip_margin = m_margin + Point::Constant(scaled(input.brim));
+ for (const Bed& b : m_beds) {
+ if (!m_shared_room)
+ for (const BoundingBox& box : boxes_around(m_bed_bb, inset(scaled(b.zones.primary_zone), m_margin)))
+ m_keep_outs.push_back({b.bed_idx, box, true});
+ for (const BoundingBoxf& strip : b.zones.collision_zones)
+ m_keep_outs.push_back({b.bed_idx, inset(scaled(strip), -strip_margin), false});
+ }
+}
+
+std::optional ImexArranger::room_in(const BoundingBoxf& zone) const
+{
+ BoundingBox room = inset(scaled(zone), m_margin);
+ room.min = room.min.cwiseMax(m_bed_bb.min);
+ room.max = room.max.cwiseMin(m_bed_bb.max);
+ if (room.min.x() >= room.max.x() || room.min.y() >= room.max.y())
+ return std::nullopt;
+ return room;
+}
+
+Points ImexArranger::bed_shape() const { return m_shared_room ? m_shared_room->polygon().points : m_bed; }
+
+void ImexArranger::add_keep_outs(arrangement::ArrangePolygons& fixed) const
+{
+ for (const KeepOut& keep_out : m_keep_outs) {
+ arrangement::ArrangePolygon ap;
+ ap.poly.contour = keep_out.box.polygon();
+ ap.is_virt_object = true;
+ ap.bed_idx = keep_out.bed_idx;
+ ap.height = 1;
+ ap.name = keep_out.fence ? kFenceName : kStripName;
+ fixed.push_back(std::move(ap));
+ }
+}
+
+bool ImexArranger::crosses_keep_out(const arrangement::ArrangePolygon& part, int bed_idx) const
+{
+ const Polygon hull = part.transformed_poly().contour;
+ return std::any_of(m_keep_outs.begin(), m_keep_outs.end(), [&](const KeepOut& keep_out) {
+ return keep_out.bed_idx == bed_idx && !intersection(hull, keep_out.box.polygon()).empty();
+ });
+}
+
+void ImexArranger::finish(arrangement::ArrangePolygons& items,
+ const arrangement::ArrangePolygons& before,
+ const arrangement::ArrangePolygons& fixed,
+ const arrangement::ArrangeParams& params) const
+{
+ for (arrangement::ArrangePolygon& part : items)
+ if (crosses_keep_out(part, part.bed_idx))
+ part.bed_idx = arrangement::UNARRANGED;
+
+ // Only fenced plates need centering, and arranging one plate never fences.
+ if (m_shared_room || !m_adds_plates)
+ return;
+
+ arrangement::ArrangeParams one_plate = params;
+ one_plate.progressind = {};
+ for (const Bed& b : m_beds) {
+ const std::optional room = room_in(b.zones.primary_zone);
+ std::vector on_bed;
+ for (size_t i = 0; i < items.size(); ++i)
+ if (items[i].bed_idx == b.bed_idx)
+ on_bed.push_back(i);
+ if (on_bed.empty() || !room)
+ continue;
+
+ arrangement::ArrangePolygons parts, bed_fixed;
+ // Bed 0, where the nester takes a part as placeable rather than as one that does not fit.
+ for (size_t i : on_bed) {
+ parts.push_back(before[i]);
+ parts.back().bed_idx = 0;
+ }
+ for (const arrangement::ArrangePolygon& ap : fixed)
+ if (ap.bed_idx == b.bed_idx && ap.name != kFenceName) {
+ bed_fixed.push_back(ap);
+ bed_fixed.back().bed_idx = 0;
+ }
+ arrangement::arrange(parts, bed_fixed, room->polygon().points, one_plate);
+ // A canceled pass leaves parts it never reached on bed 0 with their old ids.
+ if (one_plate.stopcondition && one_plate.stopcondition())
+ return;
+
+ // Keep the first layout when this one does not fit.
+ if (!std::all_of(parts.begin(), parts.end(), [&](const arrangement::ArrangePolygon& part) {
+ return part.bed_idx == 0 && !crosses_keep_out(part, b.bed_idx);
+ }))
+ continue;
+
+ // By-object printing follows the packing order, which this pass numbered 0..n-1: carry
+ // it over onto the ids the first pass gave these parts.
+ std::vector ids;
+ for (size_t i : on_bed)
+ ids.push_back(items[i].itemid);
+ std::sort(ids.begin(), ids.end());
+ for (size_t k = 0; k < on_bed.size(); ++k) {
+ items[on_bed[k]].translation = parts[k].translation;
+ items[on_bed[k]].rotation = parts[k].rotation;
+ items[on_bed[k]].itemid = ids[parts[k].itemid];
+ }
+ }
+}
+
+} // namespace Slic3r
diff --git a/src/libslic3r/IMEXArrange.hpp b/src/libslic3r/IMEXArrange.hpp
new file mode 100644
index 0000000000..c4fd3877c4
--- /dev/null
+++ b/src/libslic3r/IMEXArrange.hpp
@@ -0,0 +1,101 @@
+#pragma once
+
+#include
+#include
+
+#include "libslic3r/Arrange.hpp"
+#include "libslic3r/BoundingBox.hpp"
+#include "libslic3r/Point.hpp"
+
+namespace Slic3r {
+
+class DynamicPrintConfig;
+struct ImexZoneLayout;
+
+// A plate's IDEX/IQEX zones as Arrange sees them, in plate-local millimeters.
+struct ImexArrangeZones
+{
+ BoundingBoxf primary_zone;
+ std::vector collision_zones;
+};
+
+// The zones of `layout`, none when it has no primary zone, i.e. outside a parallel mode.
+std::optional imex_arrange_zones(const ImexZoneLayout& layout);
+
+// What an arrange needs to know about the plates it packs into.
+struct ImexArrangeInput
+{
+ // Per arranger bed, in order: the zones of the plate it packs into, none for a plate whose
+ // parts may go anywhere on the bed.
+ std::vector> beds;
+ // Whether the beds past `beds` become new plates, as when arranging every plate, and the
+ // zones such a plate gets.
+ bool adds_plates = false;
+ std::optional added_plate;
+ // Room a strip keeps for the brim: brim width plus its gap to the part, in mm.
+ double brim = 0.;
+
+ // Sets `added_plate` and `brim` from the full config. Call after setting `adds_plates`.
+ void read_config(const DynamicPrintConfig& full_config);
+};
+
+// Keeps an arrange inside each IDEX/IQEX plate's primary zone and out of that zone's carriage
+// collision strips.
+//
+// The arranger packs every bed into one bed shape. When every bed has the same primary zone,
+// that shape becomes the zone, which keeps the parts centered in it. Otherwise each plate in a
+// parallel mode fences off the rest of its bed with fixed items on its own bed, leaving the
+// other plates the whole bed, and finish() arranges each fenced plate again inside its zone,
+// since a fenced pile sits against the zone edge nearest the bed's center.
+//
+// A zone edge inside the bed gets the margin the bed's own edges get, since a copy printed from
+// a part at the zone edge sits at the far edge of the bed. A strip also gets the brim, which
+// would take the nozzle into it.
+class ImexArranger
+{
+public:
+ // `bed` is the arranger's bed shape, shrunk by params.bed_shrink_x/y.
+ ImexArranger(const ImexArrangeInput& input, const arrangement::ArrangeParams& params, const Points& bed);
+
+ // Whether any bed has a zone. Nothing below has an effect otherwise.
+ bool active() const { return !m_beds.empty(); }
+
+ // The bed shape to arrange in.
+ Points bed_shape() const;
+
+ // Adds the fixed items that keep parts out of each bed's keep-outs.
+ void add_keep_outs(arrangement::ArrangePolygons& fixed) const;
+
+ // Call after arrangement::arrange(items, fixed, bed_shape(), params), with `before` holding
+ // `items` as they were before it. Leaves unarranged any part across a keep-out: one that does
+ // not fit an empty bed is retried there without the bed's fixed items, so a part too big for
+ // its zone comes back across them. Then centers each fenced plate's parts in its zone.
+ void finish(arrangement::ArrangePolygons& items, const arrangement::ArrangePolygons& before,
+ const arrangement::ArrangePolygons& fixed, const arrangement::ArrangeParams& params) const;
+
+private:
+ struct Bed
+ {
+ int bed_idx;
+ ImexArrangeZones zones;
+ };
+ struct KeepOut
+ {
+ int bed_idx;
+ BoundingBox box;
+ bool fence; // outside the primary zone, rather than a strip
+ };
+
+ std::optional room_in(const BoundingBoxf& zone) const;
+ bool crosses_keep_out(const arrangement::ArrangePolygon& part, int bed_idx) const;
+
+ Points m_bed;
+ BoundingBox m_bed_bb;
+ Point m_margin;
+ bool m_adds_plates = false;
+ std::vector m_beds;
+ std::optional m_shared_room;
+ std::vector m_keep_outs;
+};
+
+} // namespace Slic3r
diff --git a/src/libslic3r/IMEXHelpers.cpp b/src/libslic3r/IMEXHelpers.cpp
new file mode 100644
index 0000000000..fffcdb0876
--- /dev/null
+++ b/src/libslic3r/IMEXHelpers.cpp
@@ -0,0 +1,712 @@
+#include "libslic3r/IMEXHelpers.hpp"
+
+#include
+#include
+#include
+#include