mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-02 18:20:44 +08:00
Merge branch 'lm_seq_final'
This commit is contained in:
commit
a7268efdfd
15
deps/+z3/z3.cmake
vendored
Normal file
15
deps/+z3/z3.cmake
vendored
Normal 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
|
||||
)
|
||||
|
286
resources/data/printer_gantries/geometries.txt
Normal file
286
resources/data/printer_gantries/geometries.txt
Normal 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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
BIN
resources/data/printer_gantries/prusa3d_coreone_gantry.stl
Normal file
BIN
resources/data/printer_gantries/prusa3d_coreone_gantry.stl
Normal file
Binary file not shown.
BIN
resources/data/printer_gantries/prusa3d_mini_gantry.stl
Normal file
BIN
resources/data/printer_gantries/prusa3d_mini_gantry.stl
Normal file
Binary file not shown.
BIN
resources/data/printer_gantries/prusa3d_mk3s_gantry.stl
Normal file
BIN
resources/data/printer_gantries/prusa3d_mk3s_gantry.stl
Normal file
Binary file not shown.
BIN
resources/data/printer_gantries/prusa3d_mk4_gantry.stl
Normal file
BIN
resources/data/printer_gantries/prusa3d_mk4_gantry.stl
Normal file
Binary file not shown.
BIN
resources/data/printer_gantries/prusa3d_mk4s_gantry.stl
Normal file
BIN
resources/data/printer_gantries/prusa3d_mk4s_gantry.stl
Normal file
Binary file not shown.
BIN
resources/data/printer_gantries/prusa3d_xl_gantry.stl
Normal file
BIN
resources/data/printer_gantries/prusa3d_xl_gantry.stl
Normal file
Binary file not shown.
@ -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
|
||||
|
18
resources/shaders/110/tool_marker.fs
Normal file
18
resources/shaders/110/tool_marker.fs
Normal 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);
|
||||
}
|
58
resources/shaders/110/tool_marker.vs
Normal file
58
resources/shaders/110/tool_marker.vs
Normal 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;
|
||||
}
|
19
resources/shaders/140/tool_marker.fs
Normal file
19
resources/shaders/140/tool_marker.fs
Normal 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);
|
||||
}
|
58
resources/shaders/140/tool_marker.vs
Normal file
58
resources/shaders/140/tool_marker.vs
Normal 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;
|
||||
}
|
19
resources/shaders/ES/tool_marker.fs
Normal file
19
resources/shaders/ES/tool_marker.fs
Normal 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);
|
||||
}
|
58
resources/shaders/ES/tool_marker.vs
Normal file
58
resources/shaders/ES/tool_marker.vs
Normal 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;
|
||||
}
|
@ -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 ()
|
||||
|
||||
|
35
src/libseqarrange/CMakeLists.txt
Normal file
35
src/libseqarrange/CMakeLists.txt
Normal 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()
|
||||
|
||||
|
||||
|
||||
|
216
src/libseqarrange/include/libseqarrange/seq_interface.hpp
Normal file
216
src/libseqarrange/include/libseqarrange/seq_interface.hpp
Normal 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__ */
|
73
src/libseqarrange/src/seq_defs.hpp
Normal file
73
src/libseqarrange/src/seq_defs.hpp
Normal 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__ */
|
1360
src/libseqarrange/src/seq_interface.cpp
Normal file
1360
src/libseqarrange/src/seq_interface.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1232
src/libseqarrange/src/seq_preprocess.cpp
Normal file
1232
src/libseqarrange/src/seq_preprocess.cpp
Normal file
File diff suppressed because it is too large
Load Diff
234
src/libseqarrange/src/seq_preprocess.hpp
Normal file
234
src/libseqarrange/src/seq_preprocess.hpp
Normal 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__ */
|
11663
src/libseqarrange/src/seq_sequential.cpp
Normal file
11663
src/libseqarrange/src/seq_sequential.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1688
src/libseqarrange/src/seq_sequential.hpp
Normal file
1688
src/libseqarrange/src/seq_sequential.hpp
Normal file
File diff suppressed because it is too large
Load Diff
246
src/libseqarrange/src/seq_utilities.cpp
Normal file
246
src/libseqarrange/src/seq_utilities.cpp
Normal 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
|
52
src/libseqarrange/src/seq_utilities.hpp
Normal file
52
src/libseqarrange/src/seq_utilities.hpp
Normal 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__ */
|
427
src/libseqarrange/src/sequential_decimator.cpp
Normal file
427
src/libseqarrange/src/sequential_decimator.cpp
Normal 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 ¶meter, 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;
|
||||
}
|
73
src/libseqarrange/src/sequential_decimator.hpp
Normal file
73
src/libseqarrange/src/sequential_decimator.hpp
Normal 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 ¶meter, CommandParameters ¶meters);
|
||||
int decimate_Polygons(const CommandParameters &command_parameters);
|
||||
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
#endif /* __SEQUENTIAL_DECIMATOR_HPP__ */
|
5981
src/libseqarrange/test/prusaparts.cpp
Normal file
5981
src/libseqarrange/test/prusaparts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
14
src/libseqarrange/test/prusaparts.hpp
Normal file
14
src/libseqarrange/test/prusaparts.hpp
Normal 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
|
931
src/libseqarrange/test/seq_test_interface.cpp
Normal file
931
src/libseqarrange/test/seq_test_interface.cpp
Normal 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");
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
|
18
src/libseqarrange/test/seq_test_interface.hpp
Normal file
18
src/libseqarrange/test/seq_test_interface.hpp
Normal 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__ */
|
2942
src/libseqarrange/test/seq_test_polygon.cpp
Normal file
2942
src/libseqarrange/test/seq_test_polygon.cpp
Normal file
File diff suppressed because it is too large
Load Diff
18
src/libseqarrange/test/seq_test_polygon.hpp
Normal file
18
src/libseqarrange/test/seq_test_polygon.hpp
Normal 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__ */
|
1129
src/libseqarrange/test/seq_test_preprocess.cpp
Normal file
1129
src/libseqarrange/test/seq_test_preprocess.cpp
Normal file
File diff suppressed because it is too large
Load Diff
18
src/libseqarrange/test/seq_test_preprocess.hpp
Normal file
18
src/libseqarrange/test/seq_test_preprocess.hpp
Normal 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__ */
|
2207
src/libseqarrange/test/seq_test_sequential.cpp
Normal file
2207
src/libseqarrange/test/seq_test_sequential.cpp
Normal file
File diff suppressed because it is too large
Load Diff
18
src/libseqarrange/test/seq_test_sequential.hpp
Normal file
18
src/libseqarrange/test/seq_test_sequential.hpp
Normal 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__ */
|
388
src/libslic3r/ArrangeHelper.cpp
Normal file
388
src/libslic3r/ArrangeHelper.cpp
Normal 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
|
42
src/libslic3r/ArrangeHelper.hpp
Normal file
42
src/libslic3r/ArrangeHelper.hpp
Normal 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
|
@ -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)
|
||||
|
@ -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 "";}));
|
||||
|
@ -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 = {
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -86,6 +86,10 @@ public:
|
||||
void update_build_volume(const BoundingBoxf& build_volume_bb) {
|
||||
m_build_volume_bb = build_volume_bb;
|
||||
}
|
||||
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);
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,6 +49,7 @@ void ArrangeSettingsDialogImgui::render(float pos_x, float pos_y, bool current_b
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
if (! wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")) {
|
||||
Settings settings;
|
||||
read_settings(settings, m_db.get());
|
||||
|
||||
@ -128,8 +134,15 @@ void ArrangeSettingsDialogImgui::render(float pos_x, float pos_y, bool current_b
|
||||
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 (!current_bed && ImGuiPureWrap::button(_u8L("Arrange")) && m_on_arrange_btn) {
|
||||
m_on_arrange_btn();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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 };
|
||||
|
@ -217,9 +217,19 @@ int GCodeViewer::SequentialView::ActualSpeedImguiWidget::plot(const char* label,
|
||||
}
|
||||
#endif // ENABLE_ACTUAL_SPEED_DEBUG
|
||||
|
||||
void GCodeViewer::SequentialView::Marker::init()
|
||||
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 });
|
||||
}
|
||||
|
||||
@ -228,35 +238,62 @@ 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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,6 +6383,10 @@ void GLCanvas3D::_render_overlays()
|
||||
if (_is_sequential_print_enabled()) {
|
||||
for (ModelObject* model_object : m_model->objects)
|
||||
for (ModelInstance* model_instance : model_object->instances) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
|
@ -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()); }
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
84
src/slic3r/GUI/Jobs/SeqArrangeJob.cpp
Normal file
84
src/slic3r/GUI/Jobs/SeqArrangeJob.cpp
Normal 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
|
31
src/slic3r/GUI/Jobs/SeqArrangeJob.hpp
Normal file
31
src/slic3r/GUI/Jobs/SeqArrangeJob.hpp
Normal 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
|
@ -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,42 +2400,15 @@ 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())
|
||||
// Reusing the old state.
|
||||
@ -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,31 +7111,30 @@ 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()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::arrange(Worker &w, const ArrangeSelectionMode &mode)
|
||||
@ -7757,7 +7724,7 @@ PlaterAfterLoadAutoArrange::PlaterAfterLoadAutoArrange()
|
||||
PlaterAfterLoadAutoArrange::~PlaterAfterLoadAutoArrange()
|
||||
{
|
||||
if (m_enabled)
|
||||
wxGetApp().plater()->arrange();
|
||||
wxGetApp().plater()->arrange(false);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user