I’m using an X-in-1-out hotend with a purge bucket. Since I don’t have a purge block, I can reduce waste by only purging exactly the amount of filament needed to ensure that the new color/material is fully primed. I have noticed that in general, the more “dissimilar” the filament colors are, the more purging is necessary to avoid visible color bleeding. So I thought, what if it were possible to quantify the difference between the filament colors and use that figure to determine the purge volume during a filament change?
Turns out there’s a fairly straightforward equation to determine the Euclidean distance between two colors in the RGB colorspace. I use SuperSlicer, which has the ability to export as a gcode variable the hex color code that the user assigned to the filament. So I worked up some Klipper macros to use this information to calculate the Euclidean distance between the two filament colors and increase the purge volume the further apart the colors are.
I’ll share commented snippets rather than my entire macro, since my LOAD_FILAMENT macro is otherwise pretty specific to my setup.
First though, some explanation of some of the underlying implementation:
- I have a GLOBALS macro that is used solely to store certain variables and their values so that they are accessible to other macros during the print. This is the “globals” namespace in the snippet below, and it’s where the information about the new (incoming) filament is temporarily stored and accessed.
- I also use “save_variables” to save to disk data about the filament that is successfully loaded into the hotend. This data persists between prints (and reboots) so Klipper can always know what filament (or remnants of filament) is in the nozzle. This is the “svv” namespace in the snippet below.
- Later in the macro (not shown in the snippet below) once the purge is complete, the filament data in “globals” is saved to “svv,” replacing the data that was there before.
- SuperSlicer exports the hex color code with a leading #, which is technically correct but causes problems because in gcode, everything after # is considered a comment. So far I haven’t figured out how (or if it’s even possible) to escape the # character, so as a workaround, I’m using a postprocessing script in the slicer to excise the # character from the filament color lines.
- The maximum Euclidean distance (between black and white) is about 441. For no particular reason, I divided this 0-441 range roughly into 6 segments, adding filament purge in multiples of 10mm depending on which segment the color difference falls into. So very similar colors will only have an additional 10mm of purge while very dissimilar colors will have 60mm of additional purge.
- I have not actually tested yet the specific purge volumes generated by the snippet below. 60mm for very dissimilar colors may be too much, too little, or just right. This is mostly a proof of concept at this point, but I plan to follow up after some real-world testing.
So here’s the snippet. I’m interested to hear any comments from anyone who has tried something similar to this:
{% if globals.filament_id == svv.FILAMENT_ID %}
; If filament IDs match, skip secondary purge because it's the same filament as before.
{% set SECONDARY_PURGE_VOLUME = 0 %}
{% else %}
{% set color1_hex = globals.filament_color_hex %}
{% set color2_hex = svv.filament_color_hex %}
{% set c1 = [] %}
{% set c2 = [] %}
; Get RGB values from hex
{% for i in range(0,6,2) %}
{ c1.append(color1_hex[i:i+2]|int(base=16)) }
{ c2.append(color2_hex[i:i+2]|int(base=16)) }
{% endfor %}
; Perform the Euclidean distance calculation
{% set sums = [] %}
{% for n in range(3) %}
{sums.append((c2[n] - c1[n])**2)}
{% endfor %}
{% set diff = (sums|sum())**(1/2) %}
{ action_respond_info('Color difference between new filament (%s) and last filament (%s): %.2f'|
format(globals.filament_id, svv.filament_id, diff))}
{% if diff <= 74 %}
{% set SECONDARY_PURGE_VOLUME = 10|int %}
{% elif 74 < diff <= 147 %}
{% set SECONDARY_PURGE_VOLUME = 20|int %}
{% elif 147 < diff <= 220 %}
{% set SECONDARY_PURGE_VOLUME = 30|int %}
{% elif 220 < diff <= 294 %}
{% set SECONDARY_PURGE_VOLUME = 40|int %}
{% elif 294 < diff <= 368 %}
{% set SECONDARY_PURGE_VOLUME = 50|int %}
{% elif 368 < diff %}
{% set SECONDARY_PURGE_VOLUME = 60|int %}
{% endif %}
; Additional purge needed when changing to different filament type
{% if globals.filament_type != svv.filament_type %}
{ action_respond_info('Different filament type detected. Adding 30mm to secondary purge...') }
{% set SECONDARY_PURGE_VOLUME = SECONDARY_PURGE_VOLUME + 30 %}
{% endif %}
{ action_respond_info('Setting secondary purge to %i mm...'|format(SECONDARY_PURGE_VOLUME))}
{% endif %}