Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create PurgeLinesAndUnload.py #20013

Merged
merged 35 commits into from
Feb 20, 2025
Merged

Conversation

GregValiant
Copy link
Collaborator

@GregValiant GregValiant commented Dec 9, 2024

Description

This script has 4 options.
Add Purge Lines:
Will draw lines left, right, top, or bottom of the build plate and either full length of half length. If a print takes up the entire width then the purge lines could be moved to the bottom.
Circle Around To Layer Start:
Creates an orthogonal tool path that moves the nozzle around the periphery of the build plate before moving in to the Layer Start location. This keeps strings from dragging across the print location on the build plate.
Adjust Starting E Location:
Cura adds a retraction after the StartUp Gcode. If there is also a retraction within the StartUp Gcode (or when using Add Purge Lines) it causes a double retraction. This option will change that "G1 E-" line to a "G92 E" so the filament location within the nozzle can be adjusted to provide for an exact start to the skirt/brim/raft.
Unload FIlament at Print End:
Adds an unload sequence of G1 E commands to back the filament out of the hot end and out of the extruder. Works well with long-tube bowden printers.

The script supports:
Absolute and Relative extrusion
Rectangular and Elliptic beds
Origin-At-Center whether True or False.
Disallowed Areas

Settings

image

Type of change

  • [ X] New feature (non-breaking change which adds functionality)

How Has This Been Tested?

I've been using this script for a year.

Test Configuration:
Dell Laptop 16mb of RAM

  • Operating System:
    Windows 10 Pro
    Cura versions 4.13.1 and up.

Checklist:

  • [ X] My code follows the style guidelines of this project as described in UltiMaker Meta and Cura QML best practices
  • [ X] I have read the Contribution guide
  • [ X] I have commented my code, particularly in hard-to-understand areas
  • [ X] I have uploaded any files required to test this change
  • [ X] I apologize ahead of time for my coding style.

This script has 4 options.
Add Purge Lines will draw lines left, right, top, or bottom of the build plate and either fill length of half length.  If a print takes up the entire width then the purge lines could be moved to the bottom.
@github-actions github-actions bot added the PR: Community Contribution 👑 Community Contribution PR's label Dec 9, 2024
Copy link
Contributor

github-actions bot commented Dec 9, 2024

Test Results

23 701 tests   23 699 ✅  46s ⏱️
     1 suites       2 💤
     1 files         0 ❌

Results for commit be0671c.

♻️ This comment has been updated with latest results.

@GregValiant
Copy link
Collaborator Author

@HellAholic the "Move around to the print start" function is something that came up in a post a while ago. I did account for cases where Cura adds the "Move to prime tower" line at the end of the startup. The script will add an orthogonal move to an edge before the actual move to the prime tower location.
I have high confidence in the code for "Rectangular" beds. The G2/G3 coding in the "Elliptic" section seems OK but needs another set of eyeballs.
I'll leave this as a draft for now.

@GregValiant GregValiant marked this pull request as draft December 9, 2024 02:11
@HellAholic
Copy link
Contributor

I'll take a look to see if I can update the code style. Will also try to play around with the current iteration to first get a grasp on how things work and what the output looks like. Might be a slow burner but it's on my todo list 👍

Changed 'Execute' procedure per suggestion.
Add 'G10' firmware retraction support to 'Adjust Starting E'.
@HellAholic
Copy link
Contributor

HellAholic commented Dec 14, 2024

I went with the purge everything approach and it started working, something else I had in the background was interfering with the script.
On the functionality side, would it be an idea to account for the disallowed area of the build plate?
[extreme example chosen for demonstration purposes] on the method series, the nozzle/extruder physically cannot reach there. Don't think there are any other printers with that type of restriction. But just as a discussion point, might be something to consider.
The worry I have with that (disallowed area) is then you might open up the whole shrinkage factor compensation thing, but it's just a worry, since you're applying the position in post processing, it does not get modified by the engine, so just as something to maybe check later if the disallowed area logic is introduced.

image

