Merge branch 'lm_seq_final'

This commit is contained in:
Lukas Matena 2025-02-17 11:03:33 +01:00
commit a7268efdfd
69 changed files with 32007 additions and 682 deletions

15
deps/+z3/z3.cmake vendored Normal file
View File

@ -0,0 +1,15 @@
add_cmake_project(z3
URL https://github.com/Z3Prover/z3/archive/refs/tags/z3-4.13.0.zip
URL_HASH SHA256=81543736dcbbbcb037a7df55d0be596245d509f3f69f56610df32728e48ee050
CMAKE_ARGS
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DZ3_INCLUDE_GIT_HASH=OFF
-DZ3_INCLUDE_GIT_DESCRIBE=OFF
-DZ3_USE_LIB_GMP=OFF
-DZ3_BUILD_LIBZ3_SHARED=OFF
-DZ3_ENABLE_EXAMPLE_TARGETS=OFF
-DZ3_ALWAYS_BUILD_DOCS=OFF
-DZ3_BUILD_EXECUTABLE=OFF
-DZ3_BUILD_TEST_EXECUTABLES=OFF
)

View File

@ -0,0 +1,286 @@
{
"printers": [
{
"printer_notes_regex": ".*PRINTER_MODEL_MK4(?!S).*",
"gantry_model_filename": "prusa3d_mk4_gantry.stl",
"slices": [
{
"height": "0",
"type": "convex",
"polygons": [
"-5,-5;5,-5;5,5;-5,5"
]
},
{
"height": "3",
"type": "convex",
"polygons": [
"-9,-17; 40,-17; 40,44; -9,44",
"-36,-44; 40,-44; 40,-13; -36,-13"
]
},
{
"height": "22",
"type": "convex",
"polygons": [
"-41,-45; 16,-45; 16,22; -41,22",
"11,-45; 39,-45; 39,45; 11,45"
]
},
{
"height": "11",
"type": "box",
"polygons": [
"-300,-23;300,-23;300,-35;-300,-35"
]
},
{
"height": "13",
"type": "box",
"polygons": [
"-13,-84;11,-84;11,-38;-13,-38",
"11,-300;300,-300;300,-84;11,-84"
]
}
]
},
{
"printer_notes_regex": ".*PRINTER_MODEL_MK4S.*",
"gantry_model_filename": "prusa3d_mk4s_gantry.stl",
"slices": [
{
"height": "0",
"type": "convex",
"polygons": [
"-29,-19; 28,-19; 28,37; -29,37"
]
},
{
"height": "5",
"type": "convex",
"polygons": [
"-37,-40; 34,-40; 34,43; -37,43"
]
},
{
"height": "7",
"type": "convex",
"polygons": [
"-49,7; 27,7; 27,83; -49,83",
"-42,-46; 40,-46; 40,14; -42,14"
]
},
{
"height": "11",
"type": "box",
"polygons": [
"-300,-38; 300,-38; 300,-22; -300,-22"
]
},
{
"height": "12",
"type": "box",
"polygons": [
"-14,-85; 11,-85; 11,-39; -14,-39",
"-14,-300; 300,-300; 300,-39; -14,-39"
]
}
]
},
{
"printer_notes_regex": ".*PRINTER_MODEL_MK3.*",
"gantry_model_filename": "prusa3d_mk3s_gantry.stl",
"slices": [
{
"height": "0",
"type": "convex",
"polygons": [
"-5,-5;5,-5;5,5;-5,5",
"-30,-12;-14,-12;-14,2;-30,2"
]
},
{
"height": "2",
"type": "convex",
"polygons": [
"-20,-38;44,-38;44,18;-20,18"
]
},
{
"height": "6",
"type": "convex",
"polygons": [
"-34,-43;37,-43;37,16;-34,16",
"-45,9;37,9;37,69;-45,69"
]
},
{
"height": "11",
"type": "box",
"polygons": [
"-8,-82;8,-82;8,-36;-8,-36",
"-8,-82;250,-82;250,-300;-8,-300"
]
},
{
"height": "17",
"type": "box",
"polygons": [
"-300,-35;300,-35;300,-21;-300,-21"
]
}
]
},
{
"printer_notes_regex": ".*PRINTER_MODEL_MINI.*",
"gantry_model_filename": "prusa3d_mini_gantry.stl",
"slices": [
{
"height": "0",
"type": "convex",
"polygons": [
"-5,-5;5,-5;5,5;-5,5",
"24,-3;35,-3;35,10;24,10",
"-5,4;5,4;5,18;-5,18"
]
},
{
"height": "3",
"type": "convex",
"polygons": [
"-16,-44;37,-44;37,31;-16,31"
]
},
{
"height": "10",
"type": "convex",
"polygons": [
"-10,-88;10,-88;10,-38;-10,-38",
"-17,-44;43,-44;43,33;-17,33"
]
},
{
"height": "22",
"type": "box",
"polygons": [
"-200,-28;200,-28;200,-14;-200,-14"
]
},
{
"height": "100",
"type": "box",
"polygons": [
"-200,-200;10,-200;10,10;-200,10"
]
}
]
},
{
"printer_notes_regex": ".*PRINTER_MODEL_XL.*",
"gantry_model_filename": "prusa3d_xl_gantry.stl",
"slices": [
{
"height": "0",
"type": "convex",
"polygons": [
"-5,-5;5,-5;5,5;-5,5"
]
},
{
"height": "2",
"type": "convex",
"polygons": [
"-10,-47;34,-47;34,16;-10,16",
"-34,13;32,13;32,67;-34,67"
]
},
{
"height": "23",
"type": "convex",
"polygons": [
"-42,11;32,11;32,66;-42,66",
"-33,-37;43,-37;43,18;-33,18",
"-13,-68;47,-68;47,-30;-13,-30"
]
},
{
"height": "19",
"type": "box",
"polygons": [
"-400,24;400,24;400,50;-400,50"
]
},
{
"height": "220",
"type": "box",
"polygons": [
"-400,-400;400,-400;400,10;-400,10"
]
},
{
"height": "280",
"type": "box",
"polygons": [
"-400,-400;400,-400;400,400;-400,400"
]
}
]
},
{
"printer_notes_regex": ".*PRINTER_MODEL_COREONE.*",
"gantry_model_filename": "prusa3d_coreone_gantry.stl",
"slices": [
{
"height": "0",
"type": "convex",
"polygons": [
"-33,-37; 27,-37; 27,19; -33,19"
]
},
{
"height": "2",
"type": "convex",
"polygons": [
"-40,-69; 27,-69; 27,20; -40,20"
]
},
{
"height": "23",
"type": "convex",
"polygons": [
"-44,-52; 35,-52; 35,23; -44,23"
]
},
{
"height": "23",
"type": "convex",
"polygons": [
"-37,-98; 26,-98; 26,-47; -37,-47"
]
},
{
"height": "33",
"type": "box",
"polygons": [
"-300,-49; 300,-49; 300,-24; -300,-24"
]
},
{
"height": "120",
"type": "box",
"polygons": [
"-300,-300; 300,-300; 300,10; -300,10"
]
},
{
"height": "170",
"type": "box",
"polygons": [
"-300,-300; 300,-300; 300,300; -300,300"
]
}
]
}
]
}

Binary file not shown.

Binary file not shown.

View File

@ -79,6 +79,7 @@ src/slic3r/GUI/GUI_Preview.cpp
src/slic3r/GUI/HintNotification.cpp
src/slic3r/GUI/ImGuiWrapper.cpp
src/slic3r/GUI/Jobs/ArrangeJob2.cpp
src/slic3r/GUI/Jobs/SeqArrangeJob.cpp
src/slic3r/GUI/Jobs/EmbossJob.cpp
src/slic3r/GUI/Jobs/PlaterWorker.hpp
src/slic3r/GUI/Jobs/RotoptimizeJob.hpp

View File

@ -0,0 +1,18 @@
#version 110
const vec2 ZERO = vec2(0.0, 0.0);
uniform vec4 uniform_color;
// x = diffuse, y = specular;
varying vec2 intensity;
varying vec2 clipping_planes_dots;
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a);
}

View File

@ -0,0 +1,58 @@
#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 LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
//#define LIGHT_FRONT_SHININESS 5.0
#define INTENSITY_AMBIENT 0.3
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
uniform mat3 view_normal_matrix;
uniform mat4 volume_world_matrix;
// Clipping planes used to clip the tool marker model.
uniform vec4 clipping_planes[2];
attribute vec3 v_position;
attribute vec3 v_normal;
// x = diffuse, y = specular;
varying vec2 intensity;
varying vec2 clipping_planes_dots;
void main()
{
// First transform the normal into camera space and normalize the result.
vec3 eye_normal = normalize(view_normal_matrix * v_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(eye_normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
vec4 position = view_model_matrix * vec4(v_position, 1.0);
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source (no specular applied).
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
// Point in homogenous coordinates.
vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0);
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
clipping_planes_dots.x = dot(world_pos, clipping_planes[0]);
clipping_planes_dots.y = dot(world_pos, clipping_planes[1]);
gl_Position = projection_matrix * position;
}

View File

@ -0,0 +1,19 @@
#version 140
const vec2 ZERO = vec2(0.0, 0.0);
uniform vec4 uniform_color;
// x = diffuse, y = specular;
in vec2 intensity;
in vec2 clipping_planes_dots;
out vec4 out_color;
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
out_color = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a);
}

View File

@ -0,0 +1,58 @@
#version 140
#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 LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
//#define LIGHT_FRONT_SHININESS 5.0
#define INTENSITY_AMBIENT 0.3
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
uniform mat3 view_normal_matrix;
uniform mat4 volume_world_matrix;
// Clipping planes used to clip the tool marker model.
uniform vec4 clipping_planes[2];
in vec3 v_position;
in vec3 v_normal;
// x = diffuse, y = specular;
out vec2 intensity;
out vec2 clipping_planes_dots;
void main()
{
// First transform the normal into camera space and normalize the result.
vec3 eye_normal = normalize(view_normal_matrix * v_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(eye_normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
vec4 position = view_model_matrix * vec4(v_position, 1.0);
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source (no specular applied).
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
// Point in homogenous coordinates.
vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0);
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
clipping_planes_dots.x = dot(world_pos, clipping_planes[0]);
clipping_planes_dots.y = dot(world_pos, clipping_planes[1]);
gl_Position = projection_matrix * position;
}

View File

@ -0,0 +1,19 @@
#version 100
precision highp float;
const vec2 ZERO = vec2(0.0, 0.0);
uniform vec4 uniform_color;
// x = diffuse, y = specular;
varying vec2 intensity;
varying vec2 clipping_planes_dots;
void main()
{
if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * intensity.x, uniform_color.a);
}

View File

@ -0,0 +1,58 @@
#version 100
#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 LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
//#define LIGHT_FRONT_SHININESS 5.0
#define INTENSITY_AMBIENT 0.3
uniform mat4 view_model_matrix;
uniform mat4 projection_matrix;
uniform mat3 view_normal_matrix;
uniform mat4 volume_world_matrix;
// Clipping planes used to clip the tool marker model.
uniform vec4 clipping_planes[2];
attribute vec3 v_position;
attribute vec3 v_normal;
// x = diffuse, y = specular;
varying vec2 intensity;
varying vec2 clipping_planes_dots;
void main()
{
// First transform the normal into camera space and normalize the result.
vec3 eye_normal = normalize(view_normal_matrix * v_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(eye_normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
vec4 position = view_model_matrix * vec4(v_position, 1.0);
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source (no specular applied).
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
// Point in homogenous coordinates.
vec4 world_pos = volume_world_matrix * vec4(v_position, 1.0);
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
clipping_planes_dots.x = dot(world_pos, clipping_planes[0]);
clipping_planes_dots.y = dot(world_pos, clipping_planes[1]);
gl_Position = projection_matrix * position;
}

View File

@ -20,6 +20,8 @@ endif ()
add_subdirectory(slic3r-arrange)
add_subdirectory(slic3r-arrange-wrapper)
add_subdirectory(libseqarrange)
if (SLIC3R_GUI)
add_subdirectory(libvgcode)
@ -136,39 +138,39 @@ if (NOT WIN32 AND NOT APPLE)
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
endif ()
target_link_libraries(PrusaSlicer libslic3r libcereal slic3r-arrange-wrapper stb_image)
target_link_libraries(PrusaSlicer PRIVATE libslic3r libcereal slic3r-arrange-wrapper libseqarrange stb_image)
if (APPLE)
# add_compile_options(-stdlib=libc++)
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
# -liconv: boost links to libiconv by default
target_link_libraries(PrusaSlicer "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
target_link_libraries(PrusaSlicer PRIVATE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
elseif (MSVC)
# Manifest is provided through PrusaSlicer.rc, don't generate your own.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO")
else ()
# Boost on Raspberry-Pi does not link to pthreads explicitely.
target_link_libraries(PrusaSlicer ${CMAKE_DL_LIBS} -lstdc++ Threads::Threads)
target_link_libraries(PrusaSlicer PRIVATE ${CMAKE_DL_LIBS} -lstdc++ Threads::Threads)
endif ()
# Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries.
if (SLIC3R_GUI)
# target_link_libraries(PrusaSlicer ws2_32 uxtheme setupapi libslic3r_gui ${wxWidgets_LIBRARIES})
target_link_libraries(PrusaSlicer libslic3r_gui)
target_link_libraries(PrusaSlicer PRIVATE libslic3r_gui)
if (MSVC)
# Generate debug symbols even in release mode.
target_link_options(PrusaSlicer PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
target_link_libraries(PrusaSlicer user32.lib Setupapi.lib)
target_link_libraries(PrusaSlicer PRIVATE user32.lib Setupapi.lib)
elseif (MINGW)
target_link_libraries(PrusaSlicer ws2_32 uxtheme setupapi)
target_link_libraries(PrusaSlicer PRIVATE ws2_32 uxtheme setupapi)
elseif (APPLE)
target_link_libraries(PrusaSlicer "-framework OpenGL")
target_link_libraries(PrusaSlicer PRIVATE "-framework OpenGL")
else ()
target_link_libraries(PrusaSlicer -ldl)
target_link_libraries(PrusaSlicer PRIVATE -ldl)
endif ()
if (WIN32)
find_library(PSAPI_LIB NAMES Psapi)
target_link_libraries(PrusaSlicer ${PSAPI_LIB})
target_link_libraries(PrusaSlicer PRIVATE ${PSAPI_LIB})
endif ()
endif ()

View File

@ -0,0 +1,35 @@
find_package(Z3 REQUIRED)
slic3r_remap_configs("z3::libz3" RelWithDebInfo Release)
add_library(libseqarrange STATIC src/seq_interface.cpp src/seq_preprocess.cpp src/seq_sequential.cpp src/seq_utilities.cpp)
target_include_directories(libseqarrange PUBLIC include PRIVATE src )
target_link_libraries(libseqarrange PUBLIC libslic3r PRIVATE z3::libz3)
add_executable(sequential_decimator src/sequential_decimator.cpp)
target_include_directories(sequential_decimator PRIVATE include)
target_link_libraries(sequential_decimator PRIVATE libseqarrange)
#if (SLIC3R_BUILD_TESTS)
# find_package(Catch2 3.8 REQUIRED)
# add_executable(libseqarrange_tests test/prusaparts.cpp test/seq_test_polygon.cpp test/seq_test_sequential.cpp test/seq_test_preprocess.cpp test/seq_test_interface.cpp)
# target_include_directories(libseqarrange_tests PRIVATE src )
# target_link_libraries(libseqarrange_tests PRIVATE Catch2::Catch2WithMain libseqarrange)
# set(_catch_args "exclude:[NotWorking] exclude:[Slow]")
# list(APPEND _catch_args "${CATCH_EXTRA_ARGS}")
# add_test(NAME libseqarrange_tests
# COMMAND libseqarrange_tests ${_catch_args}
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
#endif()

View File

@ -0,0 +1,216 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2024
* Company: Prusa Research
*
* File: seq_interface.hpp
*
* Interface of the sequential printing SMT model for Prusa Slic3r
*/
/*================================================================*/
#ifndef __SEQ_INTERFACE_HPP__
#define __SEQ_INTERFACE_HPP__
/*----------------------------------------------------------------*/
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
/*----------------------------------------------------------------*/
using namespace Slic3r;
/*----------------------------------------------------------------*/
namespace Sequential
{
class ObjectTooLargeException : public std::runtime_error { public: explicit ObjectTooLargeException(const std::string& msg) : std::runtime_error(msg) {}};
class InternalErrorException : public std::runtime_error { public: explicit InternalErrorException(const std::string& msg) : std::runtime_error(msg) {} };
/*----------------------------------------------------------------*/
struct PrinterGeometry
{
// must be convex; for best performance a rectangle is recommended
Slic3r::Polygon plate;
// at least height 0 (corresponding to nozzle) must be present in convex_heights
std::set<coord_t> convex_heights;
std::set<coord_t> box_heights;
// <height, polygons at given height>, at least one polygon must be present for height 0
std::map<coord_t, std::vector<Slic3r::Polygon> > extruder_slices;
bool convert_Geometry2PlateBounds(Slic3r::BoundingBox &plate_bounding_box, Slic3r::Polygon &plate_bounding_polygon) const;
};
/*----------------------------------------------------------------*/
enum DecimationPrecision
{
SEQ_DECIMATION_PRECISION_UNDEFINED,
SEQ_DECIMATION_PRECISION_LOW,
SEQ_DECIMATION_PRECISION_HIGH
};
/*----------------------------------------------------------------*/
struct SolverConfiguration
{
SolverConfiguration();
SolverConfiguration(const PrinterGeometry &printer_geometry);
void set_DecimationPrecision(DecimationPrecision decimation_precision);
void set_ObjectGroupSize(int object_group_size);
void setup(const PrinterGeometry &printer_geometry);
static double convert_DecimationPrecision2Tolerance(DecimationPrecision decimation_precision);
int bounding_box_size_optimization_step;
int minimum_bounding_box_size;
Slic3r::BoundingBox plate_bounding_box;
Slic3r::Polygon plate_bounding_polygon;
int max_refines;
int object_group_size;
int fixed_object_grouping_limit;
int temporal_spread;
DecimationPrecision decimation_precision;
std::string optimization_timeout;
};
/*----------------------------------------------------------------*/
struct ObjectToPrint
{
int id = 0;
bool glued_to_next = false; /* the next object must be scheduled right after this object */
coord_t total_height = 0;
std::vector<std::pair<coord_t, Slic3r::Polygon>> pgns_at_height;
};
struct ScheduledObject {
ScheduledObject(int _id, coord_t _x, coord_t _y)
: id(_id)
, x(_x)
, y(_y) { /* */ }
int id = 0;
coord_t x, y;
};
struct ScheduledPlate {
std::vector<ScheduledObject> scheduled_objects;
};
/*----------------------------------------------------------------*/
/*
This is the recommended interface for checking sequential printability.
Returns true if objects are sequentially printable according to their
ordering in the input vector and the arrangement on the plate specified
by the schedule. Printable means that the extruder never hits printed
objects during printing. Otherwise returns false.
Please see the corresponding example of usage (seq_test_interface.cpp)
Note: The function always succeeds, does not throw any exception.
*/
bool check_ScheduledObjectsForSequentialPrintability(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const std::vector<ObjectToPrint> &objects_to_print,
const std::vector<ScheduledPlate> &scheduled_plates);
/*
This is a variant of the interface for checking sequential printability.
If not sequentially printable returns a pair of object IDs that are in conflict,
that is, when the second object is printed the extruder will collide with the
first object. The returned conflict is not necessarily the first collision to
occur when printing the object according to the given input schedule.
Note: The function always succeeds, does not throw any exception.
*/
std::optional<std::pair<int, int> > check_ScheduledObjectsForSequentialConflict(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const std::vector<ObjectToPrint> &objects_to_print,
const std::vector<ScheduledPlate> &scheduled_plates);
/*----------------------------------------------------------------*/
/*
This is the recommended interface for sequential scheduling/arranging.
Please see the corresponding example of usage (seq_test_interface.cpp)
Note: The function should succeed except the case when there is an
object that does not fit on the plate and in the case when the solver
is unable to scedule even single object on the plate. The latter case
is detected by timeout and should not normally happen. These failures
are reported via exceptions.
The trans_bed_glue parameter should be set to false when scheduling
all objects. If only objects on a separate bed are scheduled, then
trans_bed_glue should be set to true when there is an object on the
previous bed that is temporally glued to the first scheduled object.
In such a case, the first object will be scheduled as first temporally.
*/
std::vector<ScheduledPlate> schedule_ObjectsForSequentialPrint(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const std::vector<ObjectToPrint> &objects_to_print,
std::function<void(int)> progress_callback = [](int progress){});
void schedule_ObjectsForSequentialPrint(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const std::vector<ObjectToPrint> &objects_to_print,
std::vector<ScheduledPlate> &scheduled_plates,
std::function<void(int)> progress_callback = [](int progress){});
/*----------------------------------------------------------------*/
/*
The following interface is for more internal use.
*/
int schedule_ObjectsForSequentialPrint(const SolverConfiguration &solver_configuration,
const std::vector<ObjectToPrint> &objects_to_print,
std::vector<ScheduledPlate> &scheduled_plates,
std::function<void(int)> progress_callback = [](int progress){});
void setup_ExtruderUnreachableZones(const SolverConfiguration &solver_configuration,
std::vector<std::vector<Slic3r::Polygon> > &convex_unreachable_zones,
std::vector<std::vector<Slic3r::Polygon> > &box_unreachable_zones);
int schedule_ObjectsForSequentialPrint(const SolverConfiguration &solver_configuration,
const std::vector<ObjectToPrint> &objects_to_print,
const std::vector<std::vector<Slic3r::Polygon> > &convex_unreachable_zones,
const std::vector<std::vector<Slic3r::Polygon> > &box_unreachable_zones,
std::vector<ScheduledPlate> &scheduled_plates,
std::function<void(int)> progress_callback = [](int progress){});
/*----------------------------------------------------------------*/
} // namespace Sequential
/*----------------------------------------------------------------*/
#endif /* __SEQ_INTERFACE_HPP__ */

View File

@ -0,0 +1,73 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2025
*
* File: seq_defs.h
*
* Definitions of useful macros.
*/
/*================================================================*/
#ifndef __SEQ_DEFS_HPP__
#define __SEQ_DEFS_HPP__
/*----------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <set>
/*----------------------------------------------------------------*/
using namespace std;
#define SEQ_UNUSED(x)
//#define DEBUG
//#define PROFILE
typedef wchar_t wchar;
typedef std::basic_string<char> string;
typedef std::vector<string> strings_vector;
typedef std::set<string> strings_set;
/*----------------------------------------------------------------*/
extern const string INDENT;
/*----------------------------------------------------------------*/
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
#define DFR(x,y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
#define ABS(x) (((x) < 0) ? -(x) : (x))
#define SGN(x) (((x) < 0) ? -(-1) : ((x) > 0) ? 1 : 0)
/*----------------------------------------------------------------*/
#ifdef DEBUG
#define ASSERT(condition) \
{ \
if (!(condition)) \
{ \
printf("ASSERT: assertion failed (file: %s, line:%d).\n", __FILE__, __LINE__); \
fflush(NULL); \
exit(-1); \
} \
}
#else
#define ASSERT(condition)
#endif /* DEBUG */
/*----------------------------------------------------------------*/
#endif /* __SEQ_DEFS_HPP__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,234 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2025
* Company: Prusa Research
*
* File: seq_preprocess.hpp
*
* Object preprocessing for the sequential printing SMT model.
*/
/*================================================================*/
#ifndef __SEQ_PREPROCESS_HPP__
#define __SEQ_PREPROCESS_HPP__
/*----------------------------------------------------------------*/
#include "seq_sequential.hpp"
/*----------------------------------------------------------------*/
namespace Sequential
{
/*----------------------------------------------------------------*/
const coord_t SEQ_SLICER_SCALE_FACTOR = 100000;
const double SEQ_POLYGON_DECIMATION_GROW_FACTOR = 1.005;
/*----------------------------------------------------------------*/
struct ObjectToPrint;
/*----------------------------------------------------------------*/
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_NOZZLE_LEVEL_MK3S;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_EXTRUDER_LEVEL_MK3S;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_HOSE_LEVEL_MK3S;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_GANTRY_LEVEL_MK3S;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_ALL_LEVELS_MK3S;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_CONVEX_LEVELS_MK3S;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_BOX_LEVELS_MK3S;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_NOZZLE_LEVEL_MK4;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_EXTRUDER_LEVEL_MK4;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_HOSE_LEVEL_MK4;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_GANTRY_LEVEL_MK4;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_ALL_LEVELS_MK4;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_CONVEX_LEVELS_MK4;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_BOX_LEVELS_MK4;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_NOZZLE_LEVEL_XL;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_EXTRUDER_LEVEL_XL;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_HOSE_LEVEL_XL;
extern const std::vector<Slic3r::Polygon> SEQ_UNREACHABLE_POLYGON_GANTRY_LEVEL_XL;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_ALL_LEVELS_XL;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_CONVEX_LEVELS_XL;
extern const std::vector<std::vector<Slic3r::Polygon> > SEQ_UNREACHABLE_POLYGON_BOX_LEVELS_XL;
/*----------------------------------------------------------------*/
Rational scaleDown_CoordinateForSequentialSolver(coord_t x);
void scaleDown_PolygonForSequentialSolver(const Slic3r::Polygon &polygon,
Slic3r::Polygon &scaled_polygon);
void scaleDown_PolygonForSequentialSolver(coord_t scale_factor,
const Slic3r::Polygon &polygon,
Slic3r::Polygon &scaled_polygon);
Slic3r::Polygon scaleDown_PolygonForSequentialSolver(coord_t scale_factor, const Slic3r::Polygon &polygon);
void scaleUp_PositionForSlicer(const Rational &position_X,
const Rational &position_Y,
coord_t &scaled_position_X,
coord_t &scaled_position_Y);
void scaleUp_PositionForSlicer(coord_t scale_factor,
const Rational &position_X,
const Rational &position_Y,
coord_t &scaled_position_X,
coord_t &scaled_position_Y);
void scaleUp_PositionForSlicer(double position_X,
double position_Y,
coord_t &scaled_position_X,
coord_t &scaled_position_Y);
void scaleUp_PositionForSlicer(coord_t scale_factor,
double position_X,
double position_Y,
coord_t &scaled_position_X,
coord_t &scaled_position_Y);
Slic3r::Polygon scaleUp_PolygonForSlicer(const Slic3r::Polygon &polygon);
Slic3r::Polygon scaleUp_PolygonForSlicer(coord_t scale_factor, const Slic3r::Polygon &polygon);
Slic3r::Polygon scaleUp_PolygonForSlicer(const Slic3r::Polygon &polygon, double x_pos, double y_pos);
Slic3r::Polygon scaleUp_PolygonForSlicer(coord_t scale_factor, const Slic3r::Polygon &polygon, double x_pos, double y_pos);
void ground_PolygonByBoundingBox(Slic3r::Polygon &polygon);
void ground_PolygonByFirstPoint(Slic3r::Polygon &polygon);
void shift_Polygon(Slic3r::Polygon &polygon, coord_t x_offset, coord_t y_offset);
void shift_Polygon(Slic3r::Polygon &polygon, const Slic3r::Point &offset);
/*----------------------------------------------------------------*/
Slic3r::Polygon transform_UpsideDown(const SolverConfiguration &solver_configuration, const Slic3r::Polygon &polygon);
Slic3r::Polygon transform_UpsideDown(const SolverConfiguration &solver_configuration, coord_t scale_factor, const Slic3r::Polygon &polygon);
void transform_UpsideDown(const SolverConfiguration &solver_configuration,
const coord_t &scaled_x_pos,
const coord_t &scaled_y_pos,
coord_t &transformed_x_pos,
coord_t &transformed_y_pos);
void transform_UpsideDown(const SolverConfiguration &solver_configuration,
coord_t scale_factor,
const coord_t &scaled_x_pos,
const coord_t &scaled_y_pos,
coord_t &transformed_x_pos,
coord_t &transformed_y_pos);
/*----------------------------------------------------------------*/
void grow_PolygonForContainedness(coord_t center_x, coord_t center_y, Slic3r::Polygon &polygon);
void decimate_PolygonForSequentialSolver(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
Slic3r::Polygon &scale_down_polygon,
bool extra_safety);
void decimate_PolygonForSequentialSolver(double DP_tolerance,
const Slic3r::Polygon &polygon,
Slic3r::Polygon &decimated_polygon,
bool extra_safety);
void extend_PolygonConvexUnreachableZone(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
const std::vector<Slic3r::Polygon> &extruder_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void extend_PolygonBoxUnreachableZone(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
const std::vector<Slic3r::Polygon> &extruder_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void extend_PolygonBoxUnreachableZone(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
const std::vector<Slic3r::Polygon> &extruder_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void prepare_ExtruderPolygons(const SolverConfiguration &solver_configuration,
const PrinterGeometry &printer_geometry,
const ObjectToPrint &object_to_print,
std::vector<Slic3r::Polygon> &convex_level_polygons,
std::vector<Slic3r::Polygon> &box_level_polygons,
std::vector<std::vector<Slic3r::Polygon> > &extruder_convex_level_polygons,
std::vector<std::vector<Slic3r::Polygon> > &extruder_box_level_polygons,
bool extra_safety);
void prepare_ObjectPolygons(const SolverConfiguration &solver_configuration,
const std::vector<Slic3r::Polygon> &convex_level_polygons,
const std::vector<Slic3r::Polygon> &box_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_convex_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_box_level_polygons,
Slic3r::Polygon &object_polygon,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void prepare_UnreachableZonePolygons(const SolverConfiguration &solver_configuration,
const Slic3r::Polygon &polygon,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_convex_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_box_level_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
void prepare_UnreachableZonePolygons(const SolverConfiguration &solver_configuration,
const std::vector<Slic3r::Polygon> &convex_level_polygons,
const std::vector<Slic3r::Polygon> &box_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_convex_level_polygons,
const std::vector<std::vector<Slic3r::Polygon> > &extruder_box_level_polygons,
std::vector<Slic3r::Polygon> &unreachable_polygons);
bool check_PolygonSizeFitToPlate(const SolverConfiguration &solver_configuration, const Slic3r::Polygon &polygon);
bool check_PolygonPositionWithinPlate(const SolverConfiguration &solver_configuration, coord_t x, coord_t y, const Slic3r::Polygon &polygon);
bool check_PolygonSizeFitToPlate(const SolverConfiguration &solver_configuration, coord_t scale_factor, const Slic3r::Polygon &polygon);
bool check_PolygonPositionWithinPlate(const SolverConfiguration &solver_configuration, coord_t scale_factor, coord_t x, coord_t y, const Slic3r::Polygon &polygon);
/*----------------------------------------------------------------*/
bool check_PolygonConsumation(const std::vector<Slic3r::Polygon> &polygons, const std::vector<Slic3r::Polygon> &consumer_polygons);
std::vector<std::vector<Slic3r::Polygon> > simplify_UnreachableZonePolygons(const std::vector<std::vector<Slic3r::Polygon> > &unreachable_polygons);
void glue_LowObjects(std::vector<SolvableObject> &solvable_ojects);
/*----------------------------------------------------------------*/
double calc_PolygonArea(const Slic3r::Polygon &polygon);
double calc_PolygonUnreachableZoneArea(const std::vector<Slic3r::Polygon> &unreachable_polygons);
double calc_PolygonUnreachableZoneArea(const Slic3r::Polygon &polygon,
const std::vector<Slic3r::Polygon> &unreachable_polygons);
double calc_PolygonArea(const std::vector<Slic3r::Polygon> &polygons);
double calc_PolygonArea(const std::vector<int> &fixed,
const std::vector<int> &undecided,
const std::vector<Slic3r::Polygon> &polygons);
double calc_PolygonUnreachableZoneArea(const std::vector<Slic3r::Polygon> &polygons,
const std::vector<std::vector<Slic3r::Polygon> > &unreachable_polygons);
/*----------------------------------------------------------------*/
} // namespace Sequential
/*----------------------------------------------------------------*/
#endif /* __SEQ_PREPROCESS_HPP__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2025
* Company: Prusa Research
*
* File: seq_utilities.cpp
*
* Various utilities for sequential print.
*/
/*================================================================*/
#include <sstream>
#include <iostream>
#include <fstream>
#include "seq_defs.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "seq_utilities.hpp"
#include "seq_preprocess.hpp"
/*----------------------------------------------------------------*/
using namespace std;
using namespace Slic3r;
/*----------------------------------------------------------------*/
namespace Sequential
{
/*----------------------------------------------------------------*/
bool find_and_remove(std::string &src, const std::string &key)
{
size_t pos = src.find(key);
if (pos != std::string::npos)
{
src.erase(pos, key.length());
return true;
}
return false;
}
std::vector<ObjectToPrint> load_exported_data_from_file(const std::string &filename)
{
std::ifstream in(filename);
if (!in)
{
throw std::runtime_error("NO EXPORTED FILE WAS FOUND");
}
return load_exported_data_from_stream(in);
}
std::vector<ObjectToPrint> load_exported_data_from_text(const std::string &data_text)
{
std::istringstream iss(data_text);
return load_exported_data_from_stream(iss);
}
std::vector<ObjectToPrint> load_exported_data_from_stream(std::istream &data_stream)
{
std::vector<ObjectToPrint> objects_to_print;
std::string line;
while (data_stream)
{
std::getline(data_stream, line);
if (find_and_remove(line, "OBJECT_ID")) {
objects_to_print.push_back(ObjectToPrint());
objects_to_print.back().id = std::stoi(line);
}
if (find_and_remove(line, "TOTAL_HEIGHT"))
{
objects_to_print.back().total_height = std::stoi(line);
}
if (find_and_remove(line, "POLYGON_AT_HEIGHT"))
{
objects_to_print.back().pgns_at_height.emplace_back(std::make_pair(std::stoi(line), Polygon()));
}
if (find_and_remove(line, "POINT"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
Point pt(std::stoi(val), 0);
ss >> val;
pt.y() = std::stoi(val);
objects_to_print.back().pgns_at_height.back().second.append(pt);
}
}
return objects_to_print;
}
int load_printer_geometry_from_file(const std::string &filename, PrinterGeometry &printer_geometry)
{
std::ifstream in(filename);
if (!in)
{
throw std::runtime_error("NO PRINTER GEOMETRY FILE WAS FOUND");
}
return load_printer_geometry_from_stream(in, printer_geometry);
}
int load_printer_geometry_from_text(const std::string &geometry_text, PrinterGeometry &printer_geometry)
{
std::istringstream iss(geometry_text);
return load_printer_geometry_from_stream(iss, printer_geometry);
}
int load_printer_geometry_from_stream(std::istream &geometry_stream, PrinterGeometry &printer_geometry)
{
Polygon *current_polygon = NULL;
std::string line;
coord_t x_size = -1;
coord_t y_size = -1;
while (geometry_stream)
{
std::getline(geometry_stream, line);
if (find_and_remove(line, "POLYGON_AT_HEIGHT"))
{
coord_t height = std::stoi(line);
std::map<coord_t, std::vector<Polygon> >::iterator extruder_slice = printer_geometry.extruder_slices.find(height);
if (extruder_slice != printer_geometry.extruder_slices.end())
{
extruder_slice->second.push_back(Polygon());
current_polygon = &extruder_slice->second.back();
}
else
{
vector<Polygon> polygons;
polygons.push_back(Polygon());
current_polygon = &printer_geometry.extruder_slices.insert(std::pair(height, polygons)).first->second.back();
}
}
else if (find_and_remove(line, "POINT"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
Point pt(std::stoi(val), 0);
ss >> val;
pt.y() = std::stoi(val);
assert(current_polygon != NULL);
current_polygon->append(pt);
}
else if (find_and_remove(line, "CONVEX_HEIGHT"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
coord_t height = std::stoi(val);
printer_geometry.convex_heights.insert(height);
}
else if (find_and_remove(line, "BOX_HEIGHT"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
coord_t height = std::stoi(val);
printer_geometry.box_heights.insert(height);
}
else if (find_and_remove(line, "X_SIZE"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
x_size = std::stoi(val);
}
else if (find_and_remove(line, "Y_SIZE"))
{
std::stringstream ss(line);
std::string val;
ss >> val;
y_size = std::stoi(val);
}
}
assert(x_size > 0 && y_size > 0);
printer_geometry.plate = { {0, 0}, {x_size, 0}, {x_size, y_size}, {0, y_size} };
return 0;
}
void save_import_data_to_file(const std::string &filename,
const std::map<double, int> &scheduled_polygons,
const map<int, int> &original_index_map,
const vector<Rational> &poly_positions_X,
const vector<Rational> &poly_positions_Y)
{
std::ofstream out(filename);
if (!out)
{
throw std::runtime_error("CANNOT CREATE IMPORT FILE");
}
for (const auto& scheduled_polygon: scheduled_polygons)
{
coord_t X, Y;
scaleUp_PositionForSlicer(poly_positions_X[scheduled_polygon.second],
poly_positions_Y[scheduled_polygon.second],
X,
Y);
const auto& original_index = original_index_map.find(scheduled_polygon.second);
out << original_index->second << " " << X << " " << Y << endl;
}
}
/*----------------------------------------------------------------*/
} // namespace Sequential

View File

@ -0,0 +1,52 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2025
* Company: Prusa Research
*
* File: seq_utilities.hpp
*
* Various utilities for sequential print.
*/
/*================================================================*/
#ifndef __SEQ_UTILITIES_HPP__
#define __SEQ_UTILITIES_HPP__
/*----------------------------------------------------------------*/
#include "seq_sequential.hpp"
#include "libseqarrange/seq_interface.hpp"
/*----------------------------------------------------------------*/
namespace Sequential
{
bool find_and_remove(std::string &src, const std::string &key);
std::vector<ObjectToPrint> load_exported_data_from_file(const std::string &filename);
std::vector<ObjectToPrint> load_exported_data_from_text(const std::string &data_text);
std::vector<ObjectToPrint> load_exported_data_from_stream(std::istream &data_stream);
int load_printer_geometry_from_file(const std::string& filename, PrinterGeometry &printer_geometry);
int load_printer_geometry_from_text(const std::string& geometry_text, PrinterGeometry &printer_geometry);
int load_printer_geometry_from_stream(std::istream& geometry_stream, PrinterGeometry &printer_geometry);
void save_import_data_to_file(const std::string &filename,
const std::map<double, int> &scheduled_polygons,
const map<int, int> &original_index_map,
const vector<Rational> &poly_positions_X,
const vector<Rational> &poly_positions_Y);
/*----------------------------------------------------------------*/
} // namespace Sequential
/*----------------------------------------------------------------*/
#endif /* __SEQ_UTILITIES_HPP__ */

View File

@ -0,0 +1,427 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2025
* Company: Prusa Research
*
* File: sequential_decimator.cpp
*
* Polygon decimator utility (especially for extruder models).
*/
/*================================================================*/
#include <sstream>
#include <iostream>
#include <fstream>
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "libslic3r/SVG.hpp"
#include "seq_utilities.hpp"
#include "sequential_decimator.hpp"
/*----------------------------------------------------------------*/
using namespace Slic3r;
using namespace Sequential;
/*----------------------------------------------------------------*/
#define SCALE_FACTOR 50000.0
/*----------------------------------------------------------------*/
//const Point polygon_offset_0(28000000, -16000000); // body
//const Point polygon_offset_1(-3000000, -60000000); // hose
//const Point polygon_offset_2(28000000, -16000000); // fan
//const Point polygon_offset_3(0,-24000000); // gantry
//const Point polygon_offset_4(0,0); // nozzle
const int SEQ_PRUSA_MK3S_X_SIZE = 2500;
const int SEQ_PRUSA_MK3S_Y_SIZE = 2100;
/*----------------------------------------------------------------*/
void print_IntroductoryMessage(void)
{
printf("----------------------------------------------------------------\n");
printf("Polygon decimation utility\n");
printf("(C) 2024 Prusa Research \n");
printf("================================================================\n");
}
void print_ConcludingMessage(void)
{
printf("----------------------------------------------------------------\n");
}
void print_Help(void)
{
printf("Usage:\n");
printf("sequential_decimator [--input-file=<string>]\n");
printf(" [--output-file=<string>]\n");
printf(" [--tolerance=<double>]\n");
printf(" [--x-pos=<double> (in mm)]\n");
printf(" [--y-pos=<double> (in mm)]\n");
printf(" [--x-nozzle=<int> (in coord_t)]\n");
printf(" [--y-nozzle=<int> (in coord_t)]\n");
printf(" [--help]\n");
printf("\n");
printf("\n");
printf("Defaults: --input-file=arrange_data_export.txt\n");
printf(" --output-file=arrange_data_import.txt\n");
printf(" --x-pos='random'\n");
printf(" --y-pos='random'\n");
printf(" --x-nozzle=0\n");
printf(" --y-nozzle=0\n");
printf(" --tolerance=400000 \n");
printf("\n");
}
int parse_CommandLineParameter(const string &parameter, CommandParameters &command_parameters)
{
if (parameter.find("--input-file=") == 0)
{
command_parameters.input_filename = parameter.substr(13, parameter.size());
}
else if (parameter.find("--output-file=") == 0)
{
command_parameters.output_filename = parameter.substr(14, parameter.size());
}
else if (parameter.find("--tolerance=") == 0)
{
command_parameters.tolerance = std::atof(parameter.substr(12, parameter.size()).c_str());
}
else if (parameter.find("--x-pos=") == 0)
{
command_parameters.x_position = std::atof(parameter.substr(8, parameter.size()).c_str());
command_parameters.random_position = false;
}
else if (parameter.find("--y-pos=") == 0)
{
command_parameters.y_position = std::atof(parameter.substr(8, parameter.size()).c_str());
command_parameters.random_position = false;
}
else if (parameter.find("--x-nozzle=") == 0)
{
command_parameters.x_nozzle = std::atoi(parameter.substr(11, parameter.size()).c_str());
}
else if (parameter.find("--y-nozzle=") == 0)
{
command_parameters.y_nozzle = std::atoi(parameter.substr(11, parameter.size()).c_str());
}
else if (parameter.find("--help") == 0)
{
command_parameters.help = true;
}
else
{
return -1;
}
return 0;
}
void save_DecimatedPolygons(const CommandParameters &command_parameters,
const std::vector<Slic3r::Polygon> &decimated_polygons)
{
std::ofstream out(command_parameters.output_filename);
if (!out)
throw std::runtime_error("CANNOT CREATE OUTPUT FILE");
Point nozzle_offset(-command_parameters.x_nozzle, -command_parameters.y_nozzle);
for (unsigned int i = 0; i < decimated_polygons.size(); ++i)
{
out << "[" << i << "]" << endl;
out << "{" << endl;
Slic3r::Polygon shift_polygon = scaleUp_PolygonForSlicer(1,
decimated_polygons[i],
(command_parameters.x_position * SEQ_SLICER_SCALE_FACTOR) * 10,
(command_parameters.y_position * SEQ_SLICER_SCALE_FACTOR) * 10);
shift_Polygon(shift_polygon, nozzle_offset);
for (const auto& point: shift_polygon.points)
{
out << " { " << point.x() << ", " << point.y() << "}," << endl;
}
out << "}" << endl;
}
}
int decimate_Polygons(const CommandParameters &command_parameters)
{
clock_t start, finish;
printf("Decimation ...\n");
start = clock();
SolverConfiguration solver_configuration;
std::vector<ObjectToPrint> objects_to_print = load_exported_data_from_file(command_parameters.input_filename);
std::vector<Slic3r::Polygon> decimated_polygons;
std::vector<std::vector<Slic3r::Polygon> > unreachable_polygons;
printf(" Decimating objects (polygons) ...\n");
for (unsigned int i = 0; i < objects_to_print.size(); ++i)
{
for (unsigned int j = 0; j < objects_to_print[i].pgns_at_height.size(); ++j)
{
//coord_t height = objects_to_print[i].pgns_at_height[j].first;
if (!objects_to_print[i].pgns_at_height[j].second.points.empty())
{
Polygon decimated_polygon;
//ground_PolygonByFirstPoint(objects_to_print[i].pgns_at_height[j].second);
decimate_PolygonForSequentialSolver(command_parameters.tolerance,
objects_to_print[i].pgns_at_height[j].second,
decimated_polygon,
false);
decimated_polygons.push_back(decimated_polygon);
}
}
}
printf(" Decimating objects (polygons) ... finished\n");
Point nozzle_offset(-command_parameters.x_nozzle, -command_parameters.y_nozzle);
for (unsigned int i = 0; i < decimated_polygons.size(); ++i)
{
printf(" [%d]\n", i);
Slic3r::Polygon shift_polygon = decimated_polygons[i];
shift_Polygon(shift_polygon, nozzle_offset);
shift_polygon = scaleUp_PolygonForSlicer(1,
shift_polygon,
(command_parameters.x_position * SEQ_SLICER_SCALE_FACTOR) * 10,
(command_parameters.y_position * SEQ_SLICER_SCALE_FACTOR) * 10);
for (const auto &point: shift_polygon.points)
{
cout << " " << point.x() << " " << point.y() << endl;
}
BoundingBox bounding_box = get_extents(shift_polygon);
cout << " BB" << endl;
cout << " " << bounding_box.min.x() << " " << bounding_box.min.y() << endl;
cout << " " << bounding_box.max.x() << " " << bounding_box.max.y() << endl;
cout << endl;
}
if (command_parameters.output_filename != "")
{
save_DecimatedPolygons(command_parameters, decimated_polygons);
}
string svg_filename = "sequential_decimator.svg";
SVG preview_svg(svg_filename);
solver_configuration.plate_bounding_box = BoundingBox({0,0}, {SEQ_PRUSA_MK3S_X_SIZE, SEQ_PRUSA_MK3S_Y_SIZE});
printf(" Generating output SVG ...\n");
for (unsigned int i = 0; i < decimated_polygons.size(); ++i)
{
Polygon transformed_polygon;
Polygon shift_polygon = decimated_polygons[i];
shift_Polygon(shift_polygon, nozzle_offset);
if (command_parameters.random_position)
{
transformed_polygon = transform_UpsideDown(solver_configuration,
scaleUp_PolygonForSlicer(1,
shift_polygon,
(solver_configuration.plate_bounding_box.min.x() + rand() % (solver_configuration.plate_bounding_box.max.x() - solver_configuration.plate_bounding_box.min.x())) * SEQ_SLICER_SCALE_FACTOR,
(solver_configuration.plate_bounding_box.min.y() + rand() % (solver_configuration.plate_bounding_box.max.y() - solver_configuration.plate_bounding_box.min.y()) * SEQ_SLICER_SCALE_FACTOR)));
}
else
{
transformed_polygon = transform_UpsideDown(solver_configuration,
scaleUp_PolygonForSlicer(1,
shift_polygon,
(command_parameters.x_position * SEQ_SLICER_SCALE_FACTOR) * 10,
(command_parameters.y_position * SEQ_SLICER_SCALE_FACTOR) * 10));
}
Polygon display_polygon = scaleDown_PolygonForSequentialSolver(2, transformed_polygon);
string color;
switch(i % 16)
{
case 0:
{
color = "green";
break;
}
case 1:
{
color = "blue";
break;
}
case 2:
{
color = "red";
break;
}
case 3:
{
color = "grey";
break;
}
case 4:
{
color = "cyan";
break;
}
case 5:
{
color = "magenta";
break;
}
case 6:
{
color = "yellow";
break;
}
case 7:
{
color = "black";
break;
}
case 8:
{
color = "indigo";
break;
}
case 9:
{
color = "olive";
break;
}
case 10:
{
color = "firebrick";
break;
}
case 11:
{
color = "violet";
break;
}
case 12:
{
color = "midnightblue";
break;
}
case 13:
{
color = "khaki";
break;
}
case 14:
{
color = "darkslategrey";
break;
}
case 15:
{
color = "hotpink";
break;
}
default:
{
break;
}
}
preview_svg.draw(display_polygon, color);
}
// general plate polygons are currently not supported
assert(solver_configuration.plate_bounding_polygon.points.size() == 0);
Polygon bed_polygon({ { solver_configuration.plate_bounding_box.min.x(), solver_configuration.plate_bounding_box.min.y() },
{ solver_configuration.plate_bounding_box.max.x(), solver_configuration.plate_bounding_box.min.y() },
{ solver_configuration.plate_bounding_box.max.x(), solver_configuration.plate_bounding_box.max.y() },
{ solver_configuration.plate_bounding_box.min.x(), solver_configuration.plate_bounding_box.max.y() } });
Polygon display_bed_polygon = scaleUp_PolygonForSlicer(SEQ_SVG_SCALE_FACTOR,
bed_polygon,
0,
0);
preview_svg.draw_outline(display_bed_polygon, "black");
preview_svg.Close();
printf(" Generating output SVG ... finised\n");
finish = clock();
printf("Decimation ... finished\n");
printf("Total CPU time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
return 0;
}
/*----------------------------------------------------------------------------*/
// main program
int main(int argc, char **argv)
{
int result;
CommandParameters command_parameters;
print_IntroductoryMessage();
if (argc >= 1 && argc <= 10)
{
for (int i = 1; i < argc; ++i)
{
result = parse_CommandLineParameter(argv[i], command_parameters);
if (result < 0)
{
printf("Error: Cannot parse command line parameters (code = %d).\n", result);
print_Help();
return result;
}
}
if (command_parameters.help)
{
print_Help();
}
else
{
result = decimate_Polygons(command_parameters);
if (result < 0)
{
return result;
}
}
}
else
{
print_Help();
}
print_ConcludingMessage();
return 0;
}

View File

@ -0,0 +1,73 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2025
* Company: Prusa Research
*
* File: sequential_decimator.hpp
*
* Polygon decimator utility (especially for extruder models).
*/
/*================================================================*/
#ifndef __SEQUENTIAL_DECIMATOR_HPP__
#define __SEQUENTIAL_DECIMATOR_HPP__
/*----------------------------------------------------------------*/
#include "seq_sequential.hpp"
#include "seq_preprocess.hpp"
#include "libseqarrange/seq_interface.hpp"
/*----------------------------------------------------------------*/
const double SEQ_DECIMATION_TOLERANCE = 400000.0;
/*----------------------------------------------------------------*/
struct CommandParameters
{
CommandParameters()
: tolerance(SEQ_DECIMATION_TOLERANCE)
, input_filename("arrange_data_export.txt")
, output_filename("arrange_data_import.txt")
, x_position(0)
, y_position(0)
, random_position(true)
, help(false)
, x_nozzle(0)
, y_nozzle(0)
{
/* nothing */
}
double tolerance;
string input_filename;
string output_filename;
double x_position;
double y_position;
bool random_position;
coord_t x_nozzle;
coord_t y_nozzle;
bool help;
};
/*----------------------------------------------------------------------------*/
void print_IntroductoryMessage(void);
void print_ConcludingMessage(void);
void print_Help(void);
int parse_CommandLineParameter(const string &parameter, CommandParameters &parameters);
int decimate_Polygons(const CommandParameters &command_parameters);
/*----------------------------------------------------------------*/
#endif /* __SEQUENTIAL_DECIMATOR_HPP__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
#ifndef PRUSAPARTS_H
#define PRUSAPARTS_H
#include <vector>
#include "libslic3r/ExPolygon.hpp"
using TestData = std::vector<Slic3r::Polygon>;
using TestDataEx = std::vector<Slic3r::ExPolygons>;
extern const TestData PRUSA_PART_POLYGONS;
extern const TestData PRUSA_STEGOSAUR_POLYGONS;
extern const TestDataEx PRUSA_PART_POLYGONS_EX;
#endif // PRUSAPARTS_H

View File

@ -0,0 +1,931 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2024
* Company: Prusa Research
*
* File: seq_test_interface.cpp
*
* Tests of the sequential printing interface for Prusa Slic3r
*/
/*================================================================*/
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include "libslic3r/Polygon.hpp"
#include "libslic3r/ExPolygon.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "libslic3r/SVG.hpp"
#include <z3++.h>
#include "libseqarrange/seq_interface.hpp"
#include "seq_utilities.hpp"
#include "seq_preprocess.hpp"
#include "seq_test_interface.hpp"
/*----------------------------------------------------------------*/
using namespace Sequential;
/*----------------------------------------------------------------*/
const int SEQ_PRUSA_MK3S_X_SIZE = 2500;
const int SEQ_PRUSA_MK3S_Y_SIZE = 2100;
/*----------------------------------------------------------------*/
const std::string arrange_data_export_text = "OBJECT_ID131\n\
TOTAL_HEIGHT62265434\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
OBJECT_ID66\n\
TOTAL_HEIGHT10000000\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
POLYGON_AT_HEIGHT18000000\n\
POLYGON_AT_HEIGHT26000000\n\
OBJECT_ID44\n\
TOTAL_HEIGHT10000000\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 11999992\n\
POINT17000000 15999992\n\
POINT-17000000 15999992\n\
POINT-21000000 11999992\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 3999992\n\
POINT-21000000 3999992\n\
POLYGON_AT_HEIGHT18000000\n\
POLYGON_AT_HEIGHT26000000\n\
OBJECT_ID88\n\
TOTAL_HEIGHT10000000\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
POLYGON_AT_HEIGHT18000000\n\
POLYGON_AT_HEIGHT26000000\n\
OBJECT_ID77\n\
TOTAL_HEIGHT10000000\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000008\n\
POINT17000000 16000008\n\
POINT-17000000 16000008\n\
POINT-21000000 12000008\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
POLYGON_AT_HEIGHT18000000\n\
POLYGON_AT_HEIGHT26000000\n\
OBJECT_ID120\n\
TOTAL_HEIGHT62265434\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -15999992\n\
POINT21000000 -15999992\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -15999992\n\
POINT21000000 -15999992\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-21000000 -15999992\n\
POINT21000000 -15999992\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-21000000 -15999992\n\
POINT21000000 -15999992\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
OBJECT_ID99\n\
TOTAL_HEIGHT62265434\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
OBJECT_ID151\n\
TOTAL_HEIGHT62265434\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
OBJECT_ID162\n\
TOTAL_HEIGHT62265434\n\
POLYGON_AT_HEIGHT0\n\
POINT-30189590 -16000000\n\
POINT30189576 -16000000\n\
POINT30189576 12000000\n\
POINT24439178 16000000\n\
POINT-24439194 16000000\n\
POINT-30189590 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-30189590 -16000000\n\
POINT30189576 -16000000\n\
POINT30189576 12000000\n\
POINT26286238 14715178\n\
POINT24439178 16000000\n\
POINT-24439194 16000000\n\
POINT-28342532 13284822\n\
POINT-30189590 12000000\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-30189590 -16000000\n\
POINT30189576 -16000000\n\
POINT30189576 4000000\n\
POINT-30189590 4000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-30189590 -16000000\n\
POINT30189576 -16000000\n\
POINT30189576 4000000\n\
POINT-30189590 4000000\n\
OBJECT_ID192\n\
TOTAL_HEIGHT62265434\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000000 16000000\n\
POINT-21000000 12000000\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-21000000 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
OBJECT_ID203\n\
TOTAL_HEIGHT62265434\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000000 -15999999\n\
POINT21000000 -15999999\n\
POINT21000000 12000002\n\
POINT17000000 16000002\n\
POINT-17000000 16000002\n\
POINT-21000000 12000002\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000000 -15999999\n\
POINT21000000 -15999999\n\
POINT21000000 12000002\n\
POINT17000000 16000002\n\
POINT-17000000 16000002\n\
POINT-21000000 12000002\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-21000000 -15999999\n\
POINT21000000 -15999999\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-21000000 -15999999\n\
POINT21000000 -15999999\n\
POINT21000000 4000000\n\
POINT-21000000 4000000\n\
OBJECT_ID223\n\
TOTAL_HEIGHT62265434\n\
POLYGON_AT_HEIGHT0\n\
POINT-20999998 -16000000\n\
POINT21000004 -16000000\n\
POINT21000004 12000000\n\
POINT17000004 16000000\n\
POINT-16999998 16000000\n\
POINT-20999998 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-20999998 -16000000\n\
POINT21000004 -16000000\n\
POINT21000004 12000000\n\
POINT17000004 16000000\n\
POINT-16999998 16000000\n\
POINT-20999998 12000000\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-20999998 -16000000\n\
POINT21000004 -16000000\n\
POINT21000004 4000000\n\
POINT-20999998 4000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-20999998 -16000000\n\
POINT21000004 -16000000\n\
POINT21000004 4000000\n\
POINT-20999998 4000000\n\
OBJECT_ID234\n\
TOTAL_HEIGHT62265434\n\
POLYGON_AT_HEIGHT0\n\
POINT-21000002 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000002 16000000\n\
POINT-21000002 12000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-21000002 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 12000000\n\
POINT17000000 16000000\n\
POINT-17000002 16000000\n\
POINT-21000002 12000000\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-21000002 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000002 4000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-21000002 -16000000\n\
POINT21000000 -16000000\n\
POINT21000000 4000000\n\
POINT-21000002 4000000\n\
";
const std::string printer_geometry_mk4_compatibility_text = "X_SIZE250000000\n\
Y_SIZE210000000\n\
CONVEX_HEIGHT0\n\
CONVEX_HEIGHT2000000\n\
BOX_HEIGHT18000000\n\
BOX_HEIGHT26000000\n\
POLYGON_AT_HEIGHT0\n\
POINT-500000 -500000\n\
POINT500000 -500000\n\
POINT500000 500000\n\
POINT-500000 500000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-1000000 -21000000 \n\
POINT37000000 -21000000\n\
POINT37000000 44000000\n\
POINT-1000000 44000000\n\
POLYGON_AT_HEIGHT2000000\n\
POINT-40000000 -45000000\n\
POINT38000000 -45000000\n\
POINT38000000 20000000\n\
POINT-40000000 20000000\n\
POLYGON_AT_HEIGHT18000000\n\
POINT-350000000 -23000000\n\
POINT350000000 -23000000\n\
POINT350000000 -35000000\n\
POINT-350000000 -35000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-12000000 -350000000\n\
POINT9000000 -350000000\n\
POINT9000000 -39000000\n\
POINT-12000000 -39000000\n\
POLYGON_AT_HEIGHT26000000\n\
POINT-12000000 -350000000\n\
POINT250000000 -350000000\n\
POINT250000000 -82000000\n\
POINT-12000000 -82000000\n\
";
const std::string printer_geometry_mk4_text = "X_SIZE250000000\n\
Y_SIZE210000000\n\
CONVEX_HEIGHT0\n\
CONVEX_HEIGHT3000000\n\
BOX_HEIGHT11000000\n\
BOX_HEIGHT13000000\n\
POLYGON_AT_HEIGHT0\n\
POINT-500000 -500000\n\
POINT500000 -500000\n\
POINT500000 500000\n\
POINT-500000 500000\n\
POLYGON_AT_HEIGHT3000000\n\
POINT-1000000 -21000000\n\
POINT37000000 -21000000\n\
POINT37000000 44000000\n\
POINT-1000000 44000000\n\
POLYGON_AT_HEIGHT3000000\n\
POINT-40000000 -45000000\n\
POINT38000000 -45000000\n\
POINT38000000 20000000\n\
POINT-40000000 20000000\n\
POLYGON_AT_HEIGHT11000000\n\
POINT-350000000 -23000000\n\
POINT350000000 -23000000\n\
POINT350000000 -35000000\n\
POINT-350000000 -35000000\n\
POLYGON_AT_HEIGHT13000000\n\
POINT-12000000 -350000000\n\
POINT9000000 -350000000\n\
POINT9000000 -39000000\n\
POINT-12000000 -39000000\n\
POLYGON_AT_HEIGHT13000000\n\
POINT-12000000 -350000000\n\
POINT250000000 -350000000\n\
POINT250000000 -82000000\n\
POINT-12000000 -82000000\n\
";
/*
static bool find_and_remove(std::string& src, const std::string& key)
{
size_t pos = src.find(key);
if (pos != std::string::npos) {
src.erase(pos, key.length());
return true;
}
return false;
}
*/
/*
std::vector<ObjectToPrint> load_exported_data(const std::string& filename)
{
std::vector<ObjectToPrint> objects_to_print;
std::ifstream in(filename);
if (!in)
throw std::runtime_error("NO EXPORTED FILE WAS FOUND");
std::string line;
while (in) {
std::getline(in, line);
if (find_and_remove(line, "OBJECT_ID")) {
objects_to_print.push_back(ObjectToPrint());
objects_to_print.back().id = std::stoi(line);
}
if (find_and_remove(line, "TOTAL_HEIGHT"))
objects_to_print.back().total_height = std::stoi(line);
if (find_and_remove(line, "POLYGON_AT_HEIGHT"))
objects_to_print.back().pgns_at_height.emplace_back(std::make_pair(std::stoi(line), Polygon()));
if (find_and_remove(line, "POINT")) {
std::stringstream ss(line);
std::string val;
ss >> val;
Point pt(std::stoi(val), 0);
ss >> val;
pt.y() = std::stoi(val);
objects_to_print.back().pgns_at_height.back().second.append(pt);
}
}
return objects_to_print;
}
*/
void save_import_data(const std::string &filename,
const std::map<double, int> &scheduled_polygons,
const map<int, int> &original_index_map,
const vector<Rational> &poly_positions_X,
const vector<Rational> &poly_positions_Y)
{
std::ofstream out(filename);
if (!out)
throw std::runtime_error("CANNOT CREATE IMPORT FILE");
for (const auto& scheduled_polygon: scheduled_polygons)
{
coord_t X, Y;
scaleUp_PositionForSlicer(poly_positions_X[scheduled_polygon.second],
poly_positions_Y[scheduled_polygon.second],
X,
Y);
const auto& original_index = original_index_map.find(scheduled_polygon.second);
// out << original_index_map[scheduled_polygon.second] << " " << X << " " << Y << endl;
out << original_index->second << " " << X << " " << Y << endl;
}
}
/*----------------------------------------------------------------*/
TEST_CASE("Interface test 1", "[Sequential Arrangement Interface]")
{
clock_t start, finish;
printf("Testing interface 1 ...\n");
start = clock();
SolverConfiguration solver_configuration;
solver_configuration.decimation_precision = SEQ_DECIMATION_PRECISION_HIGH;
solver_configuration.plate_bounding_box = BoundingBox({0,0}, {SEQ_PRUSA_MK3S_X_SIZE, SEQ_PRUSA_MK3S_Y_SIZE});
printf("Loading objects ...\n");
std::vector<ObjectToPrint> objects_to_print = load_exported_data_from_text(arrange_data_export_text);
REQUIRE(objects_to_print.size() > 0);
printf("Loading objects ... finished\n");
std::vector<ScheduledPlate> scheduled_plates;
printf("Scheduling objects for sequential print ...\n");
int result = schedule_ObjectsForSequentialPrint(solver_configuration,
objects_to_print,
scheduled_plates);
REQUIRE(result == 0);
if (result == 0)
{
printf("Object scheduling for sequential print SUCCESSFUL !\n");
printf("Number of plates: %ld\n", scheduled_plates.size());
REQUIRE(scheduled_plates.size() > 0);
for (unsigned int plate = 0; plate < scheduled_plates.size(); ++plate)
{
printf(" Number of objects on plate: %ld\n", scheduled_plates[plate].scheduled_objects.size());
REQUIRE(scheduled_plates[plate].scheduled_objects.size() > 0);
for (const auto& scheduled_object: scheduled_plates[plate].scheduled_objects)
{
cout << " ID: " << scheduled_object.id << " X: " << scheduled_object.x << " Y: " << scheduled_object.y << endl;
REQUIRE(scheduled_object.x >= 0);
REQUIRE(scheduled_object.x <= solver_configuration.x_plate_bounding_box_size * SEQ_SLICER_SCALE_FACTOR);
REQUIRE(scheduled_object.y >= 0);
REQUIRE(scheduled_object.y <= solver_configuration.y_plate_bounding_box_size * SEQ_SLICER_SCALE_FACTOR);
}
}
}
else
{
printf("Something went WRONG during sequential scheduling (code: %d)\n", result);
}
finish = clock();
printf("Time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
printf("Testing interface 1 ... finished\n");
}
TEST_CASE("Interface test 2", "[Sequential Arrangement Interface]")
{
clock_t start, finish;
printf("Testing interface 2 ...\n");
start = clock();
SolverConfiguration solver_configuration;
solver_configuration.decimation_precision = SEQ_DECIMATION_PRECISION_HIGH;
solver_configuration.plate_bounding_box = BoundingBox({0,0}, {SEQ_PRUSA_MK3S_X_SIZE, SEQ_PRUSA_MK3S_Y_SIZE});
printf("Loading objects ...\n");
std::vector<ObjectToPrint> objects_to_print = load_exported_data_from_text(arrange_data_export_text);
std::vector<std::vector<Slic3r::Polygon> > convex_unreachable_zones;
std::vector<std::vector<Slic3r::Polygon> > box_unreachable_zones;
printf("Preparing extruder unreachable zones ...\n");
setup_ExtruderUnreachableZones(solver_configuration, convex_unreachable_zones, box_unreachable_zones);
std::vector<ScheduledPlate> scheduled_plates;
printf("Scheduling objects for sequential print ...\n");
int result = schedule_ObjectsForSequentialPrint(solver_configuration,
objects_to_print,
convex_unreachable_zones,
box_unreachable_zones,
scheduled_plates);
REQUIRE(result == 0);
if (result == 0)
{
printf("Object scheduling for sequential print SUCCESSFUL !\n");
printf("Number of plates: %ld\n", scheduled_plates.size());
REQUIRE(scheduled_plates.size() > 0);
for (unsigned int plate = 0; plate < scheduled_plates.size(); ++plate)
{
printf(" Number of objects on plate: %ld\n", scheduled_plates[plate].scheduled_objects.size());
REQUIRE(scheduled_plates[plate].scheduled_objects.size() > 0);
for (const auto& scheduled_object: scheduled_plates[plate].scheduled_objects)
{
cout << " ID: " << scheduled_object.id << " X: " << scheduled_object.x << " Y: " << scheduled_object.y << endl;
REQUIRE(scheduled_object.x >= 0);
REQUIRE(scheduled_object.x <= solver_configuration.x_plate_bounding_box_size * SEQ_SLICER_SCALE_FACTOR);
REQUIRE(scheduled_object.y >= 0);
REQUIRE(scheduled_object.y <= solver_configuration.y_plate_bounding_box_size * SEQ_SLICER_SCALE_FACTOR);
}
}
}
else
{
printf("Something went WRONG during sequential scheduling (code: %d)\n", result);
}
finish = clock();
printf("Time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
printf("Testing interface 2 ... finished\n");
}
TEST_CASE("Interface test 3", "[Sequential Arrangement Interface]")
{
clock_t start, finish;
printf("Testing interface 3 ...\n");
start = clock();
PrinterGeometry printer_geometry;
int result = load_printer_geometry_from_text(printer_geometry_mk4_text, printer_geometry);
REQUIRE(result == 0);
if (result != 0)
{
printf("Printer geometry load error.\n");
return;
}
REQUIRE(printer_geometry.plate.points.size() == 4);
for (const auto& convex_height: printer_geometry.convex_heights)
{
cout << "convex_height:" << convex_height << endl;
}
for (const auto& box_height: printer_geometry.box_heights)
{
cout << "box_height:" << box_height << endl;
}
printf("extruder slices:\n");
REQUIRE(printer_geometry.extruder_slices.size() > 0);
for (std::map<coord_t, std::vector<Polygon> >::const_iterator extruder_slice = printer_geometry.extruder_slices.begin(); extruder_slice != printer_geometry.extruder_slices.end(); ++extruder_slice)
{
for (const auto &polygon: extruder_slice->second)
{
printf(" polygon height: %d\n", extruder_slice->first);
for (const auto &point: polygon.points)
{
cout << " " << point.x() << " " << point.y() << endl;
}
}
}
finish = clock();
printf("Time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
printf("Testing interface 3 ... finished\n");
}
TEST_CASE("Interface test 4", "[Sequential Arrangement Interface]")
{
clock_t start, finish;
printf("Testing interface 4 ...\n");
start = clock();
SolverConfiguration solver_configuration;
solver_configuration.decimation_precision = SEQ_DECIMATION_PRECISION_HIGH;
solver_configuration.object_group_size = 4;
solver_configuration.plate_bounding_box = BoundingBox({0,0}, {SEQ_PRUSA_MK3S_X_SIZE, SEQ_PRUSA_MK3S_Y_SIZE});
printf("Loading objects ...\n");
std::vector<ObjectToPrint> objects_to_print = load_exported_data_from_text(arrange_data_export_text);
printf("Loading objects ... finished\n");
PrinterGeometry printer_geometry;
printf("Loading printer geometry ...\n");
int result = load_printer_geometry_from_text(printer_geometry_mk4_compatibility_text, printer_geometry);
REQUIRE(result == 0);
if (result != 0)
{
printf("Cannot load printer geometry (code: %d).\n", result);
return;
}
solver_configuration.setup(printer_geometry);
printf("Loading printer geometry ... finished\n");
std::vector<ScheduledPlate> scheduled_plates;
printf("Scheduling objects for sequential print ...\n");
scheduled_plates = schedule_ObjectsForSequentialPrint(solver_configuration,
printer_geometry,
objects_to_print);
printf("Object scheduling for sequential print SUCCESSFUL !\n");
printf("Number of plates: %ld\n", scheduled_plates.size());
REQUIRE(scheduled_plates.size() > 0);
for (unsigned int plate = 0; plate < scheduled_plates.size(); ++plate)
{
printf(" Number of objects on plate: %ld\n", scheduled_plates[plate].scheduled_objects.size());
REQUIRE(scheduled_plates[plate].scheduled_objects.size() > 0);
for (const auto& scheduled_object: scheduled_plates[plate].scheduled_objects)
{
cout << " ID: " << scheduled_object.id << " X: " << scheduled_object.x << " Y: " << scheduled_object.y << endl;
BoundingBox plate_box = get_extents(printer_geometry.plate);
REQUIRE(scheduled_object.x >= plate_box.min.x());
REQUIRE(scheduled_object.x <= plate_box.max.x());
REQUIRE(scheduled_object.y >= plate_box.min.y());
REQUIRE(scheduled_object.y <= plate_box.max.y());
}
}
finish = clock();
printf("Time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
printf("Testing interface 4 ... finished\n");
}
TEST_CASE("Interface test 5", "[Sequential Arrangement Interface]")
{
clock_t start, finish;
printf("Testing interface 5 ...\n");
start = clock();
SolverConfiguration solver_configuration;
solver_configuration.decimation_precision = SEQ_DECIMATION_PRECISION_LOW;
solver_configuration.object_group_size = 4;
solver_configuration.plate_bounding_box = BoundingBox({0,0}, {SEQ_PRUSA_MK3S_X_SIZE, SEQ_PRUSA_MK3S_Y_SIZE});
printf("Loading objects ...\n");
std::vector<ObjectToPrint> objects_to_print = load_exported_data_from_text(arrange_data_export_text);
printf("Loading objects ... finished\n");
PrinterGeometry printer_geometry;
printf("Loading printer geometry ...\n");
int result = load_printer_geometry_from_text(printer_geometry_mk4_compatibility_text, printer_geometry);
REQUIRE(result == 0);
if (result != 0)
{
printf("Cannot load printer geometry (code: %d).\n", result);
return;
}
solver_configuration.setup(printer_geometry);
printf("Loading printer geometry ... finished\n");
std::vector<ScheduledPlate> scheduled_plates;
printf("Scheduling objects for sequential print ...\n");
scheduled_plates = schedule_ObjectsForSequentialPrint(solver_configuration,
printer_geometry,
objects_to_print,
[](int progress) { printf("Progress: %d\n", progress);
REQUIRE(progress >= 0);
REQUIRE(progress <= 100); });
printf("Object scheduling for sequential print SUCCESSFUL !\n");
printf("Number of plates: %ld\n", scheduled_plates.size());
REQUIRE(scheduled_plates.size() > 0);
for (unsigned int plate = 0; plate < scheduled_plates.size(); ++plate)
{
printf(" Number of objects on plate: %ld\n", scheduled_plates[plate].scheduled_objects.size());
REQUIRE(scheduled_plates[plate].scheduled_objects.size() > 0);
for (const auto& scheduled_object: scheduled_plates[plate].scheduled_objects)
{
cout << " ID: " << scheduled_object.id << " X: " << scheduled_object.x << " Y: " << scheduled_object.y << endl;
BoundingBox plate_box = get_extents(printer_geometry.plate);
REQUIRE(scheduled_object.x >= plate_box.min.x());
REQUIRE(scheduled_object.x <= plate_box.max.x());
REQUIRE(scheduled_object.y >= plate_box.min.y());
REQUIRE(scheduled_object.y <= plate_box.max.y());
}
}
finish = clock();
printf("Solving time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
start = clock();
printf("Checking sequential printability ...\n");
bool printable = check_ScheduledObjectsForSequentialPrintability(solver_configuration,
printer_geometry,
objects_to_print,
scheduled_plates);
printf(" Scheduled/arranged objects are sequentially printable: %s\n", (printable ? "YES" : "NO"));
REQUIRE(printable);
printf("Checking sequential printability ... finished\n");
finish = clock();
printf("Checking time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
printf("Testing interface 5 ... finished\n");
}
TEST_CASE("Interface test 6", "[Sequential Arrangement Interface]")
{
clock_t start, finish;
printf("Testing interface 6 ...\n");
start = clock();
SolverConfiguration solver_configuration;
solver_configuration.decimation_precision = SEQ_DECIMATION_PRECISION_LOW;
solver_configuration.object_group_size = 4;
solver_configuration.plate_bounding_box = BoundingBox({0,0}, {SEQ_PRUSA_MK3S_X_SIZE, SEQ_PRUSA_MK3S_Y_SIZE});
printf("Loading objects ...\n");
std::vector<ObjectToPrint> objects_to_print = load_exported_data_from_text(arrange_data_export_text);
REQUIRE(objects_to_print.size() > 0);
printf("Loading objects ... finished\n");
for (auto& object_to_print: objects_to_print)
{
object_to_print.glued_to_next = true;
}
PrinterGeometry printer_geometry;
printf("Loading printer geometry ...\n");
int result = load_printer_geometry_from_text(printer_geometry_mk4_compatibility_text, printer_geometry);
REQUIRE(result == 0);
if (result != 0)
{
printf("Cannot load printer geometry (code: %d).\n", result);
return;
}
solver_configuration.setup(printer_geometry);
printf("Loading printer geometry ... finished\n");
std::vector<ScheduledPlate> scheduled_plates;
printf("Scheduling objects for sequential print ...\n");
scheduled_plates = schedule_ObjectsForSequentialPrint(solver_configuration,
printer_geometry,
objects_to_print,
[](int progress) { printf("Progress: %d\n", progress);
REQUIRE(progress >= 0);
REQUIRE(progress <= 100); });
printf("Object scheduling for sequential print SUCCESSFUL !\n");
printf("Number of plates: %ld\n", scheduled_plates.size());
REQUIRE(scheduled_plates.size() > 0);
for (unsigned int plate = 0; plate < scheduled_plates.size(); ++plate)
{
printf(" Number of objects on plate: %ld\n", scheduled_plates[plate].scheduled_objects.size());
REQUIRE(scheduled_plates[plate].scheduled_objects.size() > 0);
for (const auto& scheduled_object: scheduled_plates[plate].scheduled_objects)
{
cout << " ID: " << scheduled_object.id << " X: " << scheduled_object.x << " Y: " << scheduled_object.y << endl;
BoundingBox plate_box = get_extents(printer_geometry.plate);
REQUIRE(scheduled_object.x >= plate_box.min.x());
REQUIRE(scheduled_object.x <= plate_box.max.x());
REQUIRE(scheduled_object.y >= plate_box.min.y());
REQUIRE(scheduled_object.y <= plate_box.max.y());
}
}
finish = clock();
printf("Solving time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
start = clock();
printf("Checking sequential printability ...\n");
bool printable = check_ScheduledObjectsForSequentialPrintability(solver_configuration,
printer_geometry,
objects_to_print,
scheduled_plates);
printf(" Scheduled/arranged objects are sequentially printable: %s\n", (printable ? "YES" : "NO"));
REQUIRE(printable);
printf("Checking sequential printability ... finished\n");
finish = clock();
printf("Checking time: %.3f\n", (finish - start) / (double)CLOCKS_PER_SEC);
printf("Testing interface 6 ... finished\n");
}
/*----------------------------------------------------------------*/

View File

@ -0,0 +1,18 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2024
* Company: Prusa Research
*
* File: seq_test_interface.hpp
*
* Tests of the sequential printing interface for Prusa Slic3r
*/
/*================================================================*/
#ifndef __SEQ_TEST_INTERFACE_HPP__
#define __SEQ_TEST_INTERFACE_HPP__
/*----------------------------------------------------------------*/
#endif /* __SEQ_TEST_PREPROCESS_HPP__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2024
* Company: Prusa Research
*
* File: seq_test_polygon.hpp
*
* Basic polygon tests.
*/
/*================================================================*/
#ifndef __SEQ_TEST_POLYGON_HPP__
#define __SEQ_TEST_POLYGON_HPP__
/*----------------------------------------------------------------*/
#endif /* __SEQ_TEST_POLYGON_HPP__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2024
* Company: Prusa Research
*
* File: seq_test_preprocess.hpp
*
* Object preprocessing for sequential priting via SMT.
*/
/*================================================================*/
#ifndef __SEQ_TEST_PREPROCESS_HPP__
#define __SEQ_TEST_PREPROCESS_HPP__
/*----------------------------------------------------------------*/
#endif /* __SEQ_TEST_PREPROCESS_HPP__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
/*================================================================*/
/*
* Author: Pavel Surynek, 2023 - 2024
* Company: Prusa Research
*
* File: seq_test_sequential.hpp
*
* Basic steel plate sequential object scheduling via SMT.
*/
/*================================================================*/
#ifndef __SEQ_TEST_SEQUENTIAL_HPP__
#define __SEQ_TEST_SEQUENTIAL_HPP__
/*----------------------------------------------------------------*/
#endif /* __SEQ_TEST_SEQUENTIAL_HPP__ */

View File

@ -0,0 +1,388 @@
#include "ArrangeHelper.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/MultipleBeds.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/BuildVolume.hpp"
#include <string>
#include "boost/regex.hpp"
#include "boost/property_tree/json_parser.hpp"
#include "boost/algorithm/string/replace.hpp"
#include <boost/nowide/fstream.hpp>
namespace Slic3r {
static bool can_arrange_selected_bed(const Model& model, int bed_idx)
{
// When arranging a single bed, all instances of each object present must be on the same bed.
// Otherwise, the resulting order may not be possible to apply without messing up order
// on the other beds.
const auto map = s_multiple_beds.get_inst_map();
for (const ModelObject* mo : model.objects) {
std::map<int, bool> used_beds;
bool mo_on_this_bed = false;
for (const ModelInstance* mi : mo->instances) {
int id = -1;
if (auto it = map.find(mi->id()); it != map.end())
id = it->second;
if (id == bed_idx)
mo_on_this_bed = true;
used_beds[id] = true;
}
if (mo_on_this_bed && used_beds.size() != 1)
return false;
}
return true;
}
static Sequential::PrinterGeometry get_printer_geometry(const ConfigBase& config)
{
enum ShapeType {
BOX,
CONVEX
};
struct ExtruderSlice {
coord_t height;
ShapeType shape_type;
std::vector<Polygon> polygons;
};
BuildVolume bv(config.opt<ConfigOptionPoints>("bed_shape")->values, 10.);
const BoundingBox& bb = bv.bounding_box();
Polygon bed_polygon;
if (bv.type() == BuildVolume::Type::Circle) {
// Generate an inscribed octagon.
double r = bv.bounding_volume2d().size().x() / 2.;
for (double a = 2*M_PI; a > 0.1; a -= M_PI/4.)
bed_polygon.points.emplace_back(Point::new_scale(r * std::sin(a), r * std::cos(a)));
} else {
// Rectangle of Custom. Just use the bounding box.
bed_polygon = bb.polygon();
}
std::vector<ExtruderSlice> slices;
const std::string printer_notes = config.opt_string("printer_notes");
{
if (! printer_notes.empty()) {
try {
boost::nowide::ifstream in(resources_dir() + "/data/printer_gantries/geometries.txt");
boost::property_tree::ptree pt;
boost::property_tree::read_json(in, pt);
for (const auto& printer : pt.get_child("printers")) {
slices = {};
std::string printer_notes_match = printer.second.get<std::string>("printer_notes_regex");
boost::regex rgx(printer_notes_match);
if (! boost::regex_match(printer_notes, rgx))
continue;
for (const auto& obj : printer.second.get_child("slices")) {
ExtruderSlice slice;
slice.height = scaled(obj.second.get<double>("height"));
std::string type_str = obj.second.get<std::string>("type");
slice.shape_type = type_str == "box" ? BOX : CONVEX;
for (const auto& polygon : obj.second.get_child("polygons")) {
Polygon pgn;
std::string pgn_str = polygon.second.data();
boost::replace_all(pgn_str, ";", " ");
boost::replace_all(pgn_str, ",", " ");
std::stringstream ss(pgn_str);
while (ss) {
double x = 0.;
double y = 0.;
ss >> x >> y;
if (ss)
pgn.points.emplace_back(Point::new_scale(x, y));
}
if (! pgn.points.empty())
slice.polygons.emplace_back(std::move(pgn));
}
slices.emplace_back(std::move(slice));
}
break;
}
}
catch (const boost::property_tree::json_parser_error&) {
// Failed to parse JSON. slices are empty, fallback will be used.
}
}
if (slices.empty()) {
// Fallback to primitive model using radius and height.
coord_t r = scaled(std::max(0.1, config.opt_float("extruder_clearance_radius")));
coord_t h = scaled(std::max(0.1, config.opt_float("extruder_clearance_height")));
double bed_x = bv.bounding_volume2d().size().x();
double bed_y = bv.bounding_volume2d().size().y();
slices.push_back(ExtruderSlice{ 0, CONVEX, { { { -5000000, -5000000 }, { 5000000, -5000000 }, { 5000000, 5000000 }, { -5000000, 5000000 } } } });
slices.push_back(ExtruderSlice{ 1000000, BOX, { { { -r, -r }, { r, -r }, { r, r }, { -r, r } } } });
slices.push_back(ExtruderSlice{ h, BOX, { { { -scaled(bed_x), -r }, { scaled(bed_x), -r }, { scaled(bed_x), r }, { -scaled(bed_x), r}}} });
}
}
// Convert the read data so libseqarrange understands them.
Sequential::PrinterGeometry out;
out.plate = bed_polygon;
for (const ExtruderSlice& slice : slices) {
(slice.shape_type == CONVEX ? out.convex_heights : out.box_heights).emplace(slice.height);
out.extruder_slices.insert(std::make_pair(slice.height, slice.polygons));
}
return out;
}
static Sequential::SolverConfiguration get_solver_config(const Sequential::PrinterGeometry& printer_geometry)
{
return Sequential::SolverConfiguration(printer_geometry);
}
static std::vector<Sequential::ObjectToPrint> get_objects_to_print(const Model& model, const Sequential::PrinterGeometry& printer_geometry, int selected_bed)
{
// First extract the heights of interest.
std::vector<double> heights;
for (const auto& [height, pgns] : printer_geometry.extruder_slices)
heights.push_back(unscaled(height));
Slic3r::sort_remove_duplicates(heights);
// Now collect all objects and projections of convex hull above respective heights.
std::vector<std::pair<Sequential::ObjectToPrint, std::vector<Sequential::ObjectToPrint>>> objects; // first = object id, the vector = ids of its instances
for (const ModelObject* mo : model.objects) {
const TriangleMesh& raw_mesh = mo->raw_mesh();
coord_t height = scaled(mo->instance_bounding_box(0).size().z());
std::vector<Sequential::ObjectToPrint> instances;
for (const ModelInstance* mi : mo->instances) {
if (selected_bed != -1) {
auto it = s_multiple_beds.get_inst_map().find(mi->id());
if (it == s_multiple_beds.get_inst_map().end() || it->second != selected_bed)
continue;
}
if (mi->printable) {
instances.emplace_back(Sequential::ObjectToPrint{int(mi->id().id), true, height, {}});
for (double height : heights) {
// It seems that zero level in the object instance is mi->get_offset().z(), however need to have bed as zero level,
// hence substracting mi->get_offset().z() from height seems to be an easy hack
Polygon pgn = its_convex_hull_2d_above(raw_mesh.its, mi->get_matrix_no_offset().cast<float>(), height - mi->get_offset().z());
instances.back().pgns_at_height.emplace_back(std::make_pair(scaled(height), pgn));
}
}
}
// Collect all instances of this object to be arranged, unglue it from the next object.
if (! instances.empty()) {
objects.emplace_back(instances.front(), instances);
objects.back().second.erase(objects.back().second.begin()); // pop_front
if (! objects.back().second.empty())
objects.back().second.back().glued_to_next = false;
else
objects.back().first.glued_to_next = false;
}
}
// Now order the objects so that the are always passed in the order of increasing id.
// That way, the algorithm will give the same result when called repeatedly.
// However, there is an exception: instances cannot be separated from their objects.
std::sort(objects.begin(), objects.end(), [](const auto& a, const auto& b) { return a.first.id < b.first.id; });
std::vector<Sequential::ObjectToPrint> objects_out;
for (const auto& o : objects) {
objects_out.emplace_back(o.first);
for (const auto& i : o.second)
objects_out.emplace_back(i);
}
return objects_out;
}
void arrange_model_sequential(Model& model, const ConfigBase& config, bool current_bed_only)
{
SeqArrange seq_arrange(model, config, current_bed_only);
seq_arrange.process_seq_arrange([](int) {});
seq_arrange.apply_seq_arrange(model);
}
SeqArrange::SeqArrange(const Model& model, const ConfigBase& config, bool current_bed_only)
{
m_selected_bed = current_bed_only ? s_multiple_beds.get_active_bed() : -1;
if (m_selected_bed != -1 && ! can_arrange_selected_bed(model, m_selected_bed))
throw ExceptionCannotAttemptSeqArrange();
m_printer_geometry = get_printer_geometry(config);
m_solver_configuration = get_solver_config(m_printer_geometry);
m_objects = get_objects_to_print(model, m_printer_geometry, m_selected_bed);
}
void SeqArrange::process_seq_arrange(std::function<void(int)> progress_fn)
{
m_plates =
Sequential::schedule_ObjectsForSequentialPrint(
m_solver_configuration,
m_printer_geometry,
m_objects, progress_fn);
// If this was arrangement of a single bed, check that all instances of a single object
// ended up on the same bed. Otherwise we cannot apply the result (instances of a single
// object always follow one another in the object list and therefore the print).
if (m_selected_bed != -1 && s_multiple_beds.get_number_of_beds() > 1) {
int expected_plate = -1;
for (const Sequential::ObjectToPrint& otp : m_objects) {
auto it = std::find_if(m_plates.begin(), m_plates.end(), [&otp](const auto& plate)
{ return std::any_of(plate.scheduled_objects.begin(), plate.scheduled_objects.end(),
[&otp](const auto& obj) { return otp.id == obj.id;
});
});
assert(it != m_plates.end());
size_t plate_id = it - m_plates.begin();
if (expected_plate != -1 && expected_plate != plate_id)
throw ExceptionCannotApplySeqArrange();
expected_plate = otp.glued_to_next ? plate_id : -1;
}
}
}
// Extract the result and move the objects in Model accordingly.
void SeqArrange::apply_seq_arrange(Model& model) const
{
struct MoveData {
Sequential::ScheduledObject scheduled_object;
size_t bed_idx;
ModelObject* mo;
};
// Iterate over the result and move the instances.
std::vector<MoveData> move_data_all; // Needed for the ordering.
size_t plate_idx = 0;
size_t new_number_of_beds = s_multiple_beds.get_number_of_beds();
std::vector<int> touched_beds;
for (const Sequential::ScheduledPlate& plate : m_plates) {
int real_bed = plate_idx;
if (m_selected_bed != -1) {
// Only a single bed was arranged. Move "first" bed to its position
// and everything else to newly created beds.
real_bed += (plate_idx == 0 ? m_selected_bed : s_multiple_beds.get_number_of_beds() - 1);
}
touched_beds.emplace_back(real_bed);
new_number_of_beds = std::max(new_number_of_beds, size_t(real_bed + 1));
const Vec3d bed_offset = s_multiple_beds.get_bed_translation(real_bed);
for (const Sequential::ScheduledObject& object : plate.scheduled_objects)
for (ModelObject* mo : model.objects)
for (ModelInstance* mi : mo->instances)
if (mi->id().id == object.id) {
move_data_all.push_back({ object, size_t(real_bed), mo });
mi->set_offset(Vec3d(unscaled(object.x) + bed_offset.x(), unscaled(object.y) + bed_offset.y(), mi->get_offset().z()));
}
++plate_idx;
}
// Create a copy of ModelObject pointers, zero ones present in move_data_all.
// The point is to only reorder ModelObject which had actually been passed to the arrange algorithm.
std::vector<ModelObject*> objects_reordered = model.objects;
for (size_t i = 0; i < objects_reordered.size(); ++i) {
ModelObject* mo = objects_reordered[i];
if (std::any_of(move_data_all.begin(), move_data_all.end(), [&mo](const MoveData& md) { return md.mo == mo; }))
objects_reordered[i] = nullptr;
}
// Fill the gaps with the arranged objects in the correct order.
for (size_t i = 0; i < objects_reordered.size(); ++i) {
if (! objects_reordered[i]) {
objects_reordered[i] = move_data_all[0].mo;
while (! move_data_all.empty() && move_data_all.front().mo == objects_reordered[i])
move_data_all.erase(move_data_all.begin());
}
}
// Check that the old and new vectors only differ in order of elements.
auto a = model.objects;
auto b = objects_reordered;
std::sort(a.begin(), a.end());
std::sort(b.begin(), b.end());
if (a != b)
std::terminate(); // A bug in the code above. Better crash now than later.
// Update objects order in the model.
std::swap(model.objects, objects_reordered);
// One last thing. Move unprintable instances to new beds. It would be nicer to
// arrange them (non-sequentially) on just one bed - maybe one day.
std::map<int, std::vector<ModelInstance*>> instances_to_move; // bed to move from and list of instances
for (ModelObject* mo : model.objects)
for (ModelInstance* mi : mo->instances)
if (!mi->printable) {
auto it = s_multiple_beds.get_inst_map().find(mi->id());
if (it == s_multiple_beds.get_inst_map().end() || (m_selected_bed != -1 && it->second != m_selected_bed))
continue;
// Was something placed on this bed during arrange? If not, we should not move anything.
if (std::find(touched_beds.begin(), touched_beds.end(), it->second) != touched_beds.end())
instances_to_move[it->second].emplace_back(mi);
}
// Now actually move them.
for (auto& [bed_idx, instances] : instances_to_move) {
Vec3d old_bed_offset = s_multiple_beds.get_bed_translation(bed_idx);
Vec3d new_bed_offset = s_multiple_beds.get_bed_translation(new_number_of_beds);
for (ModelInstance* mi : instances)
mi->set_offset(mi->get_offset() - old_bed_offset + new_bed_offset);
++new_number_of_beds;
}
}
std::optional<std::pair<std::string, std::string> > check_seq_conflict(const Model& model, const ConfigBase& config)
{
Sequential::PrinterGeometry printer_geometry = get_printer_geometry(config);
Sequential::SolverConfiguration solver_config = get_solver_config(printer_geometry);
std::vector<Sequential::ObjectToPrint> objects = get_objects_to_print(model, printer_geometry, -1);
if (printer_geometry.extruder_slices.empty()) {
// If there are no data for extruder (such as extruder_clearance_radius set to 0),
// consider it printable.
return {};
}
Sequential::ScheduledPlate plate;
for (const ModelObject* mo : model.objects) {
int inst_id = -1;
for (const ModelInstance* mi : mo->instances) {
++inst_id;
auto it = s_multiple_beds.get_inst_map().find(mi->id());
if (it == s_multiple_beds.get_inst_map().end() || it->second != s_multiple_beds.get_active_bed())
continue;
// Is this instance in objects to print? It may be unprintable or something.
auto it2 = std::find_if(objects.begin(), objects.end(), [&mi](const Sequential::ObjectToPrint& otp) { return otp.id == mi->id().id; });
if (it2 == objects.end())
continue;
Vec3d offset = s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed());
plate.scheduled_objects.emplace_back(mi->id().id, scaled(mi->get_offset().x() - offset.x()), scaled(mi->get_offset().y() - offset.y()));
}
}
std::optional<std::pair<int,int>> conflict = Sequential::check_ScheduledObjectsForSequentialConflict(solver_config, printer_geometry, objects, std::vector<Sequential::ScheduledPlate>(1, plate));
if (conflict) {
std::pair<std::string, std::string> names;
for (const ModelObject* mo : model.objects)
for (const ModelInstance* mi : mo->instances) {
if (mi->id().id == conflict->first)
names.first = mo->name;
if (mi->id().id == conflict->second)
names.second = mo->name;
}
return names;
}
return std::nullopt;
}
} // namespace Slic3r

View File

@ -0,0 +1,42 @@
#ifndef libslic3r_Arrange_Helper_hpp
#define libslic3r_Arrange_Helper_hpp
#include "libseqarrange/seq_interface.hpp"
namespace Slic3r {
class Model;
class ConfigBase;
class ExceptionCannotAttemptSeqArrange : public std::exception {};
class ExceptionCannotApplySeqArrange : public std::exception {};
void arrange_model_sequential(Model& model, const ConfigBase& config);
std::optional<std::pair<std::string, std::string>> check_seq_conflict(const Model& model, const ConfigBase& config);
// This is just a helper class to collect data for seq. arrangement, running the arrangement
// and applying the results to model. It is here so the processing itself can be offloaded
// into a separate thread without copying the Model or sharing it with UI thread.
class SeqArrange {
public:
explicit SeqArrange(const Model& model, const ConfigBase& config, bool current_bed_only);
void process_seq_arrange(std::function<void(int)> progress_fn);
void apply_seq_arrange(Model& model) const;
private:
// Following three are inputs, filled in by the constructor.
Sequential::PrinterGeometry m_printer_geometry;
Sequential::SolverConfiguration m_solver_configuration;
std::vector<Sequential::ObjectToPrint> m_objects;
int m_selected_bed = -1;
// This is the output, filled in by process_seq_arrange.
std::vector<Sequential::ScheduledPlate> m_plates;
};
}
#endif // slic3r_Arrange_Helper_hpp

View File

@ -34,6 +34,8 @@ set(SLIC3R_SOURCES
AABBTreeLines.hpp
AABBMesh.hpp
AABBMesh.cpp
ArrangeHelper.cpp
ArrangeHelper.hpp
Algorithm/LineSegmentation/LineSegmentation.cpp
Algorithm/LineSegmentation/LineSegmentation.hpp
Algorithm/PathSorting.hpp
@ -603,6 +605,7 @@ target_link_libraries(libslic3r PUBLIC
agg
ankerl
boost_headeronly
libseqarrange
)
if (APPLE)

View File

@ -1278,6 +1278,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
file.write(m_label_objects.maybe_stop_instance());
const double last_z{this->writer().get_position().z()};
file.write(this->writer().travel_to_z_force(last_z, "ensure z position"));
const double travel_z = std::max(last_z, double(m_max_layer_z));
file.write(this->writer().travel_to_z_force(travel_z, "ensure z position to clear all already printed objects"));
const Vec3crd from{to_3d(*this->last_position, scaled(this->m_last_layer_z))};
const Vec3crd to{0, 0, scaled(this->m_last_layer_z)};
file.write(this->travel_to(from, to, ExtrusionRole::None, "move to origin position for next object", [](){return "";}));

View File

@ -538,6 +538,7 @@ void GCodeProcessorResult::reset() {
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
spiral_vase_mode = false;
conflict_result = std::nullopt;
sequential_collision_detected = std::nullopt;
}
const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProcessor::Producers = {

View File

@ -163,6 +163,7 @@ namespace Slic3r {
bool spiral_vase_mode;
ConflictResultOpt conflict_result;
std::optional<std::pair<std::string, std::string>> sequential_collision_detected;
void reset();
};

View File

@ -86,7 +86,11 @@ public:
void update_build_volume(const BoundingBoxf& build_volume_bb) {
m_build_volume_bb = build_volume_bb;
}
Vec2d bed_gap() const;
Vec2d get_bed_size() const { return m_build_volume_bb.size(); }
BoundingBoxf get_build_volume_box() const { return m_build_volume_bb; }
BoundingBox get_bed_box() const { return BoundingBox({m_build_volume_bb.min.x(), m_build_volume_bb.min.y()},
{m_build_volume_bb.max.x(), m_build_volume_bb.max.y()}); }
Vec2d bed_gap() const;
Vec2crd get_bed_gap() const;
void ensure_wipe_towers_on_beds(Model& model, const std::vector<std::unique_ptr<Print>>& prints);

View File

@ -497,8 +497,8 @@ static std::vector<std::string> s_Preset_print_options {
"support_material_buildplate_only",
"support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall",
"support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter",
"dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder",
"dont_support_bridges", "thick_bridges", "notes", "complete_objects",
"gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder",
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
@ -556,7 +556,7 @@ static std::vector<std::string> s_Preset_printer_options {
"max_print_height", "default_print_profile", "inherits",
"remaining_times", "silent_mode",
"machine_limits_usage", "thumbnails", "thumbnails_format",
"nozzle_high_flow"
"nozzle_high_flow", "extruder_clearance_radius", "extruder_clearance_height"
};
static std::vector<std::string> s_Preset_sla_print_options {

View File

@ -37,6 +37,7 @@
#include "Utils.hpp"
#include "BuildVolume.hpp"
#include "format.hpp"
#include "ArrangeHelper.hpp"
#include <float.h>
@ -432,89 +433,6 @@ bool Print::has_brim() const
return std::any_of(m_objects.begin(), m_objects.end(), [](PrintObject *object) { return object->has_brim(); });
}
bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons)
{
Polygons convex_hulls_other;
if (polygons != nullptr)
polygons->clear();
std::vector<size_t> intersecting_idxs;
std::map<ObjectID, Polygon> map_model_object_to_convex_hull;
for (const PrintObject *print_object : print.objects()) {
assert(! print_object->model_object()->instances.empty());
assert(! print_object->instances().empty());
ObjectID model_object_id = print_object->model_object()->id();
auto it_convex_hull = map_model_object_to_convex_hull.find(model_object_id);
// Get convex hull of all printable volumes assigned to this print object.
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
if (it_convex_hull == map_model_object_to_convex_hull.end()) {
// Calculate the convex hull of a printable object.
// Grow convex hull with the clearance margin.
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
// which causes that the warning will be showed after arrangement with the
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
Geometry::Transformation trafo = model_instance0->get_transformation();
trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() });
Polygon ch2d = print_object->model_object()->convex_hull_2d(trafo.get_matrix());
Polygons offs_ch2d = offset(ch2d,
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
float(scale_(0.5 * print.config().extruder_clearance_radius.value - BuildVolume::BedEpsilon)), jtRound, scale_(0.1));
// for invalid geometries the vector returned by offset() may be empty
if (!offs_ch2d.empty())
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id, offs_ch2d.front());
}
if (it_convex_hull != map_model_object_to_convex_hull.end()) {
// Make a copy, so it may be rotated for instances.
Polygon convex_hull0 = it_convex_hull->second;
const double z_diff = Geometry::rotation_diff_z(model_instance0->get_matrix(), print_object->instances().front().model_instance->get_matrix());
if (std::abs(z_diff) > EPSILON)
convex_hull0.rotate(z_diff);
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for (const PrintInstance& instance : print_object->instances()) {
Polygon convex_hull = convex_hull0;
// instance.shift is a position of a centered object, while model object may not be centered.
// Convert the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
convex_hull.translate(instance.shift - print_object->center_offset());
// if output needed, collect indices (inside convex_hulls_other) of intersecting hulls
for (size_t i = 0; i < convex_hulls_other.size(); ++i) {
if (!intersection(convex_hulls_other[i], convex_hull).empty()) {
if (polygons == nullptr)
return false;
else {
intersecting_idxs.emplace_back(i);
intersecting_idxs.emplace_back(convex_hulls_other.size());
}
}
}
convex_hulls_other.emplace_back(std::move(convex_hull));
}
}
}
if (!intersecting_idxs.empty()) {
// use collected indices (inside convex_hulls_other) to update output
std::sort(intersecting_idxs.begin(), intersecting_idxs.end());
intersecting_idxs.erase(std::unique(intersecting_idxs.begin(), intersecting_idxs.end()), intersecting_idxs.end());
for (size_t i : intersecting_idxs) {
polygons->emplace_back(std::move(convex_hulls_other[i]));
}
return false;
}
return true;
}
static inline bool sequential_print_vertical_clearance_valid(const Print &print)
{
std::vector<const PrintInstance*> print_instances_ordered = sort_object_instances_by_model_order(print);
// Ignore the last instance printed.
print_instances_ordered.pop_back();
// Find the other highest instance.
auto it = std::max_element(print_instances_ordered.begin(), print_instances_ordered.end(), [](auto l, auto r) {
return l->print_object->height() < r->print_object->height();
});
return it == print_instances_ordered.end() || (*it)->print_object->height() <= scale_(print.config().extruder_clearance_height.value);
}
// Matches "G92 E0" with various forms of writing the zero and with an optional comment.
boost::regex regex_g92e0 { "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$" };
@ -544,15 +462,6 @@ std::string Print::validate(std::vector<std::string>* warnings) const
if (extruders.empty())
return _u8L("The supplied settings will cause an empty print.");
if (m_config.complete_objects) {
if (!sequential_print_horizontal_clearance_valid(*this, const_cast<Polygons*>(&m_sequential_print_clearance_contours)))
return _u8L("Some objects are too close; your extruder will collide with them.");
if (!sequential_print_vertical_clearance_valid(*this))
return _u8L("Some objects are too tall and cannot be printed without extruder collisions.");
}
else
const_cast<Polygons*>(&m_sequential_print_clearance_contours)->clear();
if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) {
return _u8L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together.");
}
@ -1061,6 +970,8 @@ void Print::process()
if (conflictRes.has_value())
BOOST_LOG_TRIVIAL(error) << boost::format("gcode path conflicts found between %1% and %2%") % conflictRes->_objName1 % conflictRes->_objName2;
m_sequential_collision_detected = config().complete_objects ? check_seq_conflict(model(), config()) : std::nullopt;
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
}
@ -1090,6 +1001,9 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor
if (m_conflict_result.has_value())
result->conflict_result = *m_conflict_result;
if (result)
result->sequential_collision_detected = m_sequential_collision_detected;
return path.c_str();
}

View File

@ -688,9 +688,6 @@ public:
const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; }
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; }
const Polygons& get_sequential_print_clearance_contours() const { return m_sequential_print_clearance_contours; }
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
// Returns if all used filaments have same shrinkage compensations.
bool has_same_shrinkage_compensations() const;
@ -744,9 +741,6 @@ private:
// Estimated print time, filament consumed.
PrintStatistics m_print_statistics;
// Cache to store sequential print clearance contours
Polygons m_sequential_print_clearance_contours;
// To allow GCode to set the Print's GCodeExport step status.
friend class GCodeGenerator;
// To allow GCodeProcessor to emit warnings.
@ -755,6 +749,7 @@ private:
friend class PrintObject;
ConflictResultOpt m_conflict_result;
std::optional<std::pair<std::string, std::string>> m_sequential_collision_detected; // names of objects (hit first when printing second)
};
} /* slic3r_Print_hpp_ */

View File

@ -1117,10 +1117,10 @@ void PrintConfigDef::init_fff_params()
def = this->add("extruder_clearance_height", coFloat);
def->label = L("Height");
def->tooltip = L("Set this to the vertical distance between your nozzle tip and (usually) the X carriage rods. "
"In other words, this is the height of the clearance cylinder around your extruder, "
"and it represents the maximum depth the extruder can peek before colliding with "
"other printed objects.");
def->tooltip = L("Only used when 'Print Settings -> Complete individual objects' is active. Set this to the vertical "
"distance between your nozzle tip and (usually) the X carriage rods so slicer can check for collisions "
"with previously printed objects and prevent them when arranging.\n"
"The value is ignored for most Prusa printers, which come with more detailed extruder model.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comExpert;
@ -1128,10 +1128,9 @@ void PrintConfigDef::init_fff_params()
def = this->add("extruder_clearance_radius", coFloat);
def->label = L("Radius");
def->tooltip = L("Set this to the clearance radius around your extruder. "
"If the extruder is not centered, choose the largest value for safety. "
"This setting is used to check for collisions and to display the graphical preview "
"in the plater.");
def->tooltip = L("Only used when 'Print Settings -> Complete individual objects' is active. Set this so slicer can "
"check for collisions with previously printed objects and prevent them when arranging.\n"
"The value is ignored for most Prusa printers, which come with more detailed extruder model.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comExpert;

View File

@ -248,6 +248,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Jobs/PlaterWorker.hpp
GUI/Jobs/ArrangeJob2.hpp
GUI/Jobs/ArrangeJob2.cpp
GUI/Jobs/SeqArrangeJob.hpp
GUI/Jobs/SeqArrangeJob.cpp
GUI/Jobs/CreateFontNameImageJob.cpp
GUI/Jobs/CreateFontNameImageJob.hpp
GUI/Jobs/CreateFontStyleImagesJob.cpp

View File

@ -7,6 +7,11 @@
#include "slic3r/GUI/format.hpp"
#include "slic3r/GUI/GUI.hpp"
// These two should not be here. 2.9.1 is getting near and we need a quick
// way of detecting if complete_objects is used.
#include "slic3r/GUI/GUI_App.hpp"
#include "libslic3r/PresetBundle.hpp"
namespace Slic3r { namespace GUI {
struct Settings {
@ -44,92 +49,100 @@ void ArrangeSettingsDialogImgui::render(float pos_x, float pos_y, bool current_b
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoCollapse);
Settings settings;
read_settings(settings, m_db.get());
if (! wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")) {
Settings settings;
read_settings(settings, m_db.get());
ImGuiPureWrap::text(GUI::format(
_L("Press %1%left mouse button to enter the exact value"),
shortkey_ctrl_prefix()));
ImGuiPureWrap::text(GUI::format(
_L("Press %1%left mouse button to enter the exact value"),
shortkey_ctrl_prefix()));
float dobj_min, dobj_max;
float dbed_min, dbed_max;
float dobj_min, dobj_max;
float dbed_min, dbed_max;
m_db->distance_from_obj_range(dobj_min, dobj_max);
m_db->distance_from_bed_range(dbed_min, dbed_max);
m_db->distance_from_obj_range(dobj_min, dobj_max);
m_db->distance_from_bed_range(dbed_min, dbed_max);
if(dobj_min > settings.d_obj) {
settings.d_obj = std::max(dobj_min, settings.d_obj);
m_db->set_distance_from_objects(settings.d_obj);
if (dobj_min > settings.d_obj) {
settings.d_obj = std::max(dobj_min, settings.d_obj);
m_db->set_distance_from_objects(settings.d_obj);
}
if (dbed_min > settings.d_bed) {
settings.d_bed = std::max(dbed_min, settings.d_bed);
m_db->set_distance_from_bed(settings.d_bed);
}
if (m_imgui->slider_float(_L("Spacing"), &settings.d_obj, dobj_min,
dobj_max, "%5.2f")) {
settings.d_obj = std::max(dobj_min, settings.d_obj);
m_db->set_distance_from_objects(settings.d_obj);
}
if (m_imgui->slider_float(_L("Spacing from bed"), &settings.d_bed,
dbed_min, dbed_max, "%5.2f")) {
settings.d_bed = std::max(dbed_min, settings.d_bed);
m_db->set_distance_from_bed(settings.d_bed);
}
if (ImGuiPureWrap::checkbox(_u8L("Enable rotations (slow)"), settings.rotations)) {
m_db->set_rotation_enabled(settings.rotations);
}
if (m_show_xl_combo_predicate() &&
settings.xl_align >= 0 &&
ImGuiPureWrap::combo(_u8L("Alignment"),
{ _u8L("Center"), _u8L("Rear left"), _u8L("Front left"),
_u8L("Front right"), _u8L("Rear right"),
_u8L("Random") },
settings.xl_align)) {
if (settings.xl_align >= 0 &&
settings.xl_align < ArrangeSettingsView::xlpCount)
m_db->set_xl_alignment(static_cast<ArrangeSettingsView::XLPivots>(
settings.xl_align));
}
// TRN ArrangeDialog
if (ImGuiPureWrap::combo(_u8L("Geometry handling"),
// TRN ArrangeDialog: Type of "Geometry handling"
{ _u8L("Fast"),
// TRN ArrangeDialog: Type of "Geometry handling"
_u8L("Balanced"),
// TRN ArrangeDialog: Type of "Geometry handling"
_u8L("Accurate") },
settings.geom_handling)) {
if (settings.geom_handling >= 0 &&
settings.geom_handling < ArrangeSettingsView::ghCount)
m_db->set_geometry_handling(
static_cast<ArrangeSettingsView::GeometryHandling>(
settings.geom_handling));
}
ImGui::Separator();
if (ImGuiPureWrap::button(_u8L("Reset defaults"))) {
arr2::ArrangeSettingsDb::Values df = m_db->get_defaults();
m_db->set_distance_from_objects(df.d_obj);
m_db->set_distance_from_bed(df.d_bed);
m_db->set_rotation_enabled(df.rotations);
if (m_show_xl_combo_predicate())
m_db->set_xl_alignment(df.xl_align);
m_db->set_geometry_handling(df.geom_handling);
m_db->set_arrange_strategy(df.arr_strategy);
if (m_on_reset_btn)
m_on_reset_btn();
}
ImGui::SameLine();
} else {
ImGui::PushTextWrapPos(350.f);
ImGuiPureWrap::text(_u8L("Sequential printing is active. Arrange algorithm will use geometry of the printer "
"to optimize objects placement and avoid collisions with the gantry."));
ImGui::PopTextWrapPos();
ImGui::Separator();
}
if (dbed_min > settings.d_bed) {
settings.d_bed = std::max(dbed_min, settings.d_bed);
m_db->set_distance_from_bed(settings.d_bed);
}
if (m_imgui->slider_float(_L("Spacing"), &settings.d_obj, dobj_min,
dobj_max, "%5.2f")) {
settings.d_obj = std::max(dobj_min, settings.d_obj);
m_db->set_distance_from_objects(settings.d_obj);
}
if (m_imgui->slider_float(_L("Spacing from bed"), &settings.d_bed,
dbed_min, dbed_max, "%5.2f")) {
settings.d_bed = std::max(dbed_min, settings.d_bed);
m_db->set_distance_from_bed(settings.d_bed);
}
if (ImGuiPureWrap::checkbox(_u8L("Enable rotations (slow)"), settings.rotations)) {
m_db->set_rotation_enabled(settings.rotations);
}
if (m_show_xl_combo_predicate() &&
settings.xl_align >= 0 &&
ImGuiPureWrap::combo(_u8L("Alignment"),
{_u8L("Center"), _u8L("Rear left"), _u8L("Front left"),
_u8L("Front right"), _u8L("Rear right"),
_u8L("Random")},
settings.xl_align)) {
if (settings.xl_align >= 0 &&
settings.xl_align < ArrangeSettingsView::xlpCount)
m_db->set_xl_alignment(static_cast<ArrangeSettingsView::XLPivots>(
settings.xl_align));
}
// TRN ArrangeDialog
if (ImGuiPureWrap::combo(_u8L("Geometry handling"),
// TRN ArrangeDialog: Type of "Geometry handling"
{_u8L("Fast"),
// TRN ArrangeDialog: Type of "Geometry handling"
_u8L("Balanced"),
// TRN ArrangeDialog: Type of "Geometry handling"
_u8L("Accurate")},
settings.geom_handling)) {
if (settings.geom_handling >= 0 &&
settings.geom_handling < ArrangeSettingsView::ghCount)
m_db->set_geometry_handling(
static_cast<ArrangeSettingsView::GeometryHandling>(
settings.geom_handling));
}
ImGui::Separator();
if (ImGuiPureWrap::button(_u8L("Reset defaults"))) {
arr2::ArrangeSettingsDb::Values df = m_db->get_defaults();
m_db->set_distance_from_objects(df.d_obj);
m_db->set_distance_from_bed(df.d_bed);
m_db->set_rotation_enabled(df.rotations);
if (m_show_xl_combo_predicate())
m_db->set_xl_alignment(df.xl_align);
m_db->set_geometry_handling(df.geom_handling);
m_db->set_arrange_strategy(df.arr_strategy);
if (m_on_reset_btn)
m_on_reset_btn();
}
ImGui::SameLine();
if (!current_bed && ImGuiPureWrap::button(_u8L("Arrange")) && m_on_arrange_btn) {
m_on_arrange_btn();

View File

@ -388,10 +388,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
for (auto el : { "ironing_type", "ironing_flowrate", "ironing_spacing", "ironing_speed" })
toggle_field(el, has_ironing);
bool have_sequential_printing = config->opt_bool("complete_objects");
for (auto el : { "extruder_clearance_radius", "extruder_clearance_height" })
toggle_field(el, have_sequential_printing);
bool have_ooze_prevention = config->opt_bool("ooze_prevention");
toggle_field("standby_temperature_delta", have_ooze_prevention);

View File

@ -773,6 +773,11 @@ void DSForLayers::render_cog_menu()
if (m_cb_change_app_config)
m_cb_change_app_config("show_estimated_times_in_dbl_slider", m_show_estimated_times ? "1" : "0");
}
if (ImGuiPureWrap::menu_item_with_icon(_u8L("Sequential slider applied only to top layer").c_str(), "", icon_sz, 0, m_seq_top_layer_only)) {
m_seq_top_layer_only = !m_seq_top_layer_only;
if (m_cb_change_app_config)
m_cb_change_app_config("seq_top_layer_only", m_seq_top_layer_only ? "1" : "0");
}
if (m_mode == MultiAsSingle && m_draw_mode == dmRegular &&
ImGuiPureWrap::menu_item_with_icon(_u8L("Set extruder sequence for the entire print").c_str(), "")) {
if (m_ticks.edit_extruder_sequence(m_ctrl.GetMaxPos(), m_mode))

View File

@ -91,6 +91,7 @@ public:
void set_imgui_wrapper(Slic3r::GUI::ImGuiWrapper* imgui) { m_imgui = imgui; }
void show_estimated_times(bool show) { m_show_estimated_times = show; }
void show_ruler(bool show, bool show_bg) { m_show_ruler = show; m_show_ruler_bg = show_bg; }
void seq_top_layer_only(bool show) { m_seq_top_layer_only = show; }
// manipulation with slider from keyboard
@ -151,6 +152,7 @@ private:
bool m_show_ruler_bg { true };
bool m_show_cog_menu { false };
bool m_show_edit_menu { false };
bool m_seq_top_layer_only { false };
int m_pos_on_move { -1 };
DrawMode m_draw_mode { dmRegular };

View File

@ -217,46 +217,83 @@ int GCodeViewer::SequentialView::ActualSpeedImguiWidget::plot(const char* label,
}
#endif // ENABLE_ACTUAL_SPEED_DEBUG
void GCodeViewer::SequentialView::Marker::init()
{
m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f));
m_model.set_color({ 1.0f, 1.0f, 1.0f, 0.5f });
}
void GCodeViewer::SequentialView::Marker::init(std::optional<std::unique_ptr<GLModel>>& model_opt)
{
if (! model_opt.has_value())
return;
m_model.reset();
m_generic_marker = (model_opt->get() == nullptr);
if (m_generic_marker)
m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f));
else
m_model = **model_opt;
m_model.set_color({ 1.0f, 1.0f, 1.0f, 0.5f });
}
void GCodeViewer::SequentialView::Marker::render()
{
if (!m_visible)
return;
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
GLShaderProgram* shader = wxGetApp().get_shader("tool_marker");
if (shader == nullptr)
return;
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
const bool curr_cull_face = glIsEnabled(GL_CULL_FACE);
glsafe(::glDisable(GL_CULL_FACE));
shader->start_using();
shader->set_uniform("emission_factor", 0.0f);
const Camera& camera = wxGetApp().plater()->get_camera();
Transform3d view_matrix = camera.get_view_matrix();
view_matrix.translate(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()));
Vec3d bed_inst_offset = s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed());
view_matrix.translate(bed_inst_offset);
std::array<std::array<float, 4>, 2> clip_planes;
if (m_generic_marker)
// dummy values, generic marker does not need clipping
clip_planes = {{ { 1.0f, 0.0f, 0.0f, FLT_MAX }, { 1.0f, 0.0f, 0.0f, FLT_MAX } }};
else {
BoundingBoxf box = s_multiple_beds.get_build_volume_box();
box.translate(to_2d(bed_inst_offset));
// add a bit on both sides
box = box.inflated(40.0f);
clip_planes = {{ { 1.0f, 0.0f, 0.0f, -box.min.cast<float>().x() } , { -1.0f, 0.0f, 0.0f, box.max.cast<float>().x() }}};
}
float scale_factor = m_scale_factor;
if (m_fixed_screen_size)
scale_factor *= 10.0f * camera.get_inv_zoom();
const Transform3d model_matrix = (Geometry::translation_transform((m_world_position + m_model_z_offset * Vec3f::UnitZ()).cast<double>()) *
Geometry::translation_transform(scale_factor * m_model.get_bounding_box().size().z() * Vec3d::UnitZ()) * Geometry::rotation_transform({ M_PI, 0.0, 0.0 })) *
Geometry::scale_transform(scale_factor);
const Transform3d model_matrix = m_generic_marker
? Geometry::translation_transform((m_world_position + m_model_z_offset * Vec3f::UnitZ()).cast<double>()) *
Geometry::translation_transform(scale_factor * m_model.get_bounding_box().size().z() * Vec3d::UnitZ()) *
Geometry::rotation_transform({ M_PI, 0.0, 0.0 }) *
Geometry::scale_transform(scale_factor)
: Geometry::translation_transform(m_world_position.cast<double>());
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
shader->set_uniform("view_normal_matrix", view_normal_matrix);
Transform3d volume_world_matrix = model_matrix;
if (!m_generic_marker)
volume_world_matrix = Geometry::translation_transform(bed_inst_offset) * volume_world_matrix;
shader->set_uniform("volume_world_matrix", volume_world_matrix);
shader->set_uniform("clipping_planes[0]", clip_planes[0]);
shader->set_uniform("clipping_planes[1]", clip_planes[1]);
m_model.render();
shader->stop_using();
if (curr_cull_face)
glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_BLEND));
}
@ -842,9 +879,6 @@ void GCodeViewer::init()
if (m_gl_data_initialized)
return;
// initializes tool marker
m_sequential_view.marker.init();
m_gl_data_initialized = true;
try
@ -1102,6 +1136,8 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
m_conflict_result = gcode_result.conflict_result;
if (m_conflict_result.has_value())
m_conflict_result->layer = m_viewer.get_layer_id_at(static_cast<float>(m_conflict_result->_height));
m_sequential_collision_detected = gcode_result.sequential_collision_detected;
}
void GCodeViewer::load_as_preview(libvgcode::GCodeInputData&& data)
@ -1166,6 +1202,13 @@ void GCodeViewer::render()
const libvgcode::PathVertex& curr_vertex = m_viewer.get_current_vertex();
m_sequential_view.marker.set_world_position(libvgcode::convert(curr_vertex.position));
m_sequential_view.marker.set_z_offset(m_z_offset);
// Following just makes sure that the shown marker is correct.
auto marker_model_opt = wxGetApp().plater()->get_current_canvas3D()->get_current_marker_model();
m_sequential_view.marker.init(marker_model_opt);
if (marker_model_opt.has_value())
m_max_bounding_box.reset();
m_sequential_view.render(legend_height, &m_viewer, curr_vertex.gcode_id);
}
}

View File

@ -127,12 +127,13 @@ public:
bool m_visible{ true };
bool m_fixed_screen_size{ false };
float m_scale_factor{ 1.0f };
bool m_generic_marker{ true };
#if ENABLE_ACTUAL_SPEED_DEBUG
ActualSpeedImguiWidget m_actual_speed_imgui_widget;
#endif // ENABLE_ACTUAL_SPEED_DEBUG
public:
void init();
void init(std::optional<std::unique_ptr<GLModel>>& model_opt);
const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); }
@ -260,6 +261,7 @@ private:
bool m_contained_in_bed{ true };
ConflictResultOpt m_conflict_result;
std::optional<std::pair<std::string, std::string>> m_sequential_collision_detected;
libvgcode::Viewer m_viewer;
bool m_loaded_as_preview{ false };
@ -356,6 +358,7 @@ public:
void invalidate_legend() { m_legend_resizer.reset(); }
const ConflictResultOpt& get_conflict_result() const { return m_conflict_result; }
std::optional<std::pair<std::string, std::string>> get_sequential_collision_detected() const { return m_sequential_collision_detected; }
void load_shells(const Print& print);

View File

@ -11,6 +11,11 @@
#include "libslic3r/libslic3r.h"
#include "GLCanvas3D.hpp"
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <igl/unproject.h> // IWYU pragma: keep
#include <LocalesUtils.hpp>
#include <nanosvgrast.h>
@ -44,6 +49,7 @@
#include "I18N.hpp"
#include "NotificationManager.hpp"
#include "format.hpp"
#include "libslic3r/ArrangeHelper.hpp"
#include "slic3r/GUI/BitmapCache.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp"
@ -76,6 +82,7 @@
#include <boost/log/trivial.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/crc.hpp>
#include <boost/regex.hpp>
#include <iostream>
#include <float.h>
@ -122,8 +129,6 @@ void GLCanvas3D::select_bed(int i, bool triggered_by_user)
}
}
wxGetApp().plater()->canvas3D()->m_process->stop();
m_sequential_print_clearance.m_evaluating = true;
reset_sequential_print_clearance();
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, is_sliceable(s_print_statuses[i])));
@ -153,6 +158,69 @@ void GLCanvas3D::select_bed(int i, bool triggered_by_user)
});
}
// Returns extruder model to visualize in the GCodeViewer:
// - nullopt = same as before
// - nullptr = none available, use generic
// - GLModel = the model to use
std::optional<std::unique_ptr<GLModel>> GLCanvas3D::get_current_marker_model() const
{
std::optional<std::unique_ptr<GLModel>> out;
static std::string last_printer_notes;
static double old_r = 0.;
static double old_h = 0.;
static bool old_seq = false;
std::string printer_notes = m_config->opt_string("printer_notes");
double r = m_config->opt_float("extruder_clearance_radius");
double h = m_config->opt_float("extruder_clearance_height");
bool seq = m_config->opt_bool("complete_objects");
if (last_printer_notes != printer_notes || r != old_r || h != old_h || seq != old_seq) {
last_printer_notes = printer_notes;
old_r = r;
old_h = h;
old_seq = seq;
out = std::make_optional(nullptr);
if (! seq)
return out;
try {
boost::nowide::ifstream in(resources_dir() + "/data/printer_gantries/geometries.txt");
boost::property_tree::ptree pt;
boost::property_tree::read_json(in, pt);
for (const auto& printer : pt.get_child("printers")) {
std::string printer_notes_match = printer.second.get<std::string>("printer_notes_regex");
boost::regex rgx(printer_notes_match);
if (boost::regex_match(printer_notes, rgx)) {
std::string filename = resources_dir() + "/data/printer_gantries/" + printer.second.get<std::string>("gantry_model_filename");
if (boost::filesystem::exists(filename)) {
std::unique_ptr<GLModel> m = std::make_unique<GLModel>();
if (m->init_from_file(filename))
out = std::make_optional(std::move(m));
}
break;
}
}
} catch (...) {
// Whatever happened, ignore it. We will return nullptr.
}
if (*out == nullptr && seq) {
// Generic sequential extruder model.
double gantry_height = 10;
auto mesh = its_make_cylinder(r, h + gantry_height - 0.001);
double d = 3 * wxGetApp().plater()->build_volume().bounding_volume2d().size().x();
auto mesh2 = its_make_cube(d,2*r, gantry_height);
its_translate(mesh2, Vec3f(-d/2, -r, h));
its_merge(mesh, mesh2);
std::unique_ptr<GLModel> m = std::make_unique<GLModel>();
m->init_from(mesh);
out = std::make_optional(std::move(m));
}
}
return out;
}
#ifdef __WXGTK3__
// wxGTK3 seems to simulate OSX behavior in regard to HiDPI scaling support.
RetinaHelper::RetinaHelper(wxWindow* window) : m_window(window), m_self(nullptr) {}
@ -923,141 +991,6 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas
ImGui::PopStyleVar(2);
}
void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& contours, bool generate_fill)
{
m_contours.clear();
m_instances.clear();
m_fill.reset();
if (contours.empty())
return;
const Vec3d bed_offset = generate_fill ? s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()) : Vec3d::Zero();
if (generate_fill) {
GLModel::Geometry fill_data;
fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f };
// vertices + indices
const ExPolygons polygons_union = union_ex(contours.contours);
unsigned int vertices_counter = 0;
for (const ExPolygon& poly : polygons_union) {
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(poly);
fill_data.reserve_vertices(fill_data.vertices_count() + triangulation.size());
fill_data.reserve_indices(fill_data.indices_count() + triangulation.size());
for (const Vec3d& v : triangulation) {
fill_data.add_vertex((Vec3f)((bed_offset + v).cast<float>() + 0.0125f * Vec3f::UnitZ())); // add a small positive z to avoid z-fighting
++vertices_counter;
if (vertices_counter % 3 == 0)
fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
}
}
m_fill.init_from(std::move(fill_data));
}
const Transform3d bed_transform = Geometry::translation_transform(bed_offset);
for (size_t i = 0; i < contours.contours.size(); ++i) {
GLModel& model = m_contours.emplace_back(GLModel());
model.init_from(contours.contours[i], 0.025f); // add a small positive z to avoid z-fighting
}
if (contours.trafos.has_value()) {
// create the requested instances
for (const auto& instance : *contours.trafos) {
m_instances.emplace_back(instance.first, bed_transform * instance.second);
}
}
else {
// no instances have been specified
// create one instance for every polygon
for (size_t i = 0; i < contours.contours.size(); ++i) {
m_instances.emplace_back(i, bed_transform);
}
}
}
void GLCanvas3D::SequentialPrintClearance::update_instances_trafos(const std::vector<Transform3d>& trafos)
{
if (trafos.size() == m_instances.size()) {
for (size_t i = 0; i < trafos.size(); ++i) {
m_instances[i].second = trafos[i];
}
}
else
assert(false);
}
void GLCanvas3D::SequentialPrintClearance::render()
{
static const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f };
static const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f };
static const ColorRGBA NO_FILL_EVALUATING_COLOR = { 1.0f, 1.0f, 0.0f, 1.0f };
if (m_contours.empty() || m_instances.empty())
return;
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader == nullptr)
return;
shader->start_using();
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_CULL_FACE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
if (!m_evaluating && !m_dragging)
m_fill.render();
#if !SLIC3R_OPENGL_ES
if (OpenGLManager::get_gl_info().is_core_profile()) {
#endif // !SLIC3R_OPENGL_ES
shader->stop_using();
#if SLIC3R_OPENGL_ES
shader = wxGetApp().get_shader("dashed_lines");
#else
shader = wxGetApp().get_shader("dashed_thick_lines");
#endif // SLIC3R_OPENGL_ES
if (shader == nullptr)
return;
shader->start_using();
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const std::array<int, 4>& viewport = camera.get_viewport();
shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3])));
shader->set_uniform("width", 1.0f);
shader->set_uniform("gap_size", 0.0f);
#if !SLIC3R_OPENGL_ES
}
else
glsafe(::glLineWidth(2.0f));
#endif // !SLIC3R_OPENGL_ES
const ColorRGBA color = (!m_evaluating && !m_dragging && m_fill.is_initialized()) ? FILL_COLOR :
m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR;
for (const auto& [id, trafo] : m_instances) {
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo);
assert(id < m_contours.size());
m_contours[id].set_color(color);
m_contours[id].render();
}
glsafe(::glDisable(GL_BLEND));
glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_DEPTH_TEST));
shader->stop_using();
}
wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
@ -1410,10 +1343,10 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas *canvas, Bed3D &bed)
return this->is_arrange_alignment_enabled();
});
m_arrange_settings_dialog.on_arrange_btn([]{
wxGetApp().plater()->arrange();
wxGetApp().plater()->arrange(false);
});
m_arrange_settings_dialog.on_arrange_bed_btn([]{
wxGetApp().plater()->arrange_current_bed();
wxGetApp().plater()->arrange(true);
});
}
@ -2216,10 +2149,6 @@ void GLCanvas3D::render()
if (show_imgui_demo_window) ImGui::ShowDemoWindow();
#endif // SHOW_IMGUI_DEMO_WINDOW
const bool is_looking_downward = camera.is_looking_downward();
// draw scene
@ -2237,7 +2166,6 @@ void GLCanvas3D::render()
_render_gcode();
_render_objects(GLVolumeCollection::ERenderType::Transparent);
_render_sequential_clearance();
#if ENABLE_RENDER_SELECTION_CENTER
_render_selection_center();
#endif // ENABLE_RENDER_SELECTION_CENTER
@ -2996,6 +2924,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co
if (wxGetApp().is_editor()) {
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
_set_warning_notification_if_needed(EWarning::GCodeConflict);
_set_warning_notification_if_needed(EWarning::SequentialCollision);
}
set_as_dirty();
@ -3888,8 +3817,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
c == GLGizmosManager::EType::Scale ||
c == GLGizmosManager::EType::Rotate) {
show_sinking_contours();
if (_is_sequential_print_enabled())
update_sequential_clearance(true);
}
}
else if (evt.LeftUp() &&
@ -4048,10 +3975,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_selection.setup_cache();
if (!evt.CmdDown())
m_mouse.drag.start_position_3D = m_mouse.scene_position;
m_sequential_print_clearance.m_first_displacement = true;
if (_is_sequential_print_enabled())
update_sequential_clearance(true);
m_sequential_print_clearance.start_dragging();
}
}
}
@ -4100,8 +4023,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
TransformationType trafo_type;
trafo_type.set_relative();
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type);
if (_is_sequential_print_enabled())
update_sequential_clearance(false);
wxGetApp().obj_manipul()->set_dirty();
m_dirty = true;
@ -4179,8 +4100,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) {
m_mouse.position = pos.cast<double>();
if (evt.LeftUp() && m_sequential_print_clearance.is_dragging())
m_sequential_print_clearance.stop_dragging();
if (evt.RightUp() && m_mouse.is_start_position_2D_defined()) {
// forces camera target to be on the plane z = 0
Camera& camera = wxGetApp().plater()->get_camera();
@ -4460,11 +4379,6 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
post_event(SimpleEvent(EVT_GLCANVAS_WIPETOWER_TOUCHED));
}
if (_is_sequential_print_enabled()) {
update_sequential_clearance(true);
m_sequential_print_clearance.m_evaluating = true;
}
m_dirty = true;
}
@ -4558,11 +4472,6 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
if (!done.empty())
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
if (_is_sequential_print_enabled()) {
update_sequential_clearance(true);
m_sequential_print_clearance.m_evaluating = true;
}
m_dirty = true;
}
@ -4635,11 +4544,6 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
if (!done.empty())
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
if (_is_sequential_print_enabled()) {
update_sequential_clearance(true);
m_sequential_print_clearance.m_evaluating = true;
}
m_dirty = true;
}
@ -4893,137 +4797,6 @@ void GLCanvas3D::mouse_up_cleanup()
m_canvas->ReleaseMouse();
}
void GLCanvas3D::update_sequential_clearance(bool force_contours_generation)
{
if (!_is_sequential_print_enabled())
return;
if (m_layers_editing.is_enabled())
return;
auto instance_transform_from_volumes = [this](int object_idx, int instance_idx) {
for (const GLVolume* v : m_volumes.volumes) {
if (v->object_idx() == object_idx && v->instance_idx() == instance_idx)
return v->get_instance_transformation();
}
assert(false);
return Geometry::Transformation();
};
auto is_object_outside_printbed = [this](int object_idx) {
for (const GLVolume* v : m_volumes.volumes) {
if (v->object_idx() == object_idx && v->is_outside)
return true;
}
return false;
};
// collects instance transformations from volumes
// first: define temporary cache
unsigned int instances_count = 0;
std::vector<std::vector<std::optional<Geometry::Transformation>>> instance_transforms;
for (size_t obj = 0; obj < m_model->objects.size(); ++obj) {
instance_transforms.emplace_back(std::vector<std::optional<Geometry::Transformation>>());
const ModelObject* model_object = m_model->objects[obj];
for (size_t i = 0; i < model_object->instances.size(); ++i) {
instance_transforms[obj].emplace_back(std::optional<Geometry::Transformation>());
++instances_count;
}
}
if (instances_count == 1)
return;
// second: fill temporary cache with data from volumes
for (const GLVolume* v : m_volumes.volumes) {
if (v->is_wipe_tower())
continue;
const int object_idx = v->object_idx();
const int instance_idx = v->instance_idx();
auto& transform = instance_transforms[object_idx][instance_idx];
if (!transform.has_value())
transform = instance_transform_from_volumes(object_idx, instance_idx);
}
// helper function to calculate the transformation to be applied to the sequential print clearance contours
auto instance_trafo = [](const Transform3d& hull_trafo, const Geometry::Transformation& inst_trafo) {
Vec3d offset = inst_trafo.get_offset() - hull_trafo.translation();
offset.z() = 0.0;
return Geometry::translation_transform(offset) *
Geometry::rotation_transform(Geometry::rotation_diff_z(hull_trafo, inst_trafo.get_matrix()) * Vec3d::UnitZ());
};
// calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid())
// this is done only the first time this method is called while moving the mouse,
// the results are then cached for following displacements
if (force_contours_generation || m_sequential_print_clearance.m_first_displacement) {
m_sequential_print_clearance.m_evaluating = false;
m_sequential_print_clearance.m_hulls_2d_cache.clear();
const float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
const double mitter_limit = scale_(0.1);
m_sequential_print_clearance.m_hulls_2d_cache.reserve(m_model->objects.size());
for (size_t i = 0; i < m_model->objects.size(); ++i) {
ModelObject* model_object = m_model->objects[i];
Geometry::Transformation trafo = instance_transform_from_volumes((int)i, 0);
trafo.set_offset({ 0.0, 0.0, trafo.get_offset().z() });
Pointf3s& new_hull_2d = m_sequential_print_clearance.m_hulls_2d_cache.emplace_back(std::make_pair(Pointf3s(), trafo.get_matrix())).first;
if (is_object_outside_printbed((int)i))
continue;
Polygon hull_2d = model_object->convex_hull_2d(trafo.get_matrix());
if (!hull_2d.empty()) {
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
const Polygons offset_res = offset(hull_2d, shrink_factor, jtRound, mitter_limit);
if (!offset_res.empty())
hull_2d = offset_res.front();
}
new_hull_2d.reserve(hull_2d.points.size());
for (const Point& p : hull_2d.points) {
new_hull_2d.emplace_back(Vec3d(unscale<double>(p.x()), unscale<double>(p.y()), 0.0));
}
}
ContoursList contours;
contours.contours.reserve(instance_transforms.size());
contours.trafos = std::vector<std::pair<size_t, Transform3d>>();
(*contours.trafos).reserve(instances_count);
for (size_t i = 0; i < instance_transforms.size(); ++i) {
const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i];
Points hull_pts;
hull_pts.reserve(hull.size());
for (size_t j = 0; j < hull.size(); ++j) {
hull_pts.emplace_back(scaled<double>(hull[j].x()), scaled<double>(hull[j].y()));
}
contours.contours.emplace_back(Geometry::convex_hull(std::move(hull_pts)));
const auto& instances = instance_transforms[i];
for (const auto& instance : instances) {
(*contours.trafos).emplace_back(i, instance_trafo(hull_trafo, *instance));
}
}
set_sequential_print_clearance_contours(contours, false);
m_sequential_print_clearance.m_first_displacement = false;
}
else {
if (!m_sequential_print_clearance.empty()) {
std::vector<Transform3d> trafos;
trafos.reserve(instances_count);
for (size_t i = 0; i < instance_transforms.size(); ++i) {
const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i];
const auto& instances = instance_transforms[i];
for (const auto& instance : instances) {
trafos.emplace_back(instance_trafo(hull_trafo, *instance));
}
}
m_sequential_print_clearance.update_instances_trafos(trafos);
}
}
}
bool GLCanvas3D::is_object_sinking(int object_idx) const
{
for (const GLVolume* v : m_volumes.volumes) {
@ -6505,32 +6278,6 @@ void GLCanvas3D::_render_selection()
#endif // ENABLE_MATRICES_DEBUG
}
void GLCanvas3D::_render_sequential_clearance()
{
if (!_is_sequential_print_enabled())
return;
if (m_layers_editing.is_enabled())
return;
switch (m_gizmos.get_current_type())
{
case GLGizmosManager::EType::Flatten:
case GLGizmosManager::EType::Cut:
case GLGizmosManager::EType::MmSegmentation:
case GLGizmosManager::EType::Measure:
case GLGizmosManager::EType::Emboss:
case GLGizmosManager::EType::Simplify:
case GLGizmosManager::EType::FdmSupports:
case GLGizmosManager::EType::Seam:
case GLGizmosManager::EType::FuzzySkin: { return; }
default: { break; }
}
m_sequential_print_clearance.render();
}
bool GLCanvas3D::check_toolbar_icon_size(float init_scale, float& new_scale_to_save, bool is_custom, int counter/* = 3*/)
{
const Size cnv_size = get_canvas_size();
@ -6636,7 +6383,11 @@ void GLCanvas3D::_render_overlays()
if (_is_sequential_print_enabled()) {
for (ModelObject* model_object : m_model->objects)
for (ModelInstance* model_instance : model_object->instances) {
sorted_instances.emplace_back(model_instance);
if (auto it = s_multiple_beds.get_inst_map().find(model_instance->id());
it != s_multiple_beds.get_inst_map().end()
&& it->second == s_multiple_beds.get_active_bed()
)
sorted_instances.emplace_back(model_instance);
}
}
m_labels.render(sorted_instances);
@ -7620,6 +7371,8 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed();
else if (warning == EWarning::GCodeConflict)
show = m_gcode_viewer.has_data() && m_gcode_viewer.is_contained_in_bed() && m_gcode_viewer.get_conflict_result().has_value();
else if (warning == EWarning::SequentialCollision)
show = m_gcode_viewer.has_data() && m_gcode_viewer.get_sequential_collision_detected().has_value();
}
}
}
@ -7659,6 +7412,16 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
error = ErrorType::SLICING_ERROR;
break;
}
case EWarning::SequentialCollision: {
auto conflict = m_gcode_viewer.get_sequential_collision_detected();
if (! conflict.has_value())
break;
// TRN: Placeholders contain names of the colliding objects.
text = format(_u8L("Extruder will crash into %1% while printing %2%."),
conflict->first, conflict->second);
error = ErrorType::SLICING_ERROR;
break;
}
}
auto& notification_manager = *wxGetApp().plater()->get_notification_manager();

View File

@ -373,7 +373,8 @@ class GLCanvas3D
SlaSupportsOutside,
SomethingNotShown,
ObjectClashed,
GCodeConflict
GCodeConflict,
SequentialCollision
};
class RenderStats
@ -624,34 +625,6 @@ public:
private:
class SequentialPrintClearance
{
GLModel m_fill;
// list of unique contours
std::vector<GLModel> m_contours;
// list of transforms used to render the contours
std::vector<std::pair<size_t, Transform3d>> m_instances;
bool m_evaluating{ false };
bool m_dragging{ false };
bool m_first_displacement{ true };
std::vector<std::pair<Pointf3s, Transform3d>> m_hulls_2d_cache;
public:
void set_contours(const ContoursList& contours, bool generate_fill);
void update_instances_trafos(const std::vector<Transform3d>& trafos);
void render();
bool empty() const { return m_contours.empty(); }
void start_dragging() { m_dragging = true; }
bool is_dragging() const { return m_dragging; }
void stop_dragging() { m_dragging = false; }
friend class GLCanvas3D;
};
SequentialPrintClearance m_sequential_print_clearance;
struct ToolbarHighlighter
{
void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY) { m_timer.SetOwner(owner, timerid); }
@ -756,6 +729,8 @@ public:
const libvgcode::Interval& get_gcode_view_visible_range() const { return m_gcode_viewer.get_gcode_view_visible_range(); }
const libvgcode::PathVertex& get_gcode_vertex_at(size_t id) const { return m_gcode_viewer.get_gcode_vertex_at(id); }
std::optional<std::unique_ptr<GLModel>> get_current_marker_model() const;
void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1, const ModelVolume* mv = nullptr);
void update_instance_printable_state_for_object(size_t obj_idx);
@ -989,39 +964,6 @@ public:
#endif
}
void reset_sequential_print_clearance() {
m_sequential_print_clearance.m_evaluating = false;
if (m_sequential_print_clearance.is_dragging())
m_sequential_print_clearance.m_first_displacement = true;
else
m_sequential_print_clearance.set_contours(ContoursList(), false);
set_as_dirty();
request_extra_frame();
}
void set_sequential_print_clearance_contours(const ContoursList& contours, bool generate_fill) {
m_sequential_print_clearance.set_contours(contours, generate_fill);
if (generate_fill)
m_sequential_print_clearance.m_evaluating = false;
set_as_dirty();
request_extra_frame();
}
bool is_sequential_print_clearance_empty() const {
return m_sequential_print_clearance.empty();
}
bool is_sequential_print_clearance_evaluating() const {
return m_sequential_print_clearance.m_evaluating;
}
void update_sequential_clearance(bool force_contours_generation);
void set_sequential_clearance_as_evaluating() {
m_sequential_print_clearance.m_evaluating = true;
set_as_dirty();
request_extra_frame();
}
const Print* fff_print() const;
const SLAPrint* sla_print() const;
@ -1067,7 +1009,6 @@ private:
void _render_gcode() { m_gcode_viewer.render(); }
void _render_gcode_cog() { m_gcode_viewer.render_cog(); }
void _render_selection();
void _render_sequential_clearance();
bool check_toolbar_icon_size(float init_scale, float& new_scale_to_save, bool is_custom, int counter = 3);
#if ENABLE_RENDER_SELECTION_CENTER
void _render_selection_center() { m_selection.render_center(m_gizmos.is_dragging()); }

