mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-21 13:19:37 +08:00

For relative mode, not only needs the retractions created to be actually relative, but it also needs to compensate on the next G1 (as opposed to absolute mode, where you'd want to go to the same absolute E position after). Rather than massively complicating the already gnarly code (once it found a G1 retraction, it scanned forwards to find G0 statements (which it rewrote to G1), so it would go over those _again_ in the middle (layer) loop). While this worked for absolute mode, but would be a nightmare to make work for relative mode as-is (if only because the compensation could _also_ potentially involve keeping track of things over the outer loop). As a bonus I think the resulting code is actually easier to read. part of CURA-10092 -- should fix #14100
118 lines
4.8 KiB
Python
118 lines
4.8 KiB
Python
# Copyright (c) 2023 UltiMaker B.V.
|
|
# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher.
|
|
|
|
from ..Script import Script
|
|
|
|
from UM.Application import Application # To get current absolute/relative setting.
|
|
from UM.Math.Vector import Vector
|
|
|
|
|
|
class RetractContinue(Script):
|
|
"""Continues retracting during all travel moves."""
|
|
|
|
def getSettingDataString(self):
|
|
return """{
|
|
"name": "Retract Continue",
|
|
"key": "RetractContinue",
|
|
"metadata": {},
|
|
"version": 2,
|
|
"settings":
|
|
{
|
|
"extra_retraction_speed":
|
|
{
|
|
"label": "Extra Retraction Ratio",
|
|
"description": "How much does it retract during the travel move, by ratio of the travel length.",
|
|
"type": "float",
|
|
"default_value": 0.05
|
|
}
|
|
}
|
|
}"""
|
|
|
|
def _getTravelMove(self, travel_move, default_pos):
|
|
travel = Vector(
|
|
self.getValue(travel_move, "X", default_pos.x),
|
|
self.getValue(travel_move, "Y", default_pos.y),
|
|
self.getValue(travel_move, "Z", default_pos.z)
|
|
)
|
|
f = self.getValue(travel_move, "F", -1.0)
|
|
return travel, f
|
|
|
|
def _travelMoveString(self, travel, f, out_e):
|
|
# Note that only G1 moves are written, since extrusion is included.
|
|
if f <= 0.0:
|
|
return f"G1 X{travel.x:.5f} Y{travel.y:.5f} Z{travel.z:.5f} E{out_e:.5f}"
|
|
else:
|
|
return f"G1 F{f:.5f} X{travel.x:.5f} Y{travel.y:.5f} Z{travel.z:.5f} E{out_e:.5f}"
|
|
|
|
def execute(self, data):
|
|
current_e = 0.0
|
|
to_compensate = 0 # Used when extrusion mode is relative.
|
|
is_active = False # Whether retract-continue is in effect.
|
|
|
|
current_pos = Vector(0.0, 0.0, 0.0)
|
|
last_pos = Vector(0.0, 0.0, 0.0)
|
|
|
|
extra_retraction_speed = self.getSettingValueByKey("extra_retraction_speed")
|
|
relative_extrusion = Application.getInstance().getGlobalContainerStack().getProperty(
|
|
"relative_extrusion", "value"
|
|
)
|
|
|
|
for layer_number, layer in enumerate(data):
|
|
lines = layer.split("\n")
|
|
for line_number, line in enumerate(lines):
|
|
|
|
# Focus on move-type lines.
|
|
code_g = self.getValue(line, "G")
|
|
if code_g not in [0, 1]:
|
|
continue
|
|
|
|
# Track X,Y,Z location.
|
|
last_pos = last_pos.set(current_pos.x, current_pos.y, current_pos.z)
|
|
current_pos = current_pos.set(
|
|
self.getValue(line, "X", current_pos.x),
|
|
self.getValue(line, "Y", current_pos.y),
|
|
self.getValue(line, "Z", current_pos.z)
|
|
)
|
|
|
|
# Track extrusion 'axis' position.
|
|
last_e = current_e
|
|
e_value = self.getValue(line, "E")
|
|
if e_value:
|
|
current_e = (current_e if relative_extrusion else 0) + e_value
|
|
|
|
# Handle lines: Detect retractions and compensate relative if G1, potential retract-continue if G0.
|
|
if code_g == 1:
|
|
if last_e > (current_e + 0.0001): # Account for floating point inaccuracies.
|
|
|
|
# There is a retraction, each following G0 command needs to continue the retraction.
|
|
is_active = True
|
|
continue
|
|
|
|
elif relative_extrusion and is_active:
|
|
|
|
# If 'relative', the first G1 command after the total retraction will have to compensate more.
|
|
travel, f = self._getTravelMove(lines[line_number], current_pos)
|
|
lines[line_number] = self._travelMoveString(travel, f, to_compensate + e_value)
|
|
to_compensate = 0.0
|
|
|
|
# There is no retraction (see continue in the retract-clause) and everything else has been handled.
|
|
is_active = False
|
|
|
|
elif code_g == 0:
|
|
if not is_active:
|
|
continue
|
|
|
|
# The retract-continue is active, so each G0 until the next extrusion needs to continue retraction.
|
|
travel, f = self._getTravelMove(lines[line_number], current_pos)
|
|
travel_length = (current_pos - last_pos).length()
|
|
extra_retract = travel_length * extra_retraction_speed
|
|
new_e = (0 if relative_extrusion else current_e) - extra_retract
|
|
to_compensate += extra_retract
|
|
current_e -= extra_retract
|
|
lines[line_number] = self._travelMoveString(travel, f, new_e)
|
|
|
|
new_layer = "\n".join(lines)
|
|
data[layer_number] = new_layer
|
|
|
|
return data
|