Salta al contenuto principale
G-code 2D Plotter
  1. Blogs/

G-code 2D Plotter

·1279 parole·7 minuti
Stampa 3D Stampa 3D Python PrusaSlicer G-Code
Indice dei contenuti

Introduzione
#

Lo script Python qui fornito consente di rappresentare graficamente il PRIMO STRATO di un processo di Stampa 3D (basato su tecnologia Material Extrusion) attraverso l’elaborazione delle istruzioni G-code create tramite un software di slicing.


Funzionamento
#

Lo script:

  1. Riconosce i tracciati di stampa (istruzioni G1) relativi al solo primo strato di stampa, discernendo tra movimenti di estrusione e non estrusione.
  2. Disegna i tracciati rilevati in un file PNG, esportato nella medesima cartella contenente lo script.

Utilizzo
#

Impostazione dello script
#

  1. Nello script Python modificare il valore delle variabili nelle sezioni indicate dai commenti LAYER CHANGE FLAG, BED, GRID, e OUTPUT, per allinearne i valori:
    • Alle dimensioni del proprio piano di stampa.
    • Alla forma dei commenti inseriti dallo slicer per la numerazione dei layer (per es. ;layer 1, ;layer 2, etc.)
    • Alle proprie esigenze di elaborazione dei tracciati (per es. il flip in X/Y e la rotazione.)
    • Alle proprie esigenze estetiche (per es. il colore dei tracciati, il passo della griglia, ecc.)

Impostazione dello slicer
#

Accertarsi che il software di slicing inserisca nel G-code i commenti relativi alla numerazione degli strati (layer). Per riconoscere il primo strato questo script legge i commenti presenti nel formato ; layer X, ove X è un numero con un valore che va da 1 a salire.

PRUSA SLICER
#

  • Aggiungere il comando ; layer [layer_num] nel campo Before layer change G-code dello slicer.
  • Nello script Python assegnare alla variabile first_layer_tag il valore 0 e alla variabile first_layer_tag il valore 1.

SIMPLIFY 3D
#

La versione 4.12. di Simplify 3D inserisce nativamente G-Code i commenti ; layer 1, ; layer 2 e successivi.


Avvio dello script
#

Immaginando:

  1. di salvare lo script Python con il nome gcode-plotter.py,
  2. che il file Gcode da processare sia denominato print.gcode,

lanciare da Terminale il comando:

py gcode-plotter.py print.gcode 

Lo script esporterà una immagine in formato .png del piano di lavoro e delle tracce del primo strato.

Esempio di rappresentazione grafica bidimensionale del primo strato di un processo di stampa 3D

Esempio di esportazione grafica del piano di lavoro e del primo strato del processo di stampa.


Il codice
#

Il codice seguente:

  • È stato verificato come funzionante con G-code prodotto da PrusaSlicer e Simplify 3D.
  • Potrebbe non essere immediatamente funzionante e/o necessitare di modifiche in relazione alla natura del G-code trattato.
  • È distribuito con licenza Creative Commons CC-BY.
# GCODE FIRST LAYER 2D PLOTTER" - Ver. 0.1
# This script plot the FIRST layer of a FDM 3D print process on a bidimensional plane,
# discerning through extrusion and non extrusion movements, exporting an image of the bed plane.
#
# You can use this script as a post-processing script in the slicer of your choiche.
#
# USAGE:
# Set the LAYER CHANGE FLAG, BED, GRID and OUTPUT variables in order to match your Gcode Output,
# the print bed size and the aesthetic output of your choiche.
# Then Link this script to your G-code creator.
#
# SIMPLIFY 3D usage:
#   Put the command «py C://*script-dir*/gcode-jager.py "[output_filepath]"»
#   inside the «Additional terminal command for post processing», changing the *script-dir" 
#   to the actual directory that contain the script.
#
# PRUSA SLICER usage:
#   - Add the «; layer [layer_num]» script inside the "layer change» field".
#   - In the read_gcode function change «layer 1» in «layer 0» and «layer 2» in «layer 1» 
#   - Call the python script inside «Post production script field»
#
# Please note that this script:
#   - Do not recognize Retractions (negative E) in order to strip the relative G1 commands from list.
#   - Do not iterate layer change recognition in order to print a layer that is not the first.
####################################################################################################

