Compare commits

..
Author SHA1 Message Date
Hanif Koh 6d218f9abf Let Plugin Windows Be Maximized on X11
wxGTK types every wxDialog as a dialog window, and Mutter offers no
maximize for anything but a normal window, so the maximize button and
shortcut did nothing on GNOME under X11. Modeless plugin windows are now
normal windows, kept off the taskbar as before. Modal ones stay dialogs
so the window manager keeps routing focus from the main window to them.
2026-10-10 14:27:45 +08:00
161 changed files with 645 additions and 18461 deletions
-2
View File
@@ -70,8 +70,6 @@ If you come across any of these in search results, please <b>report them</b> 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
-2
View File
@@ -164,7 +164,6 @@ 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
@@ -213,7 +212,6 @@ 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
+3 -1
View File
@@ -1 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M3.5,11.5v1l2,2,2-2v-1" style="fill:none; stroke:#949494; stroke-linecap:square; stroke-linejoin:round;"/><circle cx="10.5" cy="4.5" r="4" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="11.5 2.5 12.5 3.5 8.5 3.5" style="fill:none; stroke:#009688; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="12.5 5.5 8.5 5.5 9.5 6.5" style="fill:none; stroke:#009688; stroke-linecap:square; stroke-linejoin:round;"/><path d="M5.5.5H1.5C.95.5.5.95.5,1.5v9c0,.55.45,1,1,1h8c.55,0,1-.45,1-1" style="fill:none; stroke:#949494; stroke-linecap:square; stroke-linejoin:round;"/></svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.2992 1.42627C10.7774 1.42627 8.73315 3.47056 8.73315 5.99231C8.73315 8.51406 10.7774 10.5583 13.2992 10.5583C13.3609 10.5583 13.4223 10.5571 13.4834 10.5547C15.9197 10.4581 17.8652 8.45238 17.8652 5.99231C17.8652 3.47056 15.8209 1.42627 13.2992 1.42627ZM15.6185 11.0536C17.5346 10.1741 18.8652 8.23861 18.8652 5.99231C18.8652 2.91827 16.3732 0.42627 13.2992 0.42627C11.5793 0.42627 10.0417 1.2063 9.02067 2.43188H2.31853C1.93193 2.43188 1.61853 2.74528 1.61853 3.13188V12.4712C1.61853 12.6313 1.6723 12.7789 1.76278 12.8968C1.76671 13.0046 1.79621 13.1144 1.85576 13.2185L3.48408 16.0649C3.60875 16.2829 3.8406 16.4173 4.09168 16.4173H6.75859L7.97903 19.1586C8.22556 19.7123 9.01148 19.7123 9.25801 19.1586L10.4784 16.4173H13.1454C13.3965 16.4173 13.6283 16.2829 13.753 16.0649L15.3813 13.2185C15.4409 13.1144 15.4704 13.0046 15.4743 12.8968C15.5648 12.7789 15.6185 12.6313 15.6185 12.4712V11.0536ZM13.4429 11.5565C13.8469 11.5463 14.2403 11.493 14.6185 11.4011V12.1709H2.61853V3.43188H8.35573C7.95792 4.19837 7.73315 5.06909 7.73315 5.99231C7.73315 9.06635 10.2252 11.5583 13.2992 11.5583C13.3472 11.5583 13.3951 11.5577 13.4429 11.5565ZM2.98076 13.1712H14.2563L12.9714 15.4173H9.85194L9.84578 15.4173H7.39126L7.3851 15.4173H4.26569L2.98076 13.1712ZM9.38381 16.4173L8.61852 18.1363L7.85322 16.4173H9.38381ZM12.2021 3.36422C12.3973 3.55948 12.3973 3.87606 12.2021 4.07133L11.743 4.53041H16.0625C16.3386 4.53041 16.5625 4.75426 16.5625 5.03041C16.5625 5.30655 16.3386 5.53041 16.0625 5.53041H10.7773C10.2428 5.53041 9.97507 4.88412 10.353 4.50614L11.495 3.36422C11.6902 3.16896 12.0068 3.16896 12.2021 3.36422ZM14.3963 7.91305C14.2011 8.10831 14.2011 8.42489 14.3963 8.62015C14.5916 8.81542 14.9082 8.81542 15.1034 8.62015L16.2453 7.47823C16.6233 7.10026 16.3556 6.45397 15.8211 6.45397L10.5359 6.45397C10.2597 6.45397 10.0359 6.67783 10.0359 6.95397C10.0359 7.23011 10.2597 7.45397 10.5359 7.45397L14.8554 7.45397L14.3963 7.91305Z" fill="#262E30"/>
</svg>

Before

Width:  |  Height:  |  Size: 747 B

After

Width:  |  Height:  |  Size: 2.1 KiB

+10 -1
View File
@@ -1 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M3.5,3.5h8M3.5,5.5h8M3.5,1.5h8M6.5,5.5v7l1,1,1-1v-7" style="fill:none; stroke:#949494; stroke-linecap:square; stroke-linejoin:round;"/></svg>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.73486" y="1" width="10.53" height="5.12169" fill="#C2C2C2"/>
<rect x="1.73486" y="1" width="10.53" height="0.497608" fill="#858585"/>
<rect x="1.73486" y="2.70703" width="10.53" height="0.497608" fill="#858585"/>
<rect x="1.73486" y="4.41602" width="10.53" height="0.497608" fill="#858585"/>
<rect x="1.73486" y="6.12305" width="10.53" height="0.497608" fill="#858585"/>
<path d="M6.10107 11.9961H7.77047L7.30675 12.9991H6.61117L6.10107 11.9961Z" fill="#8C8C8C"/>
<rect x="4.1748" y="6.62109" width="5.65026" height="5.3765" fill="#646464"/>
<rect x="6.61475" y="6.62109" width="0.642075" height="5.35947" fill="#1D1D1D"/>
</svg>

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 737 B

@@ -1,7 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#e9e9e9" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#545454" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#545454" fill="#545454" d="m21.5275,7.14657l0,28.59908"/>
<path stroke-width="1.0" stroke="#545454" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1.0" stroke="#545454" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m34.77509,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.719,0.9102l-0.8129,1.0906l-0.7719,2.014l-0.2296,2.4972l0.257,2.9999l0.8925,0.5883l0.2145,1.5893l-0.6173,0l-0.6605,1.2606l-0.5122,1.9929l8.9594,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,7 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#54545a" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#b3b3b3" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#c4c4c4" fill="#c4c4c4" d="m21.5275,7.14657l0,28.59908"/>
<path stroke-width="1.0" stroke="#c4c4c4" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1.0" stroke="#c4c4c4" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m34.77509,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.719,0.9102l-0.8129,1.0906l-0.7719,2.014l-0.2296,2.4972l0.257,2.9999l0.8925,0.5883l0.2145,1.5893l-0.6173,0l-0.6605,1.2606l-0.5122,1.9929l8.9594,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,7 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#e9e9e9" rx="3.5" height="40" width="40" x="1.5" y="1.5"/>
<path stroke-width="1.0" fill="#009688" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#009688" fill="#009688" d="m21.5275,7.14657l0,28.59908"/>
<path stroke-width="1.0" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1.0" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m34.77509,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.719,0.9102l-0.8129,1.0906l-0.7719,2.014l-0.2296,2.4972l0.257,2.9999l0.8925,0.5883l0.2145,1.5893l-0.6173,0l-0.6605,1.2606l-0.5122,1.9929l8.9594,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,7 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#54545a" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#009688" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#009688" fill="#009688" d="m21.5275,7.14657l0,28.59908"/>
<path stroke-width="1.0" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1.0" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m34.77509,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.719,0.9102l-0.8129,1.0906l-0.7719,2.014l-0.2296,2.4972l0.257,2.9999l0.8925,0.5883l0.2145,1.5893l-0.6173,0l-0.6605,1.2606l-0.5122,1.9929l8.9594,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,10 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#e9e9e9" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#545454" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#545454" fill="#545454" d="m21.5275,5.5l0,31.9"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#545454" fill="#545454" d="m6.7,21.575891l29.2,0"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#545454" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(11.75,11.75) scale(0.7) translate(-31.15,-20.94)"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#545454" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(31.25,11.75) scale(-0.7,0.7) translate(-31.15,-20.94)"/>
<path stroke-width="1" stroke="#545454" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z" transform="translate(11.75,31.25) scale(0.7) translate(-11.85,-20.93)"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#545454" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(31.25,31.25) scale(0.7) translate(-31.15,-20.94)"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

@@ -1,10 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#54545a" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#b3b3b3" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#c4c4c4" fill="#c4c4c4" d="m21.5275,5.5l0,31.9"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#c4c4c4" fill="#c4c4c4" d="m6.7,21.575891l29.2,0"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#c4c4c4" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(11.75,11.75) scale(0.7) translate(-31.15,-20.94)"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#c4c4c4" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(31.25,11.75) scale(-0.7,0.7) translate(-31.15,-20.94)"/>
<path stroke-width="1" stroke="#c4c4c4" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z" transform="translate(11.75,31.25) scale(0.7) translate(-11.85,-20.93)"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#c4c4c4" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(31.25,31.25) scale(0.7) translate(-31.15,-20.94)"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

@@ -1,10 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#e9e9e9" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#009688" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#009688" fill="#009688" d="m21.5275,5.5l0,31.9"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#009688" fill="#009688" d="m6.7,21.575891l29.2,0"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(11.75,11.75) scale(0.7) translate(-31.15,-20.94)"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(31.25,11.75) scale(-0.7,0.7) translate(-31.15,-20.94)"/>
<path stroke-width="1" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z" transform="translate(11.75,31.25) scale(0.7) translate(-11.85,-20.93)"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(31.25,31.25) scale(0.7) translate(-31.15,-20.94)"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

@@ -1,10 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#54545a" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#009688" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#009688" fill="#009688" d="m21.5275,5.5l0,31.9"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#009688" fill="#009688" d="m6.7,21.575891l29.2,0"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(11.75,11.75) scale(0.7) translate(-31.15,-20.94)"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(31.25,11.75) scale(-0.7,0.7) translate(-31.15,-20.94)"/>
<path stroke-width="1" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z" transform="translate(11.75,31.25) scale(0.7) translate(-11.85,-20.93)"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z" transform="translate(31.25,31.25) scale(0.7) translate(-31.15,-20.94)"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

