Created
July 2, 2024 00:36
-
-
Save HB-Stratos/288777eaa019f8488e4804fa30b764e7 to your computer and use it in GitHub Desktop.
A gcode post processor to increase the extra restart distance based on travel time. currently quite messy, and does not account for acceleration of the printherad
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from dataclasses import dataclass | |
import dataclasses | |
from collections import namedtuple | |
import math | |
import re | |
#I apologize for the mess of camelcase and underscores | |
gcode_file_name = "Soldering Jig_0.2mm_LW-PLA_I3MEGA_1h37m.gcode" | |
gcode_file = open(gcode_file_name, "r") | |
#Globals: | |
@dataclass | |
class Globals: | |
is_metric:bool | |
is_movement_absolute:bool | |
is_extruder_absolute:bool | |
globals = Globals(None, None, None) | |
@dataclass | |
class Position: | |
x:float | |
y:float | |
z:float | |
@dataclass | |
class MoveData: | |
position:Position | |
velocity:float | |
acceleration:float | |
@dataclass | |
class CorrectionCurve: | |
time_points:list[float] | |
corr_points:list[float] | |
correctionCurve = CorrectionCurve( [0.0, 0.1, 0.5, 1.0], \ | |
[0.0, 0.5, 1.0, 9.0] ) | |
def bisection(array,value): #https://stackoverflow.com/a/41856629 | |
'''Given an ``array`` , and given a ``value`` , returns an index j such that ``value`` is between array[j] | |
and array[j+1]. ``array`` must be monotonic increasing. j=-1 or j=len(array) is returned | |
to indicate that ``value`` is out of range below and above respectively.''' | |
n = len(array) | |
if (value < array[0]): | |
return -1 | |
elif (value > array[n-1]): | |
return n | |
jl = 0# Initialize lower | |
ju = n-1# and upper limits. | |
while (ju-jl > 1):# If we are not yet done, | |
jm=(ju+jl) >> 1# compute a midpoint with a bitshift | |
if (value >= array[jm]): | |
jl=jm# and replace either the lower limit | |
else: | |
ju=jm# or the upper limit, as appropriate. | |
# Repeat until the test condition is satisfied. | |
if (value == array[0]):# edge cases at bottom | |
return 0 | |
elif (value == array[n-1]):# and top | |
return n-1 | |
else: | |
return jl | |
def lerp(a: float, b: float, t: float) -> float: #https://gist.github.com/laundmo/b224b1f4c8ef6ca5fe47e132c8deab56 | |
"""Linear interpolate on the scale given by a to b, using t as the point on that scale. | |
Examples | |
-------- | |
50 == lerp(0, 100, 0.5) | |
4.2 == lerp(1, 5, 0.8) | |
""" | |
return (1 - t) * a + t * b | |
def interpolate(curve:CorrectionCurve, travel_time:float) -> float: | |
idx = bisection(curve.time_points, travel_time) | |
if idx == 0: return curve.corr_points[0] | |
if idx == len(curve.time_points): return curve.corr_points[len(curve.time_points)-1] | |
return lerp(curve.corr_points[idx], curve.corr_points[idx+1], travel_time-curve.time_points[idx]) | |
def calcDistance (pos1:Position, pos2:Position) -> float: | |
return math.sqrt( (pos1.x - pos2.x)**2 + (pos1.y - pos2.y)**2 + (pos1.z - pos2.z)**2) | |
def calcSingleMoveTime (initialPos:Position, finalPos:Position, initialVelocity:float, acceleration:float) -> float: | |
distance = calcDistance(initialPos, finalPos) | |
if acceleration == 0: | |
time = distance / initialVelocity | |
else: # this is flawed, plain wrong at the moment | |
raise Exception("acceleration other than 0 is bad math") | |
time1 = -((math.sqrt(2*acceleration*distance + initialVelocity**2)+initialVelocity)/(acceleration)) | |
time2 = +((math.sqrt(2*acceleration*distance + initialVelocity**2)-initialVelocity)/(acceleration)) | |
time = max(time1, time2) | |
return time | |
def time_travel_move_list (travel_move_list:list[MoveData], globals:Globals) -> float: | |
if not globals.is_metric: raise Exception("this program currently can not work with imperial units in gcode (G20)") | |
if globals.is_extruder_absolute: raise Exception("this program currently can not work with absolute extruder values (G91)") | |
travel_time = 0 | |
previous_moveData:MoveData = None | |
for moveData in travel_move_list: | |
if previous_moveData: | |
travel_time += calcSingleMoveTime(previous_moveData.position, moveData.position, previous_moveData.velocity, 0) | |
previous_moveData = moveData | |
return travel_time | |
def grab_value_from_string(string:str, target:str): | |
if (target_pos := string.find(target)+1) >= 1: | |
return string.split(maxsplit=1)[0][1:] | |
#This needs some fixing, currently imples the target is passed with a leading space | |
previous_line:str = "" | |
has_retracted:bool = False | |
travel_move_list:list = [] | |
correction_dict:dict = {} | |
total_travel_time:float = 0 | |
last_feedrate:float = 0 | |
last_acceleration:float = 0 | |
last_position:Position = Position(None, None, None) #namedtuple("last_position", ["x", "y", "z"]) | |
for line_nr, line in enumerate(gcode_file, start=1): | |
for _ in (True,): # this merely exists so I can break; and still execute the last line | |
if line.startswith("\n"): break | |
if line.startswith(";"): break | |
if previous_line.startswith(";WIPE_"): previous_line_was_wipe = True; | |
else: previous_line_was_wipe = False | |
split_line = line.split(";")[0] #discard everything after comment | |
split_line = split_line.split() | |
for pos, element in enumerate(split_line): | |
if element.startswith(";"): | |
split_line = split_line[0:pos] | |
break | |
if split_line[0] == "G90": globals.is_movement_absolute = True; globals.is_extruder_absolute = True; break | |
if split_line[0] == "G91": globals.is_movement_absolute = False; globals.is_extruder_absolute = False; break | |
if split_line[0] == "G21": globals.is_metric = True; break | |
if split_line[0] == "G20": globals.is_metric = False; break | |
if split_line[0] == "M83": globals.is_extruder_absolute = False; break | |
if split_line[0] == "M204": | |
for element in split_line: | |
if element.startswith("S"): | |
last_acceleration = float(element[1:]) | |
break | |
if not split_line[0] == "G1": break | |
for element in split_line[1:]: | |
if element.startswith("X"): | |
last_position.x = float(element[1:]) | |
elif element.startswith("Y"): | |
last_position.y = float(element[1:]) | |
elif element.startswith("Z"): | |
last_position.z = float(element[1:]) | |
elif element.startswith("F"): | |
last_feedrate = float(element[1:]) | |
elif element.startswith("E"): | |
if element[1] == "-": | |
if not previous_line_was_wipe: has_retracted = True | |
continue | |
if has_retracted: | |
has_retracted = False | |
travel_time = time_travel_move_list(travel_move_list, globals) | |
travel_move_list.clear() | |
correction_amount = interpolate(correctionCurve, travel_time) | |
correction_dict[line_nr] = [correction_amount, line] | |
#if retracted and position is different or no positions present | |
if has_retracted and ((travel_move_list and not last_position == travel_move_list[-1].position) or not travel_move_list): | |
moveData = MoveData(dataclasses.replace(last_position), float(last_feedrate)/60, float(last_acceleration)) # specified in units of mm and s | |
travel_move_list.append(moveData) | |
previous_line = line | |
def write_lines_from_dict(lines_to_write_dict:dict, file_name:str): # https://ask.replit.com/t/writing-to-a-specific-line-in-at-txt-file/83327/8 | |
with open(file_name, "r") as file: # Open the file in read mode | |
lines = file.readlines() # Assign the file as a list to a variable | |
for line, content in lines_to_write_dict.items(): | |
lines[line-1] = content # Replace the proper line with the provided content | |
with open(file_name.replace(".gcode", "_w.gcode"), "w") as file: # Open the file in write mode | |
file.write("".join(lines)) # Write the modified content to the file | |
lines_to_write_dict:dict = {} | |
for line_nr, [correction_amount, line_content] in correction_dict.items(): | |
re_pattern = re.compile("(?<=E)[.|\d]*") | |
insert = float(re.search(re_pattern, line_content).group()) | |
insert += correction_amount | |
line_content = re.sub(re_pattern, str(insert), line_content ) | |
lines_to_write_dict[line_nr] = line_content | |
write_lines_from_dict(lines_to_write_dict, gcode_file_name) | |
print(line_nr) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment