From 7685e707434613e1a8ba50a189da61061e1a26d0 Mon Sep 17 00:00:00 2001 From: ole00 Date: Fri, 11 Feb 2022 22:06:47 +0000 Subject: [PATCH 1/2] SLA: added pwmx format exporter Pwmx format is used by Anycubic Photon Mono X printers. File format structure and rle encoding implemented according to: https://github.com/sn4k3/UVtools https://github.com/ole00/pwmx_info --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Format/pwmx.cpp | 509 ++++++++++++++++++++ src/libslic3r/Format/pwmx.hpp | 36 ++ src/libslic3r/SLA/RasterBase.cpp | 48 ++ src/libslic3r/SLA/RasterBase.hpp | 4 + src/libslic3r/SLAPrint.cpp | 18 + src/libslic3r/SLAPrint.hpp | 34 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 26 +- src/slic3r/GUI/GUI_App.cpp | 2 +- 9 files changed, 645 insertions(+), 34 deletions(-) create mode 100644 src/libslic3r/Format/pwmx.cpp create mode 100644 src/libslic3r/Format/pwmx.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index d4c8d7edc12..ea663f2e1f3 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -98,6 +98,8 @@ set(SLIC3R_SOURCES Format/SL1.cpp Format/SL1_SVG.hpp Format/SL1_SVG.cpp + Format/pwmx.hpp + Format/pwmx.cpp GCode/ThumbnailData.cpp GCode/ThumbnailData.hpp GCode/Thumbnails.cpp diff --git a/src/libslic3r/Format/pwmx.cpp b/src/libslic3r/Format/pwmx.cpp new file mode 100644 index 00000000000..99785b8d085 --- /dev/null +++ b/src/libslic3r/Format/pwmx.cpp @@ -0,0 +1,509 @@ +#include "pwmx.hpp" +#include "GCode/ThumbnailData.hpp" + +#include +#include +#include + + +#define TAG_INTRO "ANYCUBIC\0\0\0\0" +#define TAG_HEADER "HEADER\0\0\0\0\0\0" +#define TAG_PREVIEW "PREVIEW\0\0\0\0\0" +#define TAG_LAYERS "LAYERDEF\0\0\0\0" + +#define CFG_LIFT_DISTANCE "LIFT_DISTANCE" +#define CFG_LIFT_SPEED "LIFT_SPEED" +#define CFG_RETRACT_SPEED "RETRACT_SPEED" +#define CFG_DELAY_BEFORE_EXPOSURE "DELAY_BEFORE_EXPOSURE" +#define CFG_BOTTOM_LIFT_SPEED "BOTTOM_LIFT_SPEED" +#define CFG_BOTTOM_LIFT_DISTANCE "BOTTOM_LIFT_DISTANCE" + +#define PREV_W 224 +#define PREV_H 168 +#define PREV_DPI 42 + +#define LAYER_SIZE_ESTIMATE (32 * 1024) + + +namespace Slic3r { + +using ConfMap = std::map; + +typedef struct pwmx_format_intro +{ + char tag[12]; + std::uint32_t version; // value 1 + std::uint32_t area_num; // unknown - usually 4 + std::uint32_t header_data_offset; + std::float_t intro24; // unknown - usually 0 + std::uint32_t preview_data_offset; + std::float_t intro32; // unknown + std::uint32_t layer_data_offset; + std::float_t intro40; // unknown + std::uint32_t image_data_offset; +} pwmx_format_intro; + +typedef struct pwmx_format_header +{ + char tag[12]; + std::uint32_t payload_size; + std::float_t pixel_size_um; + std::float_t layer_height_mm; + std::float_t exposure_time_s; + std::float_t delay_before_exposure_s; + std::float_t bottom_exposure_time_s; + std::float_t bottom_layer_count; + std::float_t lift_distance_mm; + std::float_t lift_speed_mms; + std::float_t retract_speed_mms; + std::float_t volume_ml; + std::uint32_t antialiasing; + std::uint32_t res_x; + std::uint32_t res_y; + std::float_t weight_g; + std::float_t price; + std::uint32_t price_currency; + std::uint32_t per_layer_override; // ? unknown meaning ? + std::uint32_t print_time_s; + std::uint32_t transition_layer_count; + std::uint32_t unknown; // ? usually 0 ? + +} pwmx_format_header; + +typedef struct pwmx_format_preview +{ + char tag[12]; + std::uint32_t payload_size; + std::uint32_t preview_w; + std::uint32_t preview_dpi; + std::uint32_t preview_h; + // raw image data in BGR565 format + std::uint8_t pixels[PREV_W * PREV_H * 2]; +} pwmx_format_preview; + +typedef struct pwmx_format_layers_header +{ + char tag[12]; + std::uint32_t payload_size; + std::uint32_t layer_count; +} pwmx_format_layers_header; + +typedef struct pwmx_format_layer +{ + std::uint32_t image_offset; + std::uint32_t image_size; + std::float_t lift_distance_mm; + std::float_t lift_speed_mms; + std::float_t exposure_time_s; + std::float_t layer_height_mm; + std::float_t layer44; // unkown - usually 0 + std::float_t layer48; // unkown - usually 0 +} pwmx_format_layer; + +typedef struct pwmx_format_misc +{ + std::float_t bottom_layer_height_mm; + std::float_t bottom_lift_distance_mm; + std::float_t bottom_lift_speed_mms; + +} pwmx_format_misc; + +class PwmxFormatConfigDef : public ConfigDef +{ +public: + PwmxFormatConfigDef() + { + add(CFG_LIFT_DISTANCE, coFloat); + add(CFG_LIFT_SPEED, coFloat); + add(CFG_RETRACT_SPEED, coFloat); + add(CFG_DELAY_BEFORE_EXPOSURE, coFloat); + add(CFG_BOTTOM_LIFT_DISTANCE, coFloat); + add(CFG_BOTTOM_LIFT_SPEED, coFloat); + } +}; + +class PwmxFormatDynamicConfig : public DynamicConfig +{ +public: + PwmxFormatDynamicConfig(){}; + const ConfigDef *def() const override { return &config_def; } + +private: + PwmxFormatConfigDef config_def; +}; + +namespace { + +const char *get_cfg_value(const DynamicConfig &cfg, + const std::string & key, + const std::string & def = "0") +{ + std::string ret; + + if (cfg.has(key)) { + auto opt = cfg.option(key); + if (opt) { + ret = opt->serialize(); + } else { + return def.c_str(); + } + } else { + return def.c_str(); + } + + return ret.c_str(); +} + +template void crop_value(T &val, T val_min, T val_max) +{ + if (val < val_min) { + val = val_min; + } else if (val > val_max) { + val = val_max; + } +} + +void fill_preview(pwmx_format_preview &p, + pwmx_format_misc &m, + ThumbnailsList &thumbnails) +{ + + p.preview_w = PREV_W; + p.preview_h = PREV_H; + p.preview_dpi = PREV_DPI; + p.payload_size = sizeof(p) - sizeof(p.tag) - sizeof(p.payload_size); + + std::memset(p.pixels, 0 , sizeof(p.pixels)); + if (!thumbnails.empty()) { + std::uint32_t dst_index; + std::uint32_t i = 0; + size_t len; + size_t pixel_x = 0; + auto t = thumbnails[0]; //use the first thumbnail + len = t.pixels.size(); + //sanity check + if (len != PREV_W * PREV_H * 4) { + printf("incorrect thumbnail size. expected %ix%i\n", PREV_W, PREV_H); + return; + } + // rearange pixels: they seem to be stored from bottom to top. + dst_index = (PREV_W * (PREV_H - 1) * 2); + while (i < len) { + std::uint32_t pixel; + std::uint32_t r = t.pixels[i++]; + std::uint32_t g = t.pixels[i++]; + std::uint32_t b = t.pixels[i++]; + i++; // Alpha + // convert to BGRA565 + pixel = ((b >> 3) << 11) | ((g >>2) << 5) | (r >> 3); + p.pixels[dst_index++] = pixel & 0xFF; + p.pixels[dst_index++] = (pixel >> 8) & 0xFF; + pixel_x++; + if (pixel_x == PREV_W) { + pixel_x = 0; + dst_index -= (PREV_W * 4); + } + } + } +} + + +void fill_header(pwmx_format_header &h, + pwmx_format_misc &m, + const SLAPrint &print, + std::uint32_t layer_count) +{ + std::float_t bottle_weight_g; + std::float_t bottle_volume_ml; + std::float_t bottle_cost; + std::float_t material_density; + auto & cfg = print.full_print_config(); + std::string mnotes = cfg.option("material_notes")->serialize(); + // create a config parser from the material notes + Slic3r::PwmxFormatDynamicConfig mat_cfg; + SLAPrintStatistics stats = print.print_statistics(); + + // sanitize the string config + boost::replace_all(mnotes, "\\n", "\n"); + boost::replace_all(mnotes, "\\r", "\r"); + mat_cfg.load_from_ini_string(mnotes, + ForwardCompatibilitySubstitutionRule::Enable); + + h.layer_height_mm = std::atof(get_cfg_value(cfg, "layer_height")); + m.bottom_layer_height_mm = std::atof( + get_cfg_value(cfg, "initial_layer_height")); + h.exposure_time_s = std::atof(get_cfg_value(cfg, "exposure_time")); + h.bottom_exposure_time_s = std::atof( + get_cfg_value(cfg, "initial_exposure_time")); + h.bottom_layer_count = std::atof(get_cfg_value(cfg, "faded_layers")); + if (layer_count < h.bottom_layer_count) { + h.bottom_layer_count = layer_count; + } + h.res_x = std::atol(get_cfg_value(cfg, "display_pixels_x")); + h.res_y = std::atol(get_cfg_value(cfg, "display_pixels_y")); + bottle_weight_g = std::atof(get_cfg_value(cfg, "bottle_weight")) * 1000.0f; + bottle_volume_ml = std::atof(get_cfg_value(cfg, "bottle_volume")); + bottle_cost = std::atof(get_cfg_value(cfg, "bottle_cost")); + material_density = bottle_weight_g / bottle_volume_ml; + + h.volume_ml = (stats.objects_used_material + stats.support_used_material) / 1000; + h.weight_g = h.volume_ml * material_density; + h.price = (h.volume_ml * bottle_cost) / bottle_volume_ml; + h.price_currency = '$'; + h.antialiasing = 1; + h.per_layer_override = 0; + + // TODO - expose these variables to the UI rather than using material notes + h.delay_before_exposure_s = std::atof( + get_cfg_value(mat_cfg, CFG_DELAY_BEFORE_EXPOSURE, "0.5")); + crop_value(h.delay_before_exposure_s, 0.0f, 1000.0f); + + h.lift_distance_mm = std::atof( + get_cfg_value(mat_cfg, CFG_LIFT_DISTANCE, "8.0")); + crop_value(h.lift_distance_mm, 0.0f, 100.0f); + + if (mat_cfg.has(CFG_BOTTOM_LIFT_DISTANCE)) { + m.bottom_lift_distance_mm = std::atof( + get_cfg_value(mat_cfg, CFG_BOTTOM_LIFT_DISTANCE, "8.0")); + crop_value(h.lift_distance_mm, 0.0f, 100.0f); + } else { + m.bottom_lift_distance_mm = h.lift_distance_mm; + } + + + h.lift_speed_mms = std::atof( + get_cfg_value(mat_cfg, CFG_LIFT_SPEED, "2.0")); + crop_value(m.bottom_lift_speed_mms, 0.1f, 20.0f); + + if (mat_cfg.has(CFG_BOTTOM_LIFT_SPEED)) { + m.bottom_lift_speed_mms = std::atof( + get_cfg_value(mat_cfg, CFG_BOTTOM_LIFT_SPEED, "2.0")); + crop_value(m.bottom_lift_speed_mms, 0.1f, 20.0f); + } else { + m.bottom_lift_speed_mms = h.lift_speed_mms; + } + + h.retract_speed_mms = std::atof( + get_cfg_value(mat_cfg, CFG_RETRACT_SPEED, "3.0")); + crop_value(h.lift_speed_mms, 0.1f, 20.0f); + + h.print_time_s = (h.bottom_layer_count * h.bottom_exposure_time_s) + + ((layer_count - h.bottom_layer_count) * + h.exposure_time_s) + + (layer_count * h.lift_distance_mm / h.retract_speed_mms) + + (layer_count * h.lift_distance_mm / h.lift_speed_mms) + + (layer_count * h.delay_before_exposure_s); + + + h.payload_size = sizeof(h) - sizeof(h.tag) - sizeof(h.payload_size); + h.pixel_size_um = 50; +} + +} // namespace + +std::unique_ptr PwmxArchive::create_raster() const +{ + sla::Resolution res; + sla::PixelDim pxdim; + std::array mirror; + + double w = m_cfg.display_width.getFloat(); + double h = m_cfg.display_height.getFloat(); + auto pw = size_t(m_cfg.display_pixels_x.getInt()); + auto ph = size_t(m_cfg.display_pixels_y.getInt()); + + mirror[X] = m_cfg.display_mirror_x.getBool(); + mirror[Y] = m_cfg.display_mirror_y.getBool(); + + auto ro = m_cfg.display_orientation.getInt(); + sla::RasterBase::Orientation orientation = + ro == sla::RasterBase::roPortrait ? sla::RasterBase::roPortrait : + sla::RasterBase::roLandscape; + + if (orientation == sla::RasterBase::roPortrait) { + std::swap(w, h); + std::swap(pw, ph); + } + + res = sla::Resolution{pw, ph}; + pxdim = sla::PixelDim{w / pw, h / ph}; + sla::RasterBase::Trafo tr{orientation, mirror}; + + double gamma = m_cfg.gamma_correction.getFloat(); + + return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr); +} + +sla::RasterEncoder PwmxArchive::get_encoder() const +{ + return sla::PWXRasterEncoder{}; +} + +// Endian safe write of little endian 32bit ints +static void pwmx_write_int32(std::ofstream &out, std::uint32_t val) +{ + const char i1 = (val & 0xFF); + const char i2 = (val >> 8) & 0xFF; + const char i3 = (val >> 16) & 0xFF; + const char i4 = (val >> 24) & 0xFF; + + out.write((const char *) &i1, 1); + out.write((const char *) &i2, 1); + out.write((const char *) &i3, 1); + out.write((const char *) &i4, 1); +} +static void pwmx_write_float(std::ofstream &out, std::float_t val) +{ + std::uint32_t *f = (std::uint32_t *) &val; + pwmx_write_int32(out, *f); +} + +static void pwmx_write_intro(std::ofstream &out, pwmx_format_intro &i) +{ + out.write(TAG_INTRO, sizeof(i.tag)); + pwmx_write_int32(out, i.version); + pwmx_write_int32(out, i.area_num); + pwmx_write_int32(out, i.header_data_offset); + pwmx_write_int32(out, i.intro24); + pwmx_write_int32(out, i.preview_data_offset); + pwmx_write_int32(out, i.intro32); + pwmx_write_int32(out, i.layer_data_offset); + pwmx_write_int32(out, i.intro40); + pwmx_write_int32(out, i.image_data_offset); +} + +static void pwmx_write_header(std::ofstream &out, pwmx_format_header &h) +{ + out.write(TAG_HEADER, sizeof(h.tag)); + pwmx_write_int32(out, h.payload_size); + pwmx_write_float(out, h.pixel_size_um); + pwmx_write_float(out, h.layer_height_mm); + pwmx_write_float(out, h.exposure_time_s); + pwmx_write_float(out, h.delay_before_exposure_s); + pwmx_write_float(out, h.bottom_exposure_time_s); + pwmx_write_float(out, h.bottom_layer_count); + pwmx_write_float(out, h.lift_distance_mm); + pwmx_write_float(out, h.lift_speed_mms); + pwmx_write_float(out, h.retract_speed_mms); + pwmx_write_float(out, h.volume_ml); + pwmx_write_int32(out, h.antialiasing); + pwmx_write_int32(out, h.res_x); + pwmx_write_int32(out, h.res_y); + pwmx_write_float(out, h.weight_g); + pwmx_write_float(out, h.price); + pwmx_write_int32(out, h.price_currency); + pwmx_write_int32(out, h.per_layer_override); + pwmx_write_int32(out, h.print_time_s); + pwmx_write_int32(out, h.transition_layer_count); + pwmx_write_int32(out, h.unknown); +} + +static void pwmx_write_preview(std::ofstream &out, pwmx_format_preview &p) +{ + out.write(TAG_PREVIEW, sizeof(p.tag)); + pwmx_write_int32(out, p.payload_size); + pwmx_write_int32(out, p.preview_w); + pwmx_write_int32(out, p.preview_dpi); + pwmx_write_int32(out, p.preview_h); + out.write((const char*) p.pixels, sizeof(p.pixels)); +} + +static void pwmx_write_layers_header(std::ofstream &out, pwmx_format_layers_header &h) +{ + out.write(TAG_LAYERS, sizeof(h.tag)); + pwmx_write_int32(out, h.payload_size); + pwmx_write_int32(out, h.layer_count); +} + +static void pwmx_write_layer(std::ofstream &out, pwmx_format_layer &l) +{ + pwmx_write_int32(out, l.image_offset); + pwmx_write_int32(out, l.image_size); + pwmx_write_float(out, l.lift_distance_mm); + pwmx_write_float(out, l.lift_speed_mms); + pwmx_write_float(out, l.exposure_time_s); + pwmx_write_float(out, l.layer_height_mm); + pwmx_write_float(out, l.layer44); + pwmx_write_float(out, l.layer48); +} + +void PwmxArchive::export_print(const std::string fname, + const SLAPrint & print, + ThumbnailsList & thumbnails, + const std::string &prjname) +{ + std::uint32_t layer_count = m_layers.size(); + + pwmx_format_intro intro = {0}; + pwmx_format_header header = {0}; + pwmx_format_preview preview = {0}; + pwmx_format_layers_header layers_header = {0}; + pwmx_format_misc misc = {0}; + std::vector layer_images; + std::uint32_t image_offset; + + intro.version = 1; + intro.area_num = 4; + intro.header_data_offset = sizeof(intro); + intro.preview_data_offset = sizeof(intro) + sizeof(header); + intro.layer_data_offset = intro.preview_data_offset + sizeof(preview); + intro.image_data_offset = intro.layer_data_offset + + sizeof(layers_header) + + (sizeof(pwmx_format_layer) * layer_count); + + fill_header(header, misc, print, layer_count); + fill_preview(preview, misc, thumbnails); + + try { + // open the file and write the contents + std::ofstream out; + out.open(fname, std::ios::binary | std::ios::out | std::ios::trunc); + pwmx_write_intro(out, intro); + pwmx_write_header(out, header); + pwmx_write_preview(out, preview); + + layers_header.payload_size = intro.image_data_offset - intro.layer_data_offset - + sizeof(layers_header.tag) - sizeof(layers_header.payload_size); + layers_header.layer_count = layer_count; + pwmx_write_layers_header(out, layers_header); + + //layers + layer_images.reserve(layer_count * LAYER_SIZE_ESTIMATE); + image_offset = intro.image_data_offset; + size_t i = 0; + for (const sla::EncodedRaster &rst : m_layers) { + pwmx_format_layer l; + std::memset(&l, 0, sizeof(l)); + l.image_offset = image_offset; + l.image_size = rst.size(); + if (i < header.bottom_layer_count) { + l.exposure_time_s = header.bottom_exposure_time_s; + l.layer_height_mm = misc.bottom_layer_height_mm; + l.lift_distance_mm = misc.bottom_lift_distance_mm; + l.lift_speed_mms = misc.bottom_lift_speed_mms; + } else { + l.exposure_time_s = header.exposure_time_s; + l.layer_height_mm = header.layer_height_mm; + l.lift_distance_mm = header.lift_distance_mm; + l.lift_speed_mms = header.lift_speed_mms; + } + image_offset += l.image_size; + pwmx_write_layer(out, l); + // add the rle encoded layer image into the buffer + const char* img_start = reinterpret_cast(rst.data()); + const char* img_end = img_start + rst.size(); + std::copy(img_start, img_end, std::back_inserter(layer_images)); + i++; + } + const char* img_buffer = reinterpret_cast(layer_images.data()); + out.write(img_buffer, layer_images.size()); + out.close(); + } catch(std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + // Rethrow the exception + throw; + } + +} + +} // namespace Slic3r diff --git a/src/libslic3r/Format/pwmx.hpp b/src/libslic3r/Format/pwmx.hpp new file mode 100644 index 00000000000..69cec100c3e --- /dev/null +++ b/src/libslic3r/Format/pwmx.hpp @@ -0,0 +1,36 @@ +#ifndef _SLIC3R_FORMAT_PWMX_HPP_ +#define _SLIC3R_FORMAT_PWMX_HPP_ + +#include + +#include "libslic3r/SLAPrint.hpp" + +namespace Slic3r { + +class PwmxArchive: public SLAArchive { + SLAPrinterConfig m_cfg; + +protected: + std::unique_ptr create_raster() const override; + sla::RasterEncoder get_encoder() const override; + + SLAPrinterConfig & cfg() { return m_cfg; } + const SLAPrinterConfig & cfg() const { return m_cfg; } + +public: + + PwmxArchive() = default; + explicit PwmxArchive(const SLAPrinterConfig &cfg): m_cfg(cfg) {} + explicit PwmxArchive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {} + + void export_print(const std::string fname, + const SLAPrint &print, + ThumbnailsList &thumbnails, + const std::string &projectname = "") override; + bool uses_zipper_export() override {return false;} +}; + + +} // namespace Slic3r::sla + +#endif // _SLIC3R_FORMAT_PWMX_HPP_ diff --git a/src/libslic3r/SLA/RasterBase.cpp b/src/libslic3r/SLA/RasterBase.cpp index 0b6c45eff3e..fd3f3e062d4 100644 --- a/src/libslic3r/SLA/RasterBase.cpp +++ b/src/libslic3r/SLA/RasterBase.cpp @@ -16,6 +16,54 @@ const RasterBase::TMirroring RasterBase::MirrorX = {true, false}; const RasterBase::TMirroring RasterBase::MirrorY = {false, true}; const RasterBase::TMirroring RasterBase::MirrorXY = {true, true}; + + +static void pwx_get_pixel_span(const std::uint8_t* ptr, const std::uint8_t* end, + std::uint8_t& pixel, size_t& span_len) +{ + size_t max_len; + + span_len = 0; + pixel = (*ptr) & 0xF0; + // the maximum length of the span depends on the pixel color + max_len = (pixel == 0 || pixel == 0xF0) ? 0xFFF : 0xF; + while (((*ptr) & 0xF0) == pixel && ptr < end && span_len < max_len) { + span_len++; + ptr++; + } +} + +EncodedRaster PWXRasterEncoder::operator()(const void *ptr, size_t w, size_t h, + size_t num_components) +{ + std::vector dst; + size_t span_len; + std::uint8_t pixel; + auto size = w * h * num_components; + dst.reserve(size); + + const std::uint8_t* src = reinterpret_cast(ptr); + const std::uint8_t* src_end = src + size; + while (src < src_end) { + pwx_get_pixel_span(src, src_end, pixel, span_len); + src += span_len; + // fully transparent of fully opaque pixel + if (pixel == 0 || pixel == 0xF0) { + pixel = pixel | (span_len >> 8); + std::copy(&pixel, (&pixel) + 1, std::back_inserter(dst)); + pixel = span_len & 0xFF; + std::copy(&pixel, (&pixel) + 1, std::back_inserter(dst)); + } + // antialiased pixel + else { + pixel = pixel | span_len; + std::copy(&pixel, (&pixel) + 1, std::back_inserter(dst)); + } + } + + return EncodedRaster(std::move(dst), "pwx"); +} + EncodedRaster PNGRasterEncoder::operator()(const void *ptr, size_t w, size_t h, size_t num_components) { diff --git a/src/libslic3r/SLA/RasterBase.hpp b/src/libslic3r/SLA/RasterBase.hpp index 657fc865c2d..9d22591cc76 100644 --- a/src/libslic3r/SLA/RasterBase.hpp +++ b/src/libslic3r/SLA/RasterBase.hpp @@ -97,6 +97,10 @@ class RasterBase { virtual EncodedRaster encode(RasterEncoder encoder) const = 0; }; +struct PWXRasterEncoder { + EncodedRaster operator()(const void *ptr, size_t w, size_t h, size_t num_components); +}; + struct PNGRasterEncoder { EncodedRaster operator()(const void *ptr, size_t w, size_t h, size_t num_components); }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 7b78dfea2fe..23502c44e36 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -3,6 +3,7 @@ #include "Format/SL1.hpp" #include "Format/SL1_SVG.hpp" +#include "Format/pwmx.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" @@ -16,6 +17,8 @@ #include #include +#include + // #define SLAPRINT_DO_BENCHMARK #ifdef SLAPRINT_DO_BENCHMARK @@ -249,6 +252,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con m_archiver = std::make_unique(m_printer_config); else if (m_printer_config.sla_archive_format.value == "SL2") m_archiver = std::make_unique(m_printer_config); + else if (m_printer_config.sla_archive_format.value == "pwmx") { + m_archiver = std::make_unique(m_printer_config); + } } struct ModelObjectStatus { @@ -1265,4 +1271,16 @@ void SLAPrint::StatusReporter::operator()(SLAPrint & p, p.set_status(int(std::round(st)), msg, flags); } + +void SLAPrint::write_thumbnail(Zipper& zipper, const ThumbnailData& data) +{ + size_t png_size = 0; + void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); + if (png_data != nullptr) + { + zipper.add_entry("thumbnail/thumbnail" + std::to_string(data.width) + "x" + std::to_string(data.height) + ".png", (const std::uint8_t*)png_data, png_size); + mz_free(png_data); + } +} + } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index b2df0c4e92b..8d5b54376a1 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -9,6 +9,7 @@ #include "Point.hpp" #include "MTUtils.hpp" #include "Zipper.hpp" +#include "GCode/ThumbnailData.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp" @@ -422,12 +423,16 @@ class SLAArchive { } // Export the print into an archive using the provided zipper. - // TODO: Use an archive writer interface instead of Zipper. - // This is quite limiting as the Zipper is a complete class, not an interface. - // The output can only be a zip archive. virtual void export_print(Zipper &zipper, const SLAPrint &print, - const std::string &projectname = "") = 0; + const std::string &projectname = "") {}; + // Export the print into an archive using the provided filename. + virtual void export_print(const std::string fname, + const SLAPrint &print, + ThumbnailsList &thumbnails, + const std::string &projectname = "") {}; + // By default the exporters use zipper export. Return false to use file export. + virtual bool uses_zipper_export() { return true; } }; /** @@ -533,16 +538,27 @@ class SLAPrint : public PrintBaseWithState // The aggregated and leveled print records from various objects. // TODO: use this structure for the preview in the future. const std::vector& print_layers() const { return m_printer_input; } + + void write_thumbnail(Zipper& zipper, const ThumbnailData& data); - void export_print(Zipper &zipper, const std::string &projectname = "") + void export_print(const std::string &fname, const std::string &projectname = "") { - m_archiver->export_print(zipper, *this, projectname); + Slic3r::ThumbnailsList thumbnails; //empty thumbnail list + export_print(fname, thumbnails, projectname); } - void export_print(const std::string &fname, const std::string &projectname = "") + void export_print(const std::string &fname, Slic3r::ThumbnailsList &thumbnails, const std::string &projectname = "") { - Zipper zipper(fname); - export_print(zipper, projectname); + if (m_archiver->uses_zipper_export()) { + Zipper zipper(fname); + m_archiver->export_print(zipper, *this, projectname); + for (const ThumbnailData& data : thumbnails) + if (data.is_valid()) + write_thumbnail(zipper, data); + zipper.finalize(); + } else { + m_archiver->export_print(fname, *this, thumbnails, projectname); + } } private: diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 5d3d47c202b..37e527d649b 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -165,17 +165,6 @@ void BackgroundSlicingProcess::process_fff() } } -static void write_thumbnail(Zipper& zipper, const ThumbnailData& data) -{ - size_t png_size = 0; - void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); - if (png_data != nullptr) - { - zipper.add_entry("thumbnail/thumbnail" + std::to_string(data.width) + "x" + std::to_string(data.height) + ".png", (const std::uint8_t*)png_data, png_size); - mz_free(png_data); - } -} - void BackgroundSlicingProcess::process_sla() { assert(m_print == m_sla_print); @@ -189,12 +178,7 @@ void BackgroundSlicingProcess::process_sla() ThumbnailsList thumbnails = this->render_thumbnails( ThumbnailsParams{current_print()->full_print_config().option("thumbnails")->values, true, true, true, true}); - Zipper zipper(export_path); - m_sla_print->export_print(zipper); - for (const ThumbnailData& data : thumbnails) - if (data.is_valid()) - write_thumbnail(zipper, data); - zipper.finalize(); + m_sla_print->export_print(export_path, thumbnails); m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str()); } else if (! m_upload_job.empty()) { @@ -739,13 +723,7 @@ void BackgroundSlicingProcess::prepare_upload() ThumbnailsList thumbnails = this->render_thumbnails( ThumbnailsParams{current_print()->full_print_config().option("thumbnails")->values, true, true, true, true}); - // true, false, true, true); // renders also supports and pad - Zipper zipper{source_path.string()}; - m_sla_print->export_print(zipper, m_upload_job.upload_data.upload_path.string()); - for (const ThumbnailData& data : thumbnails) - if (data.is_valid()) - write_thumbnail(zipper, data); - zipper.finalize(); + m_sla_print->export_print(source_path.string(),thumbnails, m_upload_job.upload_data.upload_path.string()); } m_print->set_status(100, (boost::format(_utf8(L("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue"))) % m_upload_job.printhost->get_host()).str()); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b55ba0d75e6..b914d8db472 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -496,7 +496,7 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = { /* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } }, - /* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv } }, + /* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv, ".pwmx"sv } }, }; // This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms. From df0751716c2cc031cc8b3ffabd272ac55c1b0b6f Mon Sep 17 00:00:00 2001 From: ole00 Date: Sat, 12 Feb 2022 01:13:58 +0000 Subject: [PATCH 2/2] Added resources and settings for Anycubic Photon Mono X SLA printer --- resources/profiles/Anycubic.ini | 100 ++++++++++++++++++ .../Anycubic/PHOTON MONO X_thumbnail.png | Bin 0 -> 45953 bytes 2 files changed, 100 insertions(+) create mode 100644 resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png diff --git a/resources/profiles/Anycubic.ini b/resources/profiles/Anycubic.ini index 053aecbd59c..a67ba0962ff 100644 --- a/resources/profiles/Anycubic.ini +++ b/resources/profiles/Anycubic.ini @@ -64,6 +64,13 @@ technology = FFF family = PREDATOR default_materials = Generic PLA @PREDATOR; Generic PETG @PREDATOR; Generic ABS @PREDATOR +[printer_model:PHOTON MONO X] +name = Photon Mono X +variants = default +technology = SLA +family = PHOTON MONO +default_materials = Generic Blue Resin MONO @0.05 + # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -1898,3 +1905,96 @@ default_print_profile = 0.24mm 0.8 nozzle DETAILED QUALITY @PREDATOR ######################################### ########## end printer presets ########## ######################################### + +######################################### +########## SLA printer presets ########## +######################################### + + +[sla_print:*common print ANYCUBIC SLA*] +compatible_printers_condition = family=="PHOTON MONO" +layer_height = 0.05 +output_filename_format = [input_filename_base].pwmx +pad_edge_radius = 0.5 +pad_enable = 0 +pad_max_merge_distance = 50 +pad_wall_height = 0 +pad_wall_thickness = 1 +pad_wall_slope = 45 +faded_layers = 8 +slice_closing_radius = 0.005 +support_base_diameter = 3 +support_base_height = 1 +support_critical_angle = 45 +support_density_at_45 = 250 +support_density_at_horizontal = 500 +support_head_front_diameter = 0.4 +support_head_penetration = 0.4 +support_head_width = 3 +support_max_bridge_length = 10 +support_minimal_z = 0 +support_object_elevation = 5 +support_pillar_diameter = 1 +support_pillar_connection_mode = zigzag +support_pillar_widening_factor = 0 +supports_enable = 1 +support_small_pillar_diameter_percent = 60% + +[sla_print:0.05 ANYCUBIC SLA] +inherits = *common print ANYCUBIC SLA* +layer_height = 0.05 + + +########### Materials + +[sla_material:*common ANYCUBIC SLA*] +compatible_printers_condition = family=="PHOTON MONO" +compatible_prints_condition = layer_height == 0.05 +exposure_time = 7 +initial_exposure_time = 40 +initial_layer_height = 0.05 +material_correction = 1,1,1 +material_notes = LIFT_DISTANCE=8.0\nLIFT_SPEED=2.5\nRETRACT_SPEED=3.0\nBOTTOM_LIFT_SPEED=2.0\nBOTTOM_LIFT_DISTANCE=9.0\nDELAY_BEFORE_EXPOSURE=0.5 + +[sla_material:*common 0.05 ANYCUBIC SLA*] +inherits = *common ANYCUBIC SLA* + +[sla_material:Generic Blue Resin MONO @0.05] +inherits = *common 0.05 ANYCUBIC SLA* +exposure_time = 2.5 +initial_exposure_time = 40 +material_type = Tough +material_vendor = Generic +material_colour = #6080EC + +########## Printers + +[printer:Anycubic Photon Mono X] +printer_technology = SLA +printer_model = PHOTON MONO X +printer_variant = default +default_sla_material_profile = Generic Blue Resin MONO @0.05 +default_sla_print_profile = 0.05 ANYCUBIC SLA +thumbnails = 224x168 +sla_archive_format = pwmx +bed_shape = 1.48x1.02,193.48x1.02,193.48x121.02,1.48x121.02 +display_height = 120 +display_orientation = landscape +display_mirror_x = 1 +display_mirror_y = 0 +display_pixels_x = 3840 +display_pixels_y = 2400 +display_width = 192 +max_print_height = 245 +elefant_foot_compensation = 0.2 +elefant_foot_min_width = 0.2 +min_exposure_time = 1 +max_exposure_time = 120 +min_initial_exposure_time = 1 +max_initial_exposure_time = 300 +printer_correction = 1,1,1 +gamma_correction = 1 +area_fill = 45 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.'\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PHOTONMONOX\n + + diff --git a/resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png b/resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..70ad47b63defdbd22c913d211d284a1bed729860 GIT binary patch literal 45953 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