View File

@ -67,6 +67,8 @@ std::pair<bool, std::string> GLShadersManager::init()
#endif // SLIC3R_OPENGL_ES
// used to render toolpaths center of gravity
valid &= append_shader("toolpaths_cog", { prefix + "toolpaths_cog.vs", prefix + "toolpaths_cog.fs" });
// used to render tool marker
valid &= append_shader("tool_marker", { prefix + "tool_marker.vs", prefix + "tool_marker.fs" });
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview
valid &= append_shader("gouraud_light", { prefix + "gouraud_light.vs", prefix + "gouraud_light.fs" });
// extend "gouraud_light" by adding clipping, used in sla gizmos

View File

@ -301,6 +301,7 @@ void Preview::reload_print()
m_loaded = false;
load_print();
m_layers_slider->seq_top_layer_only(wxGetApp().app_config->get_bool("seq_top_layer_only"));
}
void Preview::msw_rescale()
@ -403,6 +404,7 @@ void Preview::create_sliders()
m_layers_slider->SetEmUnit(wxGetApp().em_unit());
m_layers_slider->set_imgui_wrapper(wxGetApp().imgui());
m_layers_slider->show_estimated_times(wxGetApp().app_config->get_bool("show_estimated_times_in_dbl_slider"));
m_layers_slider->seq_top_layer_only(wxGetApp().app_config->get_bool("seq_top_layer_only"));
m_layers_slider->show_ruler(wxGetApp().app_config->get_bool("show_ruler_in_dbl_slider"), wxGetApp().app_config->get_bool("show_ruler_bg_in_dbl_slider"));
m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA,
@ -410,8 +412,10 @@ void Preview::create_sliders()
m_layers_slider->set_callback_on_thumb_move( [this]() -> void { Preview::on_layers_slider_scroll_changed(); } );
m_layers_slider->set_callback_on_change_app_config([](const std::string& key, const std::string& val) -> void {
m_layers_slider->set_callback_on_change_app_config([this](const std::string& key, const std::string& val) -> void {
wxGetApp().app_config->set(key, val);
if (key == "seq_top_layer_only")
reload_print();
});
if (wxGetApp().is_editor()) {

View File

@ -0,0 +1,84 @@
#include "SeqArrangeJob.hpp"
#include "libslic3r/ArrangeHelper.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "slic3r/GUI/format.hpp"
namespace Slic3r { namespace GUI {
SeqArrangeJob::SeqArrangeJob(const Model& model, const DynamicPrintConfig& config, bool current_bed_only)
{
m_seq_arrange.reset(new SeqArrange(model, config, current_bed_only));
}
void SeqArrangeJob::process(Ctl& ctl)
{
class SeqArrangeJobException : std::exception {};
try {
m_seq_arrange->process_seq_arrange([&](int progress) {
ctl.update_status(progress, _u8L("Arranging for sequential print"));
if (ctl.was_canceled())
throw SeqArrangeJobException();
}
);
} catch (const SeqArrangeJobException&) {
// The task was canceled. Just make sure that the progress notification disappears.
ctl.update_status(100, "");
}
}
void SeqArrangeJob::finalize(bool canceled, std::exception_ptr& eptr)
{
// If the task was cancelled, the stopping exception was already caught
// in 'process' function. Any other exception propagates through here.
bool error = false;
if (eptr) {
try {
std::rethrow_exception(eptr);
} catch (const ExceptionCannotApplySeqArrange&) {
ErrorDialog dlg(wxGetApp().plater(), _L("The result of the single-bed arrange would scatter "
"instances of a single object between several beds, possibly affecting order of printing "
"of the non-selected beds. Consider using global arrange across all beds."), false);
dlg.ShowModal();
error = true;
eptr = nullptr; // The exception is handled.
} catch (const Sequential::ObjectTooLargeException&) {
ErrorDialog dlg(wxGetApp().plater(), _L("One of the objects is too large to fit the bed."), false);
dlg.ShowModal();
error = true;
eptr = nullptr; // The exception is handled.
} catch (const Sequential::InternalErrorException& ex) {
ErrorDialog dlg(wxGetApp().plater(), GUI::format_wxstr(_L("Internal error: %1%"), ex.what()), false);
dlg.ShowModal();
error = true;
eptr = nullptr; // The exception is handled.
}
}
if (! canceled && ! error) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Arrange for sequential print"));
m_seq_arrange->apply_seq_arrange(wxGetApp().model());
wxGetApp().plater()->canvas3D()->reload_scene(true, true);
wxGetApp().obj_list()->update_after_undo_redo();
}
m_seq_arrange.reset();
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,31 @@
#ifndef SEQARRANGEJOB_HPP
#define SEQARRANGEJOB_HPP
#include "Job.hpp"
namespace Slic3r {
class Model;
class SeqArrange;
class DynamicPrintConfig;
namespace GUI {
class SeqArrangeJob : public Job
{
public:
explicit SeqArrangeJob(const Model& model, const DynamicPrintConfig& config, bool current_bed_only);
virtual void process(Ctl &ctl) override;
virtual void finalize(bool /*canceled*/, std::exception_ptr&) override;
private:
std::unique_ptr<SeqArrange> m_seq_arrange;
};
} // namespace GUI
} // namespace Slic3r
#endif // ARRANGEJOB2_HPP

View File

@ -106,6 +106,7 @@
#include "ConfigWizardWebViewPage.hpp"
#include "Jobs/RotoptimizeJob.hpp"
#include "Jobs/SeqArrangeJob.hpp"
#include "Jobs/SLAImportJob.hpp"
#include "Jobs/SLAImportDialog.hpp"
#include "Jobs/NotificationProgressIndicator.hpp"
@ -138,6 +139,8 @@
#include "PresetArchiveDatabase.hpp"
#include "BulkExportDialog.hpp"
#include "libslic3r/ArrangeHelper.hpp"
#ifdef __APPLE__
#include "Gizmos/GLGizmosManager.hpp"
#endif // __APPLE__
@ -622,7 +625,7 @@ Plater::priv::priv(Plater* q, MainFrame* main_frame)
: q(q)
, main_frame(main_frame)
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "extruder_clearance_height", "skirts", "skirt_distance",
"brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
"wipe_tower", "wipe_tower_width", "wipe_tower_brim_width", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extra_flow", "wipe_tower_extruder",
"extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_notes", "printer_technology",
@ -712,8 +715,8 @@ void Plater::priv::init()
view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this);
view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this);
view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [this](SimpleEvent&) { q->remove_selected(); });
view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { this->q->arrange(); });
view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE_CURRENT_BED, [this](SimpleEvent&) { this->q->arrange_current_bed(); });
view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { this->q->arrange(false); });
view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE_CURRENT_BED, [this](SimpleEvent&) { this->q->arrange(true); });
view3D_canvas->Bind(EVT_GLCANVAS_SELECT_ALL, [this](SimpleEvent&) { this->q->select_all(); });
view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event<int>& evt)
@ -744,8 +747,8 @@ void Plater::priv::init()
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [this](SimpleEvent&) { q->remove_selected(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { delete_all_objects_from_model(); });
// view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [q](SimpleEvent&) { q->reset_with_confirm(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { this->q->arrange(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE_CURRENT_BED, [this](SimpleEvent&) { this->q->arrange_current_bed(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { this->q->arrange(false); });
view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE_CURRENT_BED, [this](SimpleEvent&) { this->q->arrange(true); });
view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [this](SimpleEvent&) { q->copy_selection_to_clipboard(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [this](SimpleEvent&) { q->paste_from_clipboard(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_MORE, [this](SimpleEvent&) { q->increase_instances(); });
@ -1964,7 +1967,6 @@ void Plater::priv::delete_all_objects_from_model()
reset_gcode_toolpaths();
std::for_each(gcode_results.begin(), gcode_results.end(), [](auto& g) { g.reset(); });
view3D->get_canvas3d()->reset_sequential_print_clearance();
view3D->get_canvas3d()->reset_all_gizmos();
m_worker.cancel_all();
@ -1998,8 +2000,6 @@ void Plater::priv::reset()
reset_gcode_toolpaths();
std::for_each(gcode_results.begin(), gcode_results.end(), [](auto& g) { g.reset(); });
view3D->get_canvas3d()->reset_sequential_print_clearance();
m_worker.cancel_all();
// Stop and reset the Print content.
@ -2354,9 +2354,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
if (view3D->is_layers_editing_enabled())
view3D->get_wxglcanvas()->Refresh();
if (invalidated == Print::APPLY_STATUS_CHANGED || background_process.empty())
view3D->get_canvas3d()->reset_sequential_print_clearance();
if (invalidated == Print::APPLY_STATUS_INVALIDATED) {
// Some previously calculated data on the Print was invalidated.
// Hide the slicing results, as the current slicing status is no more valid.
@ -2394,7 +2391,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
process_validation_warning(warnings);
if (printer_technology == ptFFF) {
GLCanvas3D* canvas = view3D->get_canvas3d();
canvas->reset_sequential_print_clearance();
canvas->set_as_dirty();
canvas->request_extra_frame();
}
@ -2404,41 +2400,14 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
// Show error as notification.
notification_manager->push_validate_error_notification(err);
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
if (printer_technology == ptFFF) {
GLCanvas3D* canvas = view3D->get_canvas3d();
if (canvas->is_sequential_print_clearance_empty() || canvas->is_sequential_print_clearance_evaluating()) {
GLCanvas3D::ContoursList contours;
contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours();
canvas->set_sequential_print_clearance_contours(contours, true);
}
}
}
}
else {
if (invalidated == Print::APPLY_STATUS_UNCHANGED && !background_process.empty()) {
if (printer_technology == ptFFF) {
// Object manipulation with gizmos may end up in a null transformation.
// In this case, we need to trigger the completion of the sequential print clearance contours evaluation
GLCanvas3D* canvas = view3D->get_canvas3d();
if (canvas->is_sequential_print_clearance_evaluating()) {
GLCanvas3D::ContoursList contours;
contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours();
canvas->set_sequential_print_clearance_contours(contours, true);
}
}
std::vector<std::string> warnings;
std::string err = background_process.validate(&warnings);
if (!err.empty()) {
if (s_multiple_beds.get_number_of_beds() > 1 && printer_technology == ptFFF) {
// user changed bed seletion,
// sequential print clearance contours were changed too
GLCanvas3D* canvas = view3D->get_canvas3d();
GLCanvas3D::ContoursList contours;
contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours();
canvas->set_sequential_print_clearance_contours(contours, true);
}
if (!err.empty())
return return_state;
}
}
if (! this->delayed_error_message.empty())
@ -6896,7 +6865,6 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
this->set_printer_technology(printer_technology);
p->sidebar->show_sliced_info_sizer(false);
p->reset_gcode_toolpaths();
p->view3D->get_canvas3d()->reset_sequential_print_clearance();
p->view3D->get_canvas3d()->set_sla_view_type(GLCanvas3D::ESLAViewType::Original);
p->preview->get_canvas3d()->reset_volumes();
}
@ -7143,30 +7111,29 @@ static std::string concat_strings(const std::set<std::string> &strings,
});
}
void Plater::arrange()
void Plater::arrange(bool current_bed_only)
{
const auto mode{
wxGetKeyState(WXK_SHIFT) ?
ArrangeSelectionMode::SelectionOnly :
ArrangeSelectionMode::Full
};
ArrangeSelectionMode mode;
if (current_bed_only)
mode = wxGetKeyState(WXK_SHIFT) ? ArrangeSelectionMode::CurrentBedSelectionOnly : ArrangeSelectionMode::CurrentBedFull;
else
mode = wxGetKeyState(WXK_SHIFT) ? ArrangeSelectionMode::SelectionOnly : ArrangeSelectionMode::Full;
const bool sequential = p->config->has("complete_objects") && p->config->opt_bool("complete_objects");
if (p->can_arrange()) {
auto &w = get_ui_job_worker();
arrange(w, mode);
}
}
void Plater::arrange_current_bed()
{
const auto mode{
wxGetKeyState(WXK_SHIFT) ?
ArrangeSelectionMode::CurrentBedSelectionOnly :
ArrangeSelectionMode::CurrentBedFull
};
if (p->can_arrange()) {
auto &w = get_ui_job_worker();
arrange(w, mode);
if (sequential) {
try {
replace_job(this->get_ui_job_worker(), std::make_unique<SeqArrangeJob>(this->model(), *p->config, current_bed_only));
} catch (const ExceptionCannotAttemptSeqArrange&) {
ErrorDialog dlg(this, _L("Sequential arrange for a single bed is only allowed when all instances of the affected objects are on the same bed."), false);
dlg.ShowModal();
}
}
else {
auto& w = get_ui_job_worker();
arrange(w, mode);
}
}
}
@ -7757,7 +7724,7 @@ PlaterAfterLoadAutoArrange::PlaterAfterLoadAutoArrange()
PlaterAfterLoadAutoArrange::~PlaterAfterLoadAutoArrange()
{
if (m_enabled)
wxGetApp().plater()->arrange();
wxGetApp().plater()->arrange(false);
}
}} // namespace Slic3r::GUI