Update:
Had a bit of a though about it, we could add an extra field (offset from the edge), this would allow for adjustments in case you would want to adjust for the disallowed area or maybe you have a build plate with damaged edges and you want to do the purge a bit more towards the inside.
Also I'm a bit annoyed by the preview not showing the post processing results and having to load things back in to check. I'll see if I can do some adjustments to re-load the preview with the changes.

@GregValiant
Copy link
Collaborator Author

Good morning.
I missed the "machine_disallowed_areas" thing. After looking at the MethodX disallowed areas I think simply excluding 'Purge Lines' and 'Move to Start' is the best way to go. The polygons in a definition file can be written differently by different authors. They are easy to draw, but tough to parse.
The same thing might be true for any multi-extruder machine that does not "Share Heater" and "Share Nozzle since both the Purge Lines and the Unload will be for the active nozzle.
I'll spend some time thinking on those.

Regarding not showing the post-processed gcode in the initial preview, I recall seeing complaints about that here in the bug reports. The Initial Preview is done before any post-processing so the file would need to be saved, and then the gcode read back in. That'll take a while. Maybe a "Load Gcode file for Preview" button (that popped up after saving, or became yet another function of the "Slice" button) would be better so the user would have an option. (If Arc-Welder is enabled then it won't show correctly anyway.)

On a different subject...
It would be nice if there was a ToolTip attached to each name in the "Post Processing Scripts" list. Something like:
image
You would still need to load the PP to see the description, but there would be an indication of what it actually does.

@HellAholic
Copy link
Contributor

HellAholic commented Dec 14, 2024

Aloha,
Let me know what you come up with, you can always add "single extruder only" to the script or something.
I checked the offset, it works somewhat, but also needs a secondary field for start and end of the purge line. There are some general patterns in how the gcode is made, so if I can make a template out of it, then adjusting the values would be easier. I'll see if I can make something that's more generic but simple and robust.

The slice result preview "should" in theory be simple enough, just have to figure out where I need to poke in the code to make it move. I think reloading the changed gcode is the doable part, but loading back to the original does not sound like a feasible option.

On a different subject...
It would be nice if there was a ToolTip attached to each name in the "Post Processing Scripts" list.

That should also be doable, but it requires some changes to the qml, adding a getter to the python with a pyqtsignal and such, and reading the description in a try/except to handle scripts without a description.
The main issue is that the qml for that part is fairly old, so it will probably need some updating:

  • List is not properly spaced (line height / label height) -> this is mostly my OCD and other things being triggered
  • The selected highlights is not great -> same as above xD
  • The text (script name) is added to the list makes it so that the tooltip only appears if you hover over the left part (first 50 pixels or something)
  • There is also always the risk of the unexpected bug popping out of nowhere

@GregValiant
Copy link
Collaborator Author

I hate unexpected bugs. The expected ones are much better.
I think I've got the "disallowed areas" figured out without having to exclude those printers.

@GregValiant
Copy link
Collaborator Author

Wow. I have been integrating the "Disallowed Areas" into the code. There wasn't a lot of breakage, but there are more variable names to deal with (and there was already a lot).
Straightening that out is going to take a minute. There are a lot of combinations to set up for to debug.
Fortunately, not many (if any) delta's have disallowed areas so I'm ignoring it for the G2/G3's.

I'll go through your suggestions and make the changes and then push another commit.

@HellAholic
Copy link
Contributor

HellAholic commented Dec 15, 2024

It might also be an idea to use enum strings, doesn't even need to be linked to the string value it can be an int, but to keep it as close to the current implementation as possible:

from enum import Enum

# class syntax
class Location(Enum):
    LEFT_FRONT = "LF"
    LEFT_REAR = "LR"
    RIGHT_FRONT = "RF"
    RIGHT_REAR =  "RR"
    
 # usage
 if purge_end_loc == Location.LEFT_FRONT

makes it easier to work with strings in the comparisons and also reduces the chance of making a typo, also helps with autocomplete xD

You can also do the calculations for some values and store them in the self, since it's being passed along to the different functions, that will reduce the number of variables you need to pass with each function call.
curaApp is one example, but probably you'll find other things that can be done the same way.
so if in your initialize() function instead of curaApp = ... you say self.curaApp = ...
then in all the following functions that have the self as the first argument, you can just reference it by saying:
self.curaApp.getProperty("something", "something")
the only thing you need to keep in mind is not adding the same name as the super class. (make unique names)

