Preferably use 0.6 nozzle

---

Bambu Studio / Orca Slicer settings:

Global, Yume_1, Yume_4:
 
- layer height: 0.28mm
- line width sparse infill: 0.9mm
- line width walls: 0.65mm
- wall loops: 0
- top shell layers: 0
- bottom shell layers: 0
- sparse infill density: 43%
- Infill pattern: Grid
- Infill anchor: 1000 (unlimited)
- Max infill anchor: 1000 (unlimited)
- Infill direction: 0°
- infill/wall overlap: 90%
- Detect narrow internal solid infill: no
- Speed sparse infill: 18 mm/s
- Reduce infill retraction: no
- Brim type: Outer, 9mm width, 0mm gap
- Enable support: yes, normal, auto, 1°, make a 100x150 support enforcer

Yume_2:

- Walls: 2
- Sparse infill density: 100%
- Sparse infill pattern: rectilinear
- Infill direction: 45°


Yume_3:

- Line width sparse infill: 0.6mm
- Sparse infill density: 33%

Filament:

- Retraction: 2.4mm
- Wipe: no
- Z hop: 1.2mm, Normal type
- Max volumetric speed: 4.8 mm^3/s 


---

Video 1 (conveyor belt printer): https://www.youtube.com/shorts/xDno1riw640

Video 2 (normal printer): https://www.youtube.com/shorts/2vDZeYJUbeY

How to design and slice: https://www.youtube.com/watch?v=nnkCee3quc0

Website: https://www.riverfamily.art/

---