@@ -1,7 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#e9e9e9" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#545454" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#545454" fill="#545454" d="m21.5275,7.14657l0,28.59908"/>
<path stroke-width="1.0" stroke="#545454" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1.0" stroke="#545454" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,7 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#54545a" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#b3b3b3" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#c4c4c4" fill="#c4c4c4" d="m21.5275,7.14657l0,28.59908"/>
<path stroke-width="1.0" stroke="#c4c4c4" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1.0" stroke="#c4c4c4" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,7 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#e9e9e9" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#009688" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#009688" fill="#009688" d="m21.5275,7.14657l0,28.59908"/>
<path stroke-width="1.0" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1.0" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,7 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#54545a" rx="3.5" height="40" width="40" y="1.5" x="1.5"/>
<path stroke-width="1.0" fill="#009688" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke-linecap="round" stroke-width="0.8" stroke="#009688" fill="#009688" d="m21.5275,7.14657l0,28.59908"/>
<path stroke-width="1.0" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m14.5112,25.5943l-0.632,0l0.632,-1.1115l-0.0305,-1.3114l-0.6445,-1.103l-0.7745,-0.632l-0.3502,-0.7142l-0.9981,-1.8354l1.4221,0.9295l1.6267,0.1116l0.8272,0.5112l0.9667,-0.4926l0.2509,-1.3664l-1.0317,-0.5763l-0.9945,-1.5429l0.1999,-0.7236l-1.1015,-1.0425l0.4275,-1.608l-0.7064,-0.0651l-0.9388,0.7157l-1.8125,0.1674l-1.71896,0.9102l-0.81298,1.0906l-0.77183,2.014l-0.22958,2.4972l0.25693,2.9999l0.89251,0.5883l0.21446,1.5893l-0.6172,0l-0.66057,1.2606l-0.51219,1.9929l8.95941,0l-0.5289,-1.9929l-0.8089,-1.2606l0,0z"/>
<path stroke-dasharray="1,2" stroke-linecap="round" stroke-width="1.0" stroke="#009688" fill-opacity="0" fill="#ffffff" fill-rule="nonzero" d="m28.4888,25.5943l0.632,0l-0.632,-1.1115l0.0305,-1.3114l0.6445,-1.103l0.7745,-0.632l0.3502,-0.7142l0.9981,-1.8354l-1.4221,0.9295l-1.6267,0.1116l-0.8272,0.5112l-0.9667,-0.4926l-0.2509,-1.3664l1.0317,-0.5763l0.9945,-1.5429l-0.1999,-0.7236l1.1015,-1.0425l-0.4275,-1.608l0.7064,-0.0651l0.9388,0.7157l1.8125,0.1674l1.719,0.9102l0.8129,1.0906l0.7719,2.014l0.2296,2.4972l-0.257,2.9999l-0.8925,0.5883l-0.2145,1.5893l0.6173,0l0.6605,1.2606l0.5122,1.9929l-8.9594,0l0.5289,-1.9929l0.8089,-1.2606l0,0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,10 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#e9e9e9" rx="3.5" height="40" width="40" x="1.5" y="1.5"/>
<path fill="#545454" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke="#545454" fill="#ffffff" fill-opacity="0" fill-rule="nonzero" stroke-width="1.0" d="m27.20643,29.4739l-1.21802,0l1.21802,-2.14213l-0.05879,-2.52738l-1.2421,-2.12576l-1.49265,-1.21801l-0.67492,-1.37644l-1.92358,-3.53726l2.74073,1.79137l3.13505,0.21508l1.59421,0.98521l1.86307,-0.94935l0.48354,-2.63339l-1.98833,-1.11067l-1.91665,-2.97354l0.38526,-1.39456l-2.12286,-2.00915l0.82389,-3.09901l-1.3614,-0.12546l-1.80929,1.37933l-3.49313,0.32262l-3.31285,1.75417l-1.56681,2.10186l-1.48751,3.88146l-0.44245,4.81271l0.49516,5.78154l1.72009,1.13379l0.41331,3.06297l-1.1895,0l-1.27306,2.42948l-0.98712,3.8408l17.26695,0l-1.01931,-3.8408l-1.55895,-2.42948l0,0z"/>
<path stroke="#545454" stroke-dasharray="2,2" d="m23.61156,6.63793l-1.16183,2.2557l-1.89806,0.27415l-2.4104,1.37699l-1.46486,2.1687l-1.05714,3.57465l-0.28913,4.51495l0.30214,6.3633" fill-opacity="0" stroke-width="1.0" fill="none"/>
<line stroke="#545454" stroke-dasharray="2,2" y2="32.58856" x2="28.96602" y1="32.58856" x1="13.5093" fill-opacity="0" stroke-width="1.0" fill="none"/>
<line stroke="#545454" y2="29.47069" x2="25.98988" y1="29.47069" x1="16.06028" fill-opacity="0" stroke-width="1.0" fill="none"/>
<path stroke="#545454" stroke-dasharray="2,2" d="m27.94317,10.33418l-2.21519,0.02258l-0.32541,0.65079l2.39086,0.92312" fill-opacity="0" stroke-width="1.0" fill="none"/>
<path stroke="#545454" stroke-dasharray="2,2" d="m31.36607,16.48593l-1.47286,-0.34296l0.38557,-0.77114" stroke-width="1.0" fill-opacity="0" fill="none"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,10 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#54545a" rx="3.5" height="40" width="40" x="1.5" y="1.5"/>
<path fill="#b3b3b3" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke="#c4c4c4" fill="#ffffff" fill-opacity="0" fill-rule="nonzero" stroke-width="1.0" d="m27.20643,29.4739l-1.21802,0l1.21802,-2.14213l-0.05879,-2.52738l-1.2421,-2.12576l-1.49265,-1.21801l-0.67492,-1.37644l-1.92358,-3.53726l2.74073,1.79137l3.13505,0.21508l1.59421,0.98521l1.86307,-0.94935l0.48354,-2.63339l-1.98833,-1.11067l-1.91665,-2.97354l0.38526,-1.39456l-2.12286,-2.00915l0.82389,-3.09901l-1.3614,-0.12546l-1.80929,1.37933l-3.49313,0.32262l-3.31285,1.75417l-1.56681,2.10186l-1.48751,3.88146l-0.44245,4.81271l0.49516,5.78154l1.72009,1.13379l0.41331,3.06297l-1.1895,0l-1.27306,2.42948l-0.98712,3.8408l17.26695,0l-1.01931,-3.8408l-1.55895,-2.42948l0,0z"/>
<path stroke="#c4c4c4" stroke-dasharray="2,2" d="m23.61156,6.63793l-1.16183,2.2557l-1.89806,0.27415l-2.4104,1.37699l-1.46486,2.1687l-1.05714,3.57465l-0.28913,4.51495l0.30214,6.3633" fill-opacity="0" stroke-width="1.0" fill="none"/>
<line stroke="#c4c4c4" stroke-dasharray="2,2" y2="32.58856" x2="28.96602" y1="32.58856" x1="13.5093" fill-opacity="0" stroke-width="1.0" fill="none"/>
<line stroke="#c4c4c4" y2="29.47069" x2="25.98988" y1="29.47069" x1="16.06028" fill-opacity="0" stroke-width="1.0" fill="none"/>
<path stroke="#c4c4c4" stroke-dasharray="2,2" d="m27.94317,10.33418l-2.21519,0.02258l-0.32541,0.65079l2.39086,0.92312" fill-opacity="0" stroke-width="1.0" fill="none"/>
<path stroke="#c4c4c4" stroke-dasharray="2,2" d="m31.36607,16.48593l-1.47286,-0.34296l0.38557,-0.77114" stroke-width="1.0" fill-opacity="0" fill="none"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,10 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#e9e9e9" rx="3.5" height="40" width="40" x="1.5" y="1.5"/>
<path fill="#009688" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke="#009688" fill="#ffffff" fill-opacity="0" fill-rule="nonzero" stroke-width="1.0" d="m27.20643,29.4739l-1.21802,0l1.21802,-2.14213l-0.05879,-2.52738l-1.2421,-2.12576l-1.49265,-1.21801l-0.67492,-1.37644l-1.92358,-3.53726l2.74073,1.79137l3.13505,0.21508l1.59421,0.98521l1.86307,-0.94935l0.48354,-2.63339l-1.98833,-1.11067l-1.91665,-2.97354l0.38526,-1.39456l-2.12286,-2.00915l0.82389,-3.09901l-1.3614,-0.12546l-1.80929,1.37933l-3.49313,0.32262l-3.31285,1.75417l-1.56681,2.10186l-1.48751,3.88146l-0.44245,4.81271l0.49516,5.78154l1.72009,1.13379l0.41331,3.06297l-1.1895,0l-1.27306,2.42948l-0.98712,3.8408l17.26695,0l-1.01931,-3.8408l-1.55895,-2.42948l0,0z"/>
<path stroke="#009688" stroke-dasharray="2,2" d="m23.61156,6.63793l-1.16183,2.2557l-1.89806,0.27415l-2.4104,1.37699l-1.46486,2.1687l-1.05714,3.57465l-0.28913,4.51495l0.30214,6.3633" fill-opacity="0" stroke-width="1.0" fill="none"/>
<line stroke="#009688" stroke-dasharray="2,2" y2="32.58856" x2="28.96602" y1="32.58856" x1="13.5093" fill-opacity="0" stroke-width="1.0" fill="none"/>
<line stroke="#009688" y2="29.47069" x2="25.98988" y1="29.47069" x1="16.06028" fill-opacity="0" stroke-width="1.0" fill="none"/>
<path stroke="#009688" stroke-dasharray="2,2" d="m27.94317,10.33418l-2.21519,0.02258l-0.32541,0.65079l2.39086,0.92312" fill-opacity="0" stroke-width="1.0" fill="none"/>
<path stroke="#009688" stroke-dasharray="2,2" d="m31.36607,16.48593l-1.47286,-0.34296l0.38557,-0.77114" stroke-width="1.0" fill-opacity="0" fill="none"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,10 +0,0 @@
<svg width="43" height="43" xmlns="http://www.w3.org/2000/svg">
<rect fill="#54545a" rx="3.5" height="40" width="40" x="1.5" y="1.5"/>
<path fill="#009688" d="m38,2a3,3 0 0 1 3,3l0,33a3,3 0 0 1 -3,3l-33,0a3,3 0 0 1 -3,-3l0,-33a3,3 0 0 1 3,-3l33,0m0,-1l-33,0a4,4 0 0 0 -4,4l0,33a4,4 0 0 0 4,4l33,0a4,4 0 0 0 4,-4l0,-33a4,4 0 0 0 -4,-4z"/>
<path stroke="#009688" fill="#ffffff" fill-opacity="0" fill-rule="nonzero" stroke-width="1.0" d="m27.20643,29.4739l-1.21802,0l1.21802,-2.14213l-0.05879,-2.52738l-1.2421,-2.12576l-1.49265,-1.21801l-0.67492,-1.37644l-1.92358,-3.53726l2.74073,1.79137l3.13505,0.21508l1.59421,0.98521l1.86307,-0.94935l0.48354,-2.63339l-1.98833,-1.11067l-1.91665,-2.97354l0.38526,-1.39456l-2.12286,-2.00915l0.82389,-3.09901l-1.3614,-0.12546l-1.80929,1.37933l-3.49313,0.32262l-3.31285,1.75417l-1.56681,2.10186l-1.48751,3.88146l-0.44245,4.81271l0.49516,5.78154l1.72009,1.13379l0.41331,3.06297l-1.1895,0l-1.27306,2.42948l-0.98712,3.8408l17.26695,0l-1.01931,-3.8408l-1.55895,-2.42948l0,0z"/>
<path stroke="#009688" stroke-dasharray="2,2" d="m23.61156,6.63793l-1.16183,2.2557l-1.89806,0.27415l-2.4104,1.37699l-1.46486,2.1687l-1.05714,3.57465l-0.28913,4.51495l0.30214,6.3633" fill-opacity="0" stroke-width="1.0" fill="none"/>
<line stroke="#009688" stroke-dasharray="2,2" y2="32.58856" x2="28.96602" y1="32.58856" x1="13.5093" fill-opacity="0" stroke-width="1.0" fill="none"/>
<line stroke="#009688" y2="29.47069" x2="25.98988" y1="29.47069" x1="16.06028" fill-opacity="0" stroke-width="1.0" fill="none"/>
<path stroke="#009688" stroke-dasharray="2,2" d="m27.94317,10.33418l-2.21519,0.02258l-0.32541,0.65079l2.39086,0.92312" fill-opacity="0" stroke-width="1.0" fill="none"/>
<path stroke="#009688" stroke-dasharray="2,2" d="m31.36607,16.48593l-1.47286,-0.34296l0.38557,-0.77114" stroke-width="1.0" fill-opacity="0" fill="none"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "Bambulab",
"url": "http://www.bambulab.com/Parameters/vendor/BBL.json",
"version": "02.08.00.13",
"version": "02.08.00.12",
"force_update": "0",
"description": "the initial version of BBL configurations",
"machine_model_list": [
@@ -94,7 +94,7 @@
"prime_tower_flat_ironing": "0",
"raft_layers": "0",
"reduce_crossing_wall": "0",
"reduce_infill_retraction": "1",
"reduce_infill_retraction_mode": "Auto",
"resolution": "0.012",
"scarf_angle_threshold": "155",
"seam_position": "aligned",
+1 -149
View File
@@ -1,6 +1,6 @@
{
"name": "Vivedino",
"version": "02.04.00.13",
"version": "02.04.00.02",
"force_update": "0",
"description": "Vivedino configurations",
"machine_model_list": [
@@ -11,22 +11,6 @@
{
"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": [
@@ -61,86 +45,6 @@
{
"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": [],
@@ -161,61 +65,9 @@
"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"
}
]
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

@@ -1,74 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Vivedino Xplorer Dual Gantry build plate. Dashed red outlines mark real reach
limits; the viewBox matches printable_area so the texture is not stretched.
Print zones are drawn by the slicer per plate, not by this texture. -->
<svg
width="400mm"
height="342.5mm"
viewBox="0 0 400 342.5"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<rect
style="fill:none;stroke:#ff0e0e;stroke-width:1;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1"
id="bound-rear"
width="400"
height="26"
x="0"
y="0" />
<rect
style="fill:none;stroke:#ff0e0e;stroke-width:1;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1"
id="bound-front"
width="400"
height="77.5"
x="0"
y="265" />
<g
id="text3-6"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 22.60772,9.2135632 H 19.221048 V 5.4247238 h -3.831173 v 3.7888394 h -2.582337 v 0.1693336 l 1.058335,2.7516712 h 1.524002 v 8.699514 h 3.831173 v -8.699514 h 3.386672 z m 1.227659,1.1006688 v 6.096009 c 0,2.878672 1.375835,4.423841 4.762507,4.423841 h 8.805348 c 3.407838,0 4.762507,-1.545169 4.762507,-4.423841 v -6.096009 c 0,-3.0903387 -1.354669,-4.8895082 -4.762507,-4.8895082 h -8.805348 c -3.386672,0 -4.762507,1.7991695 -4.762507,4.8895082 z m 4.191006,0.656167 c 0,-1.2700017 0.592668,-1.820336 1.566336,-1.820336 h 6.582844 c 1.227669,0 1.799169,0.6138344 1.799169,1.98967 v 3.915839 c 0,1.333503 -0.5715,1.905003 -1.799169,1.905003 h -6.582844 c -1.227668,0 -1.566336,-0.719667 -1.566336,-2.159003 z"
id="path25" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 14.890625,4.9238281 v 3.7890625 h -2.583984 v 0.7636719 l 1.216797,3.1582035 h 1.367187 v 8.699218 h 4.830078 v -8.699218 h 3.386719 V 8.7128906 H 19.720703 V 4.9238281 Z m 13.707031,0 c -1.781877,0 -3.138417,0.4891086 -4.019531,1.4492188 -0.881114,0.9601101 -1.242188,2.322036 -1.242188,3.9414061 v 6.095703 c 0,1.518993 0.370925,2.78417 1.261719,3.650391 0.890794,0.86622 2.230623,1.273437 4,1.273437 h 8.804688 c 1.779488,0 3.121173,-0.408321 4.009765,-1.27539 0.888593,-0.86707 1.253907,-2.13067 1.253907,-3.648438 v -6.095703 c 0,-1.6182286 -0.355554,-2.9804801 -1.234375,-3.9414061 -0.878822,-0.960926 -2.237387,-1.4492188 -4.029297,-1.4492188 z m -12.707031,1 h 2.830078 v 3.7890625 h 3.386719 v 1.9218754 h -3.386719 v 8.699218 H 15.890625 V 11.634766 H 14.208984 L 13.46875,9.7128906 h 2.421875 z m 12.707031,0 h 8.804688 c 1.615927,0 2.640543,0.4137566 3.291015,1.125 0.650473,0.7112434 0.972657,1.793515 0.972657,3.2656249 v 6.095703 c 0,1.360905 -0.312424,2.308411 -0.953125,2.933594 -0.640702,0.625183 -1.682198,0.990234 -3.310547,0.990234 h -8.804688 c -1.617294,0 -2.658943,-0.364202 -3.302734,-0.990234 -0.643792,-0.626032 -0.958984,-1.573915 -0.958985,-2.933594 v -6.095703 c 0,-1.4709684 0.325044,-2.5535656 0.978516,-3.2656249 0.653472,-0.7120593 1.678409,-1.125 3.283203,-1.125 z m 0.994141,2.7265625 c -0.581037,0 -1.137457,0.1802682 -1.517578,0.5976563 -0.380122,0.4173881 -0.546875,1.0098771 -0.546875,1.7226561 v 3.830078 c 0,0.758884 0.06928,1.390241 0.404297,1.896485 0.335014,0.506243 0.948621,0.763671 1.660156,0.763671 h 6.583984 c 0.691421,0 1.302744,-0.170803 1.720703,-0.607421 0.417959,-0.436618 0.578125,-1.060649 0.578125,-1.798828 v -3.914063 c 0,-0.757148 -0.157083,-1.3938421 -0.570312,-1.8476562 C 37.491068,8.8391546 36.872949,8.6503906 36.175781,8.6503906 Z m 0,1 h 6.583984 c 0.530501,0 0.810796,0.1173912 0.990235,0.3144532 0.179438,0.1970622 0.308593,0.5570942 0.308593,1.1757812 v 3.914063 c 0,0.595323 -0.126073,0.924914 -0.300781,1.107421 -0.174708,0.182508 -0.461798,0.298828 -0.998047,0.298828 h -6.583984 c -0.516133,0 -0.684935,-0.102981 -0.826172,-0.316406 -0.141237,-0.213424 -0.238281,-0.663298 -0.238281,-1.34375 v -3.830078 c 0,-0.557223 0.12748,-0.875502 0.287109,-1.0507811 0.15963,-0.1752795 0.384713,-0.2695313 0.777344,-0.2695313 z"
id="path26" />
</g>
<g
transform="translate(-356.014,288.234)">
<g
id="text3-8"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 378.62465,9.2135632 h -3.38667 V 5.4247238 h -3.83117 v 3.7888394 h -2.58234 v 0.1693336 l 1.05834,2.7516712 h 1.524 v 8.699514 h 3.83117 v -8.699514 h 3.38667 z m 3.70417,-0.0635 c 0.74083,0 0.97367,0.2751672 0.97367,1.0795018 v 10.604517 h 4.191 V 9.2770633 c 0,-2.7940045 -0.762,-3.8523395 -3.70417,-3.8523395 h -4.65667 v 0.1693336 l 1.37583,3.5560056 z"
id="path23" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 370.90625,4.9238281 v 3.7890625 h -2.58203 v 0.7636719 l 1.21484,3.1582035 h 1.36719 v 8.699218 h 4.83203 V 12.634766 H 379.125 V 8.7128906 h -3.38672 V 4.9238281 Z m 7.72656,0 V 5.6875 l 1.53321,3.9628906 h 2.1621 c 0.30851,0 0.37476,0.050297 0.39258,0.070312 0.0178,0.020016 0.082,0.1558452 0.082,0.5097654 v 11.103515 h 5.19141 V 9.2773438 c 0,-1.4424568 -0.18358,-2.5460127 -0.87891,-3.3105469 -0.69533,-0.7645343 -1.79513,-1.0429688 -3.32617,-1.0429688 z m -6.72656,1 h 2.83203 v 3.7890625 h 3.38672 v 1.9218754 h -3.38672 v 8.699218 h -2.83203 v -8.699218 h -1.67969 l -0.74023,-1.9218754 h 2.41992 z m 7.89062,0 h 3.99219 c 1.41114,0 2.16473,0.2517094 2.58594,0.7148438 0.42121,0.4631344 0.61914,1.2871241 0.61914,2.6386719 V 20.333984 h -3.19141 V 10.230469 c 0,-0.4504146 -0.0521,-0.85713 -0.33593,-1.1757815 -0.2838,-0.3186515 -0.70635,-0.4042969 -1.13868,-0.4042969 h -1.47656 z"
id="path24" />
</g>
</g>
<g
transform="translate(0.020506,27.6309)">
<g
id="text6-8"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.87817,149.53483 h 2.20134 v -4.826 h 4.80484 v -2.18017 h -4.80484 v -4.82601 h -2.20134 v 4.82601 h -4.80484 v 2.18017 h 4.80484 z"
id="path17" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.37891,137.20312 v 4.82618 h -4.80469 v 3.17968 h 4.80469 v 4.82618 h 3.20117 v -4.82618 h 4.80469 v -3.17968 h -4.80469 v -4.82618 z m 1,1 h 1.20117 v 4.82618 h 4.80469 v 1.17968 h -4.80469 v 4.82618 h -1.20117 v -4.82618 h -4.80469 v -1.17968 h 4.80469 z"
id="path18" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.9 KiB

@@ -1,77 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Vivedino Xplorer IDEX build plate. Dashed red outlines mark real reach
limits; the viewBox matches printable_area so the texture is not stretched.
Print zones are drawn by the slicer per plate, not by this texture. -->
<svg
width="400mm"
height="400mm"
viewBox="0 0 400 400"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<rect
style="fill:none;stroke:#ff0e0e;stroke-width:1;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1"
id="bound-left"
width="66"
height="400"
x="0"
y="0" />
<rect
style="fill:none;stroke:#ff0e0e;stroke-width:1;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1"
id="bound-right"
width="68"
height="400"
x="332"
y="0" />
<g
transform="translate(0,345.734)">
<g
id="text3-6"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 22.60772,9.2135632 H 19.221048 V 5.4247238 h -3.831173 v 3.7888394 h -2.582337 v 0.1693336 l 1.058335,2.7516712 h 1.524002 v 8.699514 h 3.831173 v -8.699514 h 3.386672 z m 1.227659,1.1006688 v 6.096009 c 0,2.878672 1.375835,4.423841 4.762507,4.423841 h 8.805348 c 3.407838,0 4.762507,-1.545169 4.762507,-4.423841 v -6.096009 c 0,-3.0903387 -1.354669,-4.8895082 -4.762507,-4.8895082 h -8.805348 c -3.386672,0 -4.762507,1.7991695 -4.762507,4.8895082 z m 4.191006,0.656167 c 0,-1.2700017 0.592668,-1.820336 1.566336,-1.820336 h 6.582844 c 1.227669,0 1.799169,0.6138344 1.799169,1.98967 v 3.915839 c 0,1.333503 -0.5715,1.905003 -1.799169,1.905003 h -6.582844 c -1.227668,0 -1.566336,-0.719667 -1.566336,-2.159003 z"
id="path25" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 14.890625,4.9238281 v 3.7890625 h -2.583984 v 0.7636719 l 1.216797,3.1582035 h 1.367187 v 8.699218 h 4.830078 v -8.699218 h 3.386719 V 8.7128906 H 19.720703 V 4.9238281 Z m 13.707031,0 c -1.781877,0 -3.138417,0.4891086 -4.019531,1.4492188 -0.881114,0.9601101 -1.242188,2.322036 -1.242188,3.9414061 v 6.095703 c 0,1.518993 0.370925,2.78417 1.261719,3.650391 0.890794,0.86622 2.230623,1.273437 4,1.273437 h 8.804688 c 1.779488,0 3.121173,-0.408321 4.009765,-1.27539 0.888593,-0.86707 1.253907,-2.13067 1.253907,-3.648438 v -6.095703 c 0,-1.6182286 -0.355554,-2.9804801 -1.234375,-3.9414061 -0.878822,-0.960926 -2.237387,-1.4492188 -4.029297,-1.4492188 z m -12.707031,1 h 2.830078 v 3.7890625 h 3.386719 v 1.9218754 h -3.386719 v 8.699218 H 15.890625 V 11.634766 H 14.208984 L 13.46875,9.7128906 h 2.421875 z m 12.707031,0 h 8.804688 c 1.615927,0 2.640543,0.4137566 3.291015,1.125 0.650473,0.7112434 0.972657,1.793515 0.972657,3.2656249 v 6.095703 c 0,1.360905 -0.312424,2.308411 -0.953125,2.933594 -0.640702,0.625183 -1.682198,0.990234 -3.310547,0.990234 h -8.804688 c -1.617294,0 -2.658943,-0.364202 -3.302734,-0.990234 -0.643792,-0.626032 -0.958984,-1.573915 -0.958985,-2.933594 v -6.095703 c 0,-1.4709684 0.325044,-2.5535656 0.978516,-3.2656249 0.653472,-0.7120593 1.678409,-1.125 3.283203,-1.125 z m 0.994141,2.7265625 c -0.581037,0 -1.137457,0.1802682 -1.517578,0.5976563 -0.380122,0.4173881 -0.546875,1.0098771 -0.546875,1.7226561 v 3.830078 c 0,0.758884 0.06928,1.390241 0.404297,1.896485 0.335014,0.506243 0.948621,0.763671 1.660156,0.763671 h 6.583984 c 0.691421,0 1.302744,-0.170803 1.720703,-0.607421 0.417959,-0.436618 0.578125,-1.060649 0.578125,-1.798828 v -3.914063 c 0,-0.757148 -0.157083,-1.3938421 -0.570312,-1.8476562 C 37.491068,8.8391546 36.872949,8.6503906 36.175781,8.6503906 Z m 0,1 h 6.583984 c 0.530501,0 0.810796,0.1173912 0.990235,0.3144532 0.179438,0.1970622 0.308593,0.5570942 0.308593,1.1757812 v 3.914063 c 0,0.595323 -0.126073,0.924914 -0.300781,1.107421 -0.174708,0.182508 -0.461798,0.298828 -0.998047,0.298828 h -6.583984 c -0.516133,0 -0.684935,-0.102981 -0.826172,-0.316406 -0.141237,-0.213424 -0.238281,-0.663298 -0.238281,-1.34375 v -3.830078 c 0,-0.557223 0.12748,-0.875502 0.287109,-1.0507811 0.15963,-0.1752795 0.384713,-0.2695313 0.777344,-0.2695313 z"
id="path26" />
</g>
</g>
<g
transform="translate(0,345.734)">
<g
id="text3-8"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 378.62465,9.2135632 h -3.38667 V 5.4247238 h -3.83117 v 3.7888394 h -2.58234 v 0.1693336 l 1.05834,2.7516712 h 1.524 v 8.699514 h 3.83117 v -8.699514 h 3.38667 z m 3.70417,-0.0635 c 0.74083,0 0.97367,0.2751672 0.97367,1.0795018 v 10.604517 h 4.191 V 9.2770633 c 0,-2.7940045 -0.762,-3.8523395 -3.70417,-3.8523395 h -4.65667 v 0.1693336 l 1.37583,3.5560056 z"
id="path23" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 370.90625,4.9238281 v 3.7890625 h -2.58203 v 0.7636719 l 1.21484,3.1582035 h 1.36719 v 8.699218 h 4.83203 V 12.634766 H 379.125 V 8.7128906 h -3.38672 V 4.9238281 Z m 7.72656,0 V 5.6875 l 1.53321,3.9628906 h 2.1621 c 0.30851,0 0.37476,0.050297 0.39258,0.070312 0.0178,0.020016 0.082,0.1558452 0.082,0.5097654 v 11.103515 h 5.19141 V 9.2773438 c 0,-1.4424568 -0.18358,-2.5460127 -0.87891,-3.3105469 -0.69533,-0.7645343 -1.79513,-1.0429688 -3.32617,-1.0429688 z m -6.72656,1 h 2.83203 v 3.7890625 h 3.38672 v 1.9218754 h -3.38672 v 8.699218 h -2.83203 v -8.699218 h -1.67969 l -0.74023,-1.9218754 h 2.41992 z m 7.89062,0 h 3.99219 c 1.41114,0 2.16473,0.2517094 2.58594,0.7148438 0.42121,0.4631344 0.61914,1.2871241 0.61914,2.6386719 V 20.333984 h -3.19141 V 10.230469 c 0,-0.4504146 -0.0521,-0.85713 -0.33593,-1.1757815 -0.2838,-0.3186515 -0.70635,-0.4042969 -1.13868,-0.4042969 h -1.47656 z"
id="path24" />
</g>
</g>
<g
transform="translate(0.020506,56.3809)">
<g
id="text6-8"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.87817,149.53483 h 2.20134 v -4.826 h 4.80484 v -2.18017 h -4.80484 v -4.82601 h -2.20134 v 4.82601 h -4.80484 v 2.18017 h 4.80484 z"
id="path17" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.37891,137.20312 v 4.82618 h -4.80469 v 3.17968 h 4.80469 v 4.82618 h 3.20117 v -4.82618 h 4.80469 v -3.17968 h -4.80469 v -4.82618 z m 1,1 h 1.20117 v 4.82618 h 4.80469 v 1.17968 h -4.80469 v 4.82618 h -1.20117 v -4.82618 h -4.80469 v -1.17968 h 4.80469 z"
id="path18" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.0 KiB

@@ -1,154 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Vivedino Xplorer IQEX build plate. Dashed red outlines mark real reach
limits; the viewBox matches printable_area so the texture is not stretched.
Print zones are drawn by the slicer per plate, not by this texture. -->
<svg
width="400mm"
height="342.5mm"
viewBox="0 0 400 342.5"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<rect
style="fill:none;stroke:#ff0e0e;stroke-width:1;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1"
id="bound-left"
width="66"
height="342.5"
x="0"
y="0" />
<rect
style="fill:none;stroke:#ff0e0e;stroke-width:1;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1"
id="bound-right"
width="68"
height="342.5"
x="332"
y="0" />
<rect
style="fill:none;stroke:#ff0e0e;stroke-width:1;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1"
id="bound-rear"
width="400"
height="26"
x="0"
y="0" />
<rect
style="fill:none;stroke:#ff0e0e;stroke-width:1;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1"
id="bound-front"
width="400"
height="77.5"
x="0"
y="265" />
<g
id="text3-6"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="M 22.60772,9.2135632 H 19.221048 V 5.4247238 h -3.831173 v 3.7888394 h -2.582337 v 0.1693336 l 1.058335,2.7516712 h 1.524002 v 8.699514 h 3.831173 v -8.699514 h 3.386672 z m 1.227659,1.1006688 v 6.096009 c 0,2.878672 1.375835,4.423841 4.762507,4.423841 h 8.805348 c 3.407838,0 4.762507,-1.545169 4.762507,-4.423841 v -6.096009 c 0,-3.0903387 -1.354669,-4.8895082 -4.762507,-4.8895082 h -8.805348 c -3.386672,0 -4.762507,1.7991695 -4.762507,4.8895082 z m 4.191006,0.656167 c 0,-1.2700017 0.592668,-1.820336 1.566336,-1.820336 h 6.582844 c 1.227669,0 1.799169,0.6138344 1.799169,1.98967 v 3.915839 c 0,1.333503 -0.5715,1.905003 -1.799169,1.905003 h -6.582844 c -1.227668,0 -1.566336,-0.719667 -1.566336,-2.159003 z"
id="path25" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 14.890625,4.9238281 v 3.7890625 h -2.583984 v 0.7636719 l 1.216797,3.1582035 h 1.367187 v 8.699218 h 4.830078 v -8.699218 h 3.386719 V 8.7128906 H 19.720703 V 4.9238281 Z m 13.707031,0 c -1.781877,0 -3.138417,0.4891086 -4.019531,1.4492188 -0.881114,0.9601101 -1.242188,2.322036 -1.242188,3.9414061 v 6.095703 c 0,1.518993 0.370925,2.78417 1.261719,3.650391 0.890794,0.86622 2.230623,1.273437 4,1.273437 h 8.804688 c 1.779488,0 3.121173,-0.408321 4.009765,-1.27539 0.888593,-0.86707 1.253907,-2.13067 1.253907,-3.648438 v -6.095703 c 0,-1.6182286 -0.355554,-2.9804801 -1.234375,-3.9414061 -0.878822,-0.960926 -2.237387,-1.4492188 -4.029297,-1.4492188 z m -12.707031,1 h 2.830078 v 3.7890625 h 3.386719 v 1.9218754 h -3.386719 v 8.699218 H 15.890625 V 11.634766 H 14.208984 L 13.46875,9.7128906 h 2.421875 z m 12.707031,0 h 8.804688 c 1.615927,0 2.640543,0.4137566 3.291015,1.125 0.650473,0.7112434 0.972657,1.793515 0.972657,3.2656249 v 6.095703 c 0,1.360905 -0.312424,2.308411 -0.953125,2.933594 -0.640702,0.625183 -1.682198,0.990234 -3.310547,0.990234 h -8.804688 c -1.617294,0 -2.658943,-0.364202 -3.302734,-0.990234 -0.643792,-0.626032 -0.958984,-1.573915 -0.958985,-2.933594 v -6.095703 c 0,-1.4709684 0.325044,-2.5535656 0.978516,-3.2656249 0.653472,-0.7120593 1.678409,-1.125 3.283203,-1.125 z m 0.994141,2.7265625 c -0.581037,0 -1.137457,0.1802682 -1.517578,0.5976563 -0.380122,0.4173881 -0.546875,1.0098771 -0.546875,1.7226561 v 3.830078 c 0,0.758884 0.06928,1.390241 0.404297,1.896485 0.335014,0.506243 0.948621,0.763671 1.660156,0.763671 h 6.583984 c 0.691421,0 1.302744,-0.170803 1.720703,-0.607421 0.417959,-0.436618 0.578125,-1.060649 0.578125,-1.798828 v -3.914063 c 0,-0.757148 -0.157083,-1.3938421 -0.570312,-1.8476562 C 37.491068,8.8391546 36.872949,8.6503906 36.175781,8.6503906 Z m 0,1 h 6.583984 c 0.530501,0 0.810796,0.1173912 0.990235,0.3144532 0.179438,0.1970622 0.308593,0.5570942 0.308593,1.1757812 v 3.914063 c 0,0.595323 -0.126073,0.924914 -0.300781,1.107421 -0.174708,0.182508 -0.461798,0.298828 -0.998047,0.298828 h -6.583984 c -0.516133,0 -0.684935,-0.102981 -0.826172,-0.316406 -0.141237,-0.213424 -0.238281,-0.663298 -0.238281,-1.34375 v -3.830078 c 0,-0.557223 0.12748,-0.875502 0.287109,-1.0507811 0.15963,-0.1752795 0.384713,-0.2695313 0.777344,-0.2695313 z"
id="path26" />
</g>
<g
id="text3-8"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 378.62465,9.2135632 h -3.38667 V 5.4247238 h -3.83117 v 3.7888394 h -2.58234 v 0.1693336 l 1.05834,2.7516712 h 1.524 v 8.699514 h 3.83117 v -8.699514 h 3.38667 z m 3.70417,-0.0635 c 0.74083,0 0.97367,0.2751672 0.97367,1.0795018 v 10.604517 h 4.191 V 9.2770633 c 0,-2.7940045 -0.762,-3.8523395 -3.70417,-3.8523395 h -4.65667 v 0.1693336 l 1.37583,3.5560056 z"
id="path23" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 370.90625,4.9238281 v 3.7890625 h -2.58203 v 0.7636719 l 1.21484,3.1582035 h 1.36719 v 8.699218 h 4.83203 V 12.634766 H 379.125 V 8.7128906 h -3.38672 V 4.9238281 Z m 7.72656,0 V 5.6875 l 1.53321,3.9628906 h 2.1621 c 0.30851,0 0.37476,0.050297 0.39258,0.070312 0.0178,0.020016 0.082,0.1558452 0.082,0.5097654 v 11.103515 h 5.19141 V 9.2773438 c 0,-1.4424568 -0.18358,-2.5460127 -0.87891,-3.3105469 -0.69533,-0.7645343 -1.79513,-1.0429688 -3.32617,-1.0429688 z m -6.72656,1 h 2.83203 v 3.7890625 h 3.38672 v 1.9218754 h -3.38672 v 8.699218 h -2.83203 v -8.699218 h -1.67969 l -0.74023,-1.9218754 h 2.41992 z m 7.89062,0 h 3.99219 c 1.41114,0 2.16473,0.2517094 2.58594,0.7148438 0.42121,0.4631344 0.61914,1.2871241 0.61914,2.6386719 V 20.333984 h -3.19141 V 10.230469 c 0,-0.4504146 -0.0521,-0.85713 -0.33593,-1.1757815 -0.2838,-0.3186515 -0.70635,-0.4042969 -1.13868,-0.4042969 h -1.47656 z"
id="path24" />
</g>
<g
id="text3"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 22.61014,297.44787 h -3.386672 v -3.78884 h -3.831173 v 3.78884 h -2.582337 v 0.16933 l 1.058335,2.75168 h 1.524002 v 8.69951 h 3.831173 v -8.69951 h 3.386672 z m 18.944187,7.81051 h -8.001013 c -1.227669,0 -2.286004,0.0212 -4.064006,0.127 v -0.14816 c 0.952501,-0.23284 1.735669,-0.46567 3.810006,-1.143 l 5.969009,-1.92617 c 1.98967,-0.63501 2.455337,-1.35467 2.455337,-3.47134 v -1.54517 c 0,-2.09551 -0.952501,-3.49251 -3.661839,-3.49251 H 23.753132 v 0.16934 l 1.312335,3.429 h 11.408852 c 0.698501,0 0.867834,0.27517 0.867834,0.78317 v 0.5715 c 0,0.55033 -0.211667,0.8255 -0.825501,1.03717 l -11.408851,3.89467 c -0.783168,0.27517 -1.016002,0.61384 -1.016002,1.56634 v 3.19617 c 0,0.55033 0.254,0.762 0.592668,0.762 h 16.86986 z"
id="path29" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 14.892578,293.1582 v 3.78907 h -2.582031 v 0.76367 l 1.214844,3.1582 h 1.367187 v 8.69922 h 4.830078 v -8.69922 h 3.386719 v -3.92187 h -3.386719 v -3.78907 z m 8.361328,0 v 0.76172 l 1.466797,3.83789 h 11.753906 c 0.283594,0 0.342775,0.0558 0.337891,0.0508 -0.0049,-0.005 0.0293,0.034 0.0293,0.23243 v 0.57031 c 0,0.21106 -0.03003,0.28002 -0.07422,0.33594 -0.04419,0.0559 -0.153527,0.13867 -0.414062,0.22851 l -11.410157,3.89649 h -0.002 c -0.437489,0.15371 -0.814289,0.36283 -1.052734,0.72656 -0.238445,0.36373 -0.296875,0.79355 -0.296875,1.31054 v 3.19727 c 0,0.35209 0.0826,0.67409 0.292969,0.91602 0.210369,0.24192 0.525326,0.3457 0.798828,0.3457 h 17.371093 v -4.81055 h -8.501953 c -0.243624,0 -0.570664,0.0232 -0.808593,0.0254 0.305142,-0.0978 0.326312,-0.0879 0.708984,-0.21289 l 5.966797,-1.92578 c 1.036192,-0.3307 1.774785,-0.71448 2.232422,-1.37109 0.457636,-0.65661 0.572265,-1.48117 0.572265,-2.57617 v -1.54493 c 0,-1.12351 -0.260337,-2.14285 -0.953125,-2.87695 -0.692788,-0.7341 -1.768378,-1.11719 -3.208984,-1.11719 z m -7.361328,1 h 2.830078 v 3.78907 h 3.386719 v 1.92187 h -3.386719 v 8.69922 h -2.830078 v -8.69922 h -1.68164 l -0.738282,-1.92187 h 2.419922 z m 8.523438,0 H 38.0625 c 1.268732,0 2.021625,0.31641 2.482422,0.80469 0.460797,0.48828 0.679687,1.21746 0.679687,2.18945 v 1.54493 c 0,1.02166 -0.119963,1.61276 -0.392578,2.0039 -0.272614,0.39114 -0.761365,0.68593 -1.714844,0.99024 h -0.002 l -5.970703,1.92773 c -2.071176,0.6763 -2.834211,0.90322 -3.773437,1.13281 l -0.38086,0.0918 v 1.07227 l 0.529297,-0.0312 c 1.770212,-0.10534 2.811291,-0.12696 4.033203,-0.12696 h 7.501953 v 2.81055 H 24.683594 c -0.06517,0 -0.04362,-4.6e-4 -0.04492,-0.002 -0.0013,-0.002 -0.04687,-0.0615 -0.04687,-0.25977 v -3.19727 c 0,-0.4355 0.05905,-0.64919 0.132812,-0.76171 0.07376,-0.11253 0.20315,-0.21058 0.548828,-0.33204 l 11.40625,-3.89257 v -0.002 c 0.353299,-0.12183 0.657948,-0.28056 0.873047,-0.55273 0.215099,-0.27217 0.289063,-0.61776 0.289063,-0.95703 v -0.57031 c 0,-0.30956 -0.05028,-0.66208 -0.314453,-0.93165 -0.264176,-0.26956 -0.637827,-0.35156 -1.052735,-0.35156 h -11.06446 z"
id="path30" />
</g>
<g
id="text3-1"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 368.78211,297.97579 h -3.38667 v -3.78883 h -3.83117 v 3.78883 h -2.58234 v 0.16934 l 1.05833,2.75167 h 1.52401 v 8.69951 h 3.83117 v -8.69951 h 3.38667 z m 13.52551,-0.27516 c 0.61384,0 0.889,0.254 0.889,0.762 v 0.74083 c 0,0.5715 -0.254,0.91017 -0.91016,0.91017 h -10.11769 v 0.16934 l 0.99484,2.70933 h 9.05935 c 0.6985,0 1.03716,0.29634 1.03716,0.9525 v 0.76201 c 0,0.71966 -0.29633,1.05833 -1.16416,1.05833 h -11.74752 v 3.83117 h 13.03868 c 3.00568,0 4.10634,-1.50283 4.10634,-4.064 v -1.16417 c 0,-1.3335 -0.6985,-2.30717 -1.56633,-2.73051 0.635,-0.46566 1.22767,-1.22766 1.22767,-2.56117 v -1.29117 c 0,-2.159 -1.03717,-3.59833 -3.89468,-3.59833 h -13.50435 v 0.16933 l 1.35467,3.34434 z"
id="path27" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 361.06445,293.6875 v 3.78906 h -2.58203 v 0.76172 l 1.21485,3.1582 h 1.36718 v 8.69922 h 4.83008 v -8.69922 h 3.38672 v -3.91992 h -3.38672 v -3.78906 z m 8.19141,0 v 0.76562 l 1.51758,3.74805 h 11.53515 c 0.23796,0 0.328,0.051 0.34571,0.0664 0.0177,0.0154 0.043,0.0316 0.043,0.19531 v 0.74023 c 0,0.21168 -0.0464,0.29622 -0.0762,0.32813 -0.0297,0.0319 -0.0919,0.082 -0.33398,0.082 h -10.61914 v 0.75781 l 1.14648,3.1211 h 9.40821 c 0.27854,0 0.40023,0.0587 0.44531,0.0996 0.0451,0.0409 0.0918,0.11145 0.0918,0.35351 v 0.76172 c 0,0.2912 -0.0525,0.39055 -0.10157,0.4375 -0.0491,0.047 -0.19362,0.12109 -0.5625,0.12109 h -12.24804 v 4.83008 h 13.53906 c 1.58617,0 2.77621,-0.40668 3.54492,-1.22851 0.76871,-0.82184 1.0625,-1.98178 1.0625,-3.33399 v -1.16601 c 0,-1.24687 -0.61226,-2.15723 -1.39258,-2.75782 0.56011,-0.56303 1.05274,-1.33541 1.05274,-2.5332 v -1.29101 c 0,-1.15957 -0.2836,-2.20926 -1.01758,-2.95899 -0.73398,-0.74972 -1.86425,-1.13867 -3.37695,-1.13867 z m -7.19141,1 h 2.83008 v 3.78906 h 3.38672 v 1.91992 h -3.38672 v 8.69922 h -2.83008 v -8.69922 h -1.68164 l -0.73828,-1.91992 h 2.41992 z m 8.36524,0 h 12.83008 c 1.3448,0 2.16312,0.3282 2.6621,0.83789 0.49899,0.50969 0.73243,1.26033 0.73243,2.25977 v 1.29101 c 0,1.18536 -0.47856,1.75863 -1.02344,2.1582 l -0.66797,0.49024 0.74414,0.36328 c 0.69133,0.33724 1.28711,1.1204 1.28711,2.2793 v 1.16601 c 0,1.20896 -0.25798,2.08052 -0.79102,2.65039 -0.53303,0.56988 -1.39689,0.91211 -2.8164,0.91211 h -12.53906 v -2.83008 h 11.24804 c 0.49896,0 0.93787,-0.0961 1.25391,-0.39843 0.31604,-0.3023 0.41016,-0.7317 0.41016,-1.16016 v -0.76172 c 0,-0.4141 -0.12105,-0.82246 -0.41993,-1.09375 -0.29887,-0.27129 -0.69723,-0.35937 -1.11718,-0.35937 h -8.71094 l -0.68945,-1.87891 h 9.46484 c 0.4141,0 0.80511,-0.12009 1.06641,-0.40039 0.26129,-0.2803 0.34375,-0.64994 0.34375,-1.00977 v -0.74023 c 0,-0.34427 -0.11339,-0.71067 -0.38672,-0.94922 -0.27333,-0.23854 -0.62607,-0.3125 -1.00196,-0.3125 h -10.86132 z"
id="path28" />
</g>
<g
id="text6-84"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.87817,19.323245 h 2.20134 v -4.826008 h 4.80484 v -2.18017 h -4.80484 V 7.4910596 h -2.20134 v 4.8260074 h -4.80484 v 2.18017 h 4.80484 z"
id="path13" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.37891,6.9902344 v 4.8261716 h -4.80469 v 3.181641 h 4.80469 v 4.826172 h 3.20117 v -4.826172 h 4.80469 v -3.181641 h -4.80469 V 6.9902344 Z m 1,1 h 1.20117 v 4.8261716 h 4.80469 v 1.181641 h -4.80469 v 4.826172 h -1.20117 v -4.826172 h -4.80469 v -1.181641 h 4.80469 z"
id="path14" />
</g>
<g
id="text6"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 28.866225,149.41589 h 2.201337 v -4.82601 h 4.804841 v -2.18017 h -4.804841 v -4.826 h -2.201337 v 4.826 h -4.804841 v 2.18017 h 4.804841 z"
id="path21" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 28.367187,137.08398 v 4.82618 h -4.80664 v 3.17968 h 4.80664 v 4.82618 h 3.201172 v -4.82618 h 4.804688 v -3.17968 h -4.804688 v -4.82618 z m 1,1 h 1.201172 v 4.82618 h 4.804688 v 1.17968 h -4.804688 v 4.82618 h -1.201172 v -4.82618 h -4.80664 v -1.17968 h 4.80664 z"
id="path22" />
</g>
<g
id="text6-8"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.87817,149.53483 h 2.20134 v -4.826 h 4.80484 v -2.18017 h -4.80484 v -4.82601 h -2.20134 v 4.82601 h -4.80484 v 2.18017 h 4.80484 z"
id="path17" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.37891,137.20312 v 4.82618 h -4.80469 v 3.17968 h 4.80469 v 4.82618 h 3.20117 v -4.82618 h 4.80469 v -3.17968 h -4.80469 v -4.82618 z m 1,1 h 1.20117 v 4.82618 h 4.80469 v 1.17968 h -4.80469 v 4.82618 h -1.20117 v -4.82618 h -4.80469 v -1.17968 h 4.80469 z"
id="path18" />
</g>
<g
id="text6-3"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 368.87506,149.45341 h 2.20133 v -4.82601 h 4.80484 v -2.18017 h -4.80484 v -4.826 h -2.20133 v 4.826 h -4.80484 v 2.18017 h 4.80484 z"
id="path19" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 368.375,137.12109 v 4.82618 h -4.80469 v 3.17968 h 4.80469 v 4.82617 h 3.20117 v -4.82617 h 4.80469 v -3.17968 h -4.80469 v -4.82618 z m 1,1 h 1.20117 v 4.82618 h 4.80469 v 1.17968 h -4.80469 v 4.82617 H 369.375 v -4.82617 h -4.80469 v -1.17968 h 4.80469 z"
id="path20" />
</g>
<g
id="text6-2"
style="opacity:1">
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;fill:#ff0000;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.8614,307.41443 h 2.20134 v -4.82601 h 4.80484 v -2.18017 h -4.80484 v -4.82601 h -2.20134 v 4.82601 h -4.80484 v 2.18017 h 4.80484 z"
id="path15" />
<path
style="font-weight:bold;font-size:21.1667px;font-family:Recharge;-inkscape-font-specification:'Recharge Bold';text-align:center;baseline-shift:baseline;text-anchor:middle;display:inline;overflow:visible;vector-effect:none;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 198.36133,295.08203 v 4.82617 h -4.80469 v 3.17969 h 4.80469 v 4.82617 h 3.20117 v -4.82617 h 4.80469 v -3.17969 h -4.80469 v -4.82617 z m 1,1 h 1.20117 v 4.82617 h 4.80469 v 1.17969 h -4.80469 v 4.82617 h -1.20117 v -4.82617 h -4.80469 v -1.17969 h 4.80469 z"
id="path16" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 20 KiB

@@ -1,261 +0,0 @@
{
"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"
]
}
@@ -1,261 +0,0 @@
{
"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"
]
}
@@ -1,261 +0,0 @@
{
"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"
]
}
@@ -1,254 +0,0 @@
{
"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"
]
}
@@ -1,254 +0,0 @@
{
"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"
]
}
@@ -1,254 +0,0 @@
{
"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"
]
}
@@ -1,399 +0,0 @@
{
"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"
]
}
@@ -1,399 +0,0 @@
{
"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"
]
}
@@ -1,399 +0,0 @@
{
"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"
]
}
@@ -1,167 +0,0 @@
{
"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"
]
}
@@ -1,167 +0,0 @@
{
"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"
]
}
@@ -1,167 +0,0 @@
{
"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"
]
}
@@ -1,51 +0,0 @@
{
"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"
}
@@ -1,12 +0,0 @@
{
"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"
}
@@ -1,12 +0,0 @@
{
"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"
}
@@ -1,12 +0,0 @@
{
"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"
}
@@ -1,12 +0,0 @@
{
"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"
}
@@ -1,28 +0,0 @@
{
"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"
]
}
@@ -1,28 +0,0 @@
{
"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"
]
}
@@ -1,28 +0,0 @@
{
"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"
]
}
@@ -1,28 +0,0 @@
{
"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"
]
}
@@ -1,28 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,27 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,26 +0,0 @@
{
"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"
]
}
@@ -1,265 +0,0 @@
{
"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"
}
@@ -1,8 +0,0 @@
{
"type": "process",
"name": "fdm_process_xplorer_common_0_6",
"inherits": "fdm_process_xplorer_common",
"from": "system",
"instantiation": "false",
"initial_layer_print_height": "0.3"
}
@@ -1,8 +0,0 @@
{
"type": "process",
"name": "fdm_process_xplorer_common_0_8",
"inherits": "fdm_process_xplorer_common",
"from": "system",
"instantiation": "false",
"initial_layer_print_height": "0.4"
}
@@ -29,11 +29,13 @@ uniform vec3 palette_lab[64];
uniform vec3 palette_rgb[64];
uniform int palette_count;
uniform bool pure_only; // match against single filaments only (flat-colour image)
// The entry's two filaments, equal for a single filament - only so a mix can be told apart. An entry's
// palette_rgb is already the colour it prints in (for a mix, its mixed filament slot's).
// How each entry prints. Every entry names a single filament: a mix is given its own mixed filament
// slot, whose components the slicer alternates per print layer, so the fragment just looks that slot's
// colour up.
uniform int palette_a[64];
uniform int palette_b[64];
uniform float prefer_pure_de; // PREFER_PURE_DE: how much better than a single filament a mix must be
uniform vec3 filament_rgb[16];
uniform int filament_count;
uniform sampler2D color_tex; // the layer's colour image, sampled at the same uv as the height
uniform bool has_color_tex;
uniform bool volume_mirrored;
@@ -208,16 +210,31 @@ int nearest_palette_entry(vec3 rgb)
best = i;
}
}
// The same bias make_palette_quantizer() applies: a mix is an interleave, so it is only worth taking
// when it beats the nearest single filament by a visible step - otherwise the preview shows mixes
// where the bake prints a single filament. Compared on the distances rather than their squares, so
// the margin means the same thing as it does on the CPU (up to CIE76 against CIEDE2000, the
// approximation already noted above).
if (best_pure >= 0 && palette_a[best] != palette_b[best] && sqrt(bd_pure) - sqrt(bd) < prefer_pure_de)
// The same bias make_palette_quantizer() applies (PREFER_PURE_DE = 10): a mix is an interleave, so
// it is only worth taking when it beats the nearest single filament by a visible step. Without it
// this picked a mix for almost every fragment - with four filaments the palette is 4 pure entries
// against 30 mixes - while the bake picked a single filament for most of them, so the preview
// interleaved the whole wall where the bake interleaves only patches. Compared on the distances
// rather than their squares, so the threshold means the same thing as it does on the CPU (up to
// CIE76 against CIEDE2000, the approximation already noted above).
if (best_pure >= 0 && palette_a[best] != palette_b[best] && sqrt(bd_pure) - sqrt(bd) < 10.0)
best = best_pure;
return best;
}
// One 2x2 Bayer cell, {0, 2; 3, 1}, for x and y in {0, 1}.
// The colour the printer lays down at world point `pos` for palette entry `index`. Every entry names a
// single filament: a mix is given its own mixed filament slot, whose components the slicer alternates
// per print layer, so there is nothing left to interleave here.
vec3 printed_color(int index)
{
int a = palette_a[index];
if (a < 0 || a >= filament_count)
return palette_rgb[index]; // no filament to resolve to: the entry's own colour
return filament_rgb[a];
}
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
@@ -337,11 +354,16 @@ void main()
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
// Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour.
// Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour -
// and, where that is a mix, the filament the interleave puts here, so the pattern that prints shows.
// Only the albedo - the specular term (intensity.y) stays white - so a coloured fragment reads as
// the same material under the same light, and the relief this preview exists to show is unaffected.
vec3 albedo = uniform_color.rgb;
if (palette_count > 0 && has_color_tex && have_uv && weight > 0.0)
albedo = palette_rgb[nearest_palette_entry(texture2D(color_tex, color_uv).rgb)];
// tex_pos, not world_pos: the bake resolves the interleave in the bake frame (world
// orientation and scale about the volume's origin, see texture_displacement_bake_frame()), so
// measuring z from the bed instead shifted the band phase by the volume origin's height - a
// different filament in the same place than the bake produces.
albedo = printed_color(nearest_palette_entry(texture2D(color_tex, color_uv).rgb));
gl_FragColor = vec4(vec3(intensity.y) + albedo * intensity.x, uniform_color.a);
}
@@ -88,11 +88,13 @@ uniform vec3 palette_lab[64];
uniform vec3 palette_rgb[64];
uniform int palette_count;
uniform bool pure_only; // match against single filaments only (flat-colour image)
// The entry's two filaments, equal for a single filament - only so a mix can be told apart. An entry's
// palette_rgb is already the colour it prints in (for a mix, its mixed filament slot's).
// How each entry prints. Every entry names a single filament: a mix is given its own mixed filament
// slot, whose components the slicer alternates per print layer, so the fragment just looks that slot's
// colour up.
uniform int palette_a[64];
uniform int palette_b[64];
uniform float prefer_pure_de; // PREFER_PURE_DE: how much better than a single filament a mix must be
uniform vec3 filament_rgb[16];
uniform int filament_count;
uniform sampler2D color_tex; // the layer's colour image, sampled at the same uv as the height
uniform bool has_color_tex;
uniform bool volume_mirrored;
@@ -274,16 +276,31 @@ int nearest_palette_entry(vec3 rgb)
best = i;
}
}
// The same bias make_palette_quantizer() applies: a mix is an interleave, so it is only worth taking
// when it beats the nearest single filament by a visible step - otherwise the preview shows mixes
// where the bake prints a single filament. Compared on the distances rather than their squares, so
// the margin means the same thing as it does on the CPU (up to CIE76 against CIEDE2000, the
// approximation already noted above).
if (best_pure >= 0 && palette_a[best] != palette_b[best] && sqrt(bd_pure) - sqrt(bd) < prefer_pure_de)
// The same bias make_palette_quantizer() applies (PREFER_PURE_DE = 10): a mix is an interleave, so
// it is only worth taking when it beats the nearest single filament by a visible step. Without it
// this picked a mix for almost every fragment - with four filaments the palette is 4 pure entries
// against 30 mixes - while the bake picked a single filament for most of them, so the preview
// interleaved the whole wall where the bake interleaves only patches. Compared on the distances
// rather than their squares, so the threshold means the same thing as it does on the CPU (up to
// CIE76 against CIEDE2000, the approximation already noted above).
if (best_pure >= 0 && palette_a[best] != palette_b[best] && sqrt(bd_pure) - sqrt(bd) < 10.0)
best = best_pure;
return best;
}
// One 2x2 Bayer cell, {0, 2; 3, 1}, for x and y in {0, 1}.
// The colour the printer lays down at world point `pos` for palette entry `index`. Every entry names a
// single filament: a mix is given its own mixed filament slot, whose components the slicer alternates
// per print layer, so there is nothing left to interleave here.
vec3 printed_color(int index)
{
int a = palette_a[index];
if (a < 0 || a >= filament_count)
return palette_rgb[index]; // no filament to resolve to: the entry's own colour
return filament_rgb[a];
}
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
@@ -425,11 +442,16 @@ void main()
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
// Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour.
// Diffuse albedo: the image's colour at this fragment, snapped to the nearest printable colour -
// and, where that is a mix, the filament the interleave puts here, so the pattern that prints shows.
// Only the albedo - the specular term (intensity.y) stays white - so a coloured fragment reads as
// the same material under the same light, and the relief this preview exists to show is unaffected.
vec3 albedo = uniform_color.rgb;
if (palette_count > 0 && has_color_tex && have_uv && weight > 0.0)
albedo = palette_rgb[nearest_palette_entry(texture(color_tex, color_uv).rgb)];
// tex_pos, not world_pos: the bake resolves the interleave in the bake frame (world
// orientation and scale about the volume's origin, see texture_displacement_bake_frame()), so
// measuring z from the bed instead shifted the band phase by the volume origin's height - a
// different filament in the same place than the bake produces.
albedo = printed_color(nearest_palette_entry(texture(color_tex, color_uv).rgb));
out_color = vec4(vec3(intensity.y) + albedo * intensity.x, uniform_color.a);
}
-6
View File
@@ -325,12 +325,6 @@ 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
-54
View File
@@ -18,7 +18,6 @@
#include <utility>
#include "ColorDecomposeRecipe.hpp"
#include "Config.hpp"
#include "FilamentMixerModel.hpp"
#include "LocalesUtils.hpp"
@@ -428,59 +427,6 @@ std::vector<double> parse_mixed_ratios(const std::string &str, size_t n_componen
return ratios;
}
std::string format_mixed_components(const std::vector<unsigned int> &components)
{
std::string out;
for (size_t i = 0; i < components.size(); ++i) {
if (i > 0)
out += ",";
out += std::to_string(components[i]);
}
return out;
}
std::string format_mixed_ratios(const std::vector<int> &weights)
{
int sum = std::accumulate(weights.begin(), weights.end(), 0);
if (sum <= 0)
sum = 100;
CNumericLocalesSetter c_locale_setter;
std::string out;
for (size_t i = 0; i < weights.size(); ++i) {
if (i > 0)
out += ",";
char buf[32];
std::snprintf(buf, sizeof(buf), "%.4f", float(weights[i]) / float(sum));
out += buf;
}
return out;
}
int find_fixed_mixed_filament(const ConfigBase &project_config,
const std::vector<unsigned int> &components,
const std::vector<int> &weights)
{
const auto *is_mixed = project_config.option<ConfigOptionBools>("filament_is_mixed");
const auto *comps = project_config.option<ConfigOptionStrings>("filament_mixed_components");
const auto *ratios = project_config.option<ConfigOptionStrings>("filament_mixed_sublayer_ratios");
if (is_mixed == nullptr || comps == nullptr || ratios == nullptr)
return -1;
// Created lazily with the first mixed slot, so an older project may not have it at all.
const auto *gradient = project_config.option<ConfigOptionBools>("filament_mixed_gradient");
const std::string comp_str = format_mixed_components(components);
const std::string ratio_str = format_mixed_ratios(weights);
for (size_t i = 0; i < is_mixed->values.size(); ++i) {
if (!is_mixed->values[i] || i >= comps->values.size() || i >= ratios->values.size())
continue;
if (gradient != nullptr && i < gradient->values.size() && gradient->values[i])
continue;
if (comps->values[i] == comp_str && ratios->values[i] == ratio_str)
return int(i);
}
return -1;
}
bool has_any_mixed_filament(const std::vector<unsigned char> &is_mixed)
{
for (unsigned char v : is_mixed)
-18
View File
@@ -11,8 +11,6 @@
namespace Slic3r {
class ConfigBase;
// Photoshop-style gradient curve control point in [0,1] x [0,1].
// (x, y) is the anchor position; (m_in, m_out) are optional cubic Hermite tangent
// overrides. NaN means "use the PCHIP-computed default", which is the case for plain
@@ -96,22 +94,6 @@ std::vector<unsigned int> parse_mixed_components(const std::string &str);
// Normalizes so the sum equals 1.0.
std::vector<double> parse_mixed_ratios(const std::string &str, size_t n_components);
// The text a mixed slot stores in filament_mixed_components, e.g. {1, 3} → "1,3".
std::string format_mixed_components(const std::vector<unsigned int> &components);
// The text a mixed slot stores in filament_mixed_sublayer_ratios: the weights normalised to sum to 1,
// four decimals, e.g. {1, 2} → "0.3333,0.6667". A non-positive sum is read as 100.
std::string format_mixed_ratios(const std::vector<int> &weights);
// The 0-based index of the mixed slot in `project_config` that blends `components` (1-based physical
// filaments) in `weights` at a fixed ratio, or -1 when there is none. Matched on the stored text, as
// format_mixed_components() and format_mixed_ratios() write it. A gradient slot never matches: its
// ratio drifts from layer to layer, so it is not the blend asked for even where its stored ratios
// are the same.
int find_fixed_mixed_filament(const ConfigBase &project_config,
const std::vector<unsigned int> &components,
const std::vector<int> &weights);
// Returns true if any element in is_mixed is true.
// ConfigOptionBools stores values as std::vector<unsigned char>.
bool has_any_mixed_filament(const std::vector<unsigned char> &is_mixed);
-30
View File
@@ -1,6 +1,5 @@
#include "../libslic3r.h"
#include "../Exception.hpp"
#include "../IMEXHelpers.hpp"
#include "../Model.hpp"
#include "../Preset.hpp"
#include "../Utils.hpp"
@@ -369,12 +368,6 @@ 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";
@@ -4583,12 +4576,6 @@ 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;
@@ -8290,23 +8277,6 @@ 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<ConfigOptionString>("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<ConfigOptionString>("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<FilamentMapMode>::get_enum_names();
+58 -360
View File
@@ -29,7 +29,6 @@
#include "enum_bitmask.hpp"
#include "libslic3r.h"
#include "I18N.hpp"
#include "IMEXHelpers.hpp"
#include "GCode.hpp"
#include <cstdio>
#include "Exception.hpp"
@@ -88,7 +87,6 @@
#include <type_traits>
#include <utility>
#include <string_view>
#include <sstream>
#include <regex>
#include <boost/algorithm/string.hpp>
@@ -1712,16 +1710,7 @@ static std::vector<Vec2d> 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);
// 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);
const std::string t_token = " T" + std::to_string(new_extruder_id);
std::string out;
out.reserve(toolchange_gcode_str.size());
size_t pos = 0;
@@ -1742,7 +1731,7 @@ static std::vector<Vec2d> 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) == heater_id;
matches_extruder = std::stoi(t_val) == new_extruder_id;
} catch (...) {
matches_extruder = false;
}
@@ -1774,6 +1763,44 @@ static std::vector<Vec2d> 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;
@@ -3224,38 +3251,6 @@ 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<int> get_imex_active_tools(const Print& print)
{
std::vector<int> 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();
@@ -3665,51 +3660,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
std::vector<unsigned char> 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));
{
@@ -3950,103 +3900,6 @@ 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<n> 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())
@@ -4220,10 +4073,6 @@ 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
@@ -4340,49 +4189,6 @@ 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();
@@ -6336,7 +6142,6 @@ 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.
@@ -6538,7 +6343,6 @@ 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";
@@ -6702,56 +6506,17 @@ LayerResult GCode::process_layer(
gcode += m_writer.set_jerk_xy(NOZZLE_CONFIG(default_jerk));
}
// 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());
}
// 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());
}
// BBS
@@ -8133,31 +7898,6 @@ 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_* = <default>`
// 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,
@@ -10564,21 +10304,9 @@ 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);
}
// 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 += set_filament_pressure_advance(new_filament_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<n> would be a duplicate. Mid-
// print toolchanges (count > 1) emit normally — Print::validate() blocks
// multi-color in configurations where mid-print T<n> 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;
gcode += m_writer.toolchange(new_filament_id, new_extruder_id);
if (Extruder *fil = m_writer.filament())
fil->set_config_index((int)get_filament_config_index((int)fil->id()));
return gcode;
@@ -10622,27 +10350,7 @@ 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.
//
// 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))
if (m_ooze_prevention.enable && m_writer.filament() != nullptr)
gcode += m_ooze_prevention.pre_toolchange(*this);
// BBS
@@ -10912,13 +10620,7 @@ 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()));
// See imex_suppresses_bare_toolchange() — only suppress the print-start initial
// T<n> in parallel modes (the user's imex_mode_gcode + machine_start_gcode owns
// tool activation there). Mid-print T<n> 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)
if (!custom_gcode_changes_tool(toolchange_gcode_parsed, m_writer.toolchange_prefix(), new_filament_id))
gcode += toolchange_command;
else {
// user provided his own toolchange gcode, no need to do anything
@@ -10991,16 +10693,14 @@ 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);
// 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));
gcode += set_filament_pressure_advance(new_filament_id);
//Orca: tool changer or IDEX's firmware may change Z position, so we set it to unknown/undefined
m_last_pos_defined = false;
return gcode;
}
std::string GCode::set_filament_pressure_advance(unsigned int filament_id, int tool)
std::string GCode::set_filament_pressure_advance(unsigned int filament_id)
{
const size_t fi = get_filament_config_index(filament_id);
if (!m_config.enable_pressure_advance.get_at(fi))
@@ -11008,9 +10708,7 @@ std::string GCode::set_filament_pressure_advance(unsigned int filament_id, int t
// Orca: Adaptive PA
// Reset Adaptive PA processor last PA value
m_pa_processor->resetPreviousPA(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);
return m_writer.set_pressure_advance(m_config.pressure_advance.get_at(fi));
}
inline std::string polygon_to_string(const Polygon &polygon, Print *print, bool is_print_space = false) {
+1 -22
View File
@@ -262,15 +262,6 @@ 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; }
@@ -311,11 +302,7 @@ 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.
// 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);
std::string set_filament_pressure_advance(unsigned int filament_id);
bool is_BBL_Printer();
WipeTowerType wipe_tower_type();
@@ -849,14 +836,6 @@ protected:
std::unique_ptr<AdaptivePAProcessor> 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<int,int> m_imex_head_filament_map;
std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
std::unique_ptr<SmallAreaInfillFlowCompensator> m_small_area_infill_flow_compensator;
-4
View File
@@ -1472,10 +1472,6 @@ 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<int> &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<unsigned int> accelerations = m_is_first_layer ? m_first_layer_normal_accelerations : m_normal_accelerations;
-22
View File
@@ -28,28 +28,6 @@ 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<ConfigOptionBool>("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<ConfigOptionBool>("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
-13
View File
@@ -44,17 +44,4 @@ 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
+17 -43
View File
@@ -6,7 +6,6 @@
#include "Point.hpp"
#include "Polygon.hpp"
#include "PrintConfig.hpp"
#include "IMEXHelpers.hpp"
#include "ClipperUtils.hpp"
#include "Geometry/ArcWelder.hpp"
#include "Line.hpp"
@@ -359,16 +358,8 @@ 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);
}
@@ -591,43 +582,26 @@ std::string GCodeWriter::set_junction_deviation(double junction_deviation){
return gcode.str();
}
std::string GCodeWriter::set_pressure_advance(double pa, int tool) const
std::string GCodeWriter::set_pressure_advance(double pa) 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" << std::setprecision(4) << pa << " L1000 M10 ; Override pressure advance value\n";
} else if (FLAVOR_IS(gcfKlipper)) {
// Klipper routes PA by extruder name, not active tool.
// Convention: first extruder is "extruder", subsequent are "extruder1", "extruder2", etc.
gcode << "SET_PRESSURE_ADVANCE ADVANCE=" << std::setprecision(4) << pa;
if (tool > 0)
gcode << " EXTRUDER=extruder" << tool;
else if (tool == 0)
gcode << " EXTRUDER=extruder";
gcode << "; Override pressure advance value\n";
} else if (FLAVOR_IS(gcfRepRapFirmware)) {
// RRF: M572 D<n> 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";
if(m_is_bbl_printers){
//SoftFever: set L1000 to use linear model
gcode << "M900 K" <<std::setprecision(4)<< pa << " L1000 M10 ; Override pressure advance value\n";
}
else{
if (FLAVOR_IS(gcfKlipper))
gcode << "SET_PRESSURE_ADVANCE ADVANCE=" << std::setprecision(4) << pa << "; Override pressure advance value\n";
else if(FLAVOR_IS(gcfRepRapFirmware))
gcode << ("M572 D0 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
gcode << "M900 K" <<std::setprecision(4)<< pa << "; Override pressure advance value\n";
}
return gcode.str();
}
+1 -1
View File
@@ -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, int tool = -1) const;
std::string set_pressure_advance(double pa) 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;
-220
View File
@@ -1,220 +0,0 @@
#include "libslic3r/IMEXArrange.hpp"
#include <algorithm>
#include <cstddef>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <libnest2d/common.hpp>
#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<BoundingBox> boxes_around(const BoundingBox& bed, const BoundingBox& zone)
{
std::vector<BoundingBox> 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<ImexArrangeZones> 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<ConfigOptionString>("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<ConfigOptionPoints>("printable_area")->values)));
}
// An estimate: auto brim picks its own width, which brim_width stands in for.
const auto* brim_type = full_config.option<ConfigOptionEnum<BrimType>>("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<ImexArrangeZones>& 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<BoundingBox> 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<BoundingBox> room = room_in(b.zones.primary_zone);
std::vector<size_t> 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<int> 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
-101
View File
@@ -1,101 +0,0 @@
#pragma once
#include <optional>
#include <vector>
#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<BoundingBoxf> collision_zones;
};
// The zones of `layout`, none when it has no primary zone, i.e. outside a parallel mode.
std::optional<ImexArrangeZones> 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<std::optional<ImexArrangeZones>> 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<ImexArrangeZones> 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<BoundingBox> 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<Bed> m_beds;
std::optional<BoundingBox> m_shared_room;
std::vector<KeepOut> m_keep_outs;
};
} // namespace Slic3r
-712
View File
@@ -1,712 +0,0 @@
#include "libslic3r/IMEXHelpers.hpp"
#include <Eigen/Core>
#include <algorithm>
#include <cstddef>
#include <map>
#include <numeric>
#include <cassert>
#include <cctype>
#include <optional>
#include <set>
#include <sstream>
#include <string>
#include <unordered_set>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <vector>
#include <utility>
#include "Config.hpp"
#include "libslic3r.h"
#include "Point.hpp"
#include "BoundingBox.hpp"
#include "Polygon.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/I18N.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/PrintConfig.hpp"
// Mark string for localization and translate.
#define L(s) Slic3r::I18N::translate(s)
namespace Slic3r {
namespace {
// Strip every whitespace byte from a token in place.
//
// Not `::isspace` directly: it takes an int that must be representable as unsigned char
// (or equal EOF), and `char` is signed on all three targets, so any byte >= 0x80 is sign-
// extended to a negative int and the call is undefined. The strings parsed below come from
// printer profiles and 3MF metadata, neither of which is guaranteed ASCII, so the unsigned
// char cast is the same one the rest of the codebase already applies to std::toupper.
void strip_whitespace(std::string& token)
{
token.erase(std::remove_if(token.begin(), token.end(),
[](unsigned char c) { return std::isspace(c) != 0; }),
token.end());
}
} // namespace
ConfigOptionInts effective_physical_extruder_map(const ConfigOptionInts* explicit_pem, int nozzle_count)
{
// One entry per logical extruder, so a profile has authored a map only when its length
// matches. The single-element PrintConfig default does not, and is replaced rather than
// treated as a one-extruder machine.
if (explicit_pem && nozzle_count > 0 && (int) explicit_pem->values.size() == nozzle_count)
return *explicit_pem;
ConfigOptionInts derived;
derived.values.resize(std::max(nozzle_count, 1));
std::iota(derived.values.begin(), derived.values.end(), 0);
return derived;
}
ConfigOptionInts effective_physical_extruder_map(const PresetBundle& pb)
{
const ConfigOptionInts* explicit_pem = pb.project_config.option<ConfigOptionInts>("physical_extruder_map");
if (!explicit_pem || explicit_pem->values.size() < 2)
explicit_pem = pb.printers.get_edited_preset().config.option<ConfigOptionInts>("physical_extruder_map");
const auto* nozzles = pb.printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter");
return effective_physical_extruder_map(explicit_pem, nozzles ? (int) nozzles->values.size() : 0);
}
int imex_pem_tool_for(int filament_id, const std::string& parallel_mode, const ConfigOptionInts& pem)
{
const bool imex_parallel = !parallel_mode.empty() && parallel_mode != kImexPrimaryMode;
if (!imex_parallel || pem.values.empty())
return -1;
// Bounds-checked, not get_at() -- see the note in IMEXHelpers.hpp for why the two index
// spaces diverge. Emitting no tool qualifier is correct for a miss; pinning PA onto the
// primary's carriage is not.
if (filament_id < 0 || filament_id >= (int) pem.values.size())
return -1;
return pem.values[filament_id];
}
int imex_physical_heater_for(bool is_imex, const ConfigOptionInts& pem, int logical_id)
{
// Bounds-check rather than get_at(): get_at() CLAMPS to values.front(), which would
// silently retarget an out-of-range id at whatever heater sits in slot 0.
if (!is_imex || logical_id < 0 || logical_id >= (int) pem.values.size())
return logical_id;
return pem.values[logical_id];
}
bool imex_suppresses_bare_toolchange(const std::string& parallel_mode, unsigned int toolchange_count)
{
return toolchange_count <= 1
&& !parallel_mode.empty()
&& parallel_mode != kImexPrimaryMode;
}
std::string imex_multicolor_block_reason(const std::string& parallel_mode,
const std::string& active_tools_str,
int tools_per_gantry,
const std::vector<int>& used_filaments_0b,
const ConfigOptionInts& pem)
{
// Not an IMEX parallel print → nothing to validate here.
if (parallel_mode.empty() || parallel_mode == kImexPrimaryMode) return {};
// Single-color prints have no toolchanges to constrain.
if (used_filaments_0b.size() <= 1) return {};
// MMU lane sharing detection: two or more used filaments routed to the same
// physical head means a mid-print MMU swap on that head, which IMEX parallel
// modes can't slave to the secondary gantry.
if (!pem.values.empty()) {
std::unordered_set<int> seen_physicals;
for (int filament : used_filaments_0b) {
if (filament < 0 || filament >= (int)pem.values.size()) continue;
const int phys = pem.values[filament]; // bounds-checked above; get_at would clamp
if (!seen_physicals.insert(phys).second) {
return L("Multi-color prints in IDEX/IQEX parallel modes require each filament "
"to have its own dedicated physical extruder. Two or more of the active "
"filaments are routed to the same physical head via the printer's "
"physical extruder map (an MMU/AFC manifold), which the slaved gantry "
"cannot follow.");
}
}
}
// Determine which physical head is the primary in this mode.
const int primary_physical = imex_primary_tool_for_mode(active_tools_str);
if (primary_physical < 0) {
return L("The active IDEX/IQEX mode does not define a primary tool, so multi-color "
"printing cannot be scheduled. Open the printer settings IDEX/IQEX Modes "
"editor and assign a Primary role to one tool.");
}
// Walk the active tools once: collect the set of distinct gantries spanned by
// the mode and check for an explicit Span marker on the primary's gantry.
// Span declares "this tool is the multicolor partner of primary on the same
// gantry" — without it, two tools on primary's gantry mean two independent
// copies, not a within-gantry toolchange topology, which can't carry
// multicolor in a parallel mode.
const int tpg = std::max(1, tools_per_gantry);
const int primary_gantry = primary_physical / tpg;
bool span_on_primary_gantry = false;
std::set<int> active_gantries;
for (const auto& [phys, role] : parse_imex_active_tools(active_tools_str)) {
active_gantries.insert(phys / tpg);
if (phys / tpg == primary_gantry && role == ImexRole::Span)
span_on_primary_gantry = true;
}
// Single-gantry mode: the active tools all sit on one gantry, so no second
// gantry is being copied/mirrored to. The "Copy"/"Mirror" label is decorative
// — there's no actual parallel printing happening, just a regular multi-tool
// single-gantry print. Multi-color in this configuration is a misconfiguration
// (use Primary mode for that), and the user's mode_gcode would still emit
// parallel-print firmware setup that doesn't apply here.
if (active_gantries.size() < 2) {
return L("Multi-color in this mode isn't a parallel-print scenario — all of the "
"active tools sit on a single gantry, so there's no second gantry being "
"copied or mirrored to. Switch the plate to Primary mode for multi-color "
"printing on a single gantry, or define an IDEX/IQEX mode that includes "
"tools on a second gantry.");
}
// Dual-gantry mode but no Span tool on the primary's gantry — the slicer would
// emit toolchanges between filaments but the mode doesn't declare a within-gantry
// multicolor partner, so the swap can't carry. The user might intend independent
// copies (each gantry tool prints its own object) which is incompatible with
// mid-print multicolor in a parallel mode.
if (!span_on_primary_gantry) {
return L("Multi-color prints in IDEX/IQEX parallel modes require a Span tool on the "
"primary's gantry — without one, the mode doesn't declare a within-gantry "
"multicolor partner. Either reduce the print to a single filament, switch to "
"Primary mode, or open the printer settings IDEX/IQEX Modes editor and mark a "
"tool on the primary's gantry as Span.");
}
// Only the primary and its Span partners print the plate's colors; every other active head
// replays them. A color routed anywhere else -- a copying head, one the mode leaves inactive,
// or past the end of the map -- has no head of its own to print from.
std::set<int> color_heads{ primary_physical };
for (const auto& [phys, role] : parse_imex_active_tools(active_tools_str))
if (phys / tpg == primary_gantry && role == ImexRole::Span)
color_heads.insert(phys);
for (int filament : used_filaments_0b) {
const int phys = pem.values.empty() ? filament
: (filament >= 0 && filament < (int) pem.values.size()) ? pem.values[filament] : -1;
if (color_heads.count(phys))
continue;
std::string tools;
for (int head : color_heads)
tools += (tools.empty() ? "T" : "/T") + std::to_string(head);
// Filaments are numbered from 1 as the sidebar shows them, heads by their T index; the map
// decides which head a filament is on, so with MMU lanes the two numbers differ.
if (phys < 0)
return (boost::format(L("In this Span mode only %1% print colors, but filament %2% isn't routed to any "
"tool. Use filaments routed to %1%, or switch this plate to Primary mode.")) %
tools % (filament + 1)).str();
return (boost::format(L("In this Span mode only %1% print colors, but filament %2% is routed to T%3%. Use "
"filaments routed to %1%, or switch this plate to Primary mode.")) %
tools % (filament + 1) % phys).str();
}
return {};
}
int first_filament_for_physical_head(const ConfigOptionInts& pem, int physical)
{
const auto& v = pem.values;
if (v.empty())
return (physical == 0) ? 0 : -1; // degenerate: treat as identity on head 0
for (size_t i = 0; i < v.size(); ++i) {
if (v[i] == physical)
return static_cast<int>(i);
}
return -1;
}
bool has_mmu(const ConfigOptionInts& pem)
{
if (pem.values.size() < 2)
return false;
std::unordered_set<int> seen;
for (int pv : pem.values) {
if (!seen.insert(pv).second)
return true;
}
return false;
}
bool has_non_primary_mmu(const ConfigOptionInts& pem, int primary_physical)
{
if (pem.values.size() < 2)
return false;
std::unordered_set<int> seen;
for (int pv : pem.values) {
if (pv == primary_physical)
continue;
if (!seen.insert(pv).second)
return true;
}
return false;
}
// The def-side match is on type() rather than a dynamic_cast: ConfigOptionPercent and
// ConfigOptionFloatOrPercent both derive from ConfigOptionFloat, so a cast would accept a
// registration whose value cfg.option<ConfigOptionFloat>() had just refused, and silently return
// the default while a real value sat in the config. For the int, float and bool accessors here
// both halves therefore agree on the type. imex_cfg_enum() is the exception and goes the other
// way -- dynamic_cast on both halves -- because every ConfigOptionEnum<T> reports coEnum and a
// type() check cannot tell one enum type from another; see the comment on it in IMEXHelpers.hpp.
//
// Reaching the final return means the key is not registered at all -- a typo in the literal, which
// the compiler cannot catch. For the clearances that would be worse than the literal it replaced:
// zero suppresses every collision strip. assert() is compiled out under NDEBUG, so it catches this
// in a debug build only; the log line is what remains in Release, and the return is still a
// degraded value. This is a programming-error path, not a runtime-input one.
void imex_cfg_report_unregistered(const char* fn, const std::string& key)
{
BOOST_LOG_TRIVIAL(error) << fn << ": no registered default for " << key;
}
int imex_cfg_int(const ConfigBase& cfg, const std::string& key)
{
if (const auto* opt = cfg.option<ConfigOptionInt>(key))
return opt->value;
if (const ConfigOptionDef* def = print_config_def.get(key))
if (const ConfigOption* dv = def->default_value.get())
if (dv->type() == ConfigOptionInt::static_type())
return static_cast<const ConfigOptionInt*>(dv)->value;
assert(false && "imex_cfg_int: key not registered in print_config_def");
imex_cfg_report_unregistered("imex_cfg_int", key);
return 0;
}
bool imex_cfg_bool(const ConfigBase& cfg, const std::string& key)
{
if (const auto* opt = cfg.option<ConfigOptionBool>(key))
return opt->value;
if (const ConfigOptionDef* def = print_config_def.get(key))
if (const ConfigOption* dv = def->default_value.get())
if (dv->type() == ConfigOptionBool::static_type())
return static_cast<const ConfigOptionBool*>(dv)->value;
assert(false && "imex_cfg_bool: key not registered in print_config_def");
imex_cfg_report_unregistered("imex_cfg_bool", key);
return false;
}
double imex_cfg_float(const ConfigBase& cfg, const std::string& key)
{
if (const auto* opt = cfg.option<ConfigOptionFloat>(key))
return opt->value;
if (const ConfigOptionDef* def = print_config_def.get(key))
if (const ConfigOption* dv = def->default_value.get())
if (dv->type() == ConfigOptionFloat::static_type())
return static_cast<const ConfigOptionFloat*>(dv)->value;
assert(false && "imex_cfg_float: key not registered in print_config_def");
imex_cfg_report_unregistered("imex_cfg_float", key);
return 0.0;
}
char imex_role_letter(ImexRole role)
{
for (const ImexRoleDesc& d : kImexRoleTable)
if (d.role == role)
return d.letter;
// Only reachable if a role was added to the enum but not to kImexRoleTable. Serializing
// it as Copy keeps the string parseable rather than emitting a token no parser accepts.
return 'C';
}
ImexRole imex_role_from_suffix(const std::string& suffix)
{
if (suffix.size() == 1)
for (const ImexRoleDesc& d : kImexRoleTable)
if (d.letter == suffix[0])
return d.role;
return ImexRole::Copy;
}
std::vector<std::pair<int, ImexRole>> parse_imex_active_tools(const std::string& active_tools_for_mode)
{
std::vector<std::pair<int, ImexRole>> out;
if (active_tools_for_mode.empty()) return out;
std::istringstream ss(active_tools_for_mode);
std::string tok;
while (std::getline(ss, tok, ',')) {
strip_whitespace(tok);
if (tok.empty()) continue;
const auto colon = tok.find(':');
const std::string idx_str = (colon == std::string::npos) ? tok : tok.substr(0, colon);
int phys = -1;
try { phys = std::stoi(idx_str); } catch (...) { continue; }
if (phys < 0 || phys >= (int)MAXIMUM_EXTRUDER_NUMBER) continue;
// A bare token (no ':') is Copy here by design -- see the header note; the
// "bare == Primary" backwards-compat rule lives in imex_primary_tool_for_mode().
const ImexRole role = (colon == std::string::npos)
? ImexRole::Copy
: imex_role_from_suffix(tok.substr(colon + 1));
out.emplace_back(phys, role);
}
return out;
}
ImexModeKind imex_mode_kind(const std::string& active_tools_for_mode)
{
const int primary = imex_primary_tool_for_mode(active_tools_for_mode);
std::set<ImexRole> roles;
for (const auto& [phys, role] : parse_imex_active_tools(active_tools_for_mode))
if (phys != primary && role != ImexRole::Primary)
roles.insert(role);
if (roles.empty())
return ImexModeKind::Primary;
if (roles.count(ImexRole::Span))
return ImexModeKind::Custom;
return roles.count(ImexRole::Mirror) ? ImexModeKind::Mirror : ImexModeKind::Copy;
}
ImexGantryGrouping group_imex_active_tools_by_gantry(const std::string& active_tools_for_mode,
int tools_per_gantry)
{
ImexGantryGrouping out;
const int tpg = std::max(1, tools_per_gantry);
const int primary = imex_primary_tool_for_mode(active_tools_for_mode);
if (primary < 0)
return out;
out.primary_phys = primary;
out.primary_gantry = primary / tpg;
const int primary_col = primary % tpg;
// Bucket every active tool by its gantry index. Skip the primary entry itself
// since it carries no role-driven semantics for grouping (it's just the source).
std::map<int, std::vector<std::pair<int, ImexRole>>> by_gantry;
bool span_seen = false;
for (const auto& [phys, role] : parse_imex_active_tools(active_tools_for_mode)) {
if (phys < 0) continue;
const int g = phys / tpg;
by_gantry[g].emplace_back(phys, role);
if (g == out.primary_gantry && role == ImexRole::Span)
span_seen = true;
}
out.span_on_primary = span_seen;
for (auto& [g, tools] : by_gantry) {
ImexGantryGroup grp;
grp.gantry_index = g;
grp.tools = tools;
// Pick representative: column-paired to primary if active, else lowest phys.
const int paired_phys = g * tpg + primary_col;
auto pick = std::find_if(tools.begin(), tools.end(),
[&](const auto& pr) { return pr.first == paired_phys; });
if (pick == tools.end())
pick = std::min_element(tools.begin(), tools.end(),
[](const auto& a, const auto& b) { return a.first < b.first; });
grp.representative_phys = pick->first;
grp.representative_role = pick->second;
// Aggregate iff Span is on primary's gantry, this is a non-primary gantry,
// it has ≥2 tools, and all those tools share the same role (so the merged
// visualization is unambiguous). Mixed-role gantries fall back to per-tool.
const bool same_role = std::all_of(tools.begin(), tools.end(),
[&](const auto& pr) { return pr.second == grp.representative_role; });
grp.aggregate = span_seen
&& g != out.primary_gantry
&& tools.size() >= 2
&& same_role;
out.groups.push_back(std::move(grp));
}
return out;
}
int imex_primary_tool_for_mode(const std::string& active_tools_for_mode)
{
if (active_tools_for_mode.empty())
return -1;
std::istringstream ss(active_tools_for_mode);
std::string token;
int first_bare = -1;
while (std::getline(ss, token, ',')) {
strip_whitespace(token);
if (token.empty())
continue;
const auto colon = token.find(':');
const std::string idx_str = (colon == std::string::npos) ? token : token.substr(0, colon);
int idx = -1;
try {
idx = std::stoi(idx_str);
} catch (...) {
continue;
}
if (idx < 0)
continue;
if (colon == std::string::npos) {
// Backwards-compat: a bare index is Primary.
if (first_bare < 0)
first_bare = idx;
continue;
}
// Same suffix reading as parse_imex_active_tools(), so the two can never disagree
// about which letter means Primary.
if (imex_role_from_suffix(token.substr(colon + 1)) == ImexRole::Primary)
return idx;
}
return first_bare;
}
std::map<int,int> parse_imex_head_filament_map(const std::string& s)
{
std::map<int,int> result;
if (s.empty()) return result;
std::istringstream ss(s);
std::string token;
while (std::getline(ss, token, ',')) {
strip_whitespace(token);
if (token.empty()) continue;
auto colon = token.find(':');
if (colon == std::string::npos) continue;
try {
int phys = std::stoi(token.substr(0, colon));
int slot = std::stoi(token.substr(colon + 1));
// Same absolute sanity cap parse_imex_active_tools applies to its own physical
// index: neither can exceed the slicer-wide extruder ceiling. This only rejects
// nonsense; the bound that matters (slot must index a filament that exists) is
// in resolve_filament_for_head, which is the one that knows the count.
if (phys >= 0 && phys < (int) MAXIMUM_EXTRUDER_NUMBER
&& slot >= 1 && slot <= (int) MAXIMUM_EXTRUDER_NUMBER)
result[phys] = slot;
} catch (...) {}
}
return result;
}
int imex_primary_logical_from_objects(const std::vector<int>& used_slots_1b,
const ConfigOptionInts& pem,
int primary_physical)
{
if (pem.values.empty()) return -1;
const int pem_size = (int)pem.values.size();
for (int slot_1b : used_slots_1b) {
const int slot_0b = slot_1b - 1;
if (slot_0b >= 0 && slot_0b < pem_size && pem.values[slot_0b] == primary_physical)
return slot_0b;
}
return -1;
}
ImexRouting imex_resolve_routing(const ConfigBase& cfg,
const std::string& parallel_mode,
const std::vector<int>& used_slots_1b,
const ConfigOptionInts& pem)
{
ImexRouting out;
// An empty mode is a plate that never carried one; kImexPrimaryMode is the reserved
// "no parallel printing" sentinel. Neither resolves a row, so neither is looked up.
if (parallel_mode.empty() || parallel_mode == kImexPrimaryMode)
return out;
out.parallel = true;
// One lookup of the mode table, one parse of its roster, one search for the Primary
// marker. Every field below is derived from these, so no caller can re-derive one of
// them differently.
out.active_tools = find_imex_mode(cfg, parallel_mode).active_tools;
out.tools = parse_imex_active_tools(out.active_tools);
out.primary_phys = imex_primary_tool_for_mode(out.active_tools);
if (out.primary_phys >= 0)
out.primary_logical = imex_primary_logical_from_objects(used_slots_1b, pem, out.primary_phys);
// Bounds-checked exactly as imex_primary_logical_from_objects checks: get_at() would
// clamp an out-of-range slot to values.front(), which is how a head that nothing routes
// to can end up listed as a head something routes to.
out.routed_heads.reserve(used_slots_1b.size());
for (int slot_1b : used_slots_1b) {
const int slot_0b = slot_1b - 1;
if (slot_0b >= 0 && slot_0b < (int) pem.values.size())
out.routed_heads.push_back(pem.values[slot_0b]);
}
std::sort(out.routed_heads.begin(), out.routed_heads.end());
out.routed_heads.erase(std::unique(out.routed_heads.begin(), out.routed_heads.end()),
out.routed_heads.end());
// The pem emptiness test is not folded into primary_logical: an unpopulated map means
// the printer has declared no routing at all, which is not the same claim as "this
// plate's filaments go somewhere else", and only the latter is an error.
out.primary_unrouted = out.primary_phys >= 0 && !pem.values.empty() && out.primary_logical < 0;
return out;
}
std::vector<int> imex_secondary_logical_slots(const std::vector<int>& active_physicals,
int primary_physical,
const std::map<int,int>& plate_head_filament_map,
const ConfigOptionInts& pem)
{
std::vector<int> out;
std::unordered_set<int> seen;
for (int phys : active_physicals) {
if (phys == primary_physical) continue;
const int logical = resolve_filament_for_head(plate_head_filament_map, pem, phys);
if (logical < 0) continue;
if (seen.insert(logical).second)
out.push_back(logical);
}
return out;
}
int resolve_filament_for_head(const std::map<int,int>& plate_map,
const ConfigOptionInts& pem,
int physical)
{
auto it = plate_map.find(physical);
if (it != plate_map.end()) {
const int zero_based = it->second - 1;
// pem is indexed by filament slot here, so an override outside it names a filament
// this printer cannot route. Bounding here rather
// than at the parse site is deliberate: the parser is handed a raw string with no
// notion of how many filaments the project has, while every consumer of this
// function's result indexes a per-filament array. The picker only ever offers
// indices into pem (IMEXFilamentPickerPopover::build_row), so nothing the UI can
// author is rejected -- only a hand-edited or corrupt 3MF. Such an override falls
// through to the printer's own routing, i.e. it behaves as if it were absent,
// rather than resolving to a wrong filament that get_at() would silently clamp.
if (zero_based >= 0 && zero_based < (int) pem.values.size())
return zero_based;
}
return first_filament_for_physical_head(pem, physical);
}
ImexMirrorAxis imex_mirror_axis_for(int primary_phys, int target_phys, int tools_per_gantry)
{
const int tpg = std::max(1, tools_per_gantry);
return (target_phys / tpg) == (primary_phys / tpg) ? ImexMirrorAxis::X : ImexMirrorAxis::Y;
}
Transform3d imex_head_transform(int /*primary*/, int /*target*/, ImexRole role,
const Vec2d& gantry_offset,
const Vec2d& primary_zone_center,
ImexMirrorAxis mirror_axis)
{
switch (role) {
case ImexRole::Primary:
case ImexRole::Span:
// Span is the within-gantry multicolor partner of Primary: it prints the same
// objects in the same zone via mid-print toolchanges, so it has no zone of its own
// to be translated or reflected into. Identity is the only transform that keeps it
// on top of the primary. calc_imex_ghosts skips Span outright (no ghost is baked
// for it, since the primary's ghost already covers that zone), so today this case
// is unreachable — it is here so that a future caller which does reach it gets the
// right answer rather than the fall-through, and so -Wswitch stays quiet.
return Transform3d::Identity();
case ImexRole::Copy:
return Eigen::Translation3d(gantry_offset.x(), gantry_offset.y(), 0.0)
* Transform3d::Identity();
case ImexRole::Mirror: {
const double len2 = gantry_offset.squaredNorm();
if (len2 < 1e-12)
return Transform3d::Identity();
// True reflection about the zone-boundary plane between the two zones: perpendicular
// to `mirror_axis`, passing through the midpoint of the zone centers along it. The
// ghost lands at the mirrored position within the target zone — matching where the
// mirror tool actually prints — and drag reflects across that plane, so the mirrored
// axis inverts while the other translates 1:1.
// X: linear = diag(-1, 1, 1), translation = (2*center.x + off.x, off.y, 0)
// Y: linear = diag( 1,-1, 1), translation = (off.x, 2*center.y + off.y, 0)
// Both have det = -1: a real mirror image, not a 180° rotation (which would be
// diag(-1,-1,1), det = +1, and would print the primary's part merely turned around).
Transform3d out = Transform3d::Identity();
if (mirror_axis == ImexMirrorAxis::Y) {
out.linear() = Eigen::DiagonalMatrix<double, 3>(1.0, -1.0, 1.0);
out.translation() = Vec3d(gantry_offset.x(),
2.0 * primary_zone_center.y() + gantry_offset.y(),
0.0);
} else {
out.linear() = Eigen::DiagonalMatrix<double, 3>(-1.0, 1.0, 1.0);
out.translation() = Vec3d(2.0 * primary_zone_center.x() + gantry_offset.x(),
gantry_offset.y(),
0.0);
}
return out;
}
}
return Transform3d::Identity();
}
Vec2d compute_imex_slice_offset(bool firmware_managed,
const std::string& parallel_mode,
const std::optional<BoundingBoxf>& primary_zone_box)
{
if (!firmware_managed) return Vec2d::Zero();
if (parallel_mode.empty()) return Vec2d::Zero();
if (parallel_mode == kImexPrimaryMode) return Vec2d::Zero();
if (!primary_zone_box.has_value()) return Vec2d::Zero();
return primary_zone_box->center();
}
namespace {
// Builds the ImexMode for row `i`, padding whatever sibling array does not reach it.
// `names` is known non-null and `i` known in range; `tools` / `gcodes` may be either.
ImexMode imex_mode_at(const ConfigOptionStrings* names,
const ConfigOptionStrings* tools,
const ConfigOptionStrings* gcodes,
size_t i)
{
ImexMode m;
m.index = (int) i;
m.name = names->values[i];
const bool has_tools = tools != nullptr && i < tools->values.size();
const bool has_gcode = gcodes != nullptr && i < gcodes->values.size();
if (has_tools) m.active_tools = tools->values[i];
if (has_gcode) m.gcode = gcodes->values[i];
m.ragged = !has_tools || !has_gcode;
return m;
}
} // namespace
ImexMode find_imex_mode(const ConfigBase& cfg, const std::string& name)
{
const auto* names = cfg.option<ConfigOptionStrings>("imex_mode_names");
if (names == nullptr)
return {};
// The siblings are fetched once, not per row: a missing option is the same answer for
// every row, and it is padding rather than a reason to fail the lookup.
const auto* tools = cfg.option<ConfigOptionStrings>("imex_mode_active_tools");
const auto* gcodes = cfg.option<ConfigOptionStrings>("imex_mode_gcodes");
for (size_t i = 0; i < names->values.size(); ++i)
if (names->values[i] == name)
return imex_mode_at(names, tools, gcodes, i);
return {};
}
std::vector<ImexMode> imex_mode_table(const ConfigBase& cfg)
{
std::vector<ImexMode> table;
const auto* names = cfg.option<ConfigOptionStrings>("imex_mode_names");
if (names == nullptr)
return table;
const auto* tools = cfg.option<ConfigOptionStrings>("imex_mode_active_tools");
const auto* gcodes = cfg.option<ConfigOptionStrings>("imex_mode_gcodes");
table.reserve(names->values.size());
for (size_t i = 0; i < names->values.size(); ++i)
table.push_back(imex_mode_at(names, tools, gcodes, i));
return table;
}
std::vector<std::string> imex_plate_mode_choices(const ConfigBase& cfg)
{
std::vector<std::string> choices{ kImexPrimaryMode };
for (const ImexMode& m : imex_mode_table(cfg))
if (!m.name.empty() && std::find(choices.begin(), choices.end(), m.name) == choices.end())
choices.push_back(m.name);
return choices;
}
bool imex_hull_violates_zones(const std::vector<BoundingBoxf3>& zones, const Polygon& hull)
{
if (hull.points.empty())
return false;
for (const auto& box : zones) {
if (!intersection({box.polygon(true)}, {hull}).empty())
return true;
}
return false;
}
} // namespace Slic3r
-609
View File
@@ -1,609 +0,0 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <map>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <Eigen/Geometry>
#include "Config.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Polygon.hpp"
namespace Slic3r {
class PresetBundle;
// Reserved sentinel name for the always-present, non-deletable Primary mode in
// `imex_mode_names`. Comparison with this constant indicates "no parallel printing
// in effect" — IMEX zone visualization, ghost rendering, secondary-tool PA / temp
// emission, and the 3MF metadata serialization all short-circuit when the active
// plate mode equals this. Treat any value NOT equal to this as a parallel mode.
inline constexpr const char* kImexPrimaryMode = "primary";
// =============================================================================
// The printer's IMEX mode table
// =============================================================================
// `imex_mode_names`, `imex_mode_active_tools` and `imex_mode_gcodes` are three
// independent ConfigOptionStrings coupled only by POSITION: entry i of each describes
// mode i. Nothing in the config system enforces equal length, and all three are on-disk
// format, so a hand-edited preset, a profile written before a key existed, or a 3MF from
// another build can arrive with a sibling array shorter than `imex_mode_names`.
//
// ImexMode is the in-memory view of one row. It is NOT a config type — nothing
// serializes it, and the three keys keep their names, their types and their on-disk
// representation exactly as before.
//
// Resolution rule. Applied by find_imex_mode() / imex_mode_table() and NOWHERE else;
// every consumer in the tree goes through one of the two:
// * `imex_mode_names` is the roster. A mode exists iff a row of that array carries its
// name, and the FIRST such row wins. (The modes editor makes a name unique when it is
// committed, so duplicates reach here from a hand-edited profile; first-match is what a
// std::find over the names array already did, and what the editor's own row order
// means.)
// * A sibling array too short to reach that row yields an EMPTY string for that field
// and sets `ragged`. It never yields an out-of-range read, and it never degrades to
// "no such mode": a missing script is a mode with no script, and the row's index
// still has to be reported to {imex_mode_index}. Callers that need tools already
// treat an empty tools string as "no tools" (imex_primary_tool_for_mode() returns -1,
// parse_imex_active_tools() returns {}), which is the behavior they had before.
// * `ragged` separates "the profile omitted this row" from "the author wrote an empty
// entry" — a distinction every open-coded call site used to lose.
//
// Reporting: a non-Primary mode that resolves to an empty tool roster produces G-code
// that is wrong rather than merely unconfigured (no per-head temperature transition, and
// a suppressed initial T<n>), so GCode::_do_export() warns and falls back to Primary,
// exactly as it already does for a name that resolves to no row at all.
struct ImexMode
{
// Position in `imex_mode_names`. -1 means no row carries the requested name — the
// not-found case, explicit rather than implied by an out-of-range index.
int index = -1;
// The row's entry in `imex_mode_names`. Empty when !found().
std::string name;
// `imex_mode_active_tools[index]`, or "" when that array is shorter than index + 1.
std::string active_tools;
// `imex_mode_gcodes[index]`, or "" when that array is shorter than index + 1.
std::string gcode;
// At least one sibling array was too short to cover `index`. Always false when
// !found().
bool ragged = false;
bool found() const { return this->index >= 0; }
explicit operator bool() const { return this->found(); }
};
// Resolves `name` against the printer's mode table; see the rule above. `cfg` is any
// config carrying the printer keys (a DynamicPrintConfig, a PrintConfig, ...). A config
// with no `imex_mode_names` yields a not-found ImexMode rather than throwing, so callers
// need no null check of their own.
ImexMode find_imex_mode(const ConfigBase& cfg, const std::string& name);
// The whole roster: one ImexMode per entry of `imex_mode_names`, in config order, padded
// by the same rule. Empty when `imex_mode_names` is absent. Use this where the caller
// wants every mode (the modes editor, the plate's mode menu) rather than one by name.
std::vector<ImexMode> imex_mode_table(const ConfigBase& cfg);
// The modes a plate can be set to, in the order its mode button cycles them: kImexPrimaryMode,
// then each name in the table once. A repeated name is listed at its first row only, the one
// find_imex_mode() resolves it to, and empty names and rows named kImexPrimaryMode are left out.
std::vector<std::string> imex_plate_mode_choices(const ConfigBase& cfg);
// =============================================================================
// PHYSICAL vs LOGICAL extruder indices — read this before adding a new IMEX call site
// =============================================================================
// IMEX has two distinct index spaces and they are NOT interchangeable on MMU/AFC
// printers (where multiple logical filament slots share one physical extruder):
//
// * PHYSICAL extruder index — identifies a hardware carriage / hotend.
// Source: `imex_mode_active_tools` ("0:P,1:C,2:M") and `printer_extruder_id`.
// Use for: user-facing labels (shown as "T0", "T1"...), per-firmware tool
// qualifiers (Klipper EXTRUDER=extruderN, RRF M572 D<N>, Marlin T<N>), and
// anything that addresses hardware.
//
// * LOGICAL filament slot index — identifies one filament in the project.
// Source: indexing into per-filament arrays — `filament_presets`,
// `nozzle_temperature_initial_layer`, bed_temp arrays per plate type, etc.
// Use for: looking up filament data by slot.
//
// On a single-extruder or pure-IDEX printer the two indices coincide. On any
// printer with `physical_extruder_map` size > 1 they diverge. Confusing them
// produces silent wrong-filament behavior — the slicer reads the wrong filament
// preset / temperature for a given carriage with no error or log line.
//
// Translate physical → logical via:
// resolve_filament_for_head(plate_head_filament_map, pem, physical_idx)
// (or the simpler `first_filament_for_physical_head` if no per-plate override).
//
// The reverse translation (logical → physical) is `imex_pem_tool_for`. Do NOT reach for
// `pem.get_at(logical_idx)`: get_at() CLAMPS an out-of-range index to values.front(), silently
// answering "physical head pem[0]" for a logical id that has no mapping at all. The map is one
// entry per nozzle while logical ids index filament slots, and nothing caps the slot count at
// the nozzle count, so out-of-range is reachable. Bounds-check and treat the miss as "no
// mapping" (-1).
//
// The same nozzle-vs-slot divergence bites in the other direction: anything derived from pem
// -- `resolve_filament_for_head`, `first_filament_for_physical_head` -- answers in NOZZLE index
// space, so bound it against the FILAMENT SLOT COUNT before using it as a filament id. Not
// against the option you are about to read: a variant-expanded option such as
// nozzle_temperature is as long as its columns, and is_extruder_used is padded to
// MAXIMUM_EXTRUDER_NUMBER, so both admit slots the project does not have.
//
// A miss is -1, and -1 is a correct tool QUALIFIER but not a skip-the-primary sentinel: no
// physical head equals it, so a test written as `head == primary` stops skipping anything. Do
// not substitute the mode's declared primary there. Which head prints a filament is answered by
// the filament and pem, never by a mode role: a primary-mode print may use any or all tools, one
// at a time, and pem is what says which. When pem cannot answer, the head is genuinely unknown -
// an omitted qualifier then applies to the active tool, which is the head printing that filament,
// so the honest options are to leave it unqualified or to emit nothing, not to name a carriage.
// Known gap, unreported: the two skip sites in GCode.cpp therefore skip nothing for a filament
// slot past the end of pem, so a plate with more slots than nozzles double-writes the primary's
// pressure advance and hands it another filament's transition temperature.
//
// -1 as a tool qualifier reaches GCodeWriter::set_pressure_advance, which omits the qualifier
// on Klipper, Marlin and BBL but substitutes the historical `D0` on RepRapFirmware
// (GCodeWriter.cpp, set_pressure_advance) -- deliberate, not a miss.
//
// Past bugs in this class:
// - GCode PA emission used the inline `pem.get_at(filament_id)` form at two
// sites (consolidated into `imex_pem_tool_for` in c2492ccc47).
// - Pre-slice warnings indexed `filament_presets` directly by physical idx,
// causing wrong-filament names on AFC/MMU layouts (fixed in fbc58d2a1d).
// - Ghost color resolution had its own inline pem lookup with a stale default
// (centralized in `effective_physical_extruder_map` in 6a3de6a28f).
//
// New call sites: if you index a per-filament array, you need a logical index.
// If you label a hardware carriage, use the physical index. When in doubt,
// route through one of the helpers below.
// =============================================================================
// physical_extruder_map has one entry per LOGICAL extruder -- the index space of
// nozzle_diameter -- with each value a physical extruder index. It is NOT derivable from
// printer_extruder_id, which is indexed by variant slot (one entry per extruder+variant pair):
// an X1 Carbon has one nozzle and printer_extruder_id {1,1}, an H2D two nozzles and {1,1,2,2,2}.
//
// A profile has authored a map only when its length matches nozzle_count; otherwise the identity
// is returned, which is the same default upstream applies (see Plater.cpp's extruder_map).
ConfigOptionInts effective_physical_extruder_map(const ConfigOptionInts* explicit_pem, int nozzle_count);
// GUI overload: resolves the effective pem from a live PresetBundle using the project_config
// → printer preset fallback. Use this instead of open-coding the lookup at ghost-color,
// tooltip, click-gate and cache-key call sites so they all agree with what the slicer sees.
ConfigOptionInts effective_physical_extruder_map(const PresetBundle& pb);
// Returns the physical extruder index to emit as a per-tool qualifier (PA / temperature)
// for the given filament slot, or -1 when the current IMEX state does not warrant a
// per-tool qualification: non-IMEX / primary mode, or the pem is empty / unpopulated.
// Used at tool-change and second-layer transition call sites where IMEX parallel modes
// route emission through the physical extruder, while ordinary tool changes emit bare
// firmware commands like any non-IMEX printer.
int imex_pem_tool_for(int filament_id, const std::string& parallel_mode, const ConfigOptionInts& pem);
// Translate a logical filament id into the physical heater that an M104/M109 `T` must
// name. Applies in every IMEX mode including Primary -- a heater command names hardware,
// so it does not depend on a parallel mode being active the way imex_pem_tool_for does.
//
// Gated on is_imex because physical_extruder_map carries two readings in this tree: the
// BBL paths index it by extruder id (GCode.cpp:3295, WipeTower.cpp:1353), the IMEX paths
// by filament id. Those coincide only when the filament and nozzle counts match, so an
// ungated mapping would impose the IMEX reading on profiles that mean the other one --
// fdm_bbl_3dp_002_common ships a non-identity [1,0] and is spared today only because
// single_extruder_multi_material suppresses the T qualifier entirely.
//
// Out-of-range ids pass through unchanged, the same rule GCodeProcessor's preheat rewrite
// uses (GCode/GCodeProcessor.cpp:1407), so a printer whose map is the registered
// single-entry default is unaffected.
int imex_physical_heater_for(bool is_imex, const ConfigOptionInts& pem, int logical_id);
// True when GCode::set_extruder should suppress its bare T<n> at the print-start
// initial-tool selection because the active IMEX parallel mode's setup macro
// (imex_mode_gcode) and machine_start_gcode already activate the primary tool
// (Klipper SET_PRINT_MODE / RRF M567 — both reference an active tool by definition),
// so any slicer-emitted T<n> at print-start is a duplicate.
//
// Mid-print toolchanges in parallel mode are intentionally NOT suppressed:
// - Single-color prints have no mid-print toolchanges anyway
// - Multi-color prints are blocked at slice-time when the active IMEX configuration
// can't physically support multi-color (see imex_multicolor_block_reason). The only
// not-blocked case is IQEX with 2+ tools active on the primary gantry, where mid-
// print T<n> is legitimately needed and the firmware handles the slaved gantry.
//
// Returns false for: non-IMEX printers (empty parallel_mode), Primary mode, and any
// toolchange after the first (toolchange_count > 1). Custom change_filament_gcode that
// contains its own T<n> is handled separately by GCode.cpp's custom_gcode_changes_tool().
bool imex_suppresses_bare_toolchange(const std::string& parallel_mode, unsigned int toolchange_count);
// Returns a user-facing block reason when multi-color printing is incompatible with
// the active IMEX parallel mode, or an empty string when the configuration is OK.
//
// Multi-color in a parallel mode requires the firmware to swap tools mid-print on the
// primary gantry while the slaved gantry follows automatically. That works only when
// every used filament has its own dedicated physical head (no MMU lane sharing), the
// active mode definition explicitly marks a Span tool on the primary's gantry (Span
// declares "this tool is the within-gantry multicolor partner of Primary"; it
// disambiguates the multicolor topology from independent same-gantry copies), AND every
// used filament routes to the primary or one of its Span tools, the only heads that print
// colors of their own.
//
// Catches:
// - IDEX (1 tool per gantry): primary's gantry can never carry a Span partner → blocked
// - IQEX 2-tool-active (e.g. T0 primary + T2 copy on different gantries): no Span on
// primary's gantry → blocked
// - MMU/AFC sharing among used filaments: multiple used filaments routed to same
// physical head → blocked (the slaved gantry can't follow MMU lane swaps)
// - IQEX 4-tool independent copies (T0:P,T1:C,T2:M,T3:M): no Span declared, T1 is an
// independent copy → blocked
// - IQEX paired-gantry multicolor (T0:P,T1:S,T2:M,T3:M): Span on T1 declares the
// multicolor partner, no MMU sharing → ALLOWED for colors on T0 and T1; a color
// routed to T2, T3 or past the end of the map → blocked
//
// Returns empty string for: non-IMEX (empty parallel_mode), Primary mode, single-color
// prints, or any configuration where multi-color is physically supportable.
//
// Inputs:
// parallel_mode — m_imex_parallel_mode at slice time
// active_tools_str — the imex_mode_active_tools entry for the active mode, e.g.
// "0:P,1:C,2:M,3:M"
// tools_per_gantry — imex_tools_per_gantry from printer config (>= 1)
// used_filaments_0b — 0-based logical filament indices used by the print
// pem — physical_extruder_map (logical idx → physical head)
std::string imex_multicolor_block_reason(const std::string& parallel_mode,
const std::string& active_tools_str,
int tools_per_gantry,
const std::vector<int>& used_filaments_0b,
const ConfigOptionInts& pem);
// Returns the lowest 0-based logical filament index L such that pem[L] == physical.
// Returns -1 if no filament routes to `physical`.
// Degenerate case: an empty pem returns 0 when `physical == 0` (identity-on-head-0
// fallback for printers that never configured a pem) and -1 otherwise.
// With identity pem, the caller sees logical == physical behavior because
// position L holds value L.
int first_filament_for_physical_head(const ConfigOptionInts& pem, int physical);
// Returns true if the printer has at least one physical head fed by >= 2 logical
// filaments (i.e., an MMU/AFC is present on some head). Drives the plater's
// click-handler branch: true -> open popover; false -> retain cycle-through.
bool has_mmu(const ConfigOptionInts& pem);
// Returns true if any physical head OTHER than `primary_physical` is fed by
// >= 2 logical filaments. Primary is the head whose filament is already
// controlled by the left sidebar's object->filament assignment, so an MMU
// there needs no popover row; only non-primary MMU heads do.
// Returns false when pem is empty or size < 2.
bool has_non_primary_mmu(const ConfigOptionInts& pem, int primary_physical);
// Parses one mode's entry from `imex_mode_active_tools` and returns the
// 0-based physical T-index carrying the `:P` (Primary) role marker.
// Accepts two forms:
// "0:P,1:C,2:C" (explicit role suffix)
// "0" (backwards-compat: a bare index == Primary)
// Returns -1 on empty/malformed input or if no primary is found.
// The caller is responsible for indexing into imex_mode_active_tools by mode.
int imex_primary_tool_for_mode(const std::string& active_tools_for_mode);
// Parses the "phys:slot,phys:slot" serialization of imex_head_filament_map.
// Keys are physical T-indices (0-based); values are 1-based filament slots.
// Returns empty map on empty/malformed input. This string arrives straight from 3MF
// metadata, so tokens outside [0, MAXIMUM_EXTRUDER_NUMBER) are dropped here as an absolute
// sanity cap; the bound against the *project's* filament count lives in
// resolve_filament_for_head, which is where that count is actually known.
std::map<int,int> parse_imex_head_filament_map(const std::string& s);
// Resolves which 0-based logical filament to use for a physical head.
// 1. If the plate map overrides `physical` with a slot that indexes an existing filament,
// returns (slot - 1) — 1-based → 0-based.
// 2. Else falls back to first_filament_for_physical_head(pem, physical).
// Returns -1 if neither source yields a valid filament.
//
// `pem` has one entry per logical filament slot, so its size is the slot count and the
// result is always a valid index into it (or -1). An override naming a slot at or past
// that size cannot be honored — every consumer indexes a per-filament array, and the
// ConfigOption get_at() several of them use clamps rather than failing, which would turn a
// corrupt 3MF's "1:9999" into a silently wrong filament. Such an override is ignored and
// the printer's own routing applies, exactly as if the override were absent.
int resolve_filament_for_head(const std::map<int,int>& plate_map,
const ConfigOptionInts& pem,
int physical);
// Returns the 0-based logical filament slot the IMEX *primary* tool prints with,
// based on what the plate's objects are actually assigned to. Walks `used_slots_1b`
// (1-based filament indices, e.g. from PartPlate::get_extruders) and returns the
// first one whose pem entry maps to `primary_physical`. Returns -1 if no object on
// the plate routes to the primary's physical extruder, or if pem is empty.
//
// On non-MMU/non-AFC printers (one logical per physical) this is unambiguous; on
// AFC layouts where multiple logicals route to one physical, the first match wins
// — sufficient for warning labels and is_extruder_used marking. Multi-color
// primaries on the AFC manifold may want all matches; that's a separate iteration.
int imex_primary_logical_from_objects(const std::vector<int>& used_slots_1b,
const ConfigOptionInts& pem,
int primary_physical);
// Returns the 0-based logical filament slots that IMEX *secondary* carriages
// will load during the print. Iterates `active_physicals` (the set returned by
// imex_mode_active_tools parsing — physical extruder indices), skips entries
// equal to `primary_physical` (the primary is owned by tool_ordering /
// per-object filament assignment, not enumerated here), and resolves each
// remaining physical via `resolve_filament_for_head` (per-plate override
// first, then first_filament_for_physical_head as fallback).
//
// Returned slots are deduplicated and -1 entries (no routing found) are
// dropped. Use this for is_extruder_used marking and pre-slice warnings'
// secondary lookup; the caller still owns whatever it does with the slots
// (mark a bool array, compare temps, etc.).
std::vector<int> imex_secondary_logical_slots(const std::vector<int>& active_physicals,
int primary_physical,
const std::map<int,int>& plate_head_filament_map,
const ConfigOptionInts& pem);
enum class ImexRole { Primary, Copy, Mirror, Span };
// The one table a new role has to be added to.
//
// `letter` is the suffix the role carries in the `imex_mode_active_tools` on-disk format
// ("0:P,1:C,2:M,3:S"). That string is written into printer profiles and into 3MF projects,
// so the letters are FORMAT: never renumber, rename or reorder them, only append. The table
// order is also the order the modes editor cycles its tiles through and lists its color
// legend in, which is why Primary comes first.
//
// Everything that used to open-code the letters or a parallel small-int encoding of the
// enum now goes through this table or through ImexRole itself, so adding a fifth role means
// editing the enum, this table, and the places that must genuinely make a new decision
// about it (the transform switch, the zone/marker classification, the editor's per-role
// color + label) — not a scattered set of int mappings that fail silently when missed.
struct ImexRoleDesc {
ImexRole role;
char letter;
};
inline constexpr ImexRoleDesc kImexRoleTable[] = {
{ ImexRole::Primary, 'P' },
{ ImexRole::Copy, 'C' },
{ ImexRole::Mirror, 'M' },
{ ImexRole::Span, 'S' },
};
// Reads an IMEX geometry key, falling back to the value registered for that option in
// print_config_def when the key is absent from `cfg`, rather than to a literal repeated at the
// call site. Only a partial or hand-built config reaches the fallback -- anything instantiated
// from the ConfigDef carries every key -- but that is exactly the case a literal gets wrong.
int imex_cfg_int(const ConfigBase& cfg, const std::string& key);
double imex_cfg_float(const ConfigBase& cfg, const std::string& key);
// Enum sibling. ConfigOptionEnum<T> derives from ConfigOptionSingle<T>, not from ConfigOptionInt,
// so it cannot go through imex_cfg_int(). Header-inline because it is a template.
bool imex_cfg_bool(const ConfigBase& cfg, const std::string& key);
// Logs an unregistered-key lookup. Out-of-line to keep boost.log out of this header.
void imex_cfg_report_unregistered(const char* fn, const std::string& key);
template<class T>
T imex_cfg_enum(const ConfigBase& cfg, const std::string& key)
{
// Matched by type, not by the coEnum tag. Every ConfigOptionEnum<T> reports coEnum, so the
// type() comparison that option<TYPE>() (Config.hpp:2656) and the int/float accessors rely on
// cannot tell one enum type from another: it would accept a ConfigOptionEnum<OtherEnum> and
// static_cast it to this T. ConfigOptionEnum<A> and ConfigOptionEnum<B> are unrelated
// siblings (both derive from ConfigOptionSingle<T>), so a dynamic_cast rejects the mismatch.
//
// A coEnum value has a second representation, ConfigOptionEnumGeneric, which derives from
// ConfigOptionInt rather than ConfigOptionSingle<T> - the enum analogue of the
// ConfigOptionPercent : ConfigOptionFloat inheritance that rules dynamic_cast out for
// imex_cfg_float. It is what a config not seeded from the static PrinterConfig holds, so it
// is read here too, keyed on the value map it carries: only T's own map yields a T.
const ConfigOptionDef* def = print_config_def.get(key);
if (const ConfigOption* opt = cfg.option(key)) {
if (const auto* e = dynamic_cast<const ConfigOptionEnum<T>*>(opt))
return e->value;
// The map the generic option was built from identifies the enum it holds: T's own map
// means T's own enumerators, so the stored int is a T. A key whose definition names a
// different enum carries a different map and falls through to the default below,
// exactly as the typed cast above would reject it.
if (const auto* g = dynamic_cast<const ConfigOptionEnumGeneric*>(opt))
if (g->keys_map == &ConfigOptionEnum<T>::get_enum_values())
return static_cast<T>(g->value);
}
if (def)
if (const ConfigOption* dv = def->default_value.get())
if (const auto* e = dynamic_cast<const ConfigOptionEnum<T>*>(dv))
return e->value;
// Same policy as imex_cfg_int/_float: an unregistered key is a typo the compiler cannot
// catch, so say so rather than return a silent zero that reads as a legitimate enumerator.
// The log call is out-of-line so this header, which much of libslic3r and the GUI includes,
// does not pull in boost.log for a path that only a typo reaches.
assert(false && "imex_cfg_enum: key not registered in print_config_def");
imex_cfg_report_unregistered("imex_cfg_enum", key);
return static_cast<T>(0);
}
// The on-disk suffix letter for `role`. Inverse of imex_role_from_suffix().
char imex_role_letter(ImexRole role);
// Reads a whole token suffix — everything after the ':' — back into a role. Anything that
// is not exactly one of the table's letters is Copy: that covers "C" itself, an empty
// suffix ("3:"), a multi-character suffix, and any letter a future build might write that
// this one does not know. Copy is the historical fallback for an unrecognised suffix and
// must stay so, or a project saved by a newer build changes meaning when reopened here.
ImexRole imex_role_from_suffix(const std::string& suffix);
// Parses `imex_mode_active_tools[mode]` into a list of (physical_head, role) pairs.
// Accepted token forms (comma-separated, whitespace-tolerant):
// "phys" — bare index, role defaults to Copy
// "phys:P" — Primary
// "phys:C" — Copy
// "phys:M" — Mirror
// "phys:S" — Span (multicolor partner of Primary on the same gantry; only
// meaningful on tools sharing primary's gantry, and only on a
// multi-gantry printer. Drives the multicolor-allow path and
// paired-gantry ghost/zone aggregation.)
// "phys:???" — unknown role suffix, treated as Copy
// Malformed tokens (unparseable int, negative phys) are skipped.
// NOTE: the bare-token → Copy default differs from `imex_primary_tool_for_mode`,
// which separately scans for a Primary; callers needing the primary index should
// use that helper. This parser is for call sites that already have the primary
// in hand and need the full head/role list (e.g. ghost factory/updater).
std::vector<std::pair<int, ImexRole>> parse_imex_active_tools(const std::string& active_tools_for_mode);
// What a mode does, as the plate's mode icon shows it. The heads beside the primary that
// imex_primary_tool_for_mode() finds decide: none is Primary, any Span head is Custom (the
// multicolor modes), any Mirror head is Mirror, and all heads copying is Copy. One Mirror head is
// enough because an IQEX mirror mode copies within the primary's gantry.
enum class ImexModeKind { Primary, Copy, Mirror, Custom };
inline constexpr size_t kImexModeKindCount = 4;
static_assert(size_t(ImexModeKind::Custom) + 1 == kImexModeKindCount);
ImexModeKind imex_mode_kind(const std::string& active_tools_for_mode);
// =============================================================================
// The one derivation of "what does this plate's mode do, and does its filament reach
// the primary" — shared by the hard block and the pre-slice warning
// =============================================================================
// Print::validate() refuses a plate whose filaments do not route to the mode's declared
// primary; Plater's collect_imex_warnings() names that same primary's filament in its
// bed-temperature and filament-type warnings. Both need the identical chain: resolve the
// mode row -> parse its tool roster -> find the declared primary -> ask whether any used
// filament slot routes there. Computed twice, the two drift, and the warning ends up
// describing a plate the block describes differently. Computed here, they cannot.
//
// Everything in ImexRouting is a pure function of the four inputs. Deliberately absent:
// the `is_imex` gate (Print::validate's, and only its, outer condition) and any bound on
// the tool roster against the project's filament count (the plater's, and only its, way of
// dropping tools it has no filament preset to name). Those are genuinely per-caller and
// each stays with the caller that owns it.
struct ImexRouting
{
// The mode is a parallel mode: non-empty and not kImexPrimaryMode. False leaves every
// other field at its default — no mode row is even looked up.
bool parallel = false;
// `imex_mode_active_tools` for this mode, or "" when the mode resolves to no row / a
// ragged table. See find_imex_mode().
std::string active_tools;
// The parsed roster, physical head + role, in the order the string lists them.
std::vector<std::pair<int, ImexRole>> tools;
// Declared primary PHYSICAL head, from imex_primary_tool_for_mode(). -1 when the mode
// resolves to no row, an empty roster, or a roster with no Primary marker.
int primary_phys = -1;
// The 0-based LOGICAL filament slot the primary prints with: the first entry of
// `used_slots_1b` whose pem entry maps to `primary_phys`. -1 when nothing on the plate
// routes there (which is exactly what `primary_unrouted` reports as a hard error, and
// what the plater's warning path falls back out of so it still has a filament to name).
int primary_logical = -1;
// Sorted, deduplicated PHYSICAL heads that `used_slots_1b` actually reach. Slots outside
// the pem are dropped rather than clamped: ConfigOption::get_at() clamps to values front,
// which would let an error message name the very head it just said nothing routes to.
std::vector<int> routed_heads;
// The mode declares a primary, the printer has a routing map, and no used filament
// reaches that primary. The precise condition Print::validate refuses the plate on.
bool primary_unrouted = false;
bool routes_to_primary() const { return this->primary_logical >= 0; }
};
// `cfg` is any config carrying the printer's mode-table keys — the Print's applied config
// in the slicer, the edited printer preset in the GUI. `used_slots_1b` is 1-based filament
// indices (Print::extruders() + 1, or PartPlate::get_extruders()). `pem` is the effective
// physical_extruder_map: already normalised on the Print's config by Print::apply(), and
// obtained from effective_physical_extruder_map(PresetBundle) in the GUI.
ImexRouting imex_resolve_routing(const ConfigBase& cfg,
const std::string& parallel_mode,
const std::vector<int>& used_slots_1b,
const ConfigOptionInts& pem);
// Per-gantry grouping derived from the active_tools string. The single source of
// truth for paired-gantry visualization aggregation: when the primary's gantry
// has at least one Span tool, every non-primary gantry's representative tool
// stands in for the whole gantry (one ghost, one zone strip). Gantries without
// the aggregation trigger keep per-tool semantics.
//
// `representative_phys` is the column-paired tool to primary on that gantry —
// i.e. `gantry_index * tools_per_gantry + (primary_phys % tools_per_gantry)` if
// active, else the lowest active phys on that gantry. Aggregation falls back
// when tools on the same non-primary gantry carry mixed roles (e.g. one Copy
// + one Mirror), since the user explicitly authored two distinct topologies.
struct ImexGantryGroup {
int gantry_index = -1;
int representative_phys = -1;
ImexRole representative_role = ImexRole::Copy;
bool aggregate = false; // collapse this gantry to one ghost/zone
std::vector<std::pair<int, ImexRole>> tools; // every active tool on this gantry
};
struct ImexGantryGrouping {
int primary_phys = -1;
int primary_gantry = -1;
bool span_on_primary = false; // any Span tool on primary's gantry
std::vector<ImexGantryGroup> groups; // every gantry with ≥1 active tool, sorted by index
};
// Groups parsed active tools by gantry (`phys / tools_per_gantry`). Aggregation
// triggers iff `span_on_primary` is true AND a non-primary gantry's tools all
// carry the same role; mixed-role non-primary gantries fall back to per-tool.
// Returns an empty grouping when active_tools_str is empty / has no primary.
ImexGantryGrouping group_imex_active_tools_by_gantry(const std::string& active_tools_for_mode,
int tools_per_gantry);
// World-space transform composed as `head_xf * primary_instance_world` to place a ghost
// copy of the primary into `target`'s frame under `role`.
// Which boundary a Mirror reflects across. Tools on the primary's own gantry sit beside
// it along X, so they mirror across the vertical boundary between their zones. Tools on a
// different gantry sit in front of / behind it along Y, so they mirror across the
// horizontal boundary between the gantry row strips — the part comes off that gantry as a
// Y-reflection of the tool directly behind it, not an X-reflection. Single-gantry printers
// only ever use X.
enum class ImexMirrorAxis { X, Y };
// The single source of truth for that choice. Gantry row is `phys / tools_per_gantry`, the
// same derivation PartPlate and GCodeViewer use to build their zone grids. Keep every caller
// on this helper: if the three of them ever disagree about the axis, the plate ghosts and the
// preview markers place the same tool in different spots for the same mode.
// `tools_per_gantry` is clamped to >= 1, so a zero/negative config divides safely. Note the
// clamp lands on "one tool per gantry", meaning every secondary is then cross-gantry and
// mirrors on Y — not "everything on one gantry". That is the honest reading of tpg = 1.
ImexMirrorAxis imex_mirror_axis_for(int primary_phys, int target_phys, int tools_per_gantry);
// `gantry_offset` = center_for(target) - center_for(primary) (XY, in mm).
// `primary_zone_center` = XY center of the primary head's zone in world coords. Only
// consulted for Mirror; Copy/Primary ignore it.
// `mirror_axis` = axis the Mirror reflection negates; get it from imex_mirror_axis_for().
// Deliberately NOT defaulted: a default would silently hand a forgetful caller the X
// reflection, which is wrong for every cross-gantry tool and would fail silently — the
// ghosts would just quietly go back to mirroring on the wrong axis. Ignored by Copy/Primary.
// Copy: pure translation by gantry_offset. Ghost tracks primary 1:1 during drag.
// Mirror: true reflection (det = -1, so chirality flips — a real mirror image) about the
// zone-boundary plane between primary and target, perpendicular to `mirror_axis`
// and passing through the midpoint of the two zone centers along that axis. The
// ghost origin lands at the mirrored position within the target zone, and primary
// drag reflects across that plane, so the mirrored axis moves opposite the primary
// while the other axis tracks 1:1.
// Zero-length gantry_offset degenerates to identity.
// Primary: identity.
// Span: identity, same as Primary. A Span tool shares the primary's gantry AND its zone,
// printing the same objects through mid-print toolchanges rather than a duplicate
// placed elsewhere, so there is nothing to translate or reflect. calc_imex_ghosts
// bakes no ghost for a Span head, so no caller reaches this today.
Transform3d imex_head_transform(int primary, int target, ImexRole role,
const Vec2d& gantry_offset,
const Vec2d& primary_zone_center,
ImexMirrorAxis mirror_axis);
// Slice-time XY shift for printers that delegate copy/mirror placement to firmware.
// When `imex_firmware_managed_zones` is on AND the active mode is non-primary, returns
// the primary zone's plate-local center so gcode emission can subtract it (yielding a
// centered slice the firmware can fan out). Returns Vec2d::Zero() in every other case:
// flag off, primary/empty mode, or no zone box (IMEX disabled, primary-only, etc.).
Vec2d compute_imex_slice_offset(bool firmware_managed,
const std::string& parallel_mode,
const std::optional<BoundingBoxf>& primary_zone_box);
// True if `hull` (scaled Clipper coords) overlaps any of `zones` (unscaled mm) —
// the one definition of "overlap" shared by the object and prime-tower placement
// checks. Area-based: Clipper yields no result for shapes sharing only an edge, so
// a hull flush against a zone boundary is NOT reported, it has to cross. That
// matches the long-standing object behavior. An empty hull never violates.
bool imex_hull_violates_zones(const std::vector<BoundingBoxf3>& zones, const Polygon& hull);
} // namespace Slic3r
-379
View File
@@ -1,379 +0,0 @@
#include "libslic3r/IMEXZones.hpp"
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "PrintConfig.hpp"
#include "BoundingBox.hpp"
#include "Config.hpp"
#include "libslic3r/IMEXHelpers.hpp"
namespace Slic3r {
ImexZoneLayout compute_imex_zone_layout(const DynamicPrintConfig& printer_cfg,
const std::string& plate_mode,
const std::string& process_mode,
const BoundingBoxf& bed_extents)
{
ImexZoneLayout out;
auto* is_imex_opt = printer_cfg.option<ConfigOptionBool>("is_imex");
if (!is_imex_opt || !is_imex_opt->value)
return out;
// Per-plate mode takes priority over the process preset.
std::string active_mode = plate_mode;
if (active_mode == kImexPrimaryMode && !process_mode.empty())
active_mode = process_mode;
if (active_mode == kImexPrimaryMode || active_mode.empty())
return out;
// Grid dimensions and tool layout from printer config
int n_cols = std::max(1, imex_cfg_int(printer_cfg, "imex_tools_per_gantry"));
int n_rows = std::max(1, imex_cfg_int(printer_cfg, "imex_gantry_count"));
// Which corner is T0? flip_x: col 0 is right(max-X); flip_y: row 0 is rear(max-Y)
const ImexToolLayout layout = imex_cfg_enum<ImexToolLayout>(printer_cfg, "imex_tool_layout");
bool flip_x = (layout == ImexToolLayout::FrontRight || layout == ImexToolLayout::RearRight);
bool flip_y = (layout == ImexToolLayout::RearLeft || layout == ImexToolLayout::RearRight);
// Convert tool index to physical (col=X-index, row=Y-index), col/row 0 = min-X/min-Y
auto tool_to_phys = [&](int idx) -> std::pair<int,int> {
int raw_col = idx % n_cols, raw_row = idx / n_cols;
return { flip_x ? (n_cols - 1 - raw_col) : raw_col,
flip_y ? (n_rows - 1 - raw_row) : raw_row };
};
int pri_col = 0, pri_row = 0;
if (n_cols == 1 && n_rows == 1)
return out; // nothing to dim with a single zone
// Look up secondary tool indices (active in mode, but NOT the primary tool).
// An unresolved mode, and a mode whose row the tools array does not reach, both come
// back as an empty string; the zone_roles guard below then returns an empty layout.
const std::string active_tools_str = find_imex_mode(printer_cfg, active_mode).active_tools;
// Which heads own a zone cell, and in what role. Roles carry through from
// parse_imex_active_tools() rather than being flattened into small ints, so the
// classification below is a comparison the compiler checks and a new role cannot slip
// through as an unhandled number.
//
// imex_primary_tool_for_mode handles the Primary slot (it owns the bare-legacy-token →
// Primary rule); parse_imex_active_tools fills in the Copy/Mirror secondaries. Mode
// strings use physical T-indices directly; filament routing is separate
// (imex_head_filament_map).
//
// Only Primary / Copy / Mirror land in this map. A Span head shares the primary's zone
// instead of owning one, so it deliberately gets no entry (and hence no zone center --
// see ImexZoneLayout::head_zone_centers), and an extra Primary entry beyond the first is
// ignored. Any further role must decide here whether it owns a cell.
std::map<int, ImexRole> zone_roles;
{
const int primary = imex_primary_tool_for_mode(active_tools_str);
if (primary >= 0 && primary < n_rows * n_cols)
zone_roles[primary] = ImexRole::Primary;
for (const auto& [phys_idx, role] : parse_imex_active_tools(active_tools_str)) {
if (phys_idx < 0 || phys_idx >= n_rows * n_cols) continue;
if (phys_idx == primary) continue;
// Exhaustive on purpose, with no default: a role added to the enum has to answer
// "does this own a zone cell?" here, and -Wswitch asks the question at compile
// time instead of letting the head silently vanish from the layout.
switch (role) {
case ImexRole::Copy:
case ImexRole::Mirror:
zone_roles[phys_idx] = role;
break;
case ImexRole::Primary: // an extra Primary beyond the first is ignored
case ImexRole::Span: // shares the primary's zone rather than owning one
break;
}
}
}
// If the mode name was found but has no tools (e.g. stale process-preset mode on a new
// printer that has no modes defined yet), there is nothing to compute.
if (zone_roles.empty())
return out;
// Identify the Primary tool from the mode definition
for (const auto& [idx, role] : zone_roles) {
if (role == ImexRole::Primary) { auto [c, r] = tool_to_phys(idx); pri_col = c; pri_row = r;
out.primary_head = idx; break; }
}
// No Primary landed inside the grid. Either the mode declares none at all, or its `:P`
// index is off-grid: the modes editor deliberately preserves tool assignments across
// imex_gantry_count changes, so a mode authored on a 4-tool IQEX with Primary on T3 keeps
// that assignment when the printer is reconfigured as a 2-tool IDEX, and the
// `primary < n_rows * n_cols` bound above then drops it from zone_roles.
//
// Every zone, strip, ghost center and slice offset below is derived from pri_col/pri_row,
// which would still be sitting at their (0,0) initializers — a position no active tool
// occupies. That yields a primary zone and a secondary zone that both claim the whole bed:
// imex_primary_zone() reports the plate clear while the blocking boxes report it full, and
// under imex_firmware_managed_zones compute_imex_slice_offset() shifts the slice by the bed
// center. Degrade to "this plate has no IMEX zones" instead — the same answer the earlier
// early returns give, and the one every consumer already handles.
if (out.primary_head < 0)
return ImexZoneLayout{};
// Per-gantry grouping drives Span-based aggregation. When the mode declares a
// Span partner on primary's gantry, non-primary gantries with multiple same-role
// tools collapse to one cell each (placed at primary's column so has_col_sep
// stays false and make_boxes expands the cell into a full-X row-strip).
// Mixed-role / single-tool / no-Span configurations keep per-tool cells.
const ImexGantryGrouping grouping =
group_imex_active_tools_by_gantry(active_tools_str, n_cols);
std::map<int, const ImexGantryGroup*> group_by_gantry;
for (const auto& grp : grouping.groups)
group_by_gantry[grp.gantry_index] = &grp;
// Separate copy and mirror secondary cells using physical coordinates
std::set<std::pair<int,int>> copy_cells, mirror_cells;
for (const auto& [idx, role] : zone_roles) {
if (role == ImexRole::Primary) continue; // primary handled separately
const int phys_gantry = idx / n_cols;
auto git = group_by_gantry.find(phys_gantry);
const ImexGantryGroup* grp = (git == group_by_gantry.end()) ? nullptr : git->second;
if (grp && grp->aggregate) {
// Only the representative contributes a cell; non-reps are folded into
// the same row-strip and skipped entirely.
if (idx != grp->representative_phys) continue;
auto [rep_c, r] = tool_to_phys(idx);
(void)rep_c; // intentionally discarded — aggregated cell pins to pri_col
if (role == ImexRole::Copy) copy_cells.insert({pri_col, r});
else if (role == ImexRole::Mirror) mirror_cells.insert({pri_col, r});
} else {
auto [c, r] = tool_to_phys(idx);
if (role == ImexRole::Copy) copy_cells.insert({c, r});
else if (role == ImexRole::Mirror) mirror_cells.insert({c, r});
}
}
// All active secondary cells combined (for separation axis computation)
std::set<std::pair<int,int>> all_secondary;
for (auto& p : copy_cells) all_secondary.insert(p);
for (auto& p : mirror_cells) all_secondary.insert(p);
double x_min = bed_extents.min(0), x_max = bed_extents.max(0);
double y_min = bed_extents.min(1), y_max = bed_extents.max(1);
// Zone sizing is based on ACTIVE tool count per axis, not total grid dimensions.
// Inactive tools (absent from zone_roles) donate their bed share to active neighbors.
// Sorted active col/row lists map physical index k → zone index k.
std::vector<int> active_cols_v, active_rows_v;
{
std::set<int> ac_set, ar_set;
for (const auto& [idx, role] : zone_roles) {
auto [c, r] = tool_to_phys(idx);
ac_set.insert(c); ar_set.insert(r);
}
active_cols_v.assign(ac_set.begin(), ac_set.end()); // sorted ascending
active_rows_v.assign(ar_set.begin(), ar_set.end());
}
int n_active_cols = std::max(1, (int)active_cols_v.size());
int n_active_rows = std::max(1, (int)active_rows_v.size());
std::map<int,int> col_to_zone, row_to_zone;
for (int k = 0; k < n_active_cols; ++k) col_to_zone[active_cols_v[k]] = k;
for (int k = 0; k < n_active_rows; ++k) row_to_zone[active_rows_v[k]] = k;
double zone_w = (x_max - x_min) / n_active_cols;
double zone_h = (y_max - y_min) / n_active_rows;
// Zone index of the primary tool
int pri_col_k = col_to_zone.count(pri_col) ? col_to_zone[pri_col] : 0;
int pri_row_k = row_to_zone.count(pri_row) ? row_to_zone[pri_row] : 0;
// Zone center per physical head — ghost placement consumes this so that
// ghosts land in their own secondary zone instead of stacking on primary.
// Every active tool (including primary) gets an entry; ghost offset math is
// simply center[head] - center[primary].
for (const auto& [idx, role] : zone_roles) {
auto [c, r] = tool_to_phys(idx);
int ck = col_to_zone.count(c) ? col_to_zone.at(c) : 0;
int rk = row_to_zone.count(r) ? row_to_zone.at(r) : 0;
out.head_zone_centers[idx] = Vec2d(
x_min + (ck + 0.5) * zone_w,
y_min + (rk + 0.5) * zone_h);
}
// Separation axes: row-sep = secondaries on a different gantry, col-sep = different column
bool has_row_sep = false, has_col_sep = false;
for (const auto& [sc, sr] : all_secondary) {
if (sr != pri_row) has_row_sep = true;
if (sc != pri_col) has_col_sep = true;
}
// Build expanded zone rects for a set of cells.
// When secondaries share only a row difference (same column as primary) → expand to full X width.
// When secondaries share only a column difference → expand to full Y height.
// When both axes differ → per-quadrant.
auto make_boxes = [&](const std::set<std::pair<int,int>>& cells) -> std::vector<BoundingBoxf> {
std::vector<BoundingBoxf> boxes;
if (cells.empty()) return boxes;
auto ck = [&](int c) { return col_to_zone.count(c) ? col_to_zone.at(c) : 0; };
auto rk = [&](int r) { return row_to_zone.count(r) ? row_to_zone.at(r) : 0; };
if (has_row_sep && !has_col_sep) {
std::set<int> rows; for (auto& [c,r] : cells) rows.insert(r);
for (int sr : rows) {
int k = rk(sr);
boxes.emplace_back(Vec2d(x_min, y_min + k*zone_h), Vec2d(x_max, y_min + (k+1)*zone_h));
}
} else if (has_col_sep && !has_row_sep) {
std::set<int> cols; for (auto& [c,r] : cells) cols.insert(c);
for (int sc : cols) {
int k = ck(sc);
boxes.emplace_back(Vec2d(x_min + k*zone_w, y_min), Vec2d(x_min + (k+1)*zone_w, y_max));
}
} else {
for (auto& [sc,sr] : cells) {
int ck_ = ck(sc), rk_ = rk(sr);
boxes.emplace_back(Vec2d(x_min + ck_*zone_w, y_min + rk_*zone_h),
Vec2d(x_min + (ck_+1)*zone_w, y_min + (rk_+1)*zone_h));
}
}
return boxes;
};
out.copy_zones = make_boxes(copy_cells);
out.mirror_zones = make_boxes(mirror_cells);
// --- Secondary zone blocking ---
// The expanded bounding boxes for all secondary (copy+mirror) zones. check_outside()
// uses these to prevent objects being placed outside the primary zone.
auto push_secondary_box = [&](const std::vector<BoundingBoxf>& boxes) {
for (const auto& b : boxes)
out.secondary_zone_boxes.emplace_back(Vec3d(b.min.x(), b.min.y(), -1.0),
Vec3d(b.max.x(), b.max.y(), 1e4));
};
push_secondary_box(out.copy_zones);
push_secondary_box(out.mirror_zones);
// --- Carriage collision danger strips ---
// Only add strips at boundaries of the PRIMARY zone — objects are only placed in the
// primary zone, so secondary-to-secondary boundaries have no relevance.
//
// Strip width is the literal nozzle clearance value on the primary side only:
// right X boundary: [bnd_x - nozzle_clearance_x, bnd_x]
// left X boundary: [bnd_x, bnd_x + nozzle_clearance_x]
// top Y boundary: [bnd_y - nozzle_clearance_y, bnd_y]
// bottom Y boundary:[bnd_y, bnd_y + nozzle_clearance_y]
//
// Strip length matches the primary zone extent (same expansion logic as make_boxes):
// !has_row_sep → full bed height; has_row_sep → primary row only
// !has_col_sep → full bed width; has_col_sep → primary column only
// Both strip blocks below are gated on `carriage_w > 0.0` / `carriage_h > 0.0`, so a zero
// clearance emits no collision strips at all while the preview still draws toolhead boxes.
// That is why these read through imex_cfg_float() rather than carrying a literal: the value
// for an absent key comes from the option's own registration, not from a number repeated here.
double carriage_w = imex_cfg_float(printer_cfg, "imex_nozzle_clearance_x");
double carriage_h = imex_cfg_float(printer_cfg, "imex_nozzle_clearance_y");
double margin = imex_cfg_float(printer_cfg, "imex_carriage_margin");
// Primary zone extent (the clear printable area):
// row-sep only → full bed width × primary row's Y band
// col-sep only → primary col's X band × full bed height
// both → primary quadrant
// Uses zone indices so inactive tools don't shrink the zone.
double pz_x0 = has_col_sep ? x_min + pri_col_k * zone_w : x_min;
double pz_x1 = has_col_sep ? x_min + (pri_col_k + 1) * zone_w : x_max;
double pz_y0 = has_row_sep ? y_min + pri_row_k * zone_h : y_min;
double pz_y1 = has_row_sep ? y_min + (pri_row_k + 1) * zone_h : y_max;
out.primary_zone_box = BoundingBoxf(Vec2d(pz_x0, pz_y0), Vec2d(pz_x1, pz_y1));
auto add_strip = [&](double sx0, double sx1, double sy0, double sy1) {
out.collision_zones.emplace_back(Vec3d(sx0, sy0, -1.0), Vec3d(sx1, sy1, 1e4));
};
auto add_margin_band = [&](double sx0, double sx1, double sy0, double sy1) {
out.margin_bands.emplace_back(Vec2d(sx0, sy0), Vec2d(sx1, sy1));
};
// Determine which directions have mirror secondaries adjacent to the primary.
// Only mirror tools can cause carriage collisions — they move toward each other.
// Copy tools always move in the same direction, so no collision strip is needed.
//
// Driven by `mirror_cells`, not by zone_roles: on an AGGREGATED gantry the cell is
// pinned to the primary's column and make_boxes then expands it into a full-width row
// strip, so the painted zone — not any one head's own cell — is what the primary's
// carriage can meet. Reading each head's own cell instead lost the strip whenever the
// representative's column differed from the primary's: at tpg=3, gantry_count=2, mode
// "0:P,1:S,4:M,5:M" the rear gantry paints a full-width mirror strip against the
// primary's rear boundary, yet neither T4 (col 1) nor T5 (col 2) is in the primary's
// column, so no strip and no margin band were emitted at all.
//
// Both axes require zone-adjacent AND same row/column on the OTHER axis: a mirror
// diagonally offset from primary (different row AND different column) can't collide
// with primary's carriage on either axis since the gantries don't overlap there.
// Without these checks, paired-gantry mc-mirror (`0:P,1:S,2:M,3:M`) would draw a
// spurious right-edge strip from T3 even though T3 lives on the other gantry's row.
//
// The adjacency tests (±1) are in ZONE indices so an inactive column or row between
// two active ones cannot hide the boundary they really share (see "inactive tools
// donate their bed share"), while the co-linearity test on the other axis stays in
// physical indices. The two forms agree for any cell that owns a zone — col_to_zone
// and row_to_zone are order-preserving bijections over exactly the active indices —
// so this is one rule written two ways, not two rules.
bool has_right_sec = false, has_left_sec = false;
bool has_top_sec = false, has_bottom_sec = false;
for (const auto& [sc, sr] : mirror_cells) {
int zc = col_to_zone.count(sc) ? col_to_zone.at(sc) : -1;
int zr = row_to_zone.count(sr) ? row_to_zone.at(sr) : -1;
if (zc < 0 || zr < 0) continue; // no zone of its own, so no boundary with primary
// X-boundary strips: mirror zone-adjacent in X, same physical row as primary.
if (zr == pri_row_k && zc == pri_col_k + 1) has_right_sec = true;
if (zr == pri_row_k && zc == pri_col_k - 1) has_left_sec = true;
// Y-boundary strips: mirror zone-adjacent in Y, same physical column as primary.
if (zr == pri_row_k + 1 && sc == pri_col) has_top_sec = true;
if (zr == pri_row_k - 1 && sc == pri_col) has_bottom_sec = true;
}
// X-axis boundaries (vertical strips, width = nozzle_clearance_x on primary side)
if (carriage_w > 0.0) {
if (has_right_sec) {
double bnd_x = x_min + (pri_col_k + 1) * zone_w;
double strip_inner = bnd_x - carriage_w;
add_strip(strip_inner, bnd_x, pz_y0, pz_y1);
if (margin > 0.0)
add_margin_band(strip_inner - margin, strip_inner, pz_y0, pz_y1);
}
if (has_left_sec) {
double bnd_x = x_min + pri_col_k * zone_w;
double strip_inner = bnd_x + carriage_w;
add_strip(bnd_x, strip_inner, pz_y0, pz_y1);
if (margin > 0.0)
add_margin_band(strip_inner, strip_inner + margin, pz_y0, pz_y1);
}
}
// Y-axis boundaries (horizontal strips, width = nozzle_clearance_y on primary side)
if (carriage_h > 0.0) {
if (has_top_sec) {
double bnd_y = y_min + (pri_row_k + 1) * zone_h;
double strip_inner = bnd_y - carriage_h;
add_strip(pz_x0, pz_x1, strip_inner, bnd_y);
if (margin > 0.0)
add_margin_band(pz_x0, pz_x1, strip_inner - margin, strip_inner);
}
if (has_bottom_sec) {
double bnd_y = y_min + pri_row_k * zone_h;
double strip_inner = bnd_y + carriage_h;
add_strip(pz_x0, pz_x1, bnd_y, strip_inner);
if (margin > 0.0)
add_margin_band(pz_x0, pz_x1, strip_inner, strip_inner + margin);
}
}
return out;
}
} // namespace Slic3r
-94
View File
@@ -1,94 +0,0 @@
#pragma once
#include <map>
#include <optional>
#include <string>
#include <vector>
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {
// Where every IMEX bed zone, carriage collision strip and advisory margin band sits, in
// plate-local millimetres. Purely geometric: no rendering, no clipping against the bed
// outline, no GUI types. PartPlate turns the rectangles into GLModels (clipping each one
// to the bed polygon as it goes) and copies the boxes straight into the members that
// check_outside() consults, so this struct is the whole answer to "which boxes".
//
// Every list is empty and `primary_zone_box` unset when no parallel mode is in effect —
// IMEX off, Primary mode, a 1x1 tool grid, a mode whose tool roster is empty, or a mode
// whose Primary tool is not on the current grid (no `:P` marker, or a `:P` index left
// over from a wider grid). The whole layout hangs off the primary's cell, so without one
// there is nothing to divide and consumers must see the same "no zones" answer.
struct ImexZoneLayout
{
// Physical T-index of the active mode's Primary tool; -1 when the layout is empty.
int primary_head = -1;
// Physical head -> XY center of that head's zone. One entry per tool the mode makes
// active, Primary included. A Span tool has no entry: it shares the primary's zone
// rather than owning one. Ghost placement offsets by center[head] - center[primary].
//
// EXCEPTION, read before using the X component. These centers are built from the head's
// OWN grid cell, but an AGGREGATED representative's zone rectangle is pinned to the
// primary's column and then expanded to a full-width row strip. So for an aggregated head
// whose own column differs from the primary's, center[head].x names a column its painted
// zone does not have, and `center[head] - center[primary]` carries a bogus X offset.
// Reachable at imex_tools_per_gantry >= 3 when the primary-column tool on the aggregated
// gantry is inactive (e.g. tpg=3, gantry_count=2, mode "0:P,1:S,4:M,5:M").
// Both current consumers pin X to the primary's center for aggregated heads --
// PartPlate::calc_imex_ghosts() via bed_x_center, GCodeViewer via sec_center_of() -- and a
// new consumer must do the same until this is either pinned here or the aggregate flag is
// exposed on the layout.
std::map<int, Vec2d> head_zone_centers;
// The clear printable area — the primary tool's zone. Objects go here and nowhere else.
std::optional<BoundingBoxf> primary_zone_box;
// Copy / Mirror zone rectangles, already expanded along whichever axis carries no
// separation: secondaries differing from primary only by row give full-bed-width
// strips, only by column give full-bed-height strips, and differing on both axes give
// per-tool quadrants.
std::vector<BoundingBoxf> copy_zones;
std::vector<BoundingBoxf> mirror_zones;
// `copy_zones` followed by `mirror_zones`, as full-height boxes for the placement
// check. An object overlapping one of these is outside the primary zone.
std::vector<BoundingBoxf3> secondary_zone_boxes;
// Carriage danger strips lying just inside the primary zone's boundary, one per
// boundary that faces an adjacent Mirror ZONE -- an aggregated gantry contributes the
// one pinned zone it paints, not one per head -- in right / left / top / bottom order.
// Width is the literal nozzle clearance for that axis. Copy tools never contribute:
// they travel in the same direction as the primary and cannot close on it.
std::vector<BoundingBoxf3> collision_zones;
// Advisory (non-blocking) bands immediately inside each collision strip, present only
// when imex_carriage_margin > 0. Same boundary order as `collision_zones`.
std::vector<BoundingBoxf> margin_bands;
};
// Computes the IMEX zone layout for one plate.
//
// `printer_cfg` — the edited printer preset's config. Read for is_imex, the tool grid
// (imex_gantry_count / imex_tools_per_gantry / imex_tool_layout), the
// mode roster (imex_mode_names / imex_mode_active_tools) and the strip
// widths (imex_nozzle_clearance_x / _y, imex_carriage_margin). A key absent
// from the config falls back to the value registered for that option in
// print_config_def, via the imex_cfg_* accessors.
// `plate_mode` — the plate's own IMEX mode. Wins over the process preset unless it is
// `kImexPrimaryMode`.
// `process_mode` — the process preset's `imex_parallel_mode`, used only as the fallback
// when `plate_mode` is `kImexPrimaryMode`. Pass "" when unavailable.
// `bed_extents` — XY extents of the plate shape, in plate-local mm. Zones subdivide it.
//
// Zone sizing keys off the count of ACTIVE tools per axis rather than the grid dimensions,
// so a tool the mode leaves out donates its share of the bed to its active neighbours.
ImexZoneLayout compute_imex_zone_layout(const DynamicPrintConfig& printer_cfg,
const std::string& plate_mode,
const std::string& process_mode,
const BoundingBoxf& bed_extents);
} // namespace Slic3r
-8
View File
@@ -1473,9 +1473,6 @@ static std::vector<std::string> s_Preset_print_options{
"interlocking_boundary_avoidance",
"interlocking_beam_width",
"calib_flowrate_topinfill_special_order",
// IDEX/IQEX parallel print mode (per-print selection)
"imex_parallel_mode",
"imex_head_filament_map",
// Z Anti-Aliasing (ZAA)
"zaa_enabled",
"zaa_minimize_perimeter_height",
@@ -1594,11 +1591,6 @@ static std::vector<std::string> s_Preset_printer_options {
"z_offset",
"disable_m73", "preferred_orientation", "emit_machine_limits_to_gcode", "pellet_modded_printer", "support_multi_bed_types", "use_3mf", "default_bed_type", "bed_mesh_min","bed_mesh_max","bed_mesh_probe_distance", "adaptive_bed_mesh_margin", "enable_long_retraction_when_cut","long_retractions_when_cut","retraction_distances_when_cut",
"bed_temperature_formula", "nozzle_flush_dataset",
// IDEX/IQEX (independent X extruder) — printer capability and user-defined modes
"is_imex", "imex_firmware_managed_zones",
"imex_gantry_count", "imex_tools_per_gantry", "imex_tool_layout",
"imex_nozzle_clearance_x", "imex_nozzle_clearance_y", "imex_carriage_margin", "imex_viz_theme",
"imex_mode_names", "imex_mode_active_tools", "imex_mode_gcodes",
// Multi-nozzle count + pre-heat model printer options
"extruder_max_nozzle_count", "group_algo_with_time", "enable_pre_heating", "hotend_heating_rate", "hotend_cooling_rate",
"machine_hotend_change_time", "machine_prepare_compensation_time",
-6
View File
@@ -114,12 +114,6 @@ static std::vector<std::string> s_project_options {
"nozzle_volume_type",
"filament_map_mode",
"filament_map",
// physical_extruder_map intentionally NOT here: it's owned by the printer
// preset (s_Preset_printer_options). Listing it project-scoped caused
// project_config's default [0] to clobber the preset's authored value
// (e.g. AFC-shaped [0,1,1,1,1]) during full_fff_config() merge, and the
// clobbered value then rode into saved 3mfs and back into the edited
// preset on reload.
// Per-filament nozzle-volume choice; project-level like filament_map so the per-filament
// slot resolution survives preset switches.
"filament_volume_map",
+3 -196
View File
@@ -55,8 +55,6 @@
#include "TriangleMesh.hpp"
#include "Config.hpp"
#include "Exception.hpp"
#include "IMEXHelpers.hpp"
#include "IMEXZones.hpp"
#include "Print.hpp"
#include "BoundingBox.hpp"
#include "Brim.hpp"
@@ -521,12 +519,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
}
else if (opt_key == "z_hop_types") {
osteps.emplace_back(posDetectOverhangsForLift);
}
// The bed-zone colour scheme and the advisory margin bands are drawn from the printer
// config but never reach the slice: compute_imex_zone_layout keeps the margin out of
// primary_zone_box, which is the only part of the layout update_imex_slice_offset reads.
// imex_tool_layout does move that box, so it is left to the fallback below.
else if (opt_key == "imex_viz_theme" || opt_key == "imex_carriage_margin") {
} else {
// for legacy, if we can't handle this option let's invalidate all steps
//FIXME invalidate all steps of all objects as well?
@@ -1859,132 +1851,6 @@ StringObjectException Print::validate(std::vector<StringObjectException> *warnin
if (extruders.empty())
return { L("No extrusions under current settings.") };
// IDEX/IQEX: block multi-color in non-primary parallel modes when the active
// configuration can't physically support it (no within-gantry toolchange path
// or MMU lane sharing). See imex_multicolor_block_reason() for the full rule.
// Both IMEX checks below need the active mode's tools string and its declared primary,
// so imex_resolve_routing() derives them once for the whole block -- and is the same call
// the plater's pre-slice warning makes, so the two cannot describe the plate differently.
// Note this block is NOT gated on extruders.size() > 1: the primary
// routing check applies to a single-filament plate too, which is its most common case.
if (m_config.is_imex.value && !m_objects.empty()) {
const std::string& parallel_mode = m_objects.front()->config().imex_parallel_mode.value;
if (!parallel_mode.empty() && parallel_mode != kImexPrimaryMode) {
// Tool changes added from the layer slider switch heads mid-print as a painted color
// does, so they count here, as the plate's warning badge counts them.
const std::vector<unsigned int> imex_extruders = this->extruders(/*conside_custom_gcode=*/true);
std::vector<int> used_filaments_0b;
std::vector<int> used_slots_1b;
used_filaments_0b.reserve(imex_extruders.size());
used_slots_1b.reserve(imex_extruders.size());
for (unsigned int e : imex_extruders) {
used_filaments_0b.push_back((int)e);
used_slots_1b.push_back((int)e + 1);
}
// The active mode's tools string, its declared primary, and whether this plate's
// filaments reach that primary are all derived ONCE, here. The plater's pre-slice
// warning (collect_imex_warnings) makes the identical call, so the sentence it
// shows before slicing and the refusal below cannot describe the plate
// differently. An unresolved mode, and a mode the tools array is too short to
// cover, both yield an empty roster and no primary.
const ImexRouting routing = imex_resolve_routing(m_config, parallel_mode, used_slots_1b,
m_config.physical_extruder_map);
// A mixed filament is blended at the nozzle by its component toolheads, and a
// parallel mode is already using those toolheads to print copies or mirrors, so
// the two cannot run at once -- regardless of where the components are routed.
//
// Runs before BOTH rules below. Against the routing rule it would otherwise be
// reported as merely unrouted: mixed slots are kept at the tail of the filament
// arrays, past the physical filament count, so on a printer whose logical extruder
// count equals that count they fall outside physical_extruder_map and resolve to no
// head. Against the multi-color rule, a mixed slot plus any second filament trips
// the >1 gate and earns a lecture about gantry topology the user never configured.
// Being unsupported outright, this dominates both.
{
const auto& is_mixed = m_config.filament_is_mixed.values;
if (std::any_of(used_filaments_0b.begin(), used_filaments_0b.end(),
[&](int slot) { return slot >= 0 && slot < (int) is_mixed.size() && is_mixed[slot]; })) {
StringObjectException err;
// "Mixed filament" is the term the rest of the UI uses -- the button that
// creates one, and the sibling refusal for the wipe tower filament.
err.string = L("Mixed filaments are not supported in IDEX/IQEX parallel modes. "
"Switch this plate to Primary mode.");
err.object = m_objects.front();
return err;
}
}
// Multi-color has the more specific rule and its remedies are self-contained
// (an MMU manifold sharing one head cannot be fixed by switching mode), so it runs
// ahead of the routing block below, which would otherwise mask it with advice that
// leads to this error on the next slice. validate() returns on the first error.
if (used_filaments_0b.size() > 1) {
const std::string reason = imex_multicolor_block_reason(
parallel_mode,
routing.active_tools,
m_config.imex_tools_per_gantry.value,
used_filaments_0b,
m_config.physical_extruder_map);
if (!reason.empty())
return { reason };
}
// The IMEX Primary tool prints the sliced paths directly, so it can only use a
// filament the printer's physical_extruder_map routes to it. The ghost filament
// picker already enforces this for the secondary tools; the primary's filament
// comes from the ordinary object filament selector, which has no IMEX awareness,
// so nothing detected the mismatch. collect_imex_warnings() reads the SAME
// routing.primary_logical off the same derivation, and discards it into a display
// fallback so its warning still has a filament to name.
//
// Blocks rather than warns, matching the multi-color rule above -- but on intent,
// not on physics, and the distinction matters to anyone tempted to relax it. The
// emitter does not use the declared primary: it re-derives an effective one from
// the filament actually in use (GCode.cpp's initial_extruder_id -> pem lookups), so
// a plate whose only filament sits on a Span tool sharing the primary's gantry does
// produce coherent G-code. It is refused anyway. A parallel mode exists to run
// carriages in parallel; a single-colour plate riding one span lane is not that, and
// silently accepting it would make the mode's declared roster meaningless.
//
// Where the routed head is absent from the mode's active tools the plate is broken
// outright, not merely off-intent: the 1st->2nd layer temperature
// branch (GCode.cpp, mutually exclusive with the standard path) skips it too, so
// that head holds nozzle_temperature_initial_layer for the whole job. First-layer
// temperatures are unaffected -- _print_first_layer_extruder_temperatures is not
// IMEX-branched -- so this is a stuck-hot nozzle, not a cold one.
//
// `primary_unrouted` is the whole condition: a declared primary, a populated
// routing map, and nothing used reaching it. Blended slots would also read as
// unrouted -- they sit past the map by construction -- but they never get here,
// the mixed-filament rule above returns first.
if (routing.primary_unrouted) {
std::string routed_list;
for (int head : routing.routed_heads)
routed_list += (routed_list.empty() ? "T" : ", T") + std::to_string(head);
// Print::apply() normalises physical_extruder_map through
// effective_physical_extruder_map (PrintApply.cpp) before validate() runs, so
// an unauthored map arrives here as the identity and every slot is in range.
// What is left is a printer with more filaments than logical extruders, where
// the tail slots fall outside the map. Narrow, but without this the sentence
// ends in a dangling "on .".
if (routed_list.empty())
routed_list = L("no configured tool");
StringObjectException err;
err.string = Slic3r::format(
L("IDEX/IQEX mode \"%1%\" prints with T%2%, but this plate's filaments are on %3%. "
"Assign a filament loaded on T%2%, or switch this plate to Primary mode."),
parallel_mode, routing.primary_phys, routed_list);
// Gives the notification a "Jump to <object>" link, which selects the object
// and switches to Prepare -- directly enabling the first suggested remedy.
err.object = m_objects.front();
return err;
}
}
}
// Belt printer validation: incompatible features.
if (m_config.belt_printer.value) {
for (const PrintObject *object : m_objects) {
@@ -3103,10 +2969,6 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
name_tbb_thread_pool_threads_set_locale();
// IMEX firmware-managed zones: settle the emission-frame shift from the applied config
// before anything runs. Zero for every printer that is not IMEX + firmware-managed.
this->update_imex_slice_offset();
//compute the PrintObject with the same geometries
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, enter, use_cache=%2%, object size=%3%")%this%use_cache%m_objects.size();
if (m_objects.empty())
@@ -3826,16 +3688,7 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor
gcode = std::make_unique<GCode>();
//BBS: compute plate offset for gcode-generator
const Vec3d origin = this->get_plate_origin();
// IMEX firmware-managed zones: writer offset gets the IMEX shift so emitted gcode is
// centered at bed origin; processor offset stays at plate_origin so the gcode-preview
// visualizer renders the centered slice at the bed center rather than the prepare-view
// zone placement. Both arms reduce to set_gcode_offset() semantics when shift is zero.
// Derived here rather than read as pushed-in state, so an exporter reached without a
// preceding process() (or after a config change that only invalidated psGCodeExport)
// still emits in the frame the current config asks for.
this->update_imex_slice_offset();
const Vec2d imex_off = this->get_imex_slice_offset();
gcode->set_gcode_offset_with_imex_shift(origin(0), origin(1), imex_off.x(), imex_off.y());
gcode->set_gcode_offset(origin(0), origin(1));
gcode->do_export(this, path.c_str(), result, thumbnail_cb);
gcode->export_layer_filaments(result);
//BBS
@@ -4263,60 +4116,14 @@ Points Print::first_layer_wipe_tower_corners(bool check_wipe_tower_existance) co
return corners;
}
// IMEX firmware-managed zones: derive the slice-time XY shift from the config that has
// already been applied to this Print, so the engine never has to be told what it can work
// out. Deriving it here rather than taking a push from PartPlate is what makes a headless
// slice correct: the CLI builds a PartPlateList but drives no plater, so nothing ever ran
// the GUI-side handoff and the shift silently stayed at zero while the mode G-code told the
// firmware to fan copies out — parts in the wrong place, no diagnostic.
//
// Inputs, all reachable without a GUI:
// * m_full_print_config — the printer preset's IMEX keys, read from the full config so this
// sees the same values the preset holds whichever representation carries them.
// * the plate's `imex_parallel_mode`, read off the object config. That is the same source
// GCode.cpp and validate() resolve the active mode from (the plate's own config is
// merged into the full config before apply() by BackgroundSlicingProcess in the GUI and
// by CLI_Main in the CLI), so the shift can never disagree with the mode actually
// emitted. compute_imex_zone_layout() yields no zones for a mode the printer does not
// define, matching GCode.cpp's fallback to Primary for an unresolved mode name.
// * printable_area — the bed in PLATE-LOCAL mm. This must not be the plate's world-frame
// outline: translate_to_print_space() and the writer offset already subtract m_origin,
// so an offset carrying the plate origin would subtract it twice and put every plate
// but the first one a full plate stride out.
//
// A non-IMEX printer, or one with firmware-managed zones off, costs two bool reads and
// keeps m_imex_slice_offset at exactly Vec2d::Zero() — which is what keeps its G-code
// byte-identical.
void Print::update_imex_slice_offset()
{
m_imex_slice_offset = Vec2d::Zero();
if (!m_config.is_imex.value || !m_config.imex_firmware_managed_zones.value || m_objects.empty())
return;
// Already the resolved plate-or-process mode: the plate config overrides the process
// preset's key on the way in, exactly as PartPlate::get_imex_mode() prefers the plate.
const std::string& active_mode = m_objects.front()->config().imex_parallel_mode.value;
const ImexZoneLayout layout = compute_imex_zone_layout(m_full_print_config, active_mode,
std::string(),
get_extents(m_config.printable_area.values));
// Empty / Primary mode and an empty layout both come back as Vec2d::Zero() here.
m_imex_slice_offset = compute_imex_slice_offset(true, active_mode, layout.primary_zone_box);
}
//SoftFever
// "Print space" is the frame the emitted gcode uses. IMEX firmware-managed mode
// shifts that frame by `m_imex_slice_offset` (the primary zone center in plate-local
// coords) so the centered slice can be fanned out by firmware. Offset is zero in
// every other case → identical behavior for non-firmware-managed printers.
Vec2d Print::translate_to_print_space(const Vec2d &point) const {
//const BoundingBoxf bed_bbox(config().printable_area.values);
return Vec2d(point(0) - m_origin(0) - m_imex_slice_offset.x(),
point(1) - m_origin(1) - m_imex_slice_offset.y());
return Vec2d(point(0) - m_origin(0), point(1) - m_origin(1));
}
Vec2d Print::translate_to_print_space(const Point &point) const {
return Vec2d(unscaled(point.x()) - m_origin(0) - m_imex_slice_offset.x(),
unscaled(point.y()) - m_origin(1) - m_imex_slice_offset.y());
return Vec2d(unscaled(point.x()) - m_origin(0), unscaled(point.y()) - m_origin(1));
}
FilamentTempType Print::get_filament_temp_type(const std::string& filament_type)
-15
View File
@@ -1287,18 +1287,6 @@ public:
//BBS: plate's origin related functions
void set_plate_origin(Vec3d origin) { m_origin = origin; }
const Vec3d get_plate_origin() const { return m_origin; }
// IMEX firmware-managed zones: additional XY shift applied at gcode emission so the
// primary zone's contents land at bed origin (0,0). Non-zero only when
// `imex_firmware_managed_zones` is on AND the active mode is non-primary; Vec2d::Zero()
// in every other case → byte-identical gcode output for everyone else.
//
// Derived, never pushed in: update_imex_slice_offset() recomputes it from the applied
// config, so a headless slice gets the same shift a GUI slice does. It is a pure
// function of the config + printable_area, so calling it twice is a no-op; process()
// and export_gcode() both call it, which is what settles it ahead of every consumer
// (the writer offset in export_gcode(), and translate_to_print_space()).
void update_imex_slice_offset();
Vec2d get_imex_slice_offset() const { return m_imex_slice_offset; }
//BBS: export gcode from previous gcode file from 3mf
void set_gcode_file_ready();
void set_gcode_file_invalidated();
@@ -1551,9 +1539,6 @@ private:
//BBS: plate's origin
Vec3d m_origin {0, 0, 0};
// IMEX firmware-managed slice offset (plate-local primary-zone center, or zero).
// Cache of update_imex_slice_offset(); nothing outside that function writes it.
Vec2d m_imex_slice_offset { Vec2d::Zero() };
//BBS: modified_count
int m_modified_count {0};
//BBS
-30
View File
@@ -1,5 +1,4 @@
#include "ClipperUtils.hpp"
#include "IMEXHelpers.hpp"
#include "Geometry.hpp"
#include "CustomGCode.hpp"
#include "Config.hpp"
@@ -1386,35 +1385,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
}
// Fill in physical_extruder_map when the printer profile has not authored one. It has one
// entry per logical extruder, so it is sized from the nozzle count -- not from
// printer_extruder_id, which is indexed by variant slot.
//
// IMEX printers only. physical_extruder_map carries two readings in this tree (see
// IMEXHelpers.hpp): the IMEX paths need one entry per logical extruder, the inherited BBL
// paths read it through the clamping get_at(), which collapses every logical extruder to
// physical 0 while the profile default is the single-element {0}. Deriving the identity
// unconditionally would impose the IMEX reading on printers that mean the other one and
// change what they emit -- {first_tools}/{first_filaments}/{first_non_support_*} and
// {most_used_physical_extruder_id}/{curr_physical_extruder_id} in custom start G-code, plus
// the `; physical_extruder_map =` line in the G-code config block -- for every multi-nozzle
// profile that never authored a map. Non-IMEX printers therefore keep the config value
// exactly as it arrives, which is what upstream slices with.
const auto* is_imex_opt = new_full_config.option<ConfigOptionBool>("is_imex");
if (is_imex_opt && is_imex_opt->value) {
auto* pem = new_full_config.option<ConfigOptionInts>("physical_extruder_map", true);
if (pem) {
pem->values = effective_physical_extruder_map(pem, extruder_count).values;
// m_ori_full_print_config was snapshotted above, before this derivation, and the
// selector write-back path rebuilds m_full_print_config from that snapshot. Without
// mirroring the derived map into it, m_full_print_config keeps the unexpanded default
// while every later apply re-derives the expanded one, so an unchanged config diffs
// on physical_extruder_map forever and invalidates all steps on every re-apply.
if (auto* ori_pem = m_ori_full_print_config.option<ConfigOptionInts>("physical_extruder_map", true))
ori_pem->values = pem->values;
}
}
auto opt_filament_map = new_full_config.option<ConfigOptionInts>("filament_map");
std::vector<int> filament_maps = opt_filament_map ? opt_filament_map->values : std::vector<int>();
-173
View File
@@ -274,7 +274,6 @@ static t_config_enum_values s_keys_map_WipeTowerType {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(WipeTowerType)
static t_config_enum_values s_keys_map_FuzzySkinMode {
{ "displacement", int(FuzzySkinMode::Displacement) },
{ "extrusion", int(FuzzySkinMode::Extrusion) },
@@ -721,24 +720,6 @@ static const t_config_enum_values s_keys_map_FilamentMapMode = {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(FilamentMapMode)
// IMEX: keep the legacy hyphenated wire strings so existing presets / 3MFs
// deserialize unchanged after the coString -> coEnum migration.
static const t_config_enum_values s_keys_map_ImexToolLayout = {
{ "front-left", int(ImexToolLayout::FrontLeft) },
{ "front-right", int(ImexToolLayout::FrontRight) },
{ "rear-left", int(ImexToolLayout::RearLeft) },
{ "rear-right", int(ImexToolLayout::RearRight) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ImexToolLayout)
static const t_config_enum_values s_keys_map_ImexVizTheme = {
{ "standard", int(ImexVizTheme::Standard) },
{ "deuteranopia", int(ImexVizTheme::Deuteranopia) },
{ "tritanopia", int(ImexVizTheme::Tritanopia) },
{ "high_contrast", int(ImexVizTheme::HighContrast) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ImexVizTheme)
// PrimeVolumeMode. Serialized string keys must stay stable; they round-trip through .3mf.
static const t_config_enum_values s_keys_map_PrimeVolumeMode = {
{ "Default", pvmDefault },
@@ -6923,139 +6904,6 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
// IDEX/IQEX (independent X extruder) — parallel printing support for IDEX/IQEX printers.
// Every key in this group is read at slice time from m_config, and the two per-plate process
// keys (imex_parallel_mode, imex_head_filament_map) round-trip through the 3MF's
// model_settings.config plate metadata, so none of them needs to appear in the exported
// g-code. They all carry non-nil defaults, so emitting them would add a line to every
// printer's dump. Kept out of the g-code config block (banned_keys).
def = this->add("is_imex", coBool);
def->label = L("IDEX/IQEX Printer");
def->tooltip = L("Enable parallel printing for printers with multiple independent carriages (IDEX, IQEX, and similar).");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
// Firmware-managed zones: slice the primary zone's contents at bed origin (0,0)
// so a center-origin printer firmware can apply its own copy/mirror offsets to
// each toolhead. The slicer's zone overlay, placement check, and mode signaling
// are unchanged; only the gcode emission frame shifts.
def = this->add("imex_firmware_managed_zones", coBool);
def->label = L("Firmware-managed zones");
def->tooltip = L("When enabled, the slicer emits g-code with the primary zone's "
"contents centered on bed origin (0,0). The printer firmware is "
"responsible for placing copies/mirrors at each physical zone "
"position. Enable only on printers whose firmware applies its own "
"zone offsets in copy/mirror mode (e.g. RepRapFirmware IDEX "
"duplication mode). Has no effect in Primary mode.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("imex_gantry_count", coInt);
def->label = L("Gantry Count");
def->tooltip = L("Number of independent Y-axis gantries. 1 for IDEX/single-rail IQEX. 2 for dual-gantry systems (divides bed into rows).");
def->min = 1;
def->max = 2;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(1));
def = this->add("imex_tools_per_gantry", coInt);
def->label = L("Tools per Gantry");
def->tooltip = L("Number of independent toolheads per gantry along X. 2 for IDEX-style.");
def->min = 1;
def->max = 4;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(2));
def = this->add("imex_tool_layout", coEnum);
def->label = L("Tool 0 Position");
def->tooltip = L("Physical position on the bed where tool T0 (index 0) is located. "
"Determines how tool indices map to bed zones. "
"For single-gantry setups (IDEX) only left/right matters; "
"for dual-gantry setups (IQEX) all four corners are selectable "
"(front = lower Y / near the operator, rear = higher Y / back of machine).");
def->mode = comAdvanced;
def->enum_keys_map = &ConfigOptionEnum<ImexToolLayout>::get_enum_values();
def->enum_values.push_back("front-left");
def->enum_values.push_back("front-right");
def->enum_values.push_back("rear-left");
def->enum_values.push_back("rear-right");
def->enum_labels.push_back(L("Front-left"));
def->enum_labels.push_back(L("Front-right"));
def->enum_labels.push_back(L("Rear-left"));
def->enum_labels.push_back(L("Rear-right"));
def->set_default_value(new ConfigOptionEnum<ImexToolLayout>(ImexToolLayout::FrontLeft));
def = this->add("imex_nozzle_clearance_x", coFloat);
def->label = L("Nozzle Clearance X");
def->tooltip = L("Distance (mm) from the nozzle to the collision-side carriage edge in X. Used to calculate the width of the collision exclusion strip at each X boundary.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(30.0));
def = this->add("imex_nozzle_clearance_y", coFloat);
def->label = L("Nozzle Clearance Y");
def->tooltip = L("Distance (mm) from the nozzle to the collision-side carriage edge in Y. Used to calculate the width of the collision exclusion strip at each Y boundary.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(30.0));
def = this->add("imex_carriage_margin", coFloat);
def->label = L("Safety Margin");
def->tooltip = L("Non-blocking advisory clearance strip (mm) drawn inside the primary zone at each carriage boundary. Parts placed within this strip will still slice; it is a visual reminder to leave extra clearance near the zone edge.");
def->sidetext = L("mm");
def->min = 0;
def->max = 200;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.0));
def = this->add("imex_viz_theme", coEnum);
def->label = L("Visualization Theme");
def->tooltip = L("Color theme for IDEX/IQEX bed zone visualization. Choose a colorblind-friendly theme if needed.");
def->mode = comAdvanced;
def->enum_keys_map = &ConfigOptionEnum<ImexVizTheme>::get_enum_values();
def->enum_values.push_back("standard");
def->enum_values.push_back("deuteranopia");
def->enum_values.push_back("tritanopia");
def->enum_values.push_back("high_contrast");
def->enum_labels.push_back(L("Standard"));
def->enum_labels.push_back(L("Deuteranopia / Protanopia (red-green)"));
def->enum_labels.push_back(L("Tritanopia (blue-yellow)"));
def->enum_labels.push_back(L("High Contrast"));
def->set_default_value(new ConfigOptionEnum<ImexVizTheme>(ImexVizTheme::Standard));
def = this->add("imex_parallel_mode", coString);
def->label = L("IDEX/IQEX Print Mode");
def->tooltip = L("Name of the active IDEX/IQEX parallel print mode, or \"primary\" for single-carriage printing. Requires IDEX/IQEX Printer enabled in the Printer preset (Printer \u2192 Multimaterial \u2192 IDEX/IQEX Configuration).");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString("primary"));
def = this->add("imex_head_filament_map", coString);
def->label = L("IDEX/IQEX head filament map");
def->tooltip = L("Per-plate override mapping physical heads to filament slots "
"(1-based). Empty means use pem-inversion defaults.");
def->mode = comDevelop;
def->set_default_value(new ConfigOptionString(""));
def = this->add("imex_mode_names", coStrings);
def->label = L("IDEX/IQEX Mode Names");
def->tooltip = L("Display names for each user-defined IDEX/IQEX parallel print mode.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings());
def = this->add("imex_mode_active_tools", coStrings);
def->label = L("IDEX/IQEX Mode Active Tools");
def->tooltip = L("Tool role assignments for each mode. Format: \"idx:P,idx:C,idx:M\" where P=Primary, C=Copy, M=Mirror (e.g. \"0:P,1:C,2:M,3:M\"). Managed by the IDEX/IQEX Modes editor in the Printer preset.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings());
def = this->add("imex_mode_gcodes", coStrings);
def->label = L("IDEX/IQEX Mode G-codes");
def->tooltip = L("G-code or macro call to inject at print start for each mode.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings());
def = this->add("manual_filament_change", coBool);
def->label = L("Manual Filament Change");
def->tooltip = L("Enable this option to omit the custom Change filament G-code only at the beginning of the print. "
@@ -9863,24 +9711,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
else if (opt_key == "wall_direction" && value == "auto") {
value = "ccw";
}
// Orca: the IDEX/IQEX parallel printing keys shipped to testers as ixex_* before the feature
// was renamed IMEX, and the two clearance keys were renamed again to say what they measure:
// nozzle to carriage edge on the collision side, not the carriage's full width. Without this
// an existing printer profile loses every one of these values silently.
// is_ixex is the master gate: without it every other key below migrates into a feature that
// stays switched off, which is worse than losing them all, because the settings then look
// configured. ixex_primary_col/_row are the only era-1 keys with no modern counterpart (the
// primary is a role in the mode's tools string now); they were never in an option list, so no
// saved file carries them, and the has() check at the end of this function drops them anyway.
else if (opt_key == "is_ixex") {
opt_key = "is_imex";
} else if (opt_key.compare(0, 5, "ixex_") == 0) {
opt_key = "imex_" + opt_key.substr(5);
if (opt_key == "imex_carriage_width_x")
opt_key = "imex_nozzle_clearance_x";
else if (opt_key == "imex_carriage_width_y")
opt_key = "imex_nozzle_clearance_y";
}
// Ignore the following obsolete configuration keys:
static std::set<std::string> ignore = {
@@ -13234,9 +13064,6 @@ OtherSlicingStatesConfigDef::OtherSlicingStatesConfigDef()
new_def("initial_no_support_extruder", coInt, "Initial no support extruder", "Zero-based index of the first extruder used for printing without support. Same as initial_no_support_tool.");
new_def("in_head_wrap_detect_zone", coBool, "In head wrap detect zone", "Indicates if the first layer overlaps with the head wrap zone.");
new_def("curr_bed_type", coString, "Current bed type", "Name of the currently selected bed plate type (e.g. 'Textured PEI Plate', 'Smooth High Temp Plate').");
new_def("imex_mode", coString, "IDEX/IQEX active mode", "Name of the active IDEX/IQEX parallel print mode for this plate (e.g. 'primary', 'mirror', 'copy'). Empty string if IDEX/IQEX is not enabled.");
new_def("imex_mode_index", coInt, "IDEX/IQEX active mode index", "Zero-based index of the active IDEX/IQEX parallel print mode within imex_mode_names.");
new_def("imex_mode_gcode", coString, "IDEX/IQEX active mode G-code", "The raw mode G-code template for the active IDEX/IQEX parallel print mode, after placeholder evaluation. Globals defined here flow into machine_start_gcode.");
}
PrintStatisticsConfigDef::PrintStatisticsConfigDef()
-40
View File
@@ -250,26 +250,6 @@ enum class WallDirection
Count,
};
// IMEX: physical bed corner where tool T0 sits. Determines how tool indices
// map to bed zones (front = lower Y near operator, rear = higher Y).
enum class ImexToolLayout {
FrontLeft,
FrontRight,
RearLeft,
RearRight,
Count,
};
// IMEX: color theme for the bed-zone visualization. Colorblind-friendly variants
// are provided for users with color-vision differences.
enum class ImexVizTheme {
Standard,
Deuteranopia,
Tritanopia,
HighContrast,
Count,
};
// Orca: print order of surface fill loops/fragments for center-based fill patterns
// (Concentric, Archimedean Chords, Octagram Spiral).
enum class SurfaceFillOrder {
@@ -1416,13 +1396,6 @@ PRINT_CONFIG_CLASS_DEFINE(
// Orca: internal use only
((ConfigOptionBool, calib_flowrate_topinfill_special_order)) // ORCA: special flag for flow rate calibration
// IDEX/IQEX parallel print mode (per-print selection, stores mode name or "primary")
((ConfigOptionString, imex_parallel_mode))
// Per-plate head→filament override for MMU-equipped printers.
// Serialized as compact "phys:slot,phys:slot" (1-based filament slots, matches UI).
// Empty string means "use first_filament_for_physical_head defaults everywhere".
((ConfigOptionString, imex_head_filament_map))
)
// This object is mapped to Perl as Slic3r::Config::PrintRegion.
@@ -1795,19 +1768,6 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionStrings, filament_start_gcode))
((ConfigOptionBool, single_extruder_multi_material))
((ConfigOptionBool, manual_filament_change))
// IDEX/IQEX (independent X extruder) — parallel printing support for IDEX/IQEX printers
((ConfigOptionBool, is_imex))
((ConfigOptionBool, imex_firmware_managed_zones))
((ConfigOptionInt, imex_gantry_count))
((ConfigOptionInt, imex_tools_per_gantry))
((ConfigOptionFloat, imex_nozzle_clearance_x))
((ConfigOptionFloat, imex_nozzle_clearance_y))
((ConfigOptionFloat, imex_carriage_margin))
((ConfigOptionEnum<ImexToolLayout>, imex_tool_layout))
((ConfigOptionEnum<ImexVizTheme>, imex_viz_theme))
((ConfigOptionStrings, imex_mode_names))
((ConfigOptionStrings, imex_mode_active_tools))
((ConfigOptionStrings, imex_mode_gcodes))
((ConfigOptionBool, single_extruder_multi_material_priming))
((ConfigOptionEnum<ToolChangeOrderingType>, toolchange_ordering))
((ConfigOptionString, toolchange_cyclic_order))
-14
View File
@@ -247,20 +247,6 @@ void smooth_height_pixels(std::vector<uint8_t> &pixels, int width, int height, f
}
} // namespace
bool height_texture_has_color(const TextureDisplacementLayer &layer)
{
if (layer.empty())
return false;
{
std::lock_guard<std::mutex> lock(g_decoded_texture_cache.mutex);
const auto it = g_decoded_texture_cache.entries.find(layer.image_data.get());
if (it != g_decoded_texture_cache.entries.end() && it->second.first.lock() == layer.image_data)
return it->second.second.has_color();
}
// Not decoded yet. Decoding caches the raw image, so this happens once per image.
return decode_height_texture(layer).has_color();
}
DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer)
{
DecodedHeightTexture result;
+5 -17
View File
@@ -1,7 +1,6 @@
#ifndef slic3r_TextureDisplacement_hpp_
#define slic3r_TextureDisplacement_hpp_
#include <cmath>
#include <cstddef>
#include <Eigen/Core>
#include <cstdint>
@@ -388,10 +387,6 @@ struct TextureDisplacementOptions
// image (TextureDetail::flat_colors): a texture of flat colours prints in single filaments, a
// photograph or gradient in mixes. Off forces single filaments everywhere.
bool color_mix_enabled = true;
// The most mixes the palette may offer. Every mix a bake paints with becomes a mixed filament slot,
// so this is also the most slots one bake can add. The mixes themselves are picked from the
// texture's colours, those that improve the match the most coming first.
int color_mix_count = 8;
// Majority-filter passes over the assigned colours. See TextureColorRequest::despeckle_passes -
// this is the control for it, and 2 is enough to clear the salt-and-pepper an image with detail
// finer than the mesh leaves behind, without eating features that are genuinely a facet wide.
@@ -401,7 +396,7 @@ struct TextureDisplacementOptions
{
ar(displace_border, smooth_enabled, smooth_strength, smooth_iterations, smooth_skip_border,
pipeline_v2, v2_refine_mm, v2_regularize, v2_max_triangles_k,
v2_relocate, color_mix_enabled, color_despeckle, color_mix_count);
v2_relocate, color_mix_enabled, color_despeckle);
}
};
@@ -485,10 +480,6 @@ struct DecodedHeightTexture
// DecodedHeightTexture if image_data is empty or is not a PNG at all.
DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer);
// decode_height_texture(layer).has_color(), answered from the decode cache rather than from a copy of the
// texture - cheap enough to ask every frame. Smoothing does not change it, so the raw decode is what is read.
bool height_texture_has_color(const TextureDisplacementLayer &layer);
// Maps a linear RGB colour in [0, 1] to an index into the caller's palette, or -1 for "no colour".
//
// Deliberately a callback rather than a function here: matching a colour to a filament is a
@@ -503,14 +494,12 @@ using ColorQuantizeFn = std::function<int(const Vec3f &)>;
// interleaving, which the slicer does per print layer. Plain data, so it can be captured into a job.
struct PrintableColor
{
Vec3f rgb = Vec3f::Zero(); // what it looks like; for a mix, the colour its mixed filament slot shows
Vec3f rgb = Vec3f::Zero(); // what it looks like; for a mix, the perceptual average of the two
int a = 0; // filament index
int b = 0; // the second filament; == a for a pure entry
int num = 1; // a's share of the interleave, out of `den`
int den = 1;
bool is_mix() const { return a != b; }
// a's share in percent, the form a mixed filament slot is created from.
int a_percent() const { return int(std::lround(100.0 * double(num) / double(den))); }
};
// Everything needed to colour a mesh, captured on the main thread and handed to a job. An empty
@@ -773,10 +762,9 @@ struct TextureColorRequest
float min_color_region_mm2 = 0.5f;
// Filled per *base mesh* triangle (the bake is topology-preserving, so this indexes the returned
// mesh too): the quantize callback's index plus one, or 0 for "this triangle takes no colour from
// the texture". The +1 lines up with EnforcerBlockerType, where 0 is NONE ("use the volume's own
// filament"): where every palette entry is a filament, these go straight to a TriangleSelector. A
// palette with mixes maps each index to the mix's filament slot first (see
// GLGizmoTextureDisplacement::palette_filaments()).
// the texture". The +1 is not arbitrary - it lines up with EnforcerBlockerType, where 0 is NONE
// ("use the volume's own filament") and 1..16 are Extruder1..16, so the caller can hand these
// straight to a TriangleSelector without a second mapping table.
std::vector<uint8_t> *out_triangle = nullptr;
};
-4
View File
@@ -283,10 +283,6 @@ set(SLIC3R_GUI_SOURCES
GUI/FilamentMapDialog.hpp
GUI/IconManager.cpp
GUI/IconManager.hpp
GUI/IMEXFilamentPickerPopover.cpp
GUI/IMEXFilamentPickerPopover.hpp
GUI/IMEXModesCtrl.cpp
GUI/IMEXModesCtrl.hpp
GUI/IdleScheduler.cpp
GUI/IdleScheduler.hpp
GUI/ImageGrid.cpp
+29 -29
View File
@@ -1112,7 +1112,7 @@ wxWindow *CreateFilamentPresetDialog::create_dialog_buttons()
if (!m_can_not_find_vendor_checkbox->GetValue()) {
if (_L("Select Vendor") == vendor_str) {
MessageDialog dlg(this, _L("Vendor is not selected; please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
} else {
@@ -1121,14 +1121,14 @@ wxWindow *CreateFilamentPresetDialog::create_dialog_buttons()
} else {
if (m_filament_custom_vendor_input->GetTextCtrl()->GetValue().empty()) {
MessageDialog dlg(this, _L("Custom vendor missing; please input custom vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
} else {
vendor_name = into_u8(m_filament_custom_vendor_input->GetTextCtrl()->GetValue());
if (vendor_name == "Bambu" || vendor_name == "Generic") {
MessageDialog dlg(this, _L("\"Bambu\" or \"Generic\" cannot be used as a Vendor for custom filaments."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
@@ -1139,7 +1139,7 @@ wxWindow *CreateFilamentPresetDialog::create_dialog_buttons()
wxString type_str = m_filament_type_combobox->GetLabel();
std::string type_name;
if (_L("Select Type") == type_str) {
MessageDialog dlg(this, _L("Filament type is not selected, please reselect type."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
MessageDialog dlg(this, _L("Filament type is not selected, please reselect type."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
} else {
@@ -1150,7 +1150,7 @@ wxWindow *CreateFilamentPresetDialog::create_dialog_buttons()
std::string serial_name;
if (serial_str.empty()) {
MessageDialog dlg(this, _L("Filament serial missing; please input serial."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
} else {
@@ -1161,7 +1161,7 @@ wxWindow *CreateFilamentPresetDialog::create_dialog_buttons()
if (vendor_name.empty() || serial_name.empty()) {
MessageDialog dlg(this, _L("There may be disallowed characters in the vendor or serial input of the filament. Please delete and re-enter."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
@@ -1169,20 +1169,20 @@ wxWindow *CreateFilamentPresetDialog::create_dialog_buttons()
boost::algorithm::trim(serial_name);
if (vendor_name.empty() || serial_name.empty()) {
MessageDialog dlg(this, _L("All inputs in the custom vendor or serial are spaces. Please re-enter."),
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
if (m_can_not_find_vendor_checkbox->GetValue() && str_is_all_digit(vendor_name)) {
MessageDialog dlg(this, _L("The vendor cannot be a number; please re-enter."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
if (!is_check_box_selected()) {
MessageDialog dlg(this, _L("You have not selected a printer or preset yet. Please select at least one."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
@@ -1818,7 +1818,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_printer_item(wxWindow *parent)
m_select_model->SetLabelColor(*wxBLACK);
}
} else {
MessageDialog dlg(this, _L("The model was not found; please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
MessageDialog dlg(this, _L("The model was not found; please reselect vendor."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
}
@@ -2818,14 +2818,14 @@ wxWindow *CreatePrinterPresetDialog::create_page2_dialog_buttons(wxWindow *paren
// Confirm if the printer preset exists
if (!m_printer_preset) {
MessageDialog dlg(this, _L("You have not yet chosen which printer preset to create based on. Please choose the vendor and model of the printer"),
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
if (!save_printable_area_config(m_printer_preset)) {
MessageDialog dlg(this, _L("You have entered a disallowed character in the printable area section on the first page. Please use only numbers."),
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
show_page1();
return;
@@ -2865,7 +2865,7 @@ wxWindow *CreatePrinterPresetDialog::create_page2_dialog_buttons(wxWindow *paren
}
}
if (selected_filament_presets.empty() && !filament_preset_is_exist) {
MessageDialog dlg(this, _L("You need to select at least one filament preset."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
MessageDialog dlg(this, _L("You need to select at least one filament preset."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
@@ -2880,7 +2880,7 @@ wxWindow *CreatePrinterPresetDialog::create_page2_dialog_buttons(wxWindow *paren
}
}
if (selected_process_presets.empty() && !process_preset_is_exist) {
MessageDialog dlg(this, _L("You need to select at least one process preset."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
MessageDialog dlg(this, _L("You need to select at least one process preset."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
@@ -3125,7 +3125,7 @@ void CreatePrinterPresetDialog::on_select_printer_model(wxCommandEvent &e)
wxArrayString printer_preset_model = printer_preset_sort_with_nozzle_diameter(m_printer_preset_vendor_selected, nozzle);
if (printer_preset_model.size() == 0) {
MessageDialog dlg(this, _L("Current vendor has no models, please reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
MessageDialog dlg(this, _L("Current vendor has no models, please reselect."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
@@ -3386,7 +3386,7 @@ bool CreatePrinterPresetDialog::validate_input_valid()
std::string model_name = get_printer_model();
if ((vendor_name.empty() || model_name.empty())) {
MessageDialog dlg(this, _L("You have not selected the vendor and model or input the custom vendor and model."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return false;
}
@@ -3395,7 +3395,7 @@ bool CreatePrinterPresetDialog::validate_input_valid()
model_name = remove_special_key(model_name);
if (vendor_name.empty() || model_name.empty()) {
MessageDialog dlg(this, _L("There may be escape characters in the custom printer vendor or model. Please delete and re-enter."),
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return false;
}
@@ -3403,13 +3403,13 @@ bool CreatePrinterPresetDialog::validate_input_valid()
boost::algorithm::trim(model_name);
if (vendor_name.empty() || model_name.empty()) {
MessageDialog dlg(this, _L("All inputs in the custom printer vendor or model are spaces. Please re-enter."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return false;
}
if (check_printable_area() == false) {
MessageDialog dlg(this, _L("Please check bed printable shape and origin input."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
MessageDialog dlg(this, _L("Please check bed printable shape and origin input."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return false;
}
@@ -3417,7 +3417,7 @@ bool CreatePrinterPresetDialog::validate_input_valid()
wxString printer_name = m_select_printer->GetStringSelection();
if (printer_name.empty()) {
MessageDialog dlg(this, _L("You have not yet selected the printer to replace the nozzle for; please choose a printer."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return false;
}
@@ -3682,22 +3682,22 @@ void ExportConfigsDialog::show_export_result(const ExportCase &export_case)
MessageDialog *msg_dlg = nullptr;
switch (export_case) {
case ExportCase::INITIALIZE_FAIL:
msg_dlg = new MessageDialog(this, _L("initialize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
msg_dlg = new MessageDialog(this, _L("initialize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::ADD_FILE_FAIL:
msg_dlg = new MessageDialog(this, _L("add file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
msg_dlg = new MessageDialog(this, _L("add file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::ADD_BUNDLE_STRUCTURE_FAIL:
msg_dlg = new MessageDialog(this, _L("add bundle structure file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
msg_dlg = new MessageDialog(this, _L("add bundle structure file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::FINALIZE_FAIL:
msg_dlg = new MessageDialog(this, _L("finalize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
msg_dlg = new MessageDialog(this, _L("finalize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::OPEN_ZIP_WRITTEN_FILE:
msg_dlg = new MessageDialog(this, _L("open zip written fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
msg_dlg = new MessageDialog(this, _L("open zip written fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::EXPORT_SUCCESS:
msg_dlg = new MessageDialog(this, _L("Export successful"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
msg_dlg = new MessageDialog(this, _L("Export successful"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
break;
}
@@ -3768,7 +3768,7 @@ std::string ExportConfigsDialog::initial_file_name(const wxString &path, const s
MessageDialog dlg(this,
wxString::Format(_L("The file: %s\nmay have been opened by another program.\nPlease close it and try again."),
encode_path(printer_export_path.string().c_str())),
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxOK | wxCENTRE);
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"), wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return "initial_failed";
}
@@ -4324,7 +4324,7 @@ wxWindow *ExportConfigsDialog::create_dialog_buttons(wxWindow* parent)
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
if (!has_check_box_selected()) {
MessageDialog dlg(this, _L("Please select at least one printer or filament."), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
@@ -5042,7 +5042,7 @@ wxWindow *CreatePresetForPrinterDialog::create_dialog_buttons()
} else {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "filament choice not find filament preset and choice is:" << filament_preset_name;
MessageDialog dlg(this, _L("The filament choice not find filament preset, please reselect it"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
dlg.ShowModal();
return;
}
+6 -6
View File
@@ -477,27 +477,27 @@ void ExportPresetBundleDialog::show_export_result(const ExportCase& export_case)
switch (export_case) {
case ExportCase::INITIALIZE_FAIL:
msg_dlg = new MessageDialog(this, _L("initialize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::ADD_FILE_FAIL:
msg_dlg = new MessageDialog(this, _L("add file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::ADD_BUNDLE_STRUCTURE_FAIL:
msg_dlg = new MessageDialog(this, _L("add bundle structure file fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::FINALIZE_FAIL:
msg_dlg = new MessageDialog(this, _L("finalize fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::OPEN_ZIP_WRITTEN_FILE:
msg_dlg = new MessageDialog(this, _L("open zip written fail"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
break;
case ExportCase::EXPORT_SUCCESS:
msg_dlg = new MessageDialog(this, _L("Export successful"), wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Info"),
wxOK | wxCENTRE);
wxYES | wxYES_DEFAULT | wxCENTRE);
break;
}
+1 -1
View File
@@ -559,7 +559,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
else if(m_opt_id == "filament_retraction_distances_when_cut" || opt_key_without_idx == "retraction_distances_when_cut"){
if (m_value.empty() || boost::any_cast<double>(m_value) != val) {
wxString msg_text = format_wxstr(_L("Value %s is out of range. The valid range is from %d to %d."), str, m_opt.min, m_opt.max);
WarningDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxOK);
WarningDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxYES);
if (dialog.ShowModal()) {
if (m_value.empty()) {
if (m_opt.min > val) val = m_opt.min;

Some files were not shown because too many files have changed in this diff Show More