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:
- Riconosce i tracciati di stampa (istruzioni
G1
) relativi al solo primo strato di stampa, discernendo tra movimenti di estrusione e non estrusione. - Disegna i tracciati rilevati in un file PNG, esportato nella medesima cartella contenente lo script.
Utilizzo #
Impostazione dello script #
- Nello script Python modificare il valore delle variabili nelle sezioni indicate dai commenti
LAYER CHANGE FLAG
,BED
,GRID
, eOUTPUT
, 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 valore0
e alla variabilefirst_layer_tag
il valore1
.
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:
- di salvare lo script Python con il nome
gcode-plotter.py
, - 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 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')