LICENSE: Public Domain (https://creativecommons.org/share-your-work/public-domain/cc0/)

Also applies to gcodes, slicing process, pictures, videos and names


---

Python script to transform gcode from cartesian to belt printer:

import math

def trova_min_y_primo_layer(nome_file_input, label_layer_1='; layer num/total_layer_count: 1/',
                            label_layer_2='; layer num/total_layer_count: 2/'):
    """
    Esegue un passaggio sul file G-code per trovare il valore minimo di Y 
    tra le righe G0/G1 del PRIMO layer (fra label_layer_1 e label_layer_2).
    Restituisce 0.0 se non trova alcuna coordinata Y.
    """
    min_y = None
    in_primo_layer = False
    
    with open(nome_file_input, 'r') as f:
        for line in f:
            # Se troviamo l'indicazione di inizio primo layer
            if line.startswith(label_layer_1):
                in_primo_layer = True
                continue
            
            # Se troviamo l'indicazione di inizio secondo layer, smettiamo di cercare
            if line.startswith(label_layer_2):
                break
            
            # Se siamo nel primo layer e la linea è G0/G1, cerchiamo una coordinata Y
            if in_primo_layer and (line.startswith('G0') or line.startswith('G1')):
                parti = line.split()
                for p in parti:
                    if p.startswith('Y'):
                        y_val = float(p[1:])
                        if (min_y is None) or (y_val < min_y):
                            min_y = y_val
    
    return min_y if min_y is not None else 0.0


def modifica_gcode4(nome_file_input,
                    nome_file_output,
                    hstrato,
                    fattore_estrusione):
    """
    Modifica un file G-code applicando:
      - Calcolo automatico di y_offset_iniziale = Y minima del primo layer
      - Traslazione e inversione asse Y (y = -(y - y_offset))
      - Scala Z di sqrt(2)
      - Moltiplicazione dell'estrusione (se presente X o Y nella riga G1/G0) per fattore_estrusione
        (esclude movimenti di pura retrazione/deretrazione, tipicamente linee G1 E±... senza X/Y)
    
    :param nome_file_input:  str - Nome del file G-code di ingresso
    :param nome_file_output: str - Nome del file G-code di uscita
    :param hstrato: float - Altezza di layer (per riconoscere multipli di Z)
    :param fattore_estrusione: float - Fattore moltiplicazione estrusione (default 1.0 = nessuna modifica)
    """

    # 1) Trova la Y minima del primo layer
    y_min_primo_layer = trova_min_y_primo_layer(nome_file_input)
    
    # Parametri fissi per la trasformazione
    coefficiente = 1
    radice_di_due = math.sqrt(2)
    z_corrente = 0.0
    epsilon = 1e-6

    with open(nome_file_input, 'r') as file_input, open(nome_file_output, 'w') as file_output:
        
        for linea in file_input:
            # Lavoriamo solo su linee che iniziano con G0/G1
            if linea.startswith('G1') or linea.startswith('G0'):
                parti = linea.split()
                x_val = None
                y_val = None
                z_val = None
                e_str = None
                f_str = None

                # Parsing parametri
                for parte in parti:
                    if parte.startswith('X'):
                        x_val = float(parte[1:])
                    elif parte.startswith('Y'):
                        y_val = float(parte[1:])
                    elif parte.startswith('Z'):
                        z_tmp = float(parte[1:])
                        # Se z_tmp è multiplo (entro epsilon) di hstrato, aggiorna z_corrente
                        ratio = z_tmp / hstrato
                        if abs(ratio - round(ratio)) < epsilon:
                            z_corrente = z_tmp
                        z_val = z_tmp
                    elif parte.startswith('E'):
                        e_str = parte  # Exy...
                    elif parte.startswith('F'):
                        f_str = parte  # Fxy...

                #  --- TRASFORMAZIONI ---

                # a) Traslazione + inversione Y
                #    y_offset_iniziale automaticamente = y_min_primo_layer
                if y_val is not None:
                    y_offset = y_min_primo_layer + z_corrente * coefficiente
                    y_val = -(y_val - y_offset)  

                # b) Scala Z per sqrt(2)
                if z_val is not None:
                    z_val *= radice_di_due

                # c) Moltiplicazione estrusione
                e_val = None
                if e_str is not None:
                    estrusione_num = float(e_str[1:])  # valore numerico dell'E

                    # Se la stessa riga sposta X o Y, moltiplichiamo (movimento di stampa)
                    if (x_val is not None) or (y_val is not None):
                        estrusione_num *= fattore_estrusione

                    e_val = f"E{estrusione_num:.5f}"

                #  --- RICOSTRUZIONE LINEA ---

                nuova_linea = 'G1' if linea.startswith('G1') else 'G0'

                if x_val is not None:
                    nuova_linea += f' X{x_val:.3f}'
                if y_val is not None:
                    nuova_linea += f' Y{y_val:.3f}'
                if z_val is not None:
                    nuova_linea += f' Z{z_val:.3f}'
                if e_val is not None:
                    nuova_linea += f' {e_val}'
                if f_str is not None:
                    nuova_linea += f' {f_str}'

                file_output.write(nuova_linea + '\n')
            else:
                # Righe che non iniziano con G0/G1 vengono copiate senza modifiche
                file_output.write(linea)


# ---------------------------
# ESEMPIO DI UTILIZZO
if __name__ == "__main__":
    input_name = '1.gcode'
    output_name = 'cr30_' + input_name
    
    modifica_gcode4(
        nome_file_input=input_name,
        nome_file_output=output_name,
        hstrato=0.36,
        fattore_estrusione=0.75    # Esempio: aumenta del 10% l'estrusione effettiva
    )




---





Python script to clean Bambu Studio or Orca Slicer gcodes to only include universal commands. Do this before tranforming from cartesian to belt

import os
import re

def filter_gcode_2(input_file, output_file, to_remove, allowed_start):
    # Inserisci il blocco di righe personalizzate per il preambolo
    custom_preamble = [
        "G28 ; Home all axes\n",
        "G29 \n",
        "M104 S225\n",
        "M109 S225\n",
        "G1 X0 Y0 Z5 F1000.0 ; Move to origin\n",
        "M83 ;relative extrusion mode\n",
        "G1 X1 Y20 Z0.3 F1000.0 ; Move to start position\n",
        "G1 X1 Y200.0 Z0.3 F1000.0 E15 ; Draw the first line\n",
        "G1 X1.4 Y200.0 Z0.3 F1000.0 ; Move to side a little\n",
        "G1 X1.4 Y20 Z0.3 F1000.0 E15 ; Draw the second line\n",
        "G1 Z2.0 F3000 ; Move up little to prevent scratching of Heat Bed\n",
        "G1 X5 Y20 Z0.3 F5000.0 ; Move over to prevent blob squish\n",
    ]

    # Inserisci il blocco di righe personalizzate per la fine
    custom_end = [
        "G91 ; relative positioning\n",
        "G1 Z1.0 F3000 ; move z up\n",
        "G90 ; absolute positioning\n",
        "G0 X0 \n",
        "M104 S0 ; turn off extruder\n",
        "M140 S0 ; turn off bed\n",
        "M84 ; disable motors\n",
        "M106 S0 ; turn off fan\n"
    ]

    # Flag per tracciare se abbiamo raggiunto la riga specifica
    reached_filament_start = False

    # Apri il file G-code in lettura
    with open(input_file, 'r') as infile:
        # Crea un nuovo file per il G-code filtrato
        with open(output_file, 'w') as outfile:
            # Scorri le righe del file
            for line in infile:
                # Se non abbiamo ancora raggiunto "total_layer_count: 1/"
                if not reached_filament_start:
                    if "total_layer_count: 1/" in line:
                        # Aggiungi il preambolo personalizzato prima della linea specifica
                        outfile.writelines(custom_preamble)
                        outfile.write(line)
                        reached_filament_start = True
                    # Continua a saltare le righe fino alla riga desiderata
                    continue

                # Se abbiamo raggiunto "; filament end gcode"
                if "; filament end gcode" in line:
                    outfile.write(line)
                    outfile.writelines(custom_end)
                    break  # Interrompi la lettura del file

                # Verifica se la riga contiene una delle stringhe da eliminare
                if any(keyword in line for keyword in to_remove):
                    continue  # Salta la riga se contiene una stringa da eliminare

                # Verifica se la riga inizia con 'G1' e contiene un commento ';'
                if line.startswith('G1') and ';' in line:
                    continue  # Salta la riga se inizia con G1 e contiene un commento

                # Verifica se la riga contiene coordinate X o Y negative
                negative_coordinate = False
                # Cerca tutte le occorrenze di X o Y seguite da un numero (positivo o negativo)
                matches = re.findall(r'[XY](-?\d+\.?\d*)', line)
                for value in matches:
                    if float(value) < 0:
                        negative_coordinate = True
                        break
                if negative_coordinate:
                    continue  # Salta la riga se contiene coordinate negative

                # Verifica se la riga inizia con ';'
                if line.startswith(';'):
                    # Se è uno dei commenti che vogliamo mantenere
                    if line.startswith('; Z_HEIGHT:') or line.startswith('; layer num/total_layer_count:'):
                        outfile.write(line)  # Scrivi la riga nel nuovo file
                    else:
                        continue  # Altrimenti salta la riga
                    continue

                # Verifica se la riga inizia con una delle stringhe consentite esattamente
                if any(line.startswith(prefix) for prefix in allowed_start):
                    outfile.write(line)  # Scrivi la riga nel nuovo file se inizia con un prefisso consentito
                else:
                    continue  # Se non inizia con i prefissi, salta la riga


# Elenco delle stringhe da eliminare
to_remove = ['G17', 'M623', 'J', 'safe', 'timelapse', 'M622.1', 'M971', 'M400', 'C', 'G2', 'P', 'M1002', 'X0']

# Elenco delle stringhe con cui una riga deve iniziare per essere conservata (senza il ';')
allowed_start = [
    'G0', 'G1', 'M104', 'M109', 'M140', 'M190', 
    'G28', 'G29', 'M82', 'M83', 'G92', 'M105', 
    'M106', 'M107', 'M25'
]

def process_folder(input_folder):
    """
    Questa funzione prende in ingresso il percorso di una cartella,
    filtra tutti i file con estensione .gcode al suo interno e
    salva i risultati nella sottocartella 'filtered'.
    """
    # Crea la sottocartella 'filtered' se non esiste
    filtered_folder = os.path.join(input_folder, "filtered")
    os.makedirs(filtered_folder, exist_ok=True)

    # Itera su tutti i file nella cartella
    for filename in os.listdir(input_folder):
        # Considera solo i file che terminano con .gcode (case-insensitive)
        if filename.lower().endswith(".gcode"):
            input_path = os.path.join(input_folder, filename)
            output_path = os.path.join(filtered_folder, filename)
            
            # Esegui il filtro sul singolo file
            filter_gcode_2(
                input_file=input_path,
                output_file=output_path,
                to_remove=to_remove,
                allowed_start=allowed_start
            )
            print(f"File filtrato: {output_path}")

# Esempio di utilizzo:
if __name__ == "__main__":
    # Imposta qui il percorso della tua cartella contenente i .gcode
    cartella_input = "input2"
    
    process_folder(cartella_input)