TLDR:
Add the bits you're comfortable with and I'll do a refactor afterwards, make sure functionality is there, I'll make it pretty :P

@GregValiant
Copy link
Collaborator Author

I have to append this to my "List of things I didn't know".
image
IS A LIE.
Any gcode file (or UFP file) opened in Cura can be post-post-processed if the "Save to..." button is clicked. That extra post-processing generally makes a mess.

Plugins have a block so that doesn't happen. I've blocked that from happening with this script.

@HellAholic
Copy link
Contributor

Although it's a bit of a rare usecase to reload a gcode and run a post processing script on it, since the information regarding the post processing is already in at the end of header of the gcode file it should be possible to allow for running additional post processing without re-running the same scripts that already were executed on the gcode.
So reading the gcode, looking if there is a POSTPROCESSED comment in it, if so, filtering out the scripts already executed, and executing the other ones.

;END_OF_HEADER
;POSTPROCESSED
;  [PurgeLinesAndUnload]

@GregValiant
Copy link
Collaborator Author

In the next week or two I'm going to tear this down and put it back together. It's working, but with the changes I've hacked in for the Prime Tower move and the Disallowed Areas it's turned into spaghetti code. It's pretty bad when even I can't stand it.

So take a break. I'll add a new commit when I get done changing it.

@GregValiant
Copy link
Collaborator Author

@HellAholic
I have made alterations, added a procedure, moved things around, found a couple of typos, and altered comments.
There are a lot of changes.
Do you want me to add a commit or go about updating this some other way?

@HellAholic
Copy link
Contributor

Hey Greg,
Add the commit and I'll work based on that. Took a break and didn't do much, mostly end of year work.

Added a "quick purge" option before the actual unload to insure the filament is free to pull back.
Made adjustments for "Machine Disallowed Areas".
Added some comments.
Re-ordered some of the code.
@GregValiant
Copy link
Collaborator Author

There it is. New and improved.

One of the printers I used for the Disallowed Areas was a UM3. It has 6 areas defined and because I used a simple method of determining the "useable space" as a rectangle (rather than the actual shape), it ends up pushing the purge lines towards the print. There aren't many printers with the problem though, so the warning message I added should (hopefully) be sufficient.

- Remove the travel move -> type probably
- Remove the second F in 3 out of 8 wipe moves
@HellAholic
Copy link
Contributor

Ok, so adding a helper function would generate twice the amount of code since we need 2 coordinates for the first move and 2 for the second one, I could not make it pretty or more understandable than the current format. So instead I made the wipe moves for the rectangular build plates more consistent. I also fixed some indentation bits.

@GregValiant
Copy link
Collaborator Author

I think I know how I deleted the 'start_x' 'start_y' lines.

I originally added the "border distance" below the start_x and start_y in that INIT section. When I deleted it to go another way I think I inadvertently deleted those two lines as well. It may have been a Senior Moment. Sorry about that.
The script has so many convolutions now it's getting to take a long time to go through the various possibilities to debug.

The wipe function was really the only one I figured could be split off per Erwan's comment. Looking at it I figured there wouldn't be much saving (if any).

This has turned out to be more work than the Cooling Fan script was. It's only 80 lines shorter as well.

@HellAholic
Copy link
Contributor

HellAholic commented Feb 10, 2025

Also when you have a typo in the commit comment about fixing a typo
I hate the autocorrect...
image

Give this one a look, let's see if Erwan is pleased with the changes tomorrow 🤞
It could be refined, if you spend like a full week on rewriting it, you could get it to be a bit shorter, something to do maybe for the next version xD
Side note: No worries, that's why we do code reviews :)

@GregValiant
Copy link
Collaborator Author

GregValiant commented Feb 10, 2025

I pad the setting names with spaces to make it obvious to the user that they are child settings.

"Add Purge Lines"
"    Purge Line Location"
"    Purge Line Length"
"    Border Distance"
"Circle around"

