mirror of
https://github.com/TengerTechnologies/Bricklayers.git
synced 2025-02-07 13:24:45 +00:00
144 lines
7.0 KiB
Python
144 lines
7.0 KiB
Python
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
# Copyright (c) [2025] [Roman Tenger]
|
|
import re
|
|
import sys
|
|
import logging
|
|
import os
|
|
import argparse
|
|
|
|
# Get the directory where the script is located
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
# Configure logging to save in the script's directory
|
|
log_file_path = os.path.join(script_dir, "z_shift_log.txt")
|
|
logging.basicConfig(
|
|
filename=log_file_path,
|
|
filemode="w",
|
|
level=logging.INFO,
|
|
format="%(asctime)s - %(message)s"
|
|
)
|
|
|
|
def process_gcode(input_file, layer_height, extrusion_multiplier):
|
|
current_layer = 0
|
|
current_z = 0.0
|
|
perimeter_type = None
|
|
perimeter_block_count = 0
|
|
inside_perimeter_block = False
|
|
z_shift = layer_height * 0.5
|
|
logging.info("Starting G-code processing")
|
|
logging.info(f"Input file: {input_file}")
|
|
logging.info(f"Z-shift: {z_shift} mm, Layer height: {layer_height} mm")
|
|
|
|
# Read the input G-code
|
|
with open(input_file, 'r') as infile:
|
|
lines = infile.readlines()
|
|
|
|
# Identify the total number of layers by looking for `G1 Z` commands
|
|
total_layers = sum(1 for line in lines if line.startswith("G1 Z"))
|
|
|
|
# Process the G-code
|
|
modified_lines = []
|
|
for line in lines:
|
|
# Detect layer changes
|
|
if line.startswith("G1 Z"):
|
|
z_match = re.search(r'Z([-\d.]+)', line)
|
|
if z_match:
|
|
current_z = float(z_match.group(1))
|
|
current_layer = int(current_z / layer_height)
|
|
|
|
perimeter_block_count = 0 # Reset block counter for new layer
|
|
logging.info(f"Layer {current_layer} detected at Z={current_z:.3f}")
|
|
modified_lines.append(line)
|
|
continue
|
|
|
|
# Detect perimeter types from PrusaSlicer comments
|
|
if ";TYPE:External perimeter" in line or ";TYPE:Outer wall" in line:
|
|
perimeter_type = "external"
|
|
inside_perimeter_block = False
|
|
logging.info(f"External perimeter detected at layer {current_layer}")
|
|
elif ";TYPE:Perimeter" in line or ";TYPE:Inner wall" in line:
|
|
perimeter_type = "internal"
|
|
inside_perimeter_block = False
|
|
logging.info(f"Internal perimeter block started at layer {current_layer}")
|
|
elif ";TYPE:" in line: # Reset for other types
|
|
perimeter_type = None
|
|
inside_perimeter_block = False
|
|
|
|
# Group lines into perimeter blocks
|
|
if perimeter_type == "internal" and line.startswith("G1") and "X" in line and "Y" in line and "E" in line:
|
|
# Start a new perimeter block if not already inside one
|
|
if not inside_perimeter_block:
|
|
perimeter_block_count += 1
|
|
inside_perimeter_block = True
|
|
logging.info(f"Perimeter block #{perimeter_block_count} detected at layer {current_layer}")
|
|
|
|
# Insert the corresponding Z height for this block
|
|
is_shifted = False # Flag for whether this block is Z-shifted
|
|
if perimeter_block_count % 2 == 1: # Apply Z-shift to odd-numbered blocks
|
|
adjusted_z = current_z + z_shift
|
|
logging.info(f"Inserting G1 Z{adjusted_z:.3f} for shifted perimeter block #{perimeter_block_count}")
|
|
modified_lines.append(f"G1 Z{adjusted_z:.3f} ; Shifted Z for block #{perimeter_block_count}\n")
|
|
is_shifted = True
|
|
else: # Reset to the true layer height for even-numbered blocks
|
|
logging.info(f"Inserting G1 Z{current_z:.3f} for non-shifted perimeter block #{perimeter_block_count}")
|
|
modified_lines.append(f"G1 Z{current_z:.3f} ; Reset Z for block #{perimeter_block_count}\n")
|
|
|
|
# Adjust extrusion (`E` values) for shifted blocks on the first and last layer
|
|
if is_shifted:
|
|
e_match = re.search(r'E([-\d.]+)', line)
|
|
if e_match:
|
|
e_value = float(e_match.group(1))
|
|
if current_layer == 0: # First layer
|
|
new_e_value = e_value * 1.5
|
|
logging.info(f"Multiplying E value by 1.5 on first layer (shifted block): {e_value:.5f} -> {new_e_value:.5f}")
|
|
line = re.sub(r'E[-\d.]+', f'E{new_e_value:.5f}', line).strip()
|
|
line += f" ; Adjusted E for first layer, block #{perimeter_block_count}\n"
|
|
elif current_layer == total_layers - 1: # Last layer
|
|
new_e_value = e_value * 0.5
|
|
logging.info(f"Multiplying E value by 0.5 on last layer (shifted block): {e_value:.5f} -> {new_e_value:.5f}")
|
|
line = re.sub(r'E[-\d.]+', f'E{new_e_value:.5f}', line).strip()
|
|
line += f" ; Adjusted E for last layer, block #{perimeter_block_count}\n"
|
|
else:
|
|
new_e_value = e_value * extrusion_multiplier
|
|
logging.info(f"Multiplying E value by extrusionMultiplier")
|
|
line = re.sub(r'E[-\d.]+', f'E{new_e_value:.5f}', line).strip()
|
|
line += f" ; Adjusted E for extrusionMultiplier, block #{perimeter_block_count}\n"
|
|
|
|
elif perimeter_type == "internal" and line.startswith("G1") and "X" in line and "Y" in line and "F" in line: # End of perimeter block
|
|
inside_perimeter_block = False
|
|
|
|
modified_lines.append(line)
|
|
|
|
# Overwrite the input file with the modified G-code
|
|
with open(input_file, 'w') as outfile:
|
|
outfile.writelines(modified_lines)
|
|
|
|
logging.info("G-code processing completed")
|
|
logging.info(f"Log file saved at {log_file_path}")
|
|
|
|
# Main execution
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Post-process G-code for Z-shifting and extrusion adjustments.")
|
|
parser.add_argument("input_file", help="Path to the input G-code file")
|
|
parser.add_argument("-layerHeight", type=float, default=0.2, help="Layer height in mm (default: 0.2mm)")
|
|
parser.add_argument("-extrusionMultiplier", type=float, default=1, help="Extrusion multiplier for first layer (default: 1.5x)")
|
|
args = parser.parse_args()
|
|
|
|
process_gcode(
|
|
input_file=args.input_file,
|
|
layer_height=args.layerHeight,
|
|
extrusion_multiplier=args.extrusionMultiplier,
|
|
)
|