# Coded by Marco Papi - www.marcopapi.it
# License: Creative Commons CC-BY


import re
import sys
import os.path
from PIL import Image, ImageDraw, ImageFont

# GET ARGUMENTS
###############
source_gcode = sys.argv[1]

# GET PATH
##########
script_dir = os.path.dirname(os.path.realpath(__file__))

global coord
coord = []

# DEFINE LAYER CHANGE FLAGS
first_layer_tag = "layer 1"
second_layer_tag ="layer 2"

# DEFINE BED
############
# Define Bed resolution (DPI) (setting to 25.4 equalize bed millimeters and pixel size)
bed_resolution = 25.4
# Define Bed size (mm)
bed_lenght_mm = 800
bed_width_mm = 400

# DEFINE GRID
#############
# Grid Size (mm)
grid_size_mm = 50
# Grid Thickness (mm)
grid_thickness_mm = 1.5
# Grid Color (rgb)
grid_col = 80,80,80
# Grid Perimeter Thickness (mm)
perimeter_thickness_mm = 4
# Grid Perimeter Color (RGB)
perimeter_col = 255,255,255

# DEFINE OUTPUT
# Gcode Flip X or Y
flip_x = True
flip_y = False
# Image rotation (degree)
rotation_angle = 0
# Print movements color (RGB)
print_mov_col = 0,255,255,0
travel_mov_col = 255,0,255,255
# Start point radius and color
el_radius_mm = 4
el_col = 255,255,0,255


# CREATE THE BED IMAGE
######################
def create_bed(bed_lenght_mm,bed_width_mm,grid_size_mm,grid_thickness_mm,grid_col,perimeter_thickness_mm,perimeter_col):
    global bed_img
    # Calculate bed size in px
    bed_lenght_px = round((bed_lenght_mm / 25.4) * bed_resolution)
    bed_width_px = round((bed_width_mm / 25.4) * bed_resolution)
    # Create the bed image
    bed_img = Image.new('RGB', (bed_lenght_px,bed_width_px), color=0)
    draw = ImageDraw.Draw(bed_img)
    # Define grid
    grid_coord_x = []
    grid_coord_y = []
    grid_thickness_px = round((grid_thickness_mm / 25.4) * bed_resolution)
    # Calculate grid steps in px
    for f_x in range(0, bed_lenght_mm, grid_size_mm):
        grid_x = round((f_x / 25.4) * bed_resolution)
        grid_coord_x.append(grid_x)
    for f_y in range(0, bed_width_mm, grid_size_mm):
        grid_y = round((f_y / 25.4) * bed_resolution)
        grid_coord_y.append(grid_y)
    # Getting lists length
    grid_coord_x_length = len(grid_coord_x)
    grid_coord_y_length = len(grid_coord_y) 
    a = 1
    b = 1
    # Iterating grid list x
    while a < grid_coord_x_length: 
        grid_start_x = grid_coord_x[a]
        # Draw grid x
        draw.line((grid_start_x,0,grid_start_x,bed_width_px), fill=(grid_col), width=grid_thickness_px)
        a += 1
    # Iterating grid list y
    while b < grid_coord_y_length: 
        grid_start_y = grid_coord_y[b]
        # Draw grid y
        draw.line((0,grid_start_y,bed_lenght_px,grid_start_y), fill=(grid_col), width=grid_thickness_px)
        b += 1
    # Draw perimeters
    perimeter_thickness_px = round((perimeter_thickness_mm / 25.4) * bed_resolution)
    draw.line((0,0,bed_lenght_px,0), fill=(perimeter_col), width=perimeter_thickness_px)
    draw.line((bed_lenght_px,0,bed_lenght_px,bed_width_px), fill=(perimeter_col), width=perimeter_thickness_px)
    draw.line((bed_lenght_px,bed_width_px,0,bed_width_px), fill=(perimeter_col), width=perimeter_thickness_px)
    draw.line((0,bed_width_px,0,0), fill=(perimeter_col), width=perimeter_thickness_px)
    # Return
    return bed_img

