Skip to content

AI PORTING for BambuStudio-PrusaSlicer-OrcaSlicer #125

@tmnaturalequilibrium-design

Description

Made with AI.. but AI isn't so intelligent..... :

#!/usr/bin/env python3

PenColorizer.py - Adapted for Orca Slicer / Bambu Studio post-processing

Adds pen changing & offset drawing logic for multi-pen plotting on 3D printer

import re
import sys
import os

class PenColorizer:
def init(self):
# These were your settings — you can make them command-line args later if needed
self.penstartx = 28.0
self.penstarty = 238.0 # note: this was called FirstPenZPosition, but used as Y!
self.xoffset = 36.1
self.yoffset = 45.8
self.zoffset = 3.2
self.extra_retraction = 5.5
self.interlace = True # only draw every second "color"/tool

    self.last_z = 0.2

def get_pen(self, pen):
    penside = pen % 2
    penoffset = float(pen // 2) * 68.0

    if penside == 0:
        return [
            f";Get pen {pen}",
            "G0 F5000 ; set speed fast",
            f"G0 X{self.penstartx + penoffset:.3f} Y{self.penstarty - 152.0:.3f} ;go under pen",
            "G0 F2000 ; set speed slow",
            f"G0 X{self.penstartx + penoffset:.3f} Y{self.penstarty:.3f} ; lift pen",
            f"G0 X{self.penstartx + 20.5 + penoffset:.3f} Y{self.penstarty:.3f} ; move pen right",
            "G0 F5000 ; set speed fast",
            f"G0 X{self.penstartx + 20.5 + penoffset:.3f} Y{self.penstarty - 152.0:.3f} ; lower pen"
        ]
    else:
        return [
            f";Get pen {pen}",
            "G0 F5000 ; set speed fast",
            f"G0 X{self.penstartx + 41.0 + penoffset:.3f} Y{self.penstarty - 152.0:.3f} ;go under pen",
            "G0 F2000 ; set speed slow",
            f"G0 X{self.penstartx + 41.0 + penoffset:.3f} Y{self.penstarty:.3f} ; lift pen",
            f"G0 X{self.penstartx + 20.5 + penoffset:.3f} Y{self.penstarty:.3f} ; move pen right",
            "G0 F5000 ; set speed fast",
            f"G0 X{self.penstartx + 20.5 + penoffset:.3f} Y{self.penstarty - 152.0:.3f} ; lower pen"
        ]

def put_pen(self, pen):
    penside = pen % 2
    penoffset = float(pen // 2) * 68.0

    if penside == 0:
        return [
            f";put pen {pen}",
            "G0 F5000 ; set speed fast",
            f"G0 X{self.penstartx + 20.5 + penoffset:.3f} Y{self.penstarty - 152.0:.3f} ; go under pen",
            f"G0 X{self.penstartx + 20.5 + penoffset:.3f} Y{self.penstarty:.3f} ; lift pen",
            "G0 F2000 ; set speed slow",
            f"G0 X{self.penstartx + penoffset:.3f} Y{self.penstarty:.3f} ; move pen left",
            f"G0 X{self.penstartx + penoffset:.3f} Y{self.penstarty - 152.0:.3f} ; lower pen",
            "G0 F5000 ; set speed fast",
            ""
        ]
    else:
        return [
            f";put pen {pen}",
            "G0 F5000 ; set speed fast",
            f"G0 X{self.penstartx + 20.5 + penoffset:.3f} Y{self.penstarty - 152.0:.3f} ; go under pen",
            f"G0 X{self.penstartx + 20.5 + penoffset:.3f} Y{self.penstarty:.3f} ; lift pen",
            "G0 F2000 ; set speed slow",
            f"G0 X{self.penstartx + 41.0 + penoffset:.3f} Y{self.penstarty:.3f} ; move pen left",
            f"G0 X{self.penstartx + 41.0 + penoffset:.3f} Y{self.penstarty - 152.0:.3f} ; lower pen",
            "G0 F5000 ; set speed fast",
            ""
        ]

def offset_gcode(self, line, extra_z=0.0):
    if not (line.startswith('G0') or line.startswith('G1')):
        return line

    parts = []
    cmd = line.split(' ', 1)[0]
    parts.append(cmd)

    coords = re.findall(r'([XYZF])(-?\d*\.?\d+)', line)

    f_found = False
    for axis, value in coords:
        val = float(value)
        if axis == 'F':
            parts.append(f"F3600")  # hard-coded as in original
            f_found = True
        elif axis == 'X':
            parts.append(f"X{val + self.xoffset:.3f}")
        elif axis == 'Y':
            parts.append(f"Y{val + self.yoffset:.3f}")
        elif axis == 'Z':
            parts.append(f"Z{val + self.zoffset + extra_z:.3f}")

    if not f_found and 'F' in line:  # fallback
        parts.append("F3600")

    return ' '.join(parts)

def lift_gcode(self, line, extra_z=0.0):
    if not (line.startswith('G0') or line.startswith('G1')):
        return line

    parts = []
    cmd = line.split(' ', 1)[0]
    parts.append(cmd)

    coords = re.findall(r'([XYZF])(-?\d*\.?\d+)', line)

    for axis, value in coords:
        val = float(value)
        if axis == 'F':
            parts.append(f"F{val:.0f}")
        elif axis == 'X':
            parts.append(f"X{val:.3f}")
        elif axis == 'Y':
            parts.append(f"Y{val:.3f}")
        elif axis == 'Z':
            parts.append(f"Z{val + extra_z:.3f}")

    return ' '.join(parts)

def get_first_position_line(self, draw_block):
    for ln in draw_block:
        if 'G0' in ln or 'G1' in ln:
            if 'X' in ln and 'Y' in ln and 'Z' in ln:
                return ln
    return None

def add_explicit_z(self, lines):
    new_lines = []
    for line in lines:
        if ('G0' in line or 'G1' in line) and 'Z' not in line:
            line += f" Z{self.last_z:.3f}"
        elif 'Z' in line:
            m = re.search(r'Z(-?\d*\.?\d+)', line)
            if m:
                self.last_z = float(m.group(1))
        new_lines.append(line)
    return new_lines

def process(self, gcode_text):
    data = gcode_text.splitlines(keepends=True)
    # We'll process in-place; collect modified lines
    output = []

    layer_num = -1
    cur_tool = 0
    last_x, last_y, last_z = 100.0, 100.0, 0.3
    is_layer = False
    is_prime_tower = False
    is_skirt_support_fill = False

    draw_buffers = [[] for _ in range(8)]  # per "tool"/pen

    i = 0
    while i < len(data):
        line = data[i].rstrip('\r\n')
        orig_line = line

        if line.startswith(';LAYER:'):
            layer_num += 1
            is_layer = True
            output.append(orig_line)
            i += 1
            continue

        if ';layer_height =' in line:
            try:
                lh = float(line.split('=')[1].strip())
                # you can use lh if needed
            except:
                pass
            output.append(orig_line)
            i += 1
            continue

        if ';TYPE:' in line:
            if 'PRIME-TOWER' in line:
                is_prime_tower = True
                is_skirt_support_fill = False
            elif any(t in line for t in ['SKIRT', 'SUPPORT', 'SUPPORT-INTERFACE', 'FILL']):
                is_skirt_support_fill = True
                is_prime_tower = False
            else:
                is_skirt_support_fill = False
                is_prime_tower = False
            output.append(orig_line)
            i += 1
            continue

        draw_this = (cur_tool >= 0 and
                     not is_skirt_support_fill and
                     not is_prime_tower and
                     ((layer_num + cur_tool) % 2 == 0 or not self.interlace))

        if ('G0' in line or 'G1' in line):
            # update last known pos
            if 'X' in line:
                m = re.search(r'X(-?\d*\.?\d+)', line)
                if m: last_x = float(m.group(1))
            if 'Y' in line:
                m = re.search(r'Y(-?\d*\.?\d+)', line)
                if m: last_y = float(m.group(1))
            if 'Z' in line:
                m = re.search(r'Z(-?\d*\.?\d+)', line)
                if m: last_z = float(m.group(1))

            e_match = re.search(r'E(-?\d*\.?\d+)', line)
            if e_match:
                e_val = float(e_match.group(1))
                if e_val < 0:
                    # retract → assume z-hop possible
                    extra_z_offset = 3.0
                else:
                    extra_z_offset = 0.0
            else:
                extra_z_offset = 0.0

            if draw_this:
                draw_buffers[cur_tool].append(self.offset_gcode(line, extra_z_offset))
            else:
                output.append(orig_line)

        elif line.startswith('T'):
            try:
                cur_tool = int(line[1:]) - 1
            except:
                cur_tool = 0
            output.append(';' + orig_line)  # comment out tool change
            i += 1
            continue

        elif any(t in line for t in ['T1','T2','T3','T4','T5','T6','T7','T8']):
            output.append(';' + orig_line)
            i += 1
            continue

        # Skip heating in object layers if desired
        elif is_layer and any(cmd in line for cmd in ['M104','M109','M105']):
            # optionally comment or remove
            pass
        else:
            output.append(orig_line)

        i += 1

    # Now insert drawing blocks per layer (simplified – you may need to adjust insertion point)
    # For simplicity we append at end of each ;LAYER block, but ideally insert before next layer or after inner walls
    # This is a simplification – test & adjust insertion logic

    final_output = []
    in_layer = False
    for ln in output:
        final_output.append(ln)
        if ln.startswith(';LAYER:') and layer_num >= 0:
            in_layer = True
            # retract before drawing
            final_output.append(f"G1 F1500 E-{self.extra_retraction}\n")

            for pen in range(8):
                block = draw_buffers[pen]
                if len(block) < 2:  # almost empty
                    continue

                first_pos = self.get_first_position_line(block)
                if first_pos:
                    final_output.append(self.lift_gcode(first_pos, 3.0) + " ; lower pen from above first spot\n")
                final_output.extend(self.get_pen(pen))
                final_output.extend(block)
                if block:
                    final_output.append(self.lift_gcode(block[-1], 3.0) + " ; raise pen after last spot\n")
                final_output.extend(self.put_pen(pen))

            # return after drawing
            final_output.append(f"G0 X{last_x:.3f} Y{last_y:.3f} Z{last_z + 2.0:.3f}\n")
            final_output.append(f"G0 X{last_x:.3f} Y{last_y:.3f} Z{last_z:.3f} ; return to last pos after drawing\n")
            final_output.append(f"G1 F1500 E{self.extra_retraction}\n\n")

    return ''.join(final_output)

if name == "main":
if len(sys.argv) < 2:
print("Usage: python PenColorizer.py <gcode_file>")
print(" The script modifies the file in place.")
sys.exit(1)

input_path = sys.argv[1]

if not os.path.isfile(input_path):
    print(f"File not found: {input_path}")
    sys.exit(1)

with open(input_path, 'r', encoding='utf-8') as f:
    gcode = f.read()

processor = PenColorizer()
modified = processor.process(gcode)

with open(input_path, 'w', encoding='utf-8') as f:
    f.write(modified)

print(f"Processed and overwritten: {input_path}")

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions