#!/usr/bin/env python # convert_sparkmaker.py # Copyright 2023 Thomas Buck # Convert PrusaSlicer .sl1 files to Sparkmaker .wow format # Lift settings can only be customized below, not in the slicer. # Should also work for Sparkmaker FHD, adjust parameters below. import sys import os from zipfile import ZipFile import re import pprint import png ############################################################################### screen_size = (854, 480) max_height = 120 initial_params = { 'layer_height': 0.1, 'first_layer_height': 0.1, 'exposure_time': 15.0, 'first_exposure_time': 120.0, 'lift_height': 5.0, 'lift_speed': 30.0, 'sink_speed': 100.0 } final_move_speed = 100.0 ############################################################################### def read_prusaslicer_ini(f, params): lines = f.decode("utf-8").split("\n") for l in lines: if l.startswith("exposure_time"): params["exposure_time"] = float(l.split(" = ")[1]) elif l.startswith("initial_exposure_time"): params["first_exposure_time"] = float(l.split(" = ")[1]) elif l.startswith("layer_height"): params["layer_height"] = float(l.split(" = ")[1]) elif l.startswith("initial_layer_height"): params["first_layer_height"] = float(l.split(" = ")[1]) def read_config_ini(f, params): lines = f.decode("utf-8").split("\n") for l in lines: if l.startswith("expTimeFirst"): params["first_exposure_time"] = float(l.split(" = ")[1]) elif l.startswith("expTime"): params["exposure_time"] = float(l.split(" = ")[1]) elif l.startswith("layerHeight"): params["layer_height"] = float(l.split(" = ")[1]) def read_sl1(f): params = initial_params files = f.namelist() if "prusaslicer.ini" in files: read_prusaslicer_ini(f.read("prusaslicer.ini"), params) else: print("No prusaslicer.ini found in file") if "config.ini" in files: read_config_ini(f.read("config.ini"), params) else: print("No config.ini found in file") sys.exit(1) imgs = [x for x in files if x.endswith(".png")] imgs = [x for x in imgs if not x.startswith("thumbnail/")] if len(imgs) <= 0: print("No slices found in file. Aborting.") sys.exit(1) res = re.findall('(\D*)\d*.*', imgs[0])[0] print("Internal name: \"" + res + "\"") print("Found " + str(len(imgs)) + " slices") images = [] for img in imgs: images.append(f.read(img)) return params, images ############################################################################### def write_image(f, img): p = png.Reader(bytes = img) width, height, rows, info = p.read() if width != screen_size[0]: print("Error: layer has wrong width " + str(width) + " != " + str(screen_size[0])) sys.exit(1) if height != screen_size[1]: print("Error: layer has wrong height " + str(height) + " != " + str(screen_size[1])) sys.exit(1) if (not info['greyscale']) or info['alpha']: print("Error: invalid image encoding") sys.exit(1) data = [0] * int(width * height / 8) y = 0 # width is 854 x = 0 # height is 480 for row in rows: for v in row: if v > 0x7F: data[int(height / 8) * y + int(x / 8)] |= (1 << x % 8) y += 1 x += 1 y = 0 for d in data: f.write(d.to_bytes(1, 'big')) def write_wow(f, params, imgs): def write(s): #print(s) f.write((s + "\n").encode()) write("G21;") write("G91;") write("M17;") write("M106 S0;") write("G28 Z0;") first_layer = True for i in range(0, len(imgs)): write(";L:" + str(int(i)) + ";") write("M106 S0;") lift_up = params['lift_height'] lift_down = -(lift_up - params['layer_height']) exposure_time = params['exposure_time'] if first_layer: lift_down = -(lift_up - params['first_layer_height']) exposure_time = params['first_exposure_time'] first_layer = False write("G1 Z" + str(lift_up) + " F" + str(params['lift_speed']) + ";") write("G1 Z" + str(lift_down) + " F" + str(params['sink_speed']) + ";") write("{{") write_image(f, imgs[i]) write("}}") write("M106 S255;") write("G4 S" + str(exposure_time) + ";") write("M106 S0;") object_height = params['layer_height'] * (len(imgs) - 1) + params['first_layer_height'] final_move = max_height - object_height if final_move >= 1.0: write("G1 Z1 F" + str(params['lift_speed']) + ";") write("G1 Z" + str(final_move - 1.0) + " F" + str(final_move_speed) + ";") write("M18;") ############################################################################### def main(): if len(sys.argv) < 2: print("Usage:") print(" " + sys.argv[0] + " input.sl1 [output.wow]") sys.exit(1) in_file_name = sys.argv[1] out_file_name = "print.wow" if len(sys.argv) >= 3: out_file_name = sys.argv[2] if os.path.exists(out_file_name): print("File already exists: \"" + out_file_name + "\". Aborting.") sys.exit(1) with ZipFile(in_file_name, 'r') as sl1: params, imgs = read_sl1(sl1) print("Using following parameters:") pprint.pprint(params) print("Height: {:.3f}".format(params['first_layer_height'] + (params['layer_height'] * (len(imgs) - 1))) + "mm") t = params['first_exposure_time'] t += (len(imgs) - 1) * params['exposure_time'] t += params['lift_height'] / params['lift_speed'] * 60 * len(imgs) t += (params['lift_height'] - params['layer_height']) / params['sink_speed'] * 60 * len(imgs) h = t / 3600 m = (t / 60) % 60 s = t % 60 print("Estimated print time: " + str(int(h)) + "h " + str(int(m)) + "m " + str(int(s)) + "s") print("Writing output to \"" + out_file_name + "\"") with open(out_file_name, 'wb') as wow: write_wow(wow, params, imgs) if __name__ == '__main__': main()