# READ THE GCODE
################
def read_gcode(flip_x, flip_y, first_layer_tag, second_layer_tag):
    global bed_resolution
    with open(os.path.join(script_dir,source_gcode)) as gcode:
        layer1 = False
        layer2 = False
        extrusion = False
        for line in gcode:
            # Detect first layer movements
            if first_layer_tag in line:
                layer1 = True
            elif second_layer_tag in line:
                layer2 = True
            # Detect extrusion movements
            if 'E' in line:
                extrusion = True
            elif 'E' not in line:
                extrusion = False
            if layer1 == True and layer2 == False:
                # Extract coordinates
                line = line.strip()
                position = re.findall(r'[XY].?\d+.\d+', line)
                if position:
                    x_position = position[0].replace('X', '')
                    y_position = position[1].replace('Y', '')
                    x_position = float(x_position)
                    y_position = float(y_position)
                    # Recalculate coordinates by bed resolution
                    x_position = (x_position / 25.4) * bed_resolution
                    y_position = (y_position / 25.4) * bed_resolution
                    # Flip y axis
                    if flip_x:
                        x_position = bed_img.size[0] - x_position
                    # Flip y axis
                    elif flip_y:
                        y_position = bed_img.size[1] - y_position
                    # Populate the coordinates list
                    coord.append([x_position, y_position, extrusion])

# DRAW THE GCODE
################
def draw_gcode(print_mov_col,travel_mov_col,el_radius_mm,el_col):
    global bed_resolution
    line_thickness_mm = 0.4
    line_thickness_px = round((line_thickness_mm / 25.4) * bed_resolution)
    # Draw start point
    el_radius_px = (el_radius_mm / 25.4) * bed_resolution
    draw = ImageDraw.Draw(bed_img)
    draw.ellipse((coord[0][0]-el_radius_px,coord[0][1]-el_radius_px,coord[0][0]+el_radius_px,coord[0][1]+el_radius_px), fill=(el_col))
    # Getting list length
    coord_length = len(coord) 
    a = 0
    b = 1
    # Iterating list
    while a <= coord_length and b < coord_length: 
        start_x_position = coord[a][0]
        start_y_position = coord[a][1]
        end_x_position = coord[b][0]
        end_y_position = coord[b][1]
        a += 1
        b += 1
        if coord[a][2]:
            # Draw extrusion movement
            draw.line((start_x_position,start_y_position,end_x_position,end_y_position), fill=(print_mov_col), width = line_thickness_px)
        else:
            # Draw non-extrusion movement
            draw.line((start_x_position,start_y_position,end_x_position,end_y_position), fill=(travel_mov_col), width = line_thickness_px)
        
# RESIZE IMAGE
##############
def rotate_image(rotation_angle):
    global bed_img
    bed_img = bed_img.rotate(rotation_angle)
    # Return
    return bed_img

# ADD TEXT
##########
def add_text(text):
    global bed_img
    draw = ImageDraw.Draw(bed_img)
    font = ImageFont.load_default()
    text_shift_mm = 10
    text_shift_px = round((text_shift_mm / 25.4) * bed_resolution)
    draw.text((text_shift_px,text_shift_px), text, font=font)
    # Return
    return bed_img

# VIEW AND SAVE IMAGE
#####################
def save_image(bed_img_path,bed_img_name):
    # Save the bed image in the GCode folder
    bed_img.save(os.path.join(bed_img_path,bed_img_name))   
    bed_img.show()

# LAUNCH FUNCTIONS
##################
create_bed(bed_lenght_mm,bed_width_mm,grid_size_mm,grid_thickness_mm,grid_col,perimeter_thickness_mm,perimeter_col)
read_gcode(flip_x,flip_y,first_layer_tag,second_layer_tag)
draw_gcode(print_mov_col,travel_mov_col,el_radius_mm,el_col)
rotate_image(rotation_angle)
add_text(source_gcode)
save_image(script_dir,'bed.png')
Marco Papi
Autore
Marco Papi