?9hL)5+9SL}$(eIdYR*>(Nn1RXY00 z_y?`E?Af!*=GK;9okh|fAvy@noZ}L$pR6G6jMJbHt59MnO^?Eq7+qpai7pC^DKJI+ zDZ@{Xvg}iq;rYf;7KVOV(u+TZp^T3g1-%Afzw|IWdyZ(#3%gZ#&z|K~h>&lh;wH+(bC{MPT{ zS(>Rx;U=5>KfkshS%|mm;E!ehM#=#5AvE{{x>WwFY}3y{V6~G(wq6#Z~IRE z(R03w5B`t$bIZ@af=l-A;|rhrEdS)2zJa>(RvMrD>rb<=xWxP4`)+>bm9ONVJokHe z_IEs&|MBj3@Y-MeIhIyd(zb8K@pJ$B7XJO4-pG4@|6TmiAAW$Jc*QFj42Rjuq~^Cx z=XO(7#}BpRRT(vU99{p$zzS&L&(>cyWEw@>6(2tK@cCPz^Of3(OSzPYAfXsjS(`U* z*HM2X=AP~2A;-)gSN?ek0SS6@Y3oezf%5gFQk4A?0S+8IM1Of7Tk9tQp)3bHc;6j- z@ZG=3bXw8Wj%qUE!|!=J|LP65a_QAK@cl3QC9eP9{vm(#&i~B!{L>%h;rs97@dqE^ z|9JlQ@h9)Too9X9^ZB+Hyp$`h`#b#P>u=>l?|wU58z*`G5B~(c;gDC|{5_mFeuT2W z&=n_h!gM<2H-Gm%-2bJ!x%!Grx%;lWSkkb#xX7)){;T}6AN>(-`jMA%{S9Bot^e)a zyyJI%hf^mvc;#zf&(m(ak!StAujAo|9-ve(7!G*rfBX;p)KC8$FTCj{p7!Z^zlT-EW(Qr#|iJ{FmQ)8~^mCAL1=-~KfV!vWv_W50lH8mzUPI&qu_?z)5F z;ws`iS6aK8UVj+!N}<2Biq@LZ6sDtf8Z46uEG;d2PIOa&cp-ADG=sqcx4!w+yy)hi z=i`6+N4)XX|2NWK@;)lzD*;sfgU~MOrvJPytBA zb9XFEP$GpA=oRG<;Jz>2E9M*u!za2z$H-gPTK|X zryuPl1adJ*t&M-o7?d(-rO{|_Z6-C({MP64vRD5ad-h+3wGAlY#;1Q1|ME4z#^TCe z#5o3w%RJ}#H}Te6Ue4OlNBGb?-^S-Y`4Rr{cfSx@JF0q0caqm5M@ru{Jg%|$FPcR*iXP`YWrr^Yh6HKNRH~r|3 zBO;7}Q)_Gd%rD%+AO67~@VeK$iof~GkMjdB_&$E|mRmr>#`;O_yz7g+@jv}1-uQ+$ zP*-D4ojl2_|DV_Mw%cyy^}l=zfB6?5;YBySh+lm5EuA!QVPTQqzV+AnzVCf5Z+`1- zjMq=_(?9hueaexYV69sGU8~mHTg3-7Db-7c@G7OYfLH9>x9?fkTyxE!wC)tx`IXa$!$hH{OsT z;tMTwmUj`9ptJ;W!aS){?G~Ajf{_q56tJ+k!hu8AQcb6fH`i(E8dDTpas7=nP0i-1 zlkDApkb~EK9pkMHzV{_R$JajnncVrAPw<7m`8Y58rC;HSuX!4y%~M?a)TeXZ)1JX- zYlD3U4)W~hy@=6xlP}+QFW+<1kMq1A`Y}dZ8$9EizmqGj`Wn_w9Os8#`qTWJ;KStqR5z7~}J5 zi%{7HE0P)jKIjxmX>FY?aFRI9nFeigDu|7q*61o{2&xb(s+8QUNGl3sP)cJxaU$t8 zt7w-&+w%9%K%*$dxuEf^0=+fZImaR_Skj3SuaTND+WbhbXi%kJY(=!z!4BHBtEX0M zw|`FZj{bd|Q%dUslooN4Fm`$Hn8~aq+l#Ts$rw7mtg_#pB{}@wj+gJpO-k{C}%O`IChc3wZzl002ovPDHLk FV1nTQC-eXS literal 0 HcmV?d00001