View File

@ -305,8 +305,7 @@ public:
void render_sliders(GLCanvas3D& canvas);
void arrange();
void arrange_current_bed();
void arrange(bool current_bed_only);
void arrange(Worker &w, const ArrangeSelectionMode &selected);
void set_current_canvas_as_dirty();

View File

@ -146,6 +146,7 @@ void PreferencesDialog::show(const std::string& highlight_opt_key /*= std::strin
,"default_action_on_select_preset" })
m_optgroup_general->set_value(opt_key, app_config->get(opt_key) == "none");
m_optgroup_general->set_value("default_action_on_dirty_project", app_config->get("default_action_on_dirty_project").empty());
m_optgroup_gui->set_value("seq_top_layer_only", app_config->get_bool("seq_top_layer_only"));
// update colors for color pickers of the labels
update_color(m_sys_colour, wxGetApp().get_label_clr_sys());

View File

@ -1625,8 +1625,6 @@ void Selection::erase()
wxGetApp().obj_list()->delete_from_model_and_list(items);
ensure_not_below_bed();
}
wxGetApp().plater()->canvas3D()->set_sequential_clearance_as_evaluating();
}
void Selection::render(float scale_factor)

View File

@ -1694,11 +1694,20 @@ void TabPrint::build()
page = add_options_page(L("Output options"), "output+page_white");
optgroup = page->new_optgroup(L("Sequential printing"));
optgroup->append_single_option_line("complete_objects", "sequential-printing_124589");
line = { L("Extruder clearance"), "" };
line.append_option(optgroup->get_option("extruder_clearance_radius"));
line.append_option(optgroup->get_option("extruder_clearance_height"));
line = Line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
ogStaticText* stat_text; // Let the pointer die, we don't need it and the parent will free it.
wxSizer* sizer = description_line_widget(parent, &stat_text);
stat_text->SetText(from_u8("Note: When using this option, the Arrange function automatically "
"accounts for the printer geometry to prevent collisions. Extruder geometry is built-in for most "
"Prusa printers, the others use generic model defined by values in Printer Settings."));
return sizer;
};
optgroup->append_line(line);
optgroup = page->new_optgroup(L("Output file"));
optgroup->append_single_option_line("gcode_comments");
optgroup->append_single_option_line("gcode_label_objects");
@ -2817,6 +2826,10 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line("variable_layer_height");
optgroup->append_single_option_line("prefer_clockwise_movements");
optgroup = page->new_optgroup(L("Sequential printing limits"));
optgroup->append_single_option_line("extruder_clearance_radius");
optgroup->append_single_option_line("extruder_clearance_height");
const int gcode_field_height = 15; // 150
const int notes_field_height = 25; // 250
page = add_options_page(L("Custom G-code"), "cog");

View File

@ -170,7 +170,7 @@ TEST_CASE_METHOD(CancelObjectFixture, "Single extruder", "[CancelObject]") {
}
TEST_CASE_METHOD(CancelObjectFixture, "Sequential print", "[CancelObject]") {
config.set_deserialize_strict({{"complete_objects", 1}});
config.set_deserialize_strict({{"complete_objects", 1} });
Print print;
print.apply(two_cubes, config);

View File

@ -8,7 +8,7 @@ add_executable(${_TEST_NAME}_tests
)
# mold linker for successful linking needs also to link TBB library and link it before libslic3r.
target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb TBB::tbbmalloc libslic3r_gui libslic3r)
target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb TBB::tbbmalloc libslic3r_gui libslic3r libseqarrange)
if (MSVC)
target_link_libraries(${_TEST_NAME}_tests Setupapi.lib)