From 0fef41c045d678daa0941eb678c2f566e7b29ce7 Mon Sep 17 00:00:00 2001 From: Matszwe02 <49353902+Matszwe02@users.noreply.github.com> Date: Sun, 9 Apr 2023 21:57:35 +0200 Subject: [PATCH 1/2] Fixes and enhancements - fixed backtransform not working in most cases - created one common settings json file - auto model offset detection - auto first layer height detection - auto skirt removal - exception when using arc moves - no need to center the model - normalised cone angle, folder naming and model naming - omitting start gcode when backtransforming Tested and working on PrusaSlicer and Cura --- .../Backtransformation_GCode_var_angle.py | 137 ++++++++++++++++-- .../Transformation_STL_var_angle.py | 27 ++-- Scripts for Variable Angle/settings.json | 16 ++ 3 files changed, 159 insertions(+), 21 deletions(-) create mode 100644 Scripts for Variable Angle/settings.json diff --git a/Scripts for Variable Angle/Backtransformation_GCode_var_angle.py b/Scripts for Variable Angle/Backtransformation_GCode_var_angle.py index 3cca849..05baa1a 100644 --- a/Scripts for Variable Angle/Backtransformation_GCode_var_angle.py +++ b/Scripts for Variable Angle/Backtransformation_GCode_var_angle.py @@ -1,17 +1,28 @@ import re import numpy as np import time +import json # ----------------------------------------------------------------------------------------- # Transformation Settings # ----------------------------------------------------------------------------------------- -FILE_NAME = 'tower_01_B.gcode' # filename including extension -FOLDER_NAME = 'gcodes/' # name of the subfolder in which the gcode is located -CONE_ANGLE = 16 # transformation angle -CONE_TYPE = 'outward' # type of the cone: 'inward' & 'outward' -FIRST_LAYER_HEIGHT = 0.2 # moves all the gcode up to this height. Use also for stacking -X_SHIFT = 110 # moves your gcode away from the origin into the center of the bed (usually bed size / 2) -Y_SHIFT = 90 +with open('settings.json', 'r') as f: + settings = json.load(f) + +FILE_NAME = settings['gcodeName'] # filename including extension +FOLDER_NAME = settings['gcodeFolder'] # name of the subfolder in which the gcode is located +FOLDER_NAME_BACKTRANSFORMED = settings['gcodeBacktransformedFolder'] +CONE_ANGLE = settings['coneAngle'] # transformation angle, negative for inward +CONE_TYPE = 'outward' +if CONE_ANGLE < 0: + CONE_ANGLE = -CONE_ANGLE + CONE_TYPE = 'inward' +FIRST_LAYER_HEIGHT = settings['firstLayerHeight'] # moves all the gcode up to this height. Use also for stacking +X_SHIFT = settings['xShift'] # moves your gcode away from the origin into the center of the bed (usually bed size / 2) +Y_SHIFT = settings['yShift'] +AUTO_SHIFT = settings['autoShift'] # determine x, y shift and layer height automatically +CUT_SKIRT = settings['cutSkirt'] # cut out 3d print skirt +USE_FIRST_LAYER_COMMAND = settings['useFirstLayerCommand'] # used for auto_shift and for start_gcode def insert_Z(row, z_value): @@ -98,7 +109,41 @@ def compute_angle_radial(x_old, y_old, x_new, y_new, inward_cone): return angle +def pretransform(data, x_shift, y_shift): + new_data = [] + pattern_X = r'X[-0-9]*[.]?[0-9]*' + pattern_Y = r'Y[-0-9]*[.]?[0-9]*' + pattern_Z = r'Z[-0-9]*[.]?[0-9]*' + pattern_E = r'E[-0-9]*[.]?[0-9]*' + pattern_G = r'\AG[0123] ' + + for row in data: + x_match = re.search(pattern_X, row) + y_match = re.search(pattern_Y, row) + z_match = re.search(pattern_Z, row) + g_match = re.search(pattern_G, row) + + if g_match is None: + new_data.append(row) + + else: + if row[1] == '2' or row[1] == '3': + raise Exception("Arc moves not supported!") + + if x_match is not None: + x_val = round(float(x_match.group(0).replace('X', '')) - x_shift) + row = re.sub(pattern_X, 'X' + str(x_val), row) + if y_match is not None: + y_val = round(float(y_match.group(0).replace('Y', '')) - y_shift) + row = re.sub(pattern_Y, 'Y' + str(y_val), row) + if z_match is not None: + z_val = float(z_match.group(0).replace('Z', '')) + row = re.sub(pattern_Z, 'Z' + str(z_val), row) + + new_data.append(row) + + return new_data def compute_U_values(angle_array): @@ -170,7 +215,7 @@ def backtransform_data_radial(data, cone_type, maximal_length, cone_angle_rad): pattern_Y = r'Y[-0-9]*[.]?[0-9]*' pattern_Z = r'Z[-0-9]*[.]?[0-9]*' pattern_E = r'E[-0-9]*[.]?[0-9]*' - pattern_G = r'\AG[1] ' + pattern_G = r'\AG[01] ' x_old, y_old = 0, 0 x_new, y_new = 0, 0 @@ -290,7 +335,7 @@ def translate_data(data, cone_type, translate_x, translate_y, z_desired, e_paral pattern_Y = r'Y[-0-9]*[.]?[0-9]*' pattern_Z = r'Z[-0-9]*[.]?[0-9]*' pattern_E = r'E[-0-9]*[.]?[0-9]*' - pattern_G = r'\AG[1] ' + pattern_G = r'\AG[0123] ' z_initialized = False u_val = 0.0 @@ -318,6 +363,9 @@ def translate_data(data, cone_type, translate_x, translate_y, z_desired, e_paral new_data.append(row) else: + if row[1] == '2' or row[1] == '3': + raise Exception("Arc moves not supported!") + if x_match is not None: x_val = round(float(x_match.group(0).replace('X', '')) + translate_x - (e_parallel * np.cos(u_val)) + (e_perpendicular * np.sin(u_val)), 3) row = re.sub(pattern_X, 'X' + str(x_val), row) @@ -366,13 +414,80 @@ def backtransform_file(path, cone_type, maximal_length, angle_comp, x_shift, y_s with open(path, 'r') as f_gcode: data = f_gcode.readlines() - data_bt = backtransform_data(data, cone_type, maximal_length, cone_angle_rad) + + if CUT_SKIRT: + try: # PrusaSlicer + index1 = data.index(';TYPE:Skirt/Brim\n') + 1 + index2 = data.index(';TYPE:External perimeter\n', index1) + data = data[0:index1] + data[index2:] + except: + pass + try: # Cura + index1 = data.index(';TYPE:SKIRT\n') + 1 + index2 = data.index(';TYPE:WALL-OUTER\n', index1) - 2 + data = data[0:index1] + data[index2:] + except: + pass + + if USE_FIRST_LAYER_COMMAND: + try: # Prusa slicer + first_layer_command_index = data.index(';LAYER_CHANGE\n') + 1 + start_print_command_index = data.index(';TYPE:External perimeter\n', first_layer_command_index) + 1 + except: + pass + try: # Cura + first_layer_command_index = data.index(';LAYER:0\n') + 1 + start_print_command_index = data.index(';TYPE:WALL-OUTER\n', first_layer_command_index) + 1 + except: + pass + + + if AUTO_SHIFT: + x_shift_new = '-' + y_shift_new = '-' + z_desired_new = '-' + if not USE_FIRST_LAYER_COMMAND: + raise Exception('useFirstLayerCommand must be ON with autoShift') + x_positions = [] + y_positions = [] + for i in data[start_print_command_index:start_print_command_index + 10]: + line = i.split() + if (line[0] == 'G0' or line[0] == 'G1'): + for index in range(line.__len__()): + if 'X' in line[index]: x_positions.append(float(line[index][1:])) + if 'Y' in line[index]: y_positions.append(float(line[index][1:])) + if 'Z' in line[index] and z_desired_new == '-': z_desired_new = float(line[index][1:]) + + if len(x_positions) * len(y_positions) == 0: + raise Exception("Exception: Couldn't find print position. Use manual xShift and yShift.") + + x_shift_new = round(sum(x_positions) / len(x_positions), 3) + y_shift_new = round(sum(y_positions) / len(y_positions), 3) + + if x_shift_new != '-' and y_shift_new != '-': + x_shift = x_shift_new + y_shift = y_shift_new + if z_desired_new != '-': z_desired = z_desired_new + print(f"X shift: {x_shift}, Y shift: {y_shift}, first layer height: {z_desired}") + + + if USE_FIRST_LAYER_COMMAND: + start_print_command_index = start_print_command_index + start_gcode = ''.join(data[0 : start_print_command_index]) + data = data[start_print_command_index :] + + data_bt = pretransform(data, x_shift, y_shift) + data_bt = backtransform_data(data_bt, cone_type, maximal_length, cone_angle_rad) data_bt_string = ''.join(data_bt) data_bt = [row + ' \n' for row in data_bt_string.split('\n')] data_bt = translate_data(data_bt, cone_type, x_shift, y_shift, z_desired, e_parallel, e_perpendicular) data_bt_string = ''.join(data_bt) + + if USE_FIRST_LAYER_COMMAND: + data_bt_string = start_gcode + data_bt_string + - path_write = re.sub(r'gcodes', 'gcodes_backtransformed', path) + path_write = re.sub(r'gcodes', FOLDER_NAME_BACKTRANSFORMED, path) path_write = re.sub(r'.gcode', '_bt_' + cone_type + '_' + angle_comp + '.gcode', path_write) print(path_write) with open(path_write, 'w+') as f_gcode_bt: diff --git a/Scripts for Variable Angle/Transformation_STL_var_angle.py b/Scripts for Variable Angle/Transformation_STL_var_angle.py index 09b16b2..723ea4d 100644 --- a/Scripts for Variable Angle/Transformation_STL_var_angle.py +++ b/Scripts for Variable Angle/Transformation_STL_var_angle.py @@ -1,18 +1,25 @@ import numpy as np from stl import mesh import time - +import json #----------------------------------------------------------------------------------------- # Transformation Settings #----------------------------------------------------------------------------------------- - -FILE_NAME = 'tower_01_-20' # Filename without extension -FOLDER_NAME_UNTRANSFORMED = 'stl/' -FOLDER_NAME_TRANSFORMED = 'stl_transformed/' # Make sure this folder exists -CONE_ANGLE = 16 # Transformation angle -REFINEMENT_ITERATIONS = 1 # refinement iterations of the stl. 2-3 is a good start for regular stls. If its already uniformaly fine, use 0 or 1. High number cause huge models and long script runtimes -TRANSFORMATION_TYPE = 'outward' # type of the cone: 'inward' & 'outward' +with open('settings.json', 'r') as f: + settings = json.load(f) + +FILE_NAME = settings['modelName'] # Filename including extension +if FILE_NAME[-4:] != '.stl': + raise Exception("Model not supported. Must be .stl") +FOLDER_NAME_UNTRANSFORMED = settings['modelFolder'] +FOLDER_NAME_TRANSFORMED = settings['modelTransformedFolder'] # Make sure this folder exists +CONE_ANGLE = settings["coneAngle"] # transformation angle, negative for inward +REFINEMENT_ITERATIONS = settings['refinement'] # refinement iterations of the stl. 2-3 is a good start for regular stls. If its already uniformaly fine, use 0 or 1. High number cause huge models and long script runtimes +CONE_TYPE = 'outward' +if CONE_ANGLE < 0: + CONE_ANGLE = -CONE_ANGLE + CONE_TYPE = 'inward' def transformation_kegel(points, cone_angle_rad, cone_type): @@ -105,7 +112,7 @@ def transformation_STL_file(path, cone_type, cone_angle_deg, nb_iterations): return my_mesh_transformed startzeit = time.time() -transformed_STL = transformation_STL_file(path=FOLDER_NAME_UNTRANSFORMED + FILE_NAME + '.stl', cone_type=TRANSFORMATION_TYPE, cone_angle_deg=CONE_ANGLE, nb_iterations=REFINEMENT_ITERATIONS) -transformed_STL.save(FOLDER_NAME_TRANSFORMED + FILE_NAME + '_' + TRANSFORMATION_TYPE + '_' + str(CONE_ANGLE) + 'deg_transformed.stl') +transformed_STL = transformation_STL_file(path=FOLDER_NAME_UNTRANSFORMED + FILE_NAME, cone_type=CONE_TYPE, cone_angle_deg=CONE_ANGLE, nb_iterations=REFINEMENT_ITERATIONS) +transformed_STL.save(FOLDER_NAME_TRANSFORMED + FILE_NAME + '_' + CONE_TYPE + '_' + str(CONE_ANGLE) + 'deg_transformed.stl') endzeit = time.time() print('Transformation time:', endzeit - startzeit) diff --git a/Scripts for Variable Angle/settings.json b/Scripts for Variable Angle/settings.json new file mode 100644 index 0000000..f49d442 --- /dev/null +++ b/Scripts for Variable Angle/settings.json @@ -0,0 +1,16 @@ +{ + "modelName" : "1.stl", + "gcodeName" : "1.gcode", + "modelFolder" : "./", + "gcodeFolder" : "./", + "modelTransformedFolder" : "./", + "gcodeBacktransformedFolder" : "./", + "coneAngle" : 20, + "refinement" : 1, + "firstLayerHeight" : 0.2, + "xShift" : 0, + "yShift" : 0, + "autoShift" : true, + "cutSkirt" : true, + "useFirstLayerCommand" : true +} \ No newline at end of file From 196cfddddfc8ae3265d685630bf3f8f100bedea1 Mon Sep 17 00:00:00 2001 From: Matszwe02 <49353902+Matszwe02@users.noreply.github.com> Date: Mon, 10 Apr 2023 09:23:01 +0200 Subject: [PATCH 2/2] Fixed serious rounding issues also better layer height detection for cura --- .../Backtransformation_GCode_var_angle.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Scripts for Variable Angle/Backtransformation_GCode_var_angle.py b/Scripts for Variable Angle/Backtransformation_GCode_var_angle.py index 05baa1a..b66f7d7 100644 --- a/Scripts for Variable Angle/Backtransformation_GCode_var_angle.py +++ b/Scripts for Variable Angle/Backtransformation_GCode_var_angle.py @@ -132,10 +132,10 @@ def pretransform(data, x_shift, y_shift): raise Exception("Arc moves not supported!") if x_match is not None: - x_val = round(float(x_match.group(0).replace('X', '')) - x_shift) + x_val = round(float(x_match.group(0).replace('X', '')) - x_shift, 3) row = re.sub(pattern_X, 'X' + str(x_val), row) if y_match is not None: - y_val = round(float(y_match.group(0).replace('Y', '')) - y_shift) + y_val = round(float(y_match.group(0).replace('Y', '')) - y_shift, 3) row = re.sub(pattern_Y, 'Y' + str(y_val), row) if z_match is not None: z_val = float(z_match.group(0).replace('Z', '')) @@ -424,7 +424,7 @@ def backtransform_file(path, cone_type, maximal_length, angle_comp, x_shift, y_s pass try: # Cura index1 = data.index(';TYPE:SKIRT\n') + 1 - index2 = data.index(';TYPE:WALL-OUTER\n', index1) - 2 + index2 = data.index(';TYPE:WALL-OUTER\n', index1) - 3 data = data[0:index1] + data[index2:] except: pass @@ -437,7 +437,7 @@ def backtransform_file(path, cone_type, maximal_length, angle_comp, x_shift, y_s pass try: # Cura first_layer_command_index = data.index(';LAYER:0\n') + 1 - start_print_command_index = data.index(';TYPE:WALL-OUTER\n', first_layer_command_index) + 1 + start_print_command_index = data.index(';TYPE:WALL-OUTER\n', first_layer_command_index) - 1 except: pass @@ -497,7 +497,6 @@ def backtransform_file(path, cone_type, maximal_length, angle_comp, x_shift, y_s return None starttime = time.time() -backtransform_file(path=FOLDER_NAME + FILE_NAME, cone_type=CONE_TYPE, maximal_length=0.5, angle_comp='radial', x_shift=X_SHIFT, y_shift=Y_SHIFT, - cone_angle_deg=CONE_ANGLE, z_desired=FIRST_LAYER_HEIGHT, e_parallel=0, e_perpendicular=0) +backtransform_file(path=FOLDER_NAME + FILE_NAME, cone_type=CONE_TYPE, maximal_length=0.5, angle_comp='radial', x_shift=X_SHIFT, y_shift=Y_SHIFT, cone_angle_deg=CONE_ANGLE, z_desired=FIRST_LAYER_HEIGHT, e_parallel=0, e_perpendicular=0) endtime = time.time() print('GCode translated, time used:', endtime - starttime) \ No newline at end of file