diff --git a/CMakeLists.txt b/CMakeLists.txt
index 72fd87d224..6c6afe6710 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -506,6 +506,12 @@ endif ()
# Find the Cereal serialization library
find_package(cereal REQUIRED)
+add_library(libcereal INTERFACE)
+if (NOT TARGET cereal::cereal)
+ target_link_libraries(libcereal INTERFACE cereal)
+else()
+ target_link_libraries(libcereal INTERFACE cereal::cereal)
+endif()
# l10n
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
diff --git a/cmake/modules/FindTBB.cmake.in b/cmake/modules/FindTBB.cmake.in
index a7eafa545f..49e405c184 100644
--- a/cmake/modules/FindTBB.cmake.in
+++ b/cmake/modules/FindTBB.cmake.in
@@ -293,7 +293,7 @@ if(NOT TBB_FOUND)
# Create targets
##################################
- if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
+ if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND AND NOT TARGET TBB::tbb)
add_library(TBB::tbb UNKNOWN IMPORTED)
set_target_properties(TBB::tbb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS}"
diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake
index 2730854ebc..1ca6735cc3 100644
--- a/deps/wxWidgets/wxWidgets.cmake
+++ b/deps/wxWidgets/wxWidgets.cmake
@@ -12,8 +12,8 @@ endif()
prusaslicer_add_cmake_project(wxWidgets
# GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
# GIT_TAG tm_cross_compile #${_wx_git_tag}
- URL https://github.com/prusa3d/wxWidgets/archive/73f029adfcc82fb3aa4b01220a013f716e57d110.zip
- URL_HASH SHA256=c35fe0187db497b6a3f477e24ed5e307028657ff0c2554385810b6e7961ad2e4
+ URL https://github.com/prusa3d/wxWidgets/archive/489f6118256853cf5b299d595868641938566cdb.zip
+ URL_HASH SHA256=5b22d465377cedd8044bba69bea958b248953fd3628c1de4913a84d4e6f6175b
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG
CMAKE_ARGS
-DwxBUILD_PRECOMP=ON
diff --git a/resources/icons/legend_cog.svg b/resources/icons/legend_cog.svg
new file mode 100644
index 0000000000..9a55fb7f5c
--- /dev/null
+++ b/resources/icons/legend_cog.svg
@@ -0,0 +1,50 @@
+
+
+
+
diff --git a/resources/profiles/Anycubic.ini b/resources/profiles/Anycubic.ini
index 053aecbd59..821d87462c 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,94 @@ 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 Normal @ANYCUBIC]
+inherits = *common print ANYCUBIC SLA*
+layer_height = 0.05
+
+########### Materials
+
+[sla_material:*common ANYCUBIC SLA*]
+compatible_printers_condition = printer_notes=~/.*PHOTONMONOX.*/
+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
+compatible_printers_condition = printer_notes=~/.*PHOTONMONOX.*/
+
+########## 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 Normal @ANYCUBIC
+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 0000000000..70ad47b63d
Binary files /dev/null and b/resources/profiles/Anycubic/PHOTON MONO X_thumbnail.png differ
diff --git a/resources/shaders/printbed.fs b/resources/shaders/printbed.fs
index d1316ca2fe..bef0751580 100644
--- a/resources/shaders/printbed.fs
+++ b/resources/shaders/printbed.fs
@@ -1,6 +1,6 @@
#version 110
-const vec3 back_color_dark = vec3(0.235, 0.235, 0.235);
+const vec3 back_color_dark = vec3(0.235, 0.235, 0.235);
const vec3 back_color_light = vec3(0.365, 0.365, 0.365);
uniform sampler2D texture;
diff --git a/resources/shaders/printbed.vs b/resources/shaders/printbed.vs
index 7633017f12..3b3f8875d2 100644
--- a/resources/shaders/printbed.vs
+++ b/resources/shaders/printbed.vs
@@ -1,14 +1,9 @@
#version 110
-attribute vec3 v_position;
-attribute vec2 v_tex_coords;
-
varying vec2 tex_coords;
void main()
{
- gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position.x, v_position.y, v_position.z, 1.0);
- // the following line leads to crash on some Intel graphics card
- //gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position, 1.0);
- tex_coords = v_tex_coords;
+ gl_Position = ftransform();
+ tex_coords = gl_MultiTexCoord0.xy;
}
diff --git a/resources/shaders/toolpaths_cog.fs b/resources/shaders/toolpaths_cog.fs
new file mode 100644
index 0000000000..f88d79b969
--- /dev/null
+++ b/resources/shaders/toolpaths_cog.fs
@@ -0,0 +1,18 @@
+#version 110
+
+const vec4 BLACK = vec4(vec3(0.1), 1.0);
+const vec4 WHITE = vec4(vec3(1.0), 1.0);
+
+const float emission_factor = 0.25;
+
+// x = tainted, y = specular;
+varying vec2 intensity;
+varying vec3 world_position;
+uniform vec3 world_center;
+
+void main()
+{
+ vec3 delta = world_position - world_center;
+ vec4 color = delta.x * delta.y * delta.z > 0.0 ? BLACK : WHITE;
+ gl_FragColor = vec4(vec3(intensity.y) + color.rgb * (intensity.x + emission_factor), 1.0);
+}
diff --git a/resources/shaders/toolpaths_cog.vs b/resources/shaders/toolpaths_cog.vs
new file mode 100644
index 0000000000..c7b1abfdbd
--- /dev/null
+++ b/resources/shaders/toolpaths_cog.vs
@@ -0,0 +1,40 @@
+#version 110
+
+#define INTENSITY_CORRECTION 0.6
+
+// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
+const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
+#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SHININESS 20.0
+
+// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
+const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
+#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
+
+#define INTENSITY_AMBIENT 0.3
+
+// x = tainted, y = specular;
+varying vec2 intensity;
+varying vec3 world_position;
+
+void main()
+{
+ // First transform the normal into camera space and normalize the result.
+ vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
+
+ // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
+ // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
+ float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
+
+ intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
+ vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
+
+ // Perform the same lighting calculation for the 2nd light source (no specular applied).
+ NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
+ intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
+
+ world_position = gl_Vertex.xyz;
+ gl_Position = ftransform();
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9e89e82f67..61a2a90d8b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -14,6 +14,7 @@ add_subdirectory(Shiny)
add_subdirectory(semver)
add_subdirectory(libigl)
add_subdirectory(hints)
+add_subdirectory(qoi)
# Adding libnest2d project for bin packing...
add_subdirectory(libnest2d)
@@ -125,7 +126,7 @@ if (NOT WIN32 AND NOT APPLE)
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
endif ()
-target_link_libraries(PrusaSlicer libslic3r cereal)
+target_link_libraries(PrusaSlicer libslic3r libcereal)
if (APPLE)
# add_compile_options(-stdlib=libc++)
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 2648fba9e2..4483d60102 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -498,8 +498,6 @@ int CLI::run(int argc, char **argv)
std::string outfile = m_config.opt_string("output");
Print fff_print;
SLAPrint sla_print;
- SL1Archive sla_archive(sla_print.printer_config());
- sla_print.set_printer(&sla_archive);
sla_print.set_status_callback(
[](const PrintBase::SlicingStatus& s)
{
@@ -539,7 +537,7 @@ int CLI::run(int argc, char **argv)
outfile = sla_print.output_filepath(outfile);
// We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata
outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
- sla_archive.export_print(outfile_final, sla_print);
+ sla_print.export_print(outfile_final);
}
if (outfile != outfile_final) {
if (Slic3r::rename_file(outfile, outfile_final)) {
@@ -838,6 +836,7 @@ extern "C" {
"leak:libnvidia-glcore.so\n" // For NVidia driver.
"leak:libnvidia-tls.so\n" // For NVidia driver.
"leak:terminator_CreateDevice\n" // For Intel Vulkan drivers.
+ "leak:swrast_dri.so\n" // For Mesa 3D software driver.
;
}
}
diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index db0e54e60d..f2c3ef0837 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -165,8 +165,9 @@ namespace ImGui
const wchar_t LegendColorChanges = 0x2612;
const wchar_t LegendPausePrints = 0x2613;
const wchar_t LegendCustomGCodes = 0x2614;
- const wchar_t LegendShells = 0x2615;
- const wchar_t LegendToolMarker = 0x2616;
+ const wchar_t LegendCOG = 0x2615;
+ const wchar_t LegendShells = 0x2616;
+ const wchar_t LegendToolMarker = 0x2617;
// void MyFunction(const char* name, const MyMatrix44& v);
}
diff --git a/src/libslic3r/BuildVolume.hpp b/src/libslic3r/BuildVolume.hpp
index 6b928d48b0..be8d224c39 100644
--- a/src/libslic3r/BuildVolume.hpp
+++ b/src/libslic3r/BuildVolume.hpp
@@ -94,6 +94,12 @@ public:
// Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices.
bool all_paths_inside_vertices_and_normals_interleaved(const std::vector& paths, const Eigen::AlignedBox& bbox, bool ignore_bottom = true) const;
+
+#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
+ const std::pair, std::vector>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; }
+ const std::pair, std::vector>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; }
+#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
+
private:
// Source definition of the print bed geometry (PrintConfig::bed_shape)
std::vector m_bed_shape;
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 39bc1aa474..5e8d681f1a 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -94,10 +94,18 @@ set(SLIC3R_SOURCES
Format/objparser.hpp
Format/STL.cpp
Format/STL.hpp
+ Format/SLAArchive.hpp
+ Format/SLAArchive.cpp
Format/SL1.hpp
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
+ GCode/Thumbnails.hpp
GCode/CoolingBuffer.cpp
GCode/CoolingBuffer.hpp
GCode/FindReplace.cpp
@@ -344,10 +352,13 @@ encoding_check(libslic3r)
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0)
target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(libslic3r PUBLIC ${EXPAT_INCLUDE_DIRS})
+
+find_package(JPEG REQUIRED)
+
target_link_libraries(libslic3r
libnest2d
admesh
- cereal
+ libcereal
libigl
miniz
boost_libs
@@ -362,6 +373,8 @@ target_link_libraries(libslic3r
${CMAKE_DL_LIBS}
PNG::PNG
ZLIB::ZLIB
+ JPEG::JPEG
+ qoi
)
if (TARGET OpenVDB::openvdb)
diff --git a/src/libslic3r/Execution/Execution.hpp b/src/libslic3r/Execution/Execution.hpp
index 62e49cfeb3..dcfd86bde8 100644
--- a/src/libslic3r/Execution/Execution.hpp
+++ b/src/libslic3r/Execution/Execution.hpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include "libslic3r/libslic3r.h"
@@ -44,7 +45,8 @@ size_t max_concurrency(const EP &ep)
template>
void for_each(const EP &ep, It from, It to, Fn &&fn, size_t granularity = 1)
{
- AsTraits::for_each(ep, from, to, std::forward(fn), granularity);
+ AsTraits::for_each(ep, from, to, std::forward(fn),
+ std::max(granularity, size_t(1)));
}
// A reduce operation with the execution policy passed as argument.
@@ -68,7 +70,7 @@ T reduce(const EP & ep,
return AsTraits::reduce(ep, from, to, init,
std::forward(mergefn),
std::forward(accessfn),
- granularity);
+ std::max(granularity, size_t(1)));
}
// An overload of reduce method to be used with iterators as 'from' and 'to'
@@ -87,7 +89,7 @@ T reduce(const EP &ep,
{
return reduce(
ep, from, to, init, std::forward(mergefn),
- [](const auto &i) { return i; }, granularity);
+ [](const auto &i) { return i; }, std::max(granularity, size_t(1)));
}
template{},
- std::forward(accessfn), granularity);
+ std::forward(accessfn),
+ std::max(granularity, size_t(1)));
}
@@ -119,7 +122,7 @@ T accumulate(const EP &ep,
{
return reduce(
ep, from, to, init, std::plus{}, [](const auto &i) { return i; },
- granularity);
+ std::max(granularity, size_t(1)));
}
} // namespace execution_policy
diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp
index f7a5c50071..d2ff650974 100644
--- a/src/libslic3r/Extruder.cpp
+++ b/src/libslic3r/Extruder.cpp
@@ -1,4 +1,5 @@
#include "Extruder.hpp"
+#include "GCodeWriter.hpp"
#include "PrintConfig.hpp"
namespace Slic3r {
@@ -7,24 +8,24 @@ Extruder::Extruder(unsigned int id, GCodeConfig *config) :
m_id(id),
m_config(config)
{
- reset();
-
// cache values that are going to be called often
m_e_per_mm3 = this->extrusion_multiplier();
if (! m_config->use_volumetric_e)
m_e_per_mm3 /= this->filament_crossection();
}
-double Extruder::extrude(double dE)
+std::pair Extruder::extrude(double dE)
{
// in case of relative E distances we always reset to 0 before any output
if (m_config->use_relative_e_distances)
m_E = 0.;
+ // Quantize extruder delta to G-code resolution.
+ dE = GCodeFormatter::quantize_e(dE);
m_E += dE;
m_absolute_E += dE;
if (dE < 0.)
m_retracted -= dE;
- return dE;
+ return std::make_pair(dE, m_E);
}
/* This method makes sure the extruder is retracted by the specified amount
@@ -34,28 +35,33 @@ double Extruder::extrude(double dE)
The restart_extra argument sets the extra length to be used for
unretraction. If we're actually performing a retraction, any restart_extra
value supplied will overwrite the previous one if any. */
-double Extruder::retract(double length, double restart_extra)
+std::pair Extruder::retract(double retract_length, double restart_extra)
{
// in case of relative E distances we always reset to 0 before any output
if (m_config->use_relative_e_distances)
m_E = 0.;
- double to_retract = std::max(0., length - m_retracted);
+ // Quantize extruder delta to G-code resolution.
+ double to_retract = this->retract_to_go(retract_length);
if (to_retract > 0.) {
m_E -= to_retract;
m_absolute_E -= to_retract;
m_retracted += to_retract;
- m_restart_extra = restart_extra;
+ m_restart_extra = restart_extra;
}
- return to_retract;
+ return std::make_pair(to_retract, m_E);
}
-double Extruder::unretract()
+double Extruder::retract_to_go(double retract_length) const
{
- double dE = m_retracted + m_restart_extra;
- this->extrude(dE);
+ return std::max(0., GCodeFormatter::quantize_e(retract_length - m_retracted));
+}
+
+std::pair Extruder::unretract()
+{
+ auto [dE, emitE] = this->extrude(m_retracted + m_restart_extra);
m_retracted = 0.;
m_restart_extra = 0.;
- return dE;
+ return std::make_pair(dE, emitE);
}
// Used filament volume in mm^3.
diff --git a/src/libslic3r/Extruder.hpp b/src/libslic3r/Extruder.hpp
index e9c6927f85..7491b1c8f5 100644
--- a/src/libslic3r/Extruder.hpp
+++ b/src/libslic3r/Extruder.hpp
@@ -12,22 +12,24 @@ class Extruder
{
public:
Extruder(unsigned int id, GCodeConfig *config);
- virtual ~Extruder() {}
-
- void reset() {
- m_E = 0;
- m_absolute_E = 0;
- m_retracted = 0;
- m_restart_extra = 0;
- }
+ ~Extruder() = default;
unsigned int id() const { return m_id; }
- double extrude(double dE);
- double retract(double length, double restart_extra);
- double unretract();
- double E() const { return m_E; }
- void reset_E() { m_E = 0.; }
+ // Following three methods emit:
+ // first - extrusion delta
+ // second - number to emit to G-code: This may be delta for relative mode or a distance from last reset_E() for absolute mode.
+ // They also quantize the E axis to G-code resolution.
+ std::pair extrude(double dE);
+ std::pair retract(double retract_length, double restart_extra);
+ std::pair unretract();
+ // How much to retract yet before retract_length is reached?
+ // The value is quantized to G-code resolution.
+ double retract_to_go(double retract_length) const;
+
+ // Reset the current state of the E axis (this is only needed for relative extruder addressing mode anyways).
+ // Returns true if the extruder was non-zero before reset.
+ bool reset_E() { bool modified = m_E != 0; m_E = 0.; return modified; }
double e_per_mm(double mm3_per_mm) const { return mm3_per_mm * m_e_per_mm3; }
double e_per_mm3() const { return m_e_per_mm3; }
// Used filament volume in mm^3.
@@ -57,14 +59,16 @@ private:
GCodeConfig *m_config;
// Print-wide global ID of this extruder.
unsigned int m_id;
- // Current state of the extruder axis, may be resetted if use_relative_e_distances.
- double m_E;
+ // Current state of the extruder axis.
+ // For absolute extruder addressing, it is the current state since the last reset (G92 E0) issued at the end of the last retraction.
+ // For relative extruder addressing, it is the E axis difference emitted into the G-code the last time.
+ double m_E { 0 };
// Current state of the extruder tachometer, used to output the extruded_volume() and used_filament() statistics.
- double m_absolute_E;
+ double m_absolute_E { 0 };
// Current positive amount of retraction.
- double m_retracted;
+ double m_retracted { 0 };
// When retracted, this value stores the extra amount of priming on deretraction.
- double m_restart_extra;
+ double m_restart_extra { 0 };
double m_e_per_mm3;
};
@@ -76,4 +80,4 @@ inline bool operator> (const Extruder &e1, const Extruder &e2) { return e1.id()
}
-#endif
+#endif // slic3r_Extruder_hpp_
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 0e44cc14ea..4259562aa8 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -121,18 +121,21 @@ static constexpr const char* LAST_TRIANGLE_ID_ATTR = "lastid";
static constexpr const char* OBJECT_TYPE = "object";
static constexpr const char* VOLUME_TYPE = "volume";
-static constexpr const char* NAME_KEY = "name";
-static constexpr const char* MODIFIER_KEY = "modifier";
+static constexpr const char* NAME_KEY = "name";
+static constexpr const char* MODIFIER_KEY = "modifier";
static constexpr const char* VOLUME_TYPE_KEY = "volume_type";
-static constexpr const char* MATRIX_KEY = "matrix";
-static constexpr const char* SOURCE_FILE_KEY = "source_file";
-static constexpr const char* SOURCE_OBJECT_ID_KEY = "source_object_id";
-static constexpr const char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
-static constexpr const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
-static constexpr const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
-static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
-static constexpr const char* SOURCE_IN_INCHES = "source_in_inches";
-static constexpr const char* SOURCE_IN_METERS = "source_in_meters";
+static constexpr const char* MATRIX_KEY = "matrix";
+static constexpr const char* SOURCE_FILE_KEY = "source_file";
+static constexpr const char* SOURCE_OBJECT_ID_KEY = "source_object_id";
+static constexpr const char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
+static constexpr const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
+static constexpr const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
+static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
+static constexpr const char* SOURCE_IN_INCHES_KEY = "source_in_inches";
+static constexpr const char* SOURCE_IN_METERS_KEY = "source_in_meters";
+#if ENABLE_RELOAD_FROM_DISK_REWORK
+static constexpr const char* SOURCE_IS_BUILTIN_VOLUME_KEY = "source_is_builtin_volume";
+#endif // ENABLE_RELOAD_FROM_DISK_REWORK
static constexpr const char* MESH_STAT_EDGES_FIXED = "edges_fixed";
static constexpr const char* MESH_STAT_DEGENERATED_FACETS = "degenerate_facets";
@@ -816,6 +819,20 @@ namespace Slic3r {
return false;
}
+#if ENABLE_RELOAD_FROM_DISK_REWORK
+ for (int obj_id = 0; obj_id < int(model.objects.size()); ++obj_id) {
+ ModelObject* o = model.objects[obj_id];
+ for (int vol_id = 0; vol_id < int(o->volumes.size()); ++vol_id) {
+ ModelVolume* v = o->volumes[vol_id];
+ if (v->source.input_file.empty())
+ v->source.input_file = v->name.empty() ? filename : v->name;
+ if (v->source.volume_idx == -1)
+ v->source.volume_idx = vol_id;
+ if (v->source.object_idx == -1)
+ v->source.object_idx = obj_id;
+ }
+ }
+#else
int object_idx = 0;
for (ModelObject* o : model.objects) {
int volume_idx = 0;
@@ -831,6 +848,7 @@ namespace Slic3r {
}
++object_idx;
}
+#endif // ENABLE_RELOAD_FROM_DISK_REWORK
// // fixes the min z of the model if negative
// model.adjust_min_z();
@@ -2052,15 +2070,19 @@ namespace Slic3r {
else if (metadata.key == SOURCE_VOLUME_ID_KEY)
volume->source.volume_idx = ::atoi(metadata.value.c_str());
else if (metadata.key == SOURCE_OFFSET_X_KEY)
- volume->source.mesh_offset(0) = ::atof(metadata.value.c_str());
+ volume->source.mesh_offset.x() = ::atof(metadata.value.c_str());
else if (metadata.key == SOURCE_OFFSET_Y_KEY)
- volume->source.mesh_offset(1) = ::atof(metadata.value.c_str());
+ volume->source.mesh_offset.y() = ::atof(metadata.value.c_str());
else if (metadata.key == SOURCE_OFFSET_Z_KEY)
- volume->source.mesh_offset(2) = ::atof(metadata.value.c_str());
- else if (metadata.key == SOURCE_IN_INCHES)
+ volume->source.mesh_offset.z() = ::atof(metadata.value.c_str());
+ else if (metadata.key == SOURCE_IN_INCHES_KEY)
volume->source.is_converted_from_inches = metadata.value == "1";
- else if (metadata.key == SOURCE_IN_METERS)
+ else if (metadata.key == SOURCE_IN_METERS_KEY)
volume->source.is_converted_from_meters = metadata.value == "1";
+#if ENABLE_RELOAD_FROM_DISK_REWORK
+ else if (metadata.key == SOURCE_IS_BUILTIN_VOLUME_KEY)
+ volume->source.is_from_builtin_objects = metadata.value == "1";
+#endif // ENABLE_RELOAD_FROM_DISK_REWORK
else
volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
}
@@ -2981,7 +3003,7 @@ namespace Slic3r {
// stores volume's local matrix
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
- Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
+ const Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
stream << matrix(r, c);
@@ -3005,9 +3027,13 @@ namespace Slic3r {
}
assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters);
if (volume->source.is_converted_from_inches)
- stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+ stream << prefix << SOURCE_IN_INCHES_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
else if (volume->source.is_converted_from_meters)
- stream << prefix << SOURCE_IN_METERS << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+ stream << prefix << SOURCE_IN_METERS_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+#if ENABLE_RELOAD_FROM_DISK_REWORK
+ if (volume->source.is_from_builtin_objects)
+ stream << prefix << SOURCE_IS_BUILTIN_VOLUME_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+#endif // ENABLE_RELOAD_FROM_DISK_REWORK
}
// stores volume's config data
@@ -3110,6 +3136,36 @@ static void handle_legacy_project_loaded(unsigned int version_project_file, Dyna
}
}
+bool is_project_3mf(const std::string& filename)
+{
+ mz_zip_archive archive;
+ mz_zip_zero_struct(&archive);
+
+ if (!open_zip_reader(&archive, filename))
+ return false;
+
+ mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
+
+ // loop the entries to search for config
+ mz_zip_archive_file_stat stat;
+ bool config_found = false;
+ for (mz_uint i = 0; i < num_entries; ++i) {
+ if (mz_zip_reader_file_stat(&archive, i, &stat)) {
+ std::string name(stat.m_filename);
+ std::replace(name.begin(), name.end(), '\\', '/');
+
+ if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE)) {
+ config_found = true;
+ break;
+ }
+ }
+ }
+
+ close_zip_reader(&archive);
+
+ return config_found;
+}
+
bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version)
{
if (path == nullptr || model == nullptr)
diff --git a/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp
index b91e90da74..5c2a2e672a 100644
--- a/src/libslic3r/Format/3mf.hpp
+++ b/src/libslic3r/Format/3mf.hpp
@@ -29,6 +29,9 @@ namespace Slic3r {
class DynamicPrintConfig;
struct ThumbnailData;
+ // Returns true if the 3mf file with the given filename is a PrusaSlicer project file (i.e. if it contains a config).
+ extern bool is_project_3mf(const std::string& filename);
+
// Load the content of a 3mf file into the given model and preset bundle.
extern bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version);
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index a8b66b15b1..f2a902758e 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -657,11 +657,16 @@ void AMFParserContext::endElement(const char * /* name */)
if (bool has_transform = !m_volume_transform.isApprox(Transform3d::Identity(), 1e-10); has_transform)
m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
- if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART)) {
+#if ENABLE_RELOAD_FROM_DISK_REWORK
+ if (m_volume->source.input_file.empty()) {
+#else
+ if (m_volume->source.input_file.empty() && m_volume->type() == ModelVolumeType::MODEL_PART) {
+#endif // ENABLE_RELOAD_FROM_DISK_REWORK
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1;
m_volume->center_geometry_after_creation();
- } else
+ }
+ else
// pass false if the mesh offset has been already taken from the data
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
@@ -792,46 +797,44 @@ void AMFParserContext::endElement(const char * /* name */)
// Is this volume a modifier volume?
// "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART);
- } else if (strcmp(opt_key, "volume_type") == 0) {
+ }
+ else if (strcmp(opt_key, "volume_type") == 0)
m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
- }
- else if (strcmp(opt_key, "matrix") == 0) {
+ else if (strcmp(opt_key, "matrix") == 0)
m_volume_transform = Slic3r::Geometry::transform3d_from_string(m_value[1]);
- }
- else if (strcmp(opt_key, "source_file") == 0) {
+ else if (strcmp(opt_key, "source_file") == 0)
m_volume->source.input_file = m_value[1];
- }
- else if (strcmp(opt_key, "source_object_id") == 0) {
+ else if (strcmp(opt_key, "source_object_id") == 0)
m_volume->source.object_idx = ::atoi(m_value[1].c_str());
- }
- else if (strcmp(opt_key, "source_volume_id") == 0) {
+ else if (strcmp(opt_key, "source_volume_id") == 0)
m_volume->source.volume_idx = ::atoi(m_value[1].c_str());
- }
- else if (strcmp(opt_key, "source_offset_x") == 0) {
- m_volume->source.mesh_offset(0) = ::atof(m_value[1].c_str());
- }
- else if (strcmp(opt_key, "source_offset_y") == 0) {
- m_volume->source.mesh_offset(1) = ::atof(m_value[1].c_str());
- }
- else if (strcmp(opt_key, "source_offset_z") == 0) {
- m_volume->source.mesh_offset(2) = ::atof(m_value[1].c_str());
- }
- else if (strcmp(opt_key, "source_in_inches") == 0) {
+ else if (strcmp(opt_key, "source_offset_x") == 0)
+ m_volume->source.mesh_offset.x() = ::atof(m_value[1].c_str());
+ else if (strcmp(opt_key, "source_offset_y") == 0)
+ m_volume->source.mesh_offset.y() = ::atof(m_value[1].c_str());
+ else if (strcmp(opt_key, "source_offset_z") == 0)
+ m_volume->source.mesh_offset.z() = ::atof(m_value[1].c_str());
+ else if (strcmp(opt_key, "source_in_inches") == 0)
m_volume->source.is_converted_from_inches = m_value[1] == "1";
- }
- else if (strcmp(opt_key, "source_in_meters") == 0) {
+ else if (strcmp(opt_key, "source_in_meters") == 0)
m_volume->source.is_converted_from_meters = m_value[1] == "1";
- }
+#if ENABLE_RELOAD_FROM_DISK_REWORK
+ else if (strcmp(opt_key, "source_is_builtin_volume") == 0)
+ m_volume->source.is_from_builtin_objects = m_value[1] == "1";
+#endif // ENABLE_RELOAD_FROM_DISK_REWORK
}
- } else if (m_path.size() == 3) {
+ }
+ else if (m_path.size() == 3) {
if (m_path[1] == NODE_TYPE_MATERIAL) {
if (m_material)
m_material->attributes[m_value[0]] = m_value[1];
- } else if (m_path[1] == NODE_TYPE_OBJECT) {
+ }
+ else if (m_path[1] == NODE_TYPE_OBJECT) {
if (m_object && m_value[0] == "name")
m_object->name = std::move(m_value[1]);
}
- } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME) {
+ }
+ else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME) {
if (m_volume && m_value[0] == "name")
m_volume->name = std::move(m_value[1]);
}
@@ -919,7 +922,11 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, ConfigSubstitut
unsigned int counter = 0;
for (ModelVolume* v : o->volumes) {
++counter;
+#if ENABLE_RELOAD_FROM_DISK_REWORK
+ if (v->source.input_file.empty())
+#else
if (v->source.input_file.empty() && v->type() == ModelVolumeType::MODEL_PART)
+#endif // ENABLE_RELOAD_FROM_DISK_REWORK
v->source.input_file = path;
if (v->name.empty()) {
v->name = o->name;
@@ -1068,7 +1075,11 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, ConfigSubsti
for (ModelObject *o : model->objects)
for (ModelVolume *v : o->volumes)
- if (v->source.input_file.empty() && (v->type() == ModelVolumeType::MODEL_PART))
+#if ENABLE_RELOAD_FROM_DISK_REWORK
+ if (v->source.input_file.empty())
+#else
+ if (v->source.input_file.empty() && v->type() == ModelVolumeType::MODEL_PART)
+#endif // ENABLE_RELOAD_FROM_DISK_REWORK
v->source.input_file = path;
return true;
@@ -1237,18 +1248,15 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
stream << " ";
const Transform3d& matrix = volume->get_matrix() * volume->source.transform.get_matrix();
stream << std::setprecision(std::numeric_limits::max_digits10);
- for (int r = 0; r < 4; ++r)
- {
- for (int c = 0; c < 4; ++c)
- {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
stream << matrix(r, c);
- if ((r != 3) || (c != 3))
+ if (r != 3 || c != 3)
stream << " ";
}
}
stream << "\n";
- if (!volume->source.input_file.empty())
- {
+ if (!volume->source.input_file.empty()) {
std::string input_file = xml_escape(fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
stream << " " << input_file << "\n";
stream << " " << volume->source.object_idx << "\n";
@@ -1262,12 +1270,16 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
stream << " 1\n";
else if (volume->source.is_converted_from_meters)
stream << " 1\n";
- stream << std::setprecision(std::numeric_limits::max_digits10);
+#if ENABLE_RELOAD_FROM_DISK_REWORK
+ if (volume->source.is_from_builtin_objects)
+ stream << " 1\n";
+#endif // ENABLE_RELOAD_FROM_DISK_REWORK
+ stream << std::setprecision(std::numeric_limits::max_digits10);
const indexed_triangle_set &its = volume->mesh().its;
for (size_t i = 0; i < its.indices.size(); ++i) {
stream << " \n";
for (int j = 0; j < 3; ++j)
- stream << " " << its.indices[i][j] + vertices_offset << "\n";
+ stream << " " << its.indices[i][j] + vertices_offset << "\n";
stream << " \n";
}
stream << " \n";
diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp
index 6ed8c5ebed..df10ce0465 100644
--- a/src/libslic3r/Format/SL1.cpp
+++ b/src/libslic3r/Format/SL1.cpp
@@ -20,11 +20,14 @@
#include "libslic3r/miniz_extension.hpp"
#include "libslic3r/PNGReadWrite.hpp"
#include "libslic3r/LocalesUtils.hpp"
+#include "libslic3r/GCode/ThumbnailData.hpp"
#include
#include
#include
+#include
+
namespace marchsq {
template<> struct _RasterTraits {
@@ -446,8 +449,8 @@ void fill_slicerconf(ConfMap &m, const SLAPrint &print)
std::unique_ptr SL1Archive::create_raster() const
{
- sla::RasterBase::Resolution res;
- sla::RasterBase::PixelDim pxdim;
+ sla::Resolution res;
+ sla::PixelDim pxdim;
std::array mirror;
double w = m_cfg.display_width.getFloat();
@@ -468,8 +471,8 @@ std::unique_ptr SL1Archive::create_raster() const
std::swap(pw, ph);
}
- res = sla::RasterBase::Resolution{pw, ph};
- pxdim = sla::RasterBase::PixelDim{w / pw, h / 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();
@@ -482,10 +485,31 @@ sla::RasterEncoder SL1Archive::get_encoder() const
return sla::PNGRasterEncoder{};
}
-void SL1Archive::export_print(Zipper& zipper,
- const SLAPrint &print,
- const std::string &prjname)
+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",
+ static_cast(png_data),
+ png_size);
+
+ mz_free(png_data);
+ }
+}
+
+void SL1Archive::export_print(const std::string fname,
+ const SLAPrint &print,
+ const ThumbnailsList &thumbnails,
+ const std::string &prjname)
+{
+ Zipper zipper{fname};
+
std::string project =
prjname.empty() ?
boost::filesystem::path(zipper.get_filename()).stem().string() :
@@ -512,6 +536,12 @@ void SL1Archive::export_print(Zipper& zipper,
zipper.add_entry(imgname.c_str(), rst.data(), rst.size());
}
+
+ for (const ThumbnailData& data : thumbnails)
+ if (data.is_valid())
+ write_thumbnail(zipper, data);
+
+ zipper.finalize();
} catch(std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
// Rethrow the exception
diff --git a/src/libslic3r/Format/SL1.hpp b/src/libslic3r/Format/SL1.hpp
index 46a82e1b85..493550db4b 100644
--- a/src/libslic3r/Format/SL1.hpp
+++ b/src/libslic3r/Format/SL1.hpp
@@ -3,8 +3,12 @@
#include
+#include "SLAArchive.hpp"
+
#include "libslic3r/Zipper.hpp"
-#include "libslic3r/SLAPrint.hpp"
+#include "libslic3r/PrintConfig.hpp"
+
+struct indexed_triangle_set;
namespace Slic3r {
@@ -15,27 +19,19 @@ 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:
SL1Archive() = default;
explicit SL1Archive(const SLAPrinterConfig &cfg): m_cfg(cfg) {}
explicit SL1Archive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {}
-
- void export_print(Zipper &zipper, const SLAPrint &print, const std::string &projectname = "");
- void export_print(const std::string &fname, const SLAPrint &print, const std::string &projectname = "")
- {
- Zipper zipper(fname);
- export_print(zipper, print, projectname);
- }
-
- void apply(const SLAPrinterConfig &cfg) override
- {
- auto diff = m_cfg.diff(cfg);
- if (!diff.empty()) {
- m_cfg.apply_only(cfg, diff);
- m_layers = {};
- }
- }
+
+ void export_print(const std::string fname,
+ const SLAPrint &print,
+ const ThumbnailsList &thumbnails,
+ const std::string &projectname = "") override;
};
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
diff --git a/src/libslic3r/Format/SL1_SVG.cpp b/src/libslic3r/Format/SL1_SVG.cpp
new file mode 100644
index 0000000000..372348283a
--- /dev/null
+++ b/src/libslic3r/Format/SL1_SVG.cpp
@@ -0,0 +1,227 @@
+#include "SL1_SVG.hpp"
+#include "SLA/RasterBase.hpp"
+#include "libslic3r/LocalesUtils.hpp"
+#include "libslic3r/ClipperUtils.hpp"
+#include "libslic3r/BoundingBox.hpp"
+
+#include
+#include
+#include
+#include
+using namespace std::literals;
+
+namespace Slic3r {
+
+namespace {
+
+size_t constexpr coord_t_bufsize = 40;
+
+char const* decimal_from(coord_t snumber, char* buffer)
+{
+ std::make_unsigned_t number = 0;
+
+ char* ret = buffer;
+
+ if( snumber < 0 ) {
+ *buffer++ = '-';
+ number = -snumber;
+ } else
+ number = snumber;
+
+ if( number == 0 ) {
+ *buffer++ = '0';
+ } else {
+ char* p_first = buffer;
+ while( number != 0 ) {
+ *buffer++ = '0' + number % 10;
+ number /= 10;
+ }
+ std::reverse( p_first, buffer );
+ }
+
+ *buffer = '\0';
+
+ return ret;
+}
+
+inline std::string coord2str(coord_t crd)
+{
+ char buf[coord_t_bufsize];
+ return decimal_from(crd, buf);
+}
+
+void transform(ExPolygon &ep, const sla::RasterBase::Trafo &tr, const BoundingBox &bb)
+{
+ if (tr.flipXY) {
+ for (auto &p : ep.contour.points) std::swap(p.x(), p.y());
+ for (auto &h : ep.holes)
+ for (auto &p : h.points) std::swap(p.x(), p.y());
+ }
+
+ if (tr.mirror_x){
+ for (auto &p : ep.contour.points) p.x() = bb.max.x() - p.x() + bb.min.x();
+ for (auto &h : ep.holes)
+ for (auto &p : h.points) p.x() = bb.max.x() - p.x() + bb.min.x();
+ }
+
+ if (tr.mirror_y){
+ for (auto &p : ep.contour.points) p.y() = bb.max.y() - p.y() + bb.min.y();
+ for (auto &h : ep.holes)
+ for (auto &p : h.points) p.y() = bb.max.y() - p.y() + bb.min.y();
+ }
+}
+
+void append_svg(std::string &buf, const Polygon &poly)
+{
+ if (poly.points.empty())
+ return;
+
+ auto c = poly.points.front();
+
+ char intbuf[coord_t_bufsize];
+
+ buf += "\n"sv;
+}
+
+} // namespace
+
+// A fake raster from SVG
+class SVGRaster : public sla::RasterBase {
+ // Resolution here will be used for svg boundaries
+ BoundingBox m_bb;
+ sla::Resolution m_res;
+ Trafo m_trafo;
+ Vec2d m_sc;
+
+ std::string m_svg;
+
+public:
+ SVGRaster(const BoundingBox &svgarea, sla::Resolution res, Trafo tr = {})
+ : m_bb{svgarea}
+ , m_res{res}
+ , m_trafo{tr}
+ , m_sc{double(m_res.width_px) / m_bb.size().x(), double(m_res.height_px) / m_bb.size().y()}
+ {
+ // Inside the svg header, the boundaries will be defined in mm to
+ // the actual bed size. The viewport is then defined to work with our
+ // scaled coordinates. All the exported polygons will be in these scaled
+ // coordinates but svg rendering software will interpret them correctly
+ // in mm due to the header's definition.
+ std::string wf = float_to_string_decimal_point(unscaled(m_bb.size().x()));
+ std::string hf = float_to_string_decimal_point(unscaled(m_bb.size().y()));
+ std::string w = coord2str(coord_t(m_res.width_px));
+ std::string h = coord2str(coord_t(m_res.height_px));
+
+ // Notice the header also defines the fill-rule as nonzero which should
+ // generate correct results for our ExPolygons.
+
+ // Add svg header.
+ m_svg =
+ "\n"
+ "\n"
+ "\n"sv;
+
+ data.reserve(m_svg.size() + std::size(finish));
+
+ std::copy(m_svg.begin(), m_svg.end(), std::back_inserter(data));
+ std::copy(finish.begin(), finish.end() - 1, std::back_inserter(data));
+
+ return sla::EncodedRaster{std::move(data), "svg"};
+ }
+};
+
+std::unique_ptr SL1_SVGArchive::create_raster() const
+{
+ auto w = cfg().display_width.getFloat();
+ auto h = cfg().display_height.getFloat();
+
+// auto res_x = size_t(cfg().display_pixels_x.getInt());
+// auto res_y = size_t(cfg().display_pixels_y.getInt());
+ float precision_nm = scaled(cfg().sla_output_precision.getFloat());
+ size_t res_x = std::round(scaled(w) / precision_nm);
+ size_t res_y = std::round(scaled(h) / precision_nm);
+
+ std::array mirror;
+
+ mirror[X] = cfg().display_mirror_x.getBool();
+ mirror[Y] = cfg().display_mirror_y.getBool();
+
+ auto ro = 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(res_x, res_y);
+ }
+
+ BoundingBox svgarea{{0, 0}, {scaled(w), scaled(h)}};
+
+ sla::RasterBase::Trafo tr{orientation, mirror};
+
+ // Gamma does not really make sense in an svg, right?
+ // double gamma = cfg().gamma_correction.getFloat();
+ return std::make_unique(svgarea, sla::Resolution{res_x, res_y}, tr);
+}
+
+sla::RasterEncoder SL1_SVGArchive::get_encoder() const
+{
+ return nullptr;
+}
+
+} // namespace Slic3r
diff --git a/src/libslic3r/Format/SL1_SVG.hpp b/src/libslic3r/Format/SL1_SVG.hpp
new file mode 100644
index 0000000000..a3afbcdfff
--- /dev/null
+++ b/src/libslic3r/Format/SL1_SVG.hpp
@@ -0,0 +1,22 @@
+#ifndef SL1_SVG_HPP
+#define SL1_SVG_HPP
+
+#include "SL1.hpp"
+
+namespace Slic3r {
+
+class SL1_SVGArchive: public SL1Archive {
+protected:
+
+ // Override the factory methods to produce svg instead of a real raster.
+ std::unique_ptr create_raster() const override;
+ sla::RasterEncoder get_encoder() const override;
+
+public:
+
+ using SL1Archive::SL1Archive;
+};
+
+} // namespace Slic3r
+
+#endif // SL1_SVG_HPP
diff --git a/src/libslic3r/Format/SLAArchive.cpp b/src/libslic3r/Format/SLAArchive.cpp
new file mode 100644
index 0000000000..8e2bf2824f
--- /dev/null
+++ b/src/libslic3r/Format/SLAArchive.cpp
@@ -0,0 +1,74 @@
+#include "SLAArchive.hpp"
+
+#include "SL1.hpp"
+#include "SL1_SVG.hpp"
+#include "pwmx.hpp"
+
+#include "libslic3r/libslic3r.h"
+
+#include
+#include