When the user looks at it they can tell it belongs to something else.

@GregValiant
Copy link
Collaborator Author

I was a checker for a long time in machine design studios. I would give any 'new kids' projects that I had previously checked as references so they would know how I liked things laid out. It saved a lot of rework down the road.
It's the same here. I won't mention names but one reviewer didn't like spaces around math symbols. I had to take them all out. So the next project I left them all out and a different reviewer had me put them in.
Now I keep a box of spares next to the keyboard.

@wawanbreton
Copy link
Contributor

"Number of purge lines".
It would always need to be positive and would involve adding an index distance. Right now the script moves the nozzle 3mm so the lines don't interfere with each other and maybe pick up boogers. An even number of lines insures that the nozzle will only end up in the "left front", or the "right rear". Odd numbers would put them into corners that are not taken into account in "Circle around...". If 2 purge lines provide 400mm of purge, and you need to add two more, is 800 + the skirt/brim going to be enough?

Could also be "Number of lines pairs" so that the final number would always be even 🙂

So I have a strange way of working, I usually have a skirt but in order to remove it properly, I don't really want it to be used as a prime and I end up manually insert the filament. But maybe I would also do the same with the purge lines because they would not be easy to remove either 😅

The wipe function was really the only one I figured could be split off per Erwan's comment. Looking at it I figured there wouldn't be much saving (if any).

Yes sorry, I know I am a bit fanatical about code duplication, I have been traumatized in the past by people copy-pasting the same piece of ~20 lines of code over and over, and sometimes they slightly changed it to fix something, but not in all places 🙂 but I also know how easy it is to make this mistake of changing a line of code, and forgetting (or not knowing) that there is a similar one somewhere else. The purpose is not really to make the file smaller, but to make sure that whoever will changes it in the future is less likely to create mistakes/inconsistencies.

Give this one a look, let's see if Erwan is pleased with the changes tomorrow 🤞

I am indeed 😄
In this case that was just suggestions for improvements, the initial code looked good already.

@GregValiant
Copy link
Collaborator Author

My printer came with a light duty 25mm wide putty knife. It's good at removing purge lines and skirts.

I ended up putting an override for "Skirt Height" into my printer definition file. I never understood how one person (obviously a fingernail biter) got to have the default skirt height set to "3".

When I design these things (all things really) I go for what I call the "Solid Gold Edition". That means all the bells and whistles I can think of. It generally adds flexibility but at the cost of making things complicated.
I had thought that "once out and once back" of purge would be sufficient and in many cases a lot more than sufficient. That's why there is a setting to cut that in half.
I mentioned that debugging for the "12 purge options" is both tedious and mind numbingly boring. Unlike doubling the purge lines - adding a prime blob would (off the top-of-my-head) be easier as it wouldn't effect where the print head ends up.
"blob/retract/move to purge start/unretract/purge using existing code".

That would have the advantage that users would not damage their fingernail polish when removing the blob as seems to be the case when removing purge lines and skirts. Just sayin'.

@GregValiant
Copy link
Collaborator Author

Three cups of coffee later...
image

    def _get_blob_code(self) -> str:
        if not self.prime_blob_enable or self.prime_blob_distance == 0:
            return ""
        # Set extruder speed for 1.75 filament
        speed_blob = round(float(self.nozzle_size) * 500)
        # Adjust speed if 2.85 filament
        if self.material_diameter > 2: speed_blob *= .4
        blob_string = "G0 F1200 Z20 ; Move up\n"
        blob_string += f"G1 F{speed_blob} E{self.prime_blob_distance} ; Blob\n"
        blob_string += f"G1 F{self.retract_speed} E-{self.retract_dist} ; Retract\n"
        blob_string += "G92 E0 ; Reset extruder\n"
        blob_string += "M300 P500 ; Beep\n"
        blob_string += "G4 S2 ; Wait\n"
        return blob_string

results in:

;TYPE:CUSTOM----------[Purge Lines at MinX]
G0 F600 Z2                      ; Move up
G92 E0                             ; Reset extruder
G0 F1200 Z20                  ; Move up
G1 F200 E35                     ; Blob
G1 F2100 E-7                    ; Retract
G92 E0                              ; Reset extruder
M300 P500 S600              ; Beep
G4 S2                                ; Wait
G0 F10500 X0 Y10            ; Move to start
blah, blah, blah...

It's just like adding more purge lines only different.

@HellAholic should we do this?

@wawanbreton
Copy link
Contributor

My printer came with a light duty 25mm wide putty knife. It's good at removing purge lines and skirts

I have a build plate that is quite soft, so I tried that once and made a scratch in it 😅 Adhesion is very good though, and also removal is very easy, so I do like it, but it is just annoying for prime lines 🙂

I had thought that "once out and once back" of purge would be sufficient and in many cases a lot more than sufficient. That's why there is a setting to cut that in half.

Maybe that is just me doing weird things, indeed ! I could also try the prime blob... We do have this feature in the engine btw, but I think it is only for printers that have a firmware implementation of it. In this case we should just make it work on all printers instead.

Added purge blob option.
Required changes in some settings to 'self'.
Fixed  'quick_purge_speed' to adjust for 2.85 filament.
@GregValiant
Copy link
Collaborator Author

Gotta be getting close here.
The code allows the purge blob when purge location is either left or front.
Adjustment to the quick_purge_speed to account for 2.85 filament.

Indents for child settings
@wawanbreton wawanbreton added the PR: Post Processing ➕ Like adding beeps, more tunability or different Gcode pause at heights label Feb 12, 2025
@PartySausage
Copy link

PartySausage commented Feb 14, 2025

With the new Prime Blob addition the prime is being performed right after homing, which is right above the centre of the bed on my S1 pro and is the usual print position so I wouldn't want the prime material potentially getting deposited on the build plate in that position,. Would it not be better if the prime took place after the print head had moved to the purge line start position?

@GregValiant
Copy link
Collaborator Author

Anything would be better than blobbing into the middle of a print.
I tried an easy fix but it was too difficult. Easier to add more settings and let the user decide where the WC should be.

When you enable the prime blob "Blob Location X" and "Blob Location Y" will now be available.
Careful though, if you put the blob in the exact location of the purge line start - the nozzle was sit down into the blob. Not good. That's a user thing though. Blob on the right and purge on the left or blob in the back and purge on the front will work.

I'll do the commit here in a minute.

Update PurgeLinesAndUnload.py

Added 2 settings so the user can dictate where the prime blob will be.
"Blob Location X" and "Blob Location Y".

Delete PurgeLinesAndUnload_old.py

I'm not sure how I do these things.
@GregValiant
Copy link
Collaborator Author

GregValiant commented Feb 14, 2025

This last commit (after three tries) adds two settings so the user can dictate where the prime blob will occur.
@PartySausage see if this fix works for your situation.

@PartySausage
Copy link

This last commit (after three tries) adds two settings so the user can dictate where the prime blob will occur. @PartySausage see if this fix works for your situation.

@GregValiant That seems to do the trick in the simulator, Well done for the quick turnaround.

I'd done an amateur fix by moving the call to the blob procedure to after the start move in the sections that generate the purge code for the Front & Left Purge lines, which seemed to work, but like you stated this also runs the risk of the purge line starting in the prime blob.

When using purge lines on the front of the plate the purge start is X10 Y0 so I thought I'd try with the purge blob at X0 Y0 & see how/if that works

@GregValiant
Copy link
Collaborator Author

I looked at simply moving the insertion point but right away I saw the potential interference with the purge lines. The user still has to be careful, but a working combination of settings should quickly become apparent.

@HellAholic HellAholic merged commit 08d15ee into Ultimaker:main Feb 20, 2025
3 checks passed
@GregValiant
Copy link
Collaborator Author

Just thinking about this...
Should there be a setting for a user to input "Minimum Z Height"?
Having a fancy emoji is all well and good, but doesn't really help.

I've written it in and it's sitting here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: Community Contribution 👑 Community Contribution PR's PR: Post Processing ➕ Like adding beeps, more tunability or different Gcode pause at heights
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants