Fixed conflicts after merge with master
This commit is contained in:
commit
ad72e29612
163 changed files with 8950 additions and 6561 deletions
2
deps/wxWidgets/wxWidgets.cmake
vendored
2
deps/wxWidgets/wxWidgets.cmake
vendored
|
@ -13,7 +13,7 @@ prusaslicer_add_cmake_project(wxWidgets
|
|||
# GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
|
||||
# GIT_TAG tm_cross_compile #${_wx_git_tag}
|
||||
URL https://github.com/prusa3d/wxWidgets/archive/refs/heads/v3.1.4-patched.zip
|
||||
URL_HASH SHA256=21ed12eb5c215b00999f0374af652be0a6f785df10d18d0dfec8d81ed4abaea3
|
||||
URL_HASH SHA256=ed36a2159c781cce07b06378664e683ebd8cb2f51914aba9acd3bfca3d63d7d3
|
||||
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG
|
||||
CMAKE_ARGS
|
||||
-DwxBUILD_PRECOMP=ON
|
||||
|
|
|
@ -13,9 +13,21 @@ This guide describes building PrusaSlicer statically against dependencies pulled
|
|||
|
||||
#### 0. Prerequisities
|
||||
|
||||
CMake, GNU build tools, git and m4 macro processor have to be installed. Unless that's already the case, install them as usual from your distribution packages. E.g. on Ubuntu, run `sudo apt-get install cmake build-essential git m4`. The names of the packages may be different on different distros.
|
||||
GNU build tools, CMake, git and other libraries have to be installed on the build machine.
|
||||
Unless that's already the case, install them as usual from your distribution packages.
|
||||
E.g. on Ubuntu 20.10, run
|
||||
```shell
|
||||
sudo apt-get install -y \
|
||||
git \
|
||||
build-essential \
|
||||
autoconf \
|
||||
cmake \
|
||||
libglu1-mesa-dev \
|
||||
libgtk-3-dev \
|
||||
libdbus-1-dev \
|
||||
|
||||
Although most of dependencies are handled by the build script, PrusaSlicer still expects that some libraries will be available in the system (GTK, MESA, gettext). E.g., on Ubuntu, install the required packages by running `sudo apt-get install libgtk-3-dev libglu1-mesa-dev gettext`. The names of the packages may be different on different distros.
|
||||
```
|
||||
The names of the packages may be different on different distros.
|
||||
|
||||
#### 1. Cloning the repository
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-
|
|||
disabled_tags = SLA
|
||||
|
||||
[hint:Insert Custom G-code]
|
||||
text = Insert Custom G-code\nDid you know that you can insert a custom G-code at a specific layer? Right-click the layer in the Preview and select Add custom G-code. With this function you can, for example, create a temperature tower. Read more in the documentation.
|
||||
text = Insert Custom G-code\nDid you know that you can insert a custom G-code at a specific layer? Left-click the layer in the Preview, Right-click the plus icon and select Add custom G-code. With this function you can, for example, create a temperature tower. Read more in the documentation.
|
||||
documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-custom-g-code-at-layer
|
||||
disabled_tags = SLA
|
||||
|
||||
|
@ -203,8 +203,8 @@ hypertext_type = menubar
|
|||
hypertext_menubar_menu_name = Configuration
|
||||
hypertext_menubar_item_name = Configuration Snapshots
|
||||
|
||||
[hint:Minimum wall thickness]
|
||||
text = Minimum wall thickness\nDid you know that instead of the number of top and bottom layers, you can define the<a>Minimum shell thickness</a>in millimeters? This feature is especially useful when using the variable layer height function.
|
||||
[hint:Minimum shell thickness]
|
||||
text = Minimum shell thickness\nDid you know that instead of the number of top and bottom layers, you can define the<a>Minimum shell thickness</a>in millimeters? This feature is especially useful when using the variable layer height function.
|
||||
hypertext_type = settings
|
||||
hypertext_settings_opt = top_solid_min_thickness
|
||||
hypertext_settings_type = 1
|
||||
|
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,5 @@
|
|||
min_slic3r_version = 2.3.1-beta
|
||||
0.0.4 Fixed first layer height in 0.28mm profile.
|
||||
0.0.3 Fixed Genius bed size.
|
||||
0.0.2 Updated start g-code.
|
||||
0.0.1 Initial Artillery bundle
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
name = Artillery
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the PrusaSlicer configuration to be downgraded.
|
||||
config_version = 0.0.3
|
||||
config_version = 0.0.4
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Artillery/
|
||||
# changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
|
||||
|
@ -300,6 +300,7 @@ top_solid_layers = 4
|
|||
[print:*0.28mm*]
|
||||
inherits = *common*
|
||||
layer_height = 0.28
|
||||
first_layer_height = 0.36
|
||||
top_infill_extrusion_width = 0.45
|
||||
first_layer_extrusion_width = 0.75
|
||||
bottom_solid_layers = 3
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
min_slic3r_version = 2.3.1-beta
|
||||
0.0.1 Initial version
|
||||
0.0.3 Set default filament profile.
|
||||
0.0.2 Improved start gcode, changed filename format
|
||||
0.0.1 Initial version
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
[vendor]
|
||||
# Vendor name will be shown by the Config Wizard.
|
||||
name = INAT
|
||||
config_version = 0.0.2
|
||||
config_version = 0.0.3
|
||||
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/INAT/
|
||||
|
||||
###
|
||||
|
@ -15,12 +15,14 @@ name = INAT Proton X Rail
|
|||
variants = 0.4
|
||||
technology = FFF
|
||||
family = Proton
|
||||
default_materials = PLA @PROTON_X
|
||||
|
||||
[printer_model:PROTON_X_ROD]
|
||||
name = INAT Proton X Rod
|
||||
variants = 0.4
|
||||
technology = FFF
|
||||
family = Proton
|
||||
default_materials = PLA @PROTON_X
|
||||
|
||||
|
||||
###
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
min_slic3r_version = 2.4.0-beta0
|
||||
min_slic3r_version = 2.4.0-beta2
|
||||
1.4.0-beta2 Added SLA material colors. Updated BASF filament profiles.
|
||||
min_slic3r_version = 2.4.0-beta0
|
||||
1.4.0-beta1 Updated pad wall slope angle for SLA printers. Updated Filatech Filacarbon profile for Prusa MINI.
|
||||
1.4.0-beta0 Added multiple Filatech and BASF filament profiles. Added material profiles for SL1S.
|
||||
min_slic3r_version = 2.4.0-alpha0
|
||||
|
|
|
@ -4955,7 +4955,7 @@ exposure_time = 5
|
|||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour = #007EFD
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Super Low Odor Magenta Tough @0.025]
|
||||
inherits = *common 0.025*
|
||||
|
@ -5665,7 +5665,7 @@ exposure_time = 7.5
|
|||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour = #FFEEE6
|
||||
material_colour = #FF8040
|
||||
|
||||
[sla_material:Prusa Grey Tough @0.05]
|
||||
inherits = *common 0.05*
|
||||
|
@ -5710,7 +5710,7 @@ exposure_time = 6
|
|||
initial_exposure_time = 35
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Super Low Odor Magenta Tough @0.05]
|
||||
inherits = *common 0.05*
|
||||
|
@ -6125,7 +6125,7 @@ exposure_time = 1.8
|
|||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Magenta Tough @0.025 SL1S]
|
||||
inherits = *0.025_sl1s*
|
||||
|
@ -6371,7 +6371,7 @@ exposure_time = 2
|
|||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Magenta Tough @0.05 SL1S]
|
||||
inherits = *0.05_sl1s*
|
||||
|
@ -6617,7 +6617,7 @@ exposure_time = 2.6
|
|||
initial_exposure_time = 25
|
||||
material_type = Tough
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #79FFFF
|
||||
|
||||
[sla_material:Prusa Magenta Tough @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
|
@ -6657,7 +6657,7 @@ exposure_time = 2.6
|
|||
initial_exposure_time = 25
|
||||
material_type = Flexible
|
||||
material_vendor = Made for Prusa
|
||||
material_colour =
|
||||
material_colour = #007EFD
|
||||
|
||||
[sla_material:Prusa Grey Tough @0.1 SL1S]
|
||||
inherits = *0.1_sl1s*
|
||||
|
|
|
@ -22,6 +22,19 @@ const vec3 WHITE = vec3(1.0, 1.0, 1.0);
|
|||
const float EPSILON = 0.0001;
|
||||
const float BANDS_WIDTH = 10.0;
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
// 0 = rectangle, 1 = circle, 2 = custom, 3 = invalid
|
||||
int type;
|
||||
// type = 0 (rectangle):
|
||||
// x = min.x, y = min.y, z = max.x, w = max.y
|
||||
// type = 1 (circle):
|
||||
// x = center.x, y = center.y, z = radius
|
||||
vec4 xy_data;
|
||||
// x = min z, y = max z
|
||||
vec2 z_data;
|
||||
};
|
||||
|
||||
struct SlopeDetection
|
||||
{
|
||||
bool actived;
|
||||
|
@ -44,11 +57,10 @@ varying vec3 clipping_planes_dots;
|
|||
// x = diffuse, y = specular;
|
||||
varying vec2 intensity;
|
||||
|
||||
varying vec3 delta_box_min;
|
||||
varying vec3 delta_box_max;
|
||||
uniform PrintVolumeDetection print_volume;
|
||||
|
||||
varying vec4 model_pos;
|
||||
varying float world_pos_z;
|
||||
varying vec4 world_pos;
|
||||
varying float world_normal_z;
|
||||
varying vec3 eye_normal;
|
||||
|
||||
|
@ -63,15 +75,30 @@ void main()
|
|||
color = vec3(0.7, 0.7, 1.0);
|
||||
alpha = 1.0;
|
||||
}
|
||||
|
||||
// if the fragment is outside the print volume -> use darker color
|
||||
color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(color, ZERO, 0.3333) : color;
|
||||
vec3 pv_check_min = ZERO;
|
||||
vec3 pv_check_max = ZERO;
|
||||
if (print_volume.type == 0) {
|
||||
// rectangle
|
||||
pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x);
|
||||
pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y);
|
||||
}
|
||||
else if (print_volume.type == 1) {
|
||||
// circle
|
||||
float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy);
|
||||
pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x);
|
||||
pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y);
|
||||
}
|
||||
color = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color, ZERO, 0.3333) : color;
|
||||
|
||||
#ifdef ENABLE_ENVIRONMENT_MAP
|
||||
if (use_environment_tex)
|
||||
gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha);
|
||||
else
|
||||
#endif
|
||||
gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha);
|
||||
|
||||
|
||||
// In the support painting gizmo and the seam painting gizmo are painted triangles rendered over the already
|
||||
// rendered object. To resolved z-fighting between previously rendered object and painted triangles, values
|
||||
// inside the depth buffer are offset by small epsilon for painted triangles inside those gizmos.
|
||||
|
|
|
@ -18,14 +18,6 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
|||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
struct PrintBoxDetection
|
||||
{
|
||||
bool actived;
|
||||
vec3 min;
|
||||
vec3 max;
|
||||
mat4 volume_world_matrix;
|
||||
};
|
||||
|
||||
struct SlopeDetection
|
||||
{
|
||||
bool actived;
|
||||
|
@ -33,7 +25,7 @@ struct SlopeDetection
|
|||
mat3 volume_world_normal_matrix;
|
||||
};
|
||||
|
||||
uniform PrintBoxDetection print_box;
|
||||
uniform mat4 volume_world_matrix;
|
||||
uniform SlopeDetection slope;
|
||||
|
||||
// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane.
|
||||
|
@ -44,46 +36,33 @@ uniform vec4 clipping_plane;
|
|||
// x = diffuse, y = specular;
|
||||
varying vec2 intensity;
|
||||
|
||||
varying vec3 delta_box_min;
|
||||
varying vec3 delta_box_max;
|
||||
|
||||
varying vec3 clipping_planes_dots;
|
||||
|
||||
varying vec4 model_pos;
|
||||
varying float world_pos_z;
|
||||
varying vec4 world_pos;
|
||||
varying float world_normal_z;
|
||||
varying vec3 eye_normal;
|
||||
|
||||
void main()
|
||||
{
|
||||
// First transform the normal into camera space and normalize the result.
|
||||
eye_normal = normalize(gl_NormalMatrix * gl_Normal);
|
||||
// First transform the normal into camera space and normalize the result.
|
||||
eye_normal = normalize(gl_NormalMatrix * gl_Normal);
|
||||
|
||||
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
|
||||
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
|
||||
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
|
||||
// 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;
|
||||
vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
|
||||
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
|
||||
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
|
||||
vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
|
||||
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, 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;
|
||||
// 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;
|
||||
|
||||
model_pos = gl_Vertex;
|
||||
// Point in homogenous coordinates.
|
||||
vec4 world_pos = print_box.volume_world_matrix * gl_Vertex;
|
||||
world_pos_z = world_pos.z;
|
||||
|
||||
// compute deltas for out of print volume detection (world coordinates)
|
||||
if (print_box.actived) {
|
||||
delta_box_min = world_pos.xyz - print_box.min;
|
||||
delta_box_max = world_pos.xyz - print_box.max;
|
||||
} else {
|
||||
delta_box_min = ZERO;
|
||||
delta_box_max = ZERO;
|
||||
}
|
||||
world_pos = volume_world_matrix * gl_Vertex;
|
||||
|
||||
// z component of normal vector in world coordinate used for slope shading
|
||||
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0;
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
#version 110
|
||||
|
||||
#define INTENSITY_CORRECTION 0.6
|
||||
|
||||
// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
|
||||
const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
|
||||
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
|
||||
#define LIGHT_TOP_SHININESS 20.0
|
||||
|
||||
// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
|
||||
const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
||||
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
|
||||
|
||||
#define INTENSITY_AMBIENT 0.3
|
||||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
const vec3 GREEN = vec3(0.0, 0.7, 0.0);
|
||||
const vec3 YELLOW = vec3(0.5, 0.7, 0.0);
|
||||
const vec3 RED = vec3(0.7, 0.0, 0.0);
|
||||
const vec3 WHITE = vec3(1.0, 1.0, 1.0);
|
||||
const float EPSILON = 0.0001;
|
||||
const float BANDS_WIDTH = 10.0;
|
||||
|
||||
struct PrintVolumeDetection
|
||||
{
|
||||
// 0 = rectangle, 1 = circle, 2 = custom, 3 = invalid
|
||||
int type;
|
||||
// type = 0 (rectangle):
|
||||
// x = min.x, y = min.y, z = max.x, w = max.y
|
||||
// type = 1 (circle):
|
||||
// x = center.x, y = center.y, z = radius
|
||||
vec4 xy_data;
|
||||
// x = min z, y = max z
|
||||
vec2 z_data;
|
||||
};
|
||||
|
||||
struct SlopeDetection
|
||||
{
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
};
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
uniform SlopeDetection slope;
|
||||
|
||||
uniform bool offset_depth_buffer;
|
||||
|
||||
#ifdef ENABLE_ENVIRONMENT_MAP
|
||||
uniform sampler2D environment_tex;
|
||||
uniform bool use_environment_tex;
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
||||
varying vec3 clipping_planes_dots;
|
||||
|
||||
// x = diffuse, y = specular;
|
||||
varying vec2 intensity;
|
||||
|
||||
uniform PrintVolumeDetection print_volume;
|
||||
|
||||
varying vec4 model_pos;
|
||||
varying vec4 world_pos;
|
||||
varying float world_normal_z;
|
||||
varying vec3 eye_normal;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||
discard;
|
||||
vec3 color = uniform_color.rgb;
|
||||
float alpha = uniform_color.a;
|
||||
|
||||
if (slope.actived && world_normal_z < slope.normal_z - EPSILON) {
|
||||
color = vec3(0.7, 0.7, 1.0);
|
||||
alpha = 1.0;
|
||||
}
|
||||
|
||||
// if the fragment is outside the print volume -> use darker color
|
||||
vec3 pv_check_min = ZERO;
|
||||
vec3 pv_check_max = ZERO;
|
||||
if (print_volume.type == 0) {
|
||||
// rectangle
|
||||
pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x);
|
||||
pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y);
|
||||
}
|
||||
else if (print_volume.type == 1) {
|
||||
// circle
|
||||
float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy);
|
||||
pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x);
|
||||
pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y);
|
||||
}
|
||||
color = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color, ZERO, 0.3333) : color;
|
||||
|
||||
#ifdef ENABLE_ENVIRONMENT_MAP
|
||||
if (use_environment_tex)
|
||||
gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha);
|
||||
else
|
||||
#endif
|
||||
gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha);
|
||||
|
||||
// In the support painting gizmo and the seam painting gizmo are painted triangles rendered over the already
|
||||
// rendered object. To resolved z-fighting between previously rendered object and painted triangles, values
|
||||
// inside the depth buffer are offset by small epsilon for painted triangles inside those gizmos.
|
||||
gl_FragDepth = gl_FragCoord.z - (offset_depth_buffer ? EPSILON : 0.0);
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
#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
|
||||
|
||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
struct SlopeDetection
|
||||
{
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
};
|
||||
|
||||
uniform mat4 volume_world_matrix;
|
||||
uniform SlopeDetection slope;
|
||||
|
||||
// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane.
|
||||
uniform vec2 z_range;
|
||||
// Clipping plane - general orientation. Used by the SLA gizmo.
|
||||
uniform vec4 clipping_plane;
|
||||
|
||||
// x = diffuse, y = specular;
|
||||
varying vec2 intensity;
|
||||
|
||||
varying vec3 clipping_planes_dots;
|
||||
|
||||
varying vec4 model_pos;
|
||||
varying vec4 world_pos;
|
||||
varying float world_normal_z;
|
||||
varying vec3 eye_normal;
|
||||
|
||||
void main()
|
||||
{
|
||||
// First transform the normal into camera space and normalize the result.
|
||||
eye_normal = normalize(gl_NormalMatrix * gl_Normal);
|
||||
|
||||
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
|
||||
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
|
||||
float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
|
||||
|
||||
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
|
||||
vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
|
||||
intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, 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;
|
||||
|
||||
model_pos = gl_Vertex;
|
||||
// Point in homogenous coordinates.
|
||||
world_pos = volume_world_matrix * gl_Vertex;
|
||||
|
||||
// z component of normal vector in world coordinate used for slope shading
|
||||
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0;
|
||||
|
||||
gl_Position = ftransform();
|
||||
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
|
||||
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||
}
|
|
@ -595,6 +595,19 @@ int CLI::run(int argc, char **argv)
|
|||
|
||||
if (start_gui) {
|
||||
#ifdef SLIC3R_GUI
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
// likely some linux / unix system
|
||||
const char *display = boost::nowide::getenv("DISPLAY");
|
||||
// const char *wayland_display = boost::nowide::getenv("WAYLAND_DISPLAY");
|
||||
//if (! ((display && *display) || (wayland_display && *wayland_display))) {
|
||||
if (! (display && *display)) {
|
||||
// DISPLAY not set.
|
||||
boost::nowide::cerr << "DISPLAY not set, GUI mode not available." << std::endl << std::endl;
|
||||
this->print_help(false);
|
||||
// Indicate an error.
|
||||
return 1;
|
||||
}
|
||||
#endif // some linux / unix system
|
||||
Slic3r::GUI::GUI_InitParams params;
|
||||
params.argc = argc;
|
||||
params.argv = argv;
|
||||
|
|
5
src/fast_float/README.md
Normal file
5
src/fast_float/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
**The fast_float library provides fast header-only implementations for the C++ from_chars functions for float and double types.**
|
||||
|
||||
For more information go to https://github.com/fastfloat/fast_float.
|
||||
|
||||
THIS DIRECTORY CONTAINS THE fast_float-2.0.0 fe1ce58 SOURCE DISTRIBUTION.
|
|
@ -139,16 +139,16 @@ namespace ImGui
|
|||
const wchar_t EjectHoverButton = 0x13;
|
||||
const wchar_t CancelButton = 0x14;
|
||||
const wchar_t CancelHoverButton = 0x15;
|
||||
const wchar_t VarLayerHeightMarker = 0x16;
|
||||
// const wchar_t VarLayerHeightMarker = 0x16;
|
||||
|
||||
const wchar_t RightArrowButton = 0x18;
|
||||
const wchar_t RightArrowHoverButton = 0x19;
|
||||
const wchar_t PreferencesButton = 0x1A;
|
||||
const wchar_t PreferencesHoverButton = 0x1B;
|
||||
const wchar_t SinkingObjectMarker = 0x1C;
|
||||
const wchar_t CustomSupportsMarker = 0x1D;
|
||||
const wchar_t CustomSeamMarker = 0x1E;
|
||||
const wchar_t MmuSegmentationMarker = 0x1F;
|
||||
// const wchar_t SinkingObjectMarker = 0x1C;
|
||||
// const wchar_t CustomSupportsMarker = 0x1D;
|
||||
// const wchar_t CustomSeamMarker = 0x1E;
|
||||
// const wchar_t MmuSegmentationMarker = 0x1F;
|
||||
// Do not forget use following letters only in wstring
|
||||
const wchar_t DocumentationButton = 0x2600;
|
||||
const wchar_t DocumentationHoverButton = 0x2601;
|
||||
|
|
|
@ -111,7 +111,7 @@ public:
|
|||
void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; }
|
||||
void translate(const Vec3d &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
BoundingBox3Base<PointClass> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
coordf_t max_size() const;
|
||||
|
||||
|
|
422
src/libslic3r/BuildVolume.cpp
Normal file
422
src/libslic3r/BuildVolume.cpp
Normal file
|
@ -0,0 +1,422 @@
|
|||
#include "BuildVolume.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "GCode/GCodeProcessor.hpp"
|
||||
#include "Point.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
BuildVolume::BuildVolume(const std::vector<Vec2d> &bed_shape, const double max_print_height) : m_bed_shape(bed_shape), m_max_print_height(max_print_height)
|
||||
{
|
||||
assert(max_print_height >= 0);
|
||||
|
||||
m_polygon = Polygon::new_scale(bed_shape);
|
||||
|
||||
// Calcuate various metrics of the input polygon.
|
||||
m_convex_hull = Geometry::convex_hull(m_polygon.points);
|
||||
m_bbox = get_extents(m_convex_hull);
|
||||
m_area = m_polygon.area();
|
||||
|
||||
BoundingBoxf bboxf = get_extents(bed_shape);
|
||||
m_bboxf = BoundingBoxf3{ to_3d(bboxf.min, 0.), to_3d(bboxf.max, max_print_height) };
|
||||
|
||||
if (bed_shape.size() >= 4 && std::abs((m_area - double(m_bbox.size().x()) * double(m_bbox.size().y()))) < sqr(SCALED_EPSILON)) {
|
||||
// Square print bed, use the bounding box for collision detection.
|
||||
m_type = Type::Rectangle;
|
||||
m_circle.center = 0.5 * (m_bbox.min.cast<double>() + m_bbox.max.cast<double>());
|
||||
m_circle.radius = 0.5 * m_bbox.size().cast<double>().norm();
|
||||
} else if (bed_shape.size() > 3) {
|
||||
// Circle was discretized, formatted into text with limited accuracy, thus the circle was deformed.
|
||||
// RANSAC is slightly more accurate than the iterative Taubin / Newton method with such an input.
|
||||
// m_circle = Geometry::circle_taubin_newton(bed_shape);
|
||||
m_circle = Geometry::circle_ransac(bed_shape);
|
||||
bool is_circle = true;
|
||||
#ifndef NDEBUG
|
||||
// Measuring maximum absolute error of interpolating an input polygon with circle.
|
||||
double max_error = 0;
|
||||
#endif // NDEBUG
|
||||
Vec2d prev = bed_shape.back();
|
||||
for (const Vec2d &p : bed_shape) {
|
||||
#ifndef NDEBUG
|
||||
max_error = std::max(max_error, std::abs((p - m_circle.center).norm() - m_circle.radius));
|
||||
#endif // NDEBUG
|
||||
if (// Polygon vertices must lie very close the circle.
|
||||
std::abs((p - m_circle.center).norm() - m_circle.radius) > 0.005 ||
|
||||
// Midpoints of polygon edges must not undercat more than 3mm. This corresponds to 72 edges per circle generated by BedShapePanel::update_shape().
|
||||
m_circle.radius - (0.5 * (prev + p) - m_circle.center).norm() > 3.) {
|
||||
is_circle = false;
|
||||
break;
|
||||
}
|
||||
prev = p;
|
||||
}
|
||||
if (is_circle) {
|
||||
m_type = Type::Circle;
|
||||
m_circle.center = scaled<double>(m_circle.center);
|
||||
m_circle.radius = scaled<double>(m_circle.radius);
|
||||
}
|
||||
}
|
||||
|
||||
if (bed_shape.size() >= 3 && m_type == Type::Invalid) {
|
||||
// Circle check is not used for Convex / Custom shapes, fill it with something reasonable.
|
||||
m_circle = Geometry::smallest_enclosing_circle_welzl(m_convex_hull.points);
|
||||
m_type = (m_convex_hull.area() - m_area) < sqr(SCALED_EPSILON) ? Type::Convex : Type::Custom;
|
||||
// Initialize the top / bottom decomposition for inside convex polygon check. Do it with two different epsilons applied.
|
||||
auto convex_decomposition = [](const Polygon &in, double epsilon) {
|
||||
Polygon src = expand(in, float(epsilon)).front();
|
||||
std::vector<Vec2d> pts;
|
||||
pts.reserve(src.size());
|
||||
for (const Point &pt : src.points)
|
||||
pts.emplace_back(unscaled<double>(pt.cast<double>().eval()));
|
||||
return Geometry::decompose_convex_polygon_top_bottom(pts);
|
||||
};
|
||||
m_top_bottom_convex_hull_decomposition_scene = convex_decomposition(m_convex_hull, SceneEpsilon);
|
||||
m_top_bottom_convex_hull_decomposition_bed = convex_decomposition(m_convex_hull, BedEpsilon);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "BuildVolume bed_shape clasified as: " << this->type_name();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Tests intersections of projected triangles, not just their vertices against a bounding box.
|
||||
// This test also correctly evaluates collision of a non-convex object with the bounding box.
|
||||
// Not used, slower than simple bounding box collision check and nobody complained about the inaccuracy of the simple test.
|
||||
static inline BuildVolume::ObjectState rectangle_test(const indexed_triangle_set &its, const Transform3f &trafo, const Vec2f min, const Vec2f max, const float max_z)
|
||||
{
|
||||
bool inside = false;
|
||||
bool outside = false;
|
||||
|
||||
auto sign = [](const Vec3f& pt) -> char { return pt.z() > 0 ? 1 : pt.z() < 0 ? -1 : 0; };
|
||||
|
||||
// Returns true if both inside and outside are set, thus early exit.
|
||||
auto test_intersection = [&inside, &outside, min, max, max_z](const Vec3f& p1, const Vec3f& p2, const Vec3f& p3) -> bool {
|
||||
// First test whether the triangle is completely inside or outside the bounding box.
|
||||
Vec3f pmin = p1.cwiseMin(p2).cwiseMin(p3);
|
||||
Vec3f pmax = p1.cwiseMax(p2).cwiseMax(p3);
|
||||
bool tri_inside = false;
|
||||
bool tri_outside = false;
|
||||
if (pmax.x() < min.x() || pmin.x() > max.x() || pmax.y() < min.y() || pmin.y() > max.y()) {
|
||||
// Separated by one of the rectangle sides.
|
||||
tri_outside = true;
|
||||
} else if (pmin.x() >= min.x() && pmax.x() <= max.x() && pmin.y() >= min.y() && pmax.y() <= max.y()) {
|
||||
// Fully inside the rectangle.
|
||||
tri_inside = true;
|
||||
} else {
|
||||
// Bounding boxes overlap. Test triangle sides against the bbox corners.
|
||||
Vec2f v1(- p2.y() + p1.y(), p2.x() - p1.x());
|
||||
Vec2f v2(- p2.y() + p2.y(), p3.x() - p2.x());
|
||||
Vec2f v3(- p1.y() + p3.y(), p1.x() - p3.x());
|
||||
bool ccw = cross2(v1, v2) > 0;
|
||||
for (const Vec2f &p : { Vec2f{ min.x(), min.y() }, Vec2f{ min.x(), max.y() }, Vec2f{ max.x(), min.y() }, Vec2f{ max.x(), max.y() } }) {
|
||||
auto dot = v1.dot(p);
|
||||
if (ccw ? dot >= 0 : dot <= 0)
|
||||
tri_inside = true;
|
||||
else
|
||||
tri_outside = true;
|
||||
}
|
||||
}
|
||||
inside |= tri_inside;
|
||||
outside |= tri_outside;
|
||||
return inside && outside;
|
||||
};
|
||||
|
||||
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||
auto clip_edge = [](const Vec3f &p1, const Vec3f &p2) -> Vec3f {
|
||||
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
|
||||
return { p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z };
|
||||
};
|
||||
|
||||
// Clip at (p1, p2), p3 must be on the clipping plane.
|
||||
// Returns true if both inside and outside are set, thus early exit.
|
||||
auto clip_and_test1 = [&test_intersection, &clip_edge](const Vec3f &p1, const Vec3f &p2, const Vec3f &p3, bool p1above) -> bool {
|
||||
Vec3f pa = clip_edge(p1, p2);
|
||||
return p1above ? test_intersection(p1, pa, p3) : test_intersection(pa, p2, p3);
|
||||
};
|
||||
|
||||
// Clip at (p1, p2) and (p2, p3).
|
||||
// Returns true if both inside and outside are set, thus early exit.
|
||||
auto clip_and_test2 = [&test_intersection, &clip_edge](const Vec3f &p1, const Vec3f &p2, const Vec3f &p3, bool p2above) -> bool {
|
||||
Vec3f pa = clip_edge(p1, p2);
|
||||
Vec3f pb = clip_edge(p2, p3);
|
||||
return p2above ? test_intersection(pa, p2, pb) : test_intersection(p1, pa, p3) || test_intersection(p3, pa, pb);
|
||||
};
|
||||
|
||||
for (const stl_triangle_vertex_indices &tri : its.indices) {
|
||||
const Vec3f pts[3] = { trafo * its.vertices[tri(0)], trafo * its.vertices[tri(1)], trafo * its.vertices[tri(2)] };
|
||||
char signs[3] = { sign(pts[0]), sign(pts[1]), sign(pts[2]) };
|
||||
bool clips[3] = { signs[0] * signs[1] == -1, signs[1] * signs[2] == -1, signs[2] * signs[0] == -1 };
|
||||
if (clips[0]) {
|
||||
if (clips[1]) {
|
||||
// Clipping at (pt0, pt1) and (pt1, pt2).
|
||||
if (clip_and_test2(pts[0], pts[1], pts[2], signs[1] > 0))
|
||||
break;
|
||||
} else if (clips[2]) {
|
||||
// Clipping at (pt0, pt1) and (pt0, pt2).
|
||||
if (clip_and_test2(pts[2], pts[0], pts[1], signs[0] > 0))
|
||||
break;
|
||||
} else {
|
||||
// Clipping at (pt0, pt1), pt2 must be on the clipping plane.
|
||||
if (clip_and_test1(pts[0], pts[1], pts[2], signs[0] > 0))
|
||||
break;
|
||||
}
|
||||
} else if (clips[1]) {
|
||||
if (clips[2]) {
|
||||
// Clipping at (pt1, pt2) and (pt0, pt2).
|
||||
if (clip_and_test2(pts[0], pts[1], pts[2], signs[1] > 0))
|
||||
break;
|
||||
} else {
|
||||
// Clipping at (pt1, pt2), pt0 must be on the clipping plane.
|
||||
if (clip_and_test1(pts[1], pts[2], pts[0], signs[1] > 0))
|
||||
break;
|
||||
}
|
||||
} else if (clips[2]) {
|
||||
// Clipping at (pt0, pt2), pt1 must be on the clipping plane.
|
||||
if (clip_and_test1(pts[2], pts[0], pts[1], signs[2] > 0))
|
||||
break;
|
||||
} else if (signs[0] >= 0 && signs[1] >= 0 && signs[2] >= 0) {
|
||||
// The triangle is above or on the clipping plane.
|
||||
if (test_intersection(pts[0], pts[1], pts[2]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inside ? (outside ? BuildVolume::ObjectState::Colliding : BuildVolume::ObjectState::Inside) : BuildVolume::ObjectState::Outside;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Trim the input transformed triangle mesh with print bed and test the remaining vertices with is_inside callback.
|
||||
// Return inside / colliding / outside state.
|
||||
template<typename InsideFn>
|
||||
BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed, InsideFn is_inside)
|
||||
{
|
||||
size_t num_inside = 0;
|
||||
size_t num_above = 0;
|
||||
bool inside = false;
|
||||
bool outside = false;
|
||||
static constexpr const auto world_min_z = float(-BuildVolume::SceneEpsilon);
|
||||
|
||||
if (may_be_below_bed)
|
||||
{
|
||||
// Slower test, needs to clip the object edges with the print bed plane.
|
||||
// 1) Allocate transformed vertices with their position with respect to print bed surface.
|
||||
std::vector<char> sides;
|
||||
sides.reserve(its.vertices.size());
|
||||
|
||||
const auto sign = [](const stl_vertex& pt) { return pt.z() > world_min_z ? 1 : pt.z() < world_min_z ? -1 : 0; };
|
||||
|
||||
for (const stl_vertex &v : its.vertices) {
|
||||
const stl_vertex pt = trafo * v;
|
||||
const int s = sign(pt);
|
||||
sides.emplace_back(s);
|
||||
if (s >= 0) {
|
||||
// Vertex above or on print bed surface. Test whether it is inside the build volume.
|
||||
++ num_above;
|
||||
if (is_inside(pt))
|
||||
++ num_inside;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_above == 0)
|
||||
// Special case, the object is completely below the print bed, thus it is outside,
|
||||
// however we want to allow an object to be still printable if some of its parts are completely below the print bed.
|
||||
return BuildVolume::ObjectState::Below;
|
||||
|
||||
// 2) Calculate intersections of triangle edges with the build surface.
|
||||
inside = num_inside > 0;
|
||||
outside = num_inside < num_above;
|
||||
if (num_above < its.vertices.size() && ! (inside && outside)) {
|
||||
// Not completely above the build surface and status may still change by testing edges intersecting the build platform.
|
||||
for (const stl_triangle_vertex_indices &tri : its.indices) {
|
||||
const int s[3] = { sides[tri(0)], sides[tri(1)], sides[tri(2)] };
|
||||
if (std::min(s[0], std::min(s[1], s[2])) < 0 && std::max(s[0], std::max(s[1], s[2])) > 0) {
|
||||
// Some edge of this triangle intersects the build platform. Calculate the intersection.
|
||||
int iprev = 2;
|
||||
for (int iedge = 0; iedge < 3; ++ iedge) {
|
||||
if (s[iprev] * s[iedge] == -1) {
|
||||
// edge intersects the build surface. Calculate intersection point.
|
||||
const stl_vertex p1 = trafo * its.vertices[tri(iprev)];
|
||||
const stl_vertex p2 = trafo * its.vertices[tri(iedge)];
|
||||
assert(sign(p1) == s[iprev]);
|
||||
assert(sign(p2) == s[iedge]);
|
||||
assert(p1.z() * p2.z() < 0);
|
||||
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
|
||||
(is_inside(Vec3f(p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z)) ? inside : outside) = true;
|
||||
}
|
||||
iprev = iedge;
|
||||
}
|
||||
if (inside && outside)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Much simpler and faster code, not clipping the object with the print bed.
|
||||
assert(! may_be_below_bed);
|
||||
num_above = its.vertices.size();
|
||||
for (const stl_vertex &v : its.vertices) {
|
||||
const stl_vertex pt = trafo * v;
|
||||
assert(pt.z() >= world_min_z);
|
||||
if (is_inside(pt))
|
||||
++ num_inside;
|
||||
}
|
||||
inside = num_inside > 0;
|
||||
outside = num_inside < num_above;
|
||||
}
|
||||
|
||||
return inside ? (outside ? BuildVolume::ObjectState::Colliding : BuildVolume::ObjectState::Inside) : BuildVolume::ObjectState::Outside;
|
||||
}
|
||||
|
||||
BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set& its, const Transform3f& trafo, bool may_be_below_bed, bool ignore_bottom) const
|
||||
{
|
||||
switch (m_type) {
|
||||
case Type::Rectangle:
|
||||
{
|
||||
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(SceneEpsilon);
|
||||
if (m_max_print_height == 0.0)
|
||||
build_volume.max.z() = std::numeric_limits<double>::max();
|
||||
if (ignore_bottom)
|
||||
build_volume.min.z() = -std::numeric_limits<double>::max();
|
||||
BoundingBox3Base<Vec3f> build_volumef(build_volume.min.cast<float>(), build_volume.max.cast<float>());
|
||||
// The following test correctly interprets intersection of a non-convex object with a rectangular build volume.
|
||||
//return rectangle_test(its, trafo, to_2d(build_volume.min), to_2d(build_volume.max), build_volume.max.z());
|
||||
//FIXME This test does NOT correctly interprets intersection of a non-convex object with a rectangular build volume.
|
||||
return object_state_templ(its, trafo, may_be_below_bed, [build_volumef](const Vec3f &pt) { return build_volumef.contains(pt); });
|
||||
}
|
||||
case Type::Circle:
|
||||
{
|
||||
Geometry::Circlef circle { unscaled<float>(m_circle.center), unscaled<float>(m_circle.radius + SceneEpsilon) };
|
||||
return m_max_print_height == 0.0 ?
|
||||
object_state_templ(its, trafo, may_be_below_bed, [circle](const Vec3f &pt) { return circle.contains(to_2d(pt)); }) :
|
||||
object_state_templ(its, trafo, may_be_below_bed, [circle, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && circle.contains(to_2d(pt)); });
|
||||
}
|
||||
case Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case Type::Custom:
|
||||
return m_max_print_height == 0.0 ?
|
||||
object_state_templ(its, trafo, may_be_below_bed, [this](const Vec3f &pt) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); }) :
|
||||
object_state_templ(its, trafo, may_be_below_bed, [this, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); });
|
||||
case Type::Invalid:
|
||||
default:
|
||||
return ObjectState::Inside;
|
||||
}
|
||||
}
|
||||
|
||||
BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3& volume_bbox, bool ignore_bottom) const
|
||||
{
|
||||
assert(m_type == Type::Rectangle);
|
||||
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(SceneEpsilon);
|
||||
if (m_max_print_height == 0.0)
|
||||
build_volume.max.z() = std::numeric_limits<double>::max();
|
||||
if (ignore_bottom)
|
||||
build_volume.min.z() = -std::numeric_limits<double>::max();
|
||||
return build_volume.max.z() <= - SceneEpsilon ? ObjectState::Below :
|
||||
build_volume.contains(volume_bbox) ? ObjectState::Inside :
|
||||
build_volume.intersects(volume_bbox) ? ObjectState::Colliding : ObjectState::Outside;
|
||||
}
|
||||
|
||||
bool BuildVolume::all_paths_inside(const GCodeProcessorResult& paths, const BoundingBoxf3& paths_bbox, bool ignore_bottom) const
|
||||
{
|
||||
auto move_valid = [](const GCodeProcessorResult::MoveVertex &move) {
|
||||
return move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.f && move.height != 0.f;
|
||||
};
|
||||
static constexpr const double epsilon = BedEpsilon;
|
||||
|
||||
switch (m_type) {
|
||||
case Type::Rectangle:
|
||||
{
|
||||
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(epsilon);
|
||||
if (m_max_print_height == 0.0)
|
||||
build_volume.max.z() = std::numeric_limits<double>::max();
|
||||
if (ignore_bottom)
|
||||
build_volume.min.z() = -std::numeric_limits<double>::max();
|
||||
return build_volume.contains(paths_bbox);
|
||||
}
|
||||
case Type::Circle:
|
||||
{
|
||||
const Vec2f c = unscaled<float>(m_circle.center);
|
||||
const float r = unscaled<double>(m_circle.radius) + epsilon;
|
||||
const float r2 = sqr(r);
|
||||
return m_max_print_height == 0.0 ?
|
||||
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, c, r2](const GCodeProcessorResult::MoveVertex &move)
|
||||
{ return ! move_valid(move) || (to_2d(move.position) - c).squaredNorm() <= r2; }) :
|
||||
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, c, r2, z = m_max_print_height + epsilon](const GCodeProcessorResult::MoveVertex& move)
|
||||
{ return ! move_valid(move) || ((to_2d(move.position) - c).squaredNorm() <= r2 && move.position.z() <= z); });
|
||||
}
|
||||
case Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case Type::Custom:
|
||||
return m_max_print_height == 0.0 ?
|
||||
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, this](const GCodeProcessorResult::MoveVertex &move)
|
||||
{ return ! move_valid(move) || Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(move.position).cast<double>()); }) :
|
||||
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, this, z = m_max_print_height + epsilon](const GCodeProcessorResult::MoveVertex &move)
|
||||
{ return ! move_valid(move) || (Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(move.position).cast<double>()) && move.position.z() <= z); });
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Fn>
|
||||
inline bool all_inside_vertices_normals_interleaved(const std::vector<float> &paths, Fn fn)
|
||||
{
|
||||
for (auto it = paths.begin(); it != paths.end(); ) {
|
||||
it += 3;
|
||||
if (! fn({ *it, *(it + 1), *(it + 2) }))
|
||||
return false;
|
||||
it += 3;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuildVolume::all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& paths_bbox, bool ignore_bottom) const
|
||||
{
|
||||
assert(paths.size() % 6 == 0);
|
||||
static constexpr const double epsilon = BedEpsilon;
|
||||
switch (m_type) {
|
||||
case Type::Rectangle:
|
||||
{
|
||||
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(epsilon);
|
||||
if (m_max_print_height == 0.0)
|
||||
build_volume.max.z() = std::numeric_limits<double>::max();
|
||||
if (ignore_bottom)
|
||||
build_volume.min.z() = -std::numeric_limits<double>::max();
|
||||
return build_volume.contains(paths_bbox.min().cast<double>()) && build_volume.contains(paths_bbox.max().cast<double>());
|
||||
}
|
||||
case Type::Circle:
|
||||
{
|
||||
const Vec2f c = unscaled<float>(m_circle.center);
|
||||
const float r = unscaled<double>(m_circle.radius) + float(epsilon);
|
||||
const float r2 = sqr(r);
|
||||
return m_max_print_height == 0.0 ?
|
||||
all_inside_vertices_normals_interleaved(paths, [c, r2](Vec3f p) { return (to_2d(p) - c).squaredNorm() <= r2; }) :
|
||||
all_inside_vertices_normals_interleaved(paths, [c, r2, z = m_max_print_height + epsilon](Vec3f p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.z() <= z; });
|
||||
}
|
||||
case Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case Type::Custom:
|
||||
return m_max_print_height == 0.0 ?
|
||||
all_inside_vertices_normals_interleaved(paths, [this](Vec3f p) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(p).cast<double>()); }) :
|
||||
all_inside_vertices_normals_interleaved(paths, [this, z = m_max_print_height + epsilon](Vec3f p) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(p).cast<double>()) && p.z() <= z; });
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view BuildVolume::type_name(Type type)
|
||||
{
|
||||
using namespace std::literals;
|
||||
switch (type) {
|
||||
case Type::Invalid: return "Invalid"sv;
|
||||
case Type::Rectangle: return "Rectangle"sv;
|
||||
case Type::Circle: return "Circle"sv;
|
||||
case Type::Convex: return "Convex"sv;
|
||||
case Type::Custom: return "Custom"sv;
|
||||
}
|
||||
// make visual studio happy
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
126
src/libslic3r/BuildVolume.hpp
Normal file
126
src/libslic3r/BuildVolume.hpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
#ifndef slic3r_BuildVolume_hpp_
|
||||
#define slic3r_BuildVolume_hpp_
|
||||
|
||||
#include "Point.hpp"
|
||||
#include "Geometry/Circle.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include <admesh/stl.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct GCodeProcessorResult;
|
||||
|
||||
// For collision detection of objects and G-code (extrusion paths) against the build volume.
|
||||
class BuildVolume
|
||||
{
|
||||
public:
|
||||
enum class Type : unsigned char
|
||||
{
|
||||
// Not set yet or undefined.
|
||||
Invalid,
|
||||
// Rectangular print bed. Most common, cheap to work with.
|
||||
Rectangle,
|
||||
// Circular print bed. Common on detals, cheap to work with.
|
||||
Circle,
|
||||
// Convex print bed. Complex to process.
|
||||
Convex,
|
||||
// Some non convex shape.
|
||||
Custom
|
||||
};
|
||||
|
||||
// Initialized to empty, all zeros, Invalid.
|
||||
BuildVolume() {}
|
||||
// Initialize from PrintConfig::bed_shape and PrintConfig::max_print_height
|
||||
BuildVolume(const std::vector<Vec2d> &bed_shape, const double max_print_height);
|
||||
|
||||
// Source data, unscaled coordinates.
|
||||
const std::vector<Vec2d>& bed_shape() const { return m_bed_shape; }
|
||||
double max_print_height() const { return m_max_print_height; }
|
||||
|
||||
// Derived data
|
||||
Type type() const { return m_type; }
|
||||
// Format the type for console output.
|
||||
static std::string_view type_name(Type type);
|
||||
std::string_view type_name() const { return type_name(m_type); }
|
||||
bool valid() const { return m_type != Type::Invalid; }
|
||||
// Same as bed_shape(), but scaled coordinates.
|
||||
const Polygon& polygon() const { return m_polygon; }
|
||||
// Bounding box of polygon(), scaled.
|
||||
const BoundingBox& bounding_box() const { return m_bbox; }
|
||||
// Bounding volume of bed_shape(), max_print_height(), unscaled.
|
||||
const BoundingBoxf3& bounding_volume() const { return m_bboxf; }
|
||||
BoundingBoxf bounding_volume2d() const { return { to_2d(m_bboxf.min), to_2d(m_bboxf.max) }; }
|
||||
|
||||
// Center of the print bed, unscaled.
|
||||
Vec2d bed_center() const { return to_2d(m_bboxf.center()); }
|
||||
// Convex hull of polygon(), scaled.
|
||||
const Polygon& convex_hull() const { return m_convex_hull; }
|
||||
// Smallest enclosing circle of polygon(), scaled.
|
||||
const Geometry::Circled& circle() const { return m_circle; }
|
||||
|
||||
enum class ObjectState : unsigned char
|
||||
{
|
||||
// Inside the build volume, thus printable.
|
||||
Inside,
|
||||
// Colliding with the build volume boundary, thus not printable and error is shown.
|
||||
Colliding,
|
||||
// Outside of the build volume means the object is ignored: Not printed and no error is shown.
|
||||
Outside,
|
||||
// Completely below the print bed. The same as Outside, but an object with one printable part below the print bed
|
||||
// and at least one part above the print bed is still printable.
|
||||
Below,
|
||||
};
|
||||
|
||||
// 1) Tests called on the plater.
|
||||
// Using SceneEpsilon for all tests.
|
||||
static constexpr const double SceneEpsilon = EPSILON;
|
||||
// Called by Plater to update Inside / Colliding / Outside state of ModelObjects before slicing.
|
||||
// Called from Model::update_print_volume_state() -> ModelObject::update_instances_print_volume_state()
|
||||
// Using SceneEpsilon
|
||||
ObjectState object_state(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed, bool ignore_bottom = true) const;
|
||||
// Called by GLVolumeCollection::check_outside_state() after an object is manipulated with gizmos for example.
|
||||
// Called for a rectangular bed:
|
||||
ObjectState volume_state_bbox(const BoundingBoxf3& volume_bbox, bool ignore_bottom = true) const;
|
||||
|
||||
// 2) Test called on G-code paths.
|
||||
// Using BedEpsilon for all tests.
|
||||
static constexpr const double BedEpsilon = 3. * EPSILON;
|
||||
// Called on final G-code paths.
|
||||
//FIXME The test does not take the thickness of the extrudates into account!
|
||||
bool all_paths_inside(const GCodeProcessorResult& paths, const BoundingBoxf3& paths_bbox, bool ignore_bottom = true) const;
|
||||
// Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices.
|
||||
bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const;
|
||||
|
||||
private:
|
||||
// Source definition of the print bed geometry (PrintConfig::bed_shape)
|
||||
std::vector<Vec2d> m_bed_shape;
|
||||
// Source definition of the print volume height (PrintConfig::max_print_height)
|
||||
double m_max_print_height;
|
||||
|
||||
// Derived values.
|
||||
Type m_type { Type::Invalid };
|
||||
// Geometry of the print bed, scaled copy of m_bed_shape.
|
||||
Polygon m_polygon;
|
||||
// Scaled snug bounding box around m_polygon.
|
||||
BoundingBox m_bbox;
|
||||
// 3D bounding box around m_shape, m_max_print_height.
|
||||
BoundingBoxf3 m_bboxf;
|
||||
// Area of m_polygon, scaled.
|
||||
double m_area { 0. };
|
||||
// Convex hull of m_polygon, scaled.
|
||||
Polygon m_convex_hull;
|
||||
// For collision detection against a convex build volume. Only filled in for m_type == Convex or Custom.
|
||||
// Variant with SceneEpsilon applied.
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> m_top_bottom_convex_hull_decomposition_scene;
|
||||
// Variant with BedEpsilon applied.
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> m_top_bottom_convex_hull_decomposition_bed;
|
||||
// Smallest enclosing circle of m_polygon, scaled.
|
||||
Geometry::Circled m_circle { Vec2d::Zero(), 0 };
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_BuildVolume_hpp_
|
|
@ -23,6 +23,8 @@ add_library(libslic3r STATIC
|
|||
BridgeDetector.hpp
|
||||
Brim.cpp
|
||||
Brim.hpp
|
||||
BuildVolume.cpp
|
||||
BuildVolume.hpp
|
||||
clipper.cpp
|
||||
clipper.hpp
|
||||
ClipperUtils.cpp
|
||||
|
@ -116,6 +118,8 @@ add_library(libslic3r STATIC
|
|||
Geometry.hpp
|
||||
Geometry/Circle.cpp
|
||||
Geometry/Circle.hpp
|
||||
Geometry/ConvexHull.cpp
|
||||
Geometry/ConvexHull.hpp
|
||||
Geometry/MedialAxis.cpp
|
||||
Geometry/MedialAxis.hpp
|
||||
Geometry/Voronoi.hpp
|
||||
|
@ -284,7 +288,7 @@ set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE ON CACHE BOOL "" FORCE)
|
|||
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(SET CMP0011 NEW)
|
||||
find_package(CGAL REQUIRED)
|
||||
find_package(CGAL 4.13 REQUIRED)
|
||||
cmake_policy(POP)
|
||||
|
||||
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp
|
||||
|
|
|
@ -542,6 +542,8 @@ static inline Polygons _clipper(ClipperLib::ClipType clipType, TSubj &&subject,
|
|||
return to_polygons(clipper_do<ClipperLib::Paths>(clipType, std::forward<TSubj>(subject), std::forward<TClip>(clip), ClipperLib::pftNonZero, do_safety_offset));
|
||||
}
|
||||
|
||||
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
|
||||
|
|
|
@ -331,6 +331,8 @@ Slic3r::ExPolygons union_safety_offset_ex(const Slic3r::Polygons &polygons);
|
|||
Slic3r::ExPolygons union_safety_offset_ex(const Slic3r::ExPolygons &expolygons);
|
||||
|
||||
// Aliases for the various offset(...) functions, conveying the purpose of the offset.
|
||||
inline Slic3r::Polygons expand(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
|
||||
{ assert(delta > 0); return offset(polygon, delta, joinType, miterLimit); }
|
||||
inline Slic3r::Polygons expand(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
|
||||
{ assert(delta > 0); return offset(polygons, delta, joinType, miterLimit); }
|
||||
inline Slic3r::ExPolygons expand_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
|
||||
|
@ -378,6 +380,7 @@ inline Slic3r::ExPolygons opening_ex(const Slic3r::Surfaces &surfaces, const flo
|
|||
Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, const Slic3r::Polygons &clip);
|
||||
|
||||
// Safety offset is applied to the clipping polygons only.
|
||||
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
|
|
|
@ -740,11 +740,7 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo
|
|||
}
|
||||
|
||||
// Load the config keys from the given string.
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
|
||||
#else
|
||||
static inline size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
{
|
||||
if (str == nullptr)
|
||||
return 0;
|
||||
|
|
|
@ -1764,6 +1764,8 @@ public:
|
|||
// By setting min=0, only nonnegative input is allowed.
|
||||
int min = INT_MIN;
|
||||
int max = INT_MAX;
|
||||
// To check if it's not a typo and a % is missing
|
||||
double max_literal = 1;
|
||||
ConfigOptionMode mode = comSimple;
|
||||
// Legacy names for this configuration option.
|
||||
// Used when parsing legacy configuration file.
|
||||
|
@ -2015,9 +2017,7 @@ public:
|
|||
// Set all the nullable values to nils.
|
||||
void null_nullables();
|
||||
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
static size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions);
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
|
||||
private:
|
||||
// Set a configuration value from a string.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "ExPolygonCollection.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
|
@ -2710,16 +2710,10 @@ namespace Slic3r {
|
|||
|
||||
bool _3MF_Exporter::_add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items)
|
||||
{
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
// This happens for empty projects
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
if (build_items.size() == 0) {
|
||||
add_error("No build item found");
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
}
|
||||
|
||||
stream << " <" << BUILD_TAG << ">\n";
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libslic3r/SLA/RasterBase.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
#include "libslic3r/PNGReadWrite.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
@ -321,11 +322,13 @@ ConfigSubstitutions import_sla_archive(
|
|||
lh_opt != arch.config.not_found())
|
||||
{
|
||||
auto lh_str = lh_opt->second.data();
|
||||
try {
|
||||
double lh = std::stod(lh_str); // TODO replace with std::from_chars
|
||||
|
||||
size_t pos;
|
||||
double lh = string_to_double_decimal_point(lh_str, &pos);
|
||||
if (pos) { // TODO: verify that pos is 0 when parsing fails
|
||||
profile_out.set("layer_height", lh);
|
||||
profile_out.set("initial_layer_height", lh);
|
||||
} catch(...) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,6 +382,7 @@ void fill_iniconf(ConfMap &m, const SLAPrint &print)
|
|||
m["layerHeight"] = get_cfg_value(cfg, "layer_height");
|
||||
m["expTime"] = get_cfg_value(cfg, "exposure_time");
|
||||
m["expTimeFirst"] = get_cfg_value(cfg, "initial_exposure_time");
|
||||
m["expUserProfile"] = get_cfg_value(cfg, "material_print_speed") == "slow" ? "1" : "0";
|
||||
m["materialName"] = get_cfg_value(cfg, "sla_material_settings_id");
|
||||
m["printerModel"] = get_cfg_value(cfg, "printer_model");
|
||||
m["printerVariant"] = get_cfg_value(cfg, "printer_variant");
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "Exception.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "GCode/PrintExtents.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
|
@ -475,6 +475,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||
std::vector<GCode::LayerToPrint> layers_to_print;
|
||||
layers_to_print.reserve(object.layers().size() + object.support_layers().size());
|
||||
|
||||
/*
|
||||
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
||||
// This is the same logic as in support generator.
|
||||
//FIXME should we use the printing extruders instead?
|
||||
|
@ -488,7 +489,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||
for (auto lh : object.print()->config().min_layer_height.values)
|
||||
support_layer_height_min = std::min(support_layer_height_min, std::max(0.01, lh));
|
||||
gap_over_supports += support_layer_height_min;
|
||||
}
|
||||
}*/
|
||||
|
||||
std::vector<std::pair<double, double>> warning_ranges;
|
||||
|
||||
|
@ -528,22 +529,23 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||
if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|
||||
// Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
|
||||
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) {
|
||||
double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer)
|
||||
? gap_over_supports
|
||||
: 0.;
|
||||
double top_cd = object.config().support_material_contact_distance;
|
||||
double bottom_cd = object.config().support_material_bottom_contact_distance == 0. ? top_cd : object.config().support_material_bottom_contact_distance;
|
||||
|
||||
double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd);
|
||||
|
||||
double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.)
|
||||
+ layer_to_print.layer()->height
|
||||
+ support_contact_z;
|
||||
+ std::max(0., extra_gap);
|
||||
// Negative support_contact_z is not taken into account, it can result in false positives in cases
|
||||
// where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752)
|
||||
|
||||
if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON)
|
||||
warning_ranges.emplace_back(std::make_pair((last_extrusion_layer ? last_extrusion_layer->print_z() : 0.), layers_to_print.back().print_z()));
|
||||
|
||||
// Remember last layer with extrusions.
|
||||
if (has_extrusions)
|
||||
last_extrusion_layer = &layers_to_print.back();
|
||||
}
|
||||
// Remember last layer with extrusions.
|
||||
if (has_extrusions)
|
||||
last_extrusion_layer = &layers_to_print.back();
|
||||
}
|
||||
|
||||
if (! warning_ranges.empty()) {
|
||||
|
@ -622,7 +624,7 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
|||
namespace DoExport {
|
||||
// static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics)
|
||||
// {
|
||||
// const GCodeProcessor::Result& result = processor.get_result();
|
||||
// const GCodeProcessorResult& result = processor.get_result();
|
||||
// print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
|
||||
// print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
|
||||
// get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
|
||||
|
@ -630,7 +632,7 @@ namespace DoExport {
|
|||
|
||||
static void update_print_estimated_stats(const GCodeProcessor& processor, const std::vector<Extruder>& extruders, PrintStatistics& print_statistics)
|
||||
{
|
||||
const GCodeProcessor::Result& result = processor.get_result();
|
||||
const GCodeProcessorResult& result = processor.get_result();
|
||||
print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
|
||||
print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
|
||||
get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
|
||||
|
@ -723,7 +725,7 @@ namespace DoExport {
|
|||
}
|
||||
} // namespace DoExport
|
||||
|
||||
void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
{
|
||||
PROFILE_CLEAR();
|
||||
|
||||
|
@ -1542,30 +1544,37 @@ void GCode::process_layers(
|
|||
const size_t single_object_idx,
|
||||
GCodeOutputStream &output_stream)
|
||||
{
|
||||
// The pipeline is fixed: Neither wipe tower nor vase mode are implemented for sequential print.
|
||||
// The pipeline is variable: The vase mode filter is optional.
|
||||
size_t layer_to_print_idx = 0;
|
||||
tbb::parallel_pipeline(12,
|
||||
tbb::make_filter<void, GCode::LayerResult>(
|
||||
tbb::filter::serial_in_order,
|
||||
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> GCode::LayerResult {
|
||||
if (layer_to_print_idx == layers_to_print.size()) {
|
||||
fc.stop();
|
||||
return {};
|
||||
} else {
|
||||
LayerToPrint &layer = layers_to_print[layer_to_print_idx ++];
|
||||
print.throw_if_canceled();
|
||||
return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, single_object_idx);
|
||||
}
|
||||
}) &
|
||||
tbb::make_filter<GCode::LayerResult, std::string>(
|
||||
tbb::filter::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string {
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
}) &
|
||||
tbb::make_filter<std::string, void>(
|
||||
tbb::filter::serial_in_order,
|
||||
[&output_stream](std::string s) { output_stream.write(s); }
|
||||
));
|
||||
const auto generator = tbb::make_filter<void, GCode::LayerResult>(tbb::filter::serial_in_order,
|
||||
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> GCode::LayerResult {
|
||||
if (layer_to_print_idx == layers_to_print.size()) {
|
||||
fc.stop();
|
||||
return {};
|
||||
} else {
|
||||
LayerToPrint &layer = layers_to_print[layer_to_print_idx ++];
|
||||
print.throw_if_canceled();
|
||||
return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, single_object_idx);
|
||||
}
|
||||
});
|
||||
const auto spiral_vase = tbb::make_filter<GCode::LayerResult, GCode::LayerResult>(tbb::filter::serial_in_order,
|
||||
[&spiral_vase = *this->m_spiral_vase.get()](GCode::LayerResult in)->GCode::LayerResult {
|
||||
spiral_vase.enable(in.spiral_vase_enable);
|
||||
return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
||||
});
|
||||
const auto cooling = tbb::make_filter<GCode::LayerResult, std::string>(tbb::filter::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in)->std::string {
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
});
|
||||
const auto output = tbb::make_filter<std::string, void>(tbb::filter::serial_in_order,
|
||||
[&output_stream](std::string s) { output_stream.write(s); }
|
||||
);
|
||||
|
||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||
if (m_spiral_vase)
|
||||
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output);
|
||||
else
|
||||
tbb::parallel_pipeline(12, generator & cooling & output);
|
||||
}
|
||||
|
||||
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||
|
|
|
@ -143,7 +143,7 @@ public:
|
|||
|
||||
// throws std::runtime_exception on error,
|
||||
// throws CanceledException through print->throw_if_canceled().
|
||||
void do_export(Print* print, const char* path, GCodeProcessor::Result* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
void do_export(Print* print, const char* path, GCodeProcessorResult* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
|
||||
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
|
||||
const Vec2d& origin() const { return m_origin; }
|
||||
|
|
|
@ -343,7 +343,7 @@ void GCodeProcessor::TimeProcessor::reset()
|
|||
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
|
||||
}
|
||||
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends)
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends)
|
||||
{
|
||||
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
|
||||
if (in.f == nullptr)
|
||||
|
@ -636,7 +636,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
|
|||
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
|
||||
unsigned int curr_offset_id = 0;
|
||||
unsigned int total_offset = 0;
|
||||
for (MoveVertex& move : moves) {
|
||||
for (GCodeProcessorResult::MoveVertex& move : moves) {
|
||||
while (curr_offset_id < static_cast<unsigned int>(offsets.size()) && offsets[curr_offset_id].first <= move.gcode_id) {
|
||||
total_offset += offsets[curr_offset_id].second;
|
||||
++curr_offset_id;
|
||||
|
@ -716,12 +716,10 @@ void GCodeProcessor::UsedFilaments::process_caches(GCodeProcessor* processor)
|
|||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
void GCodeProcessor::Result::reset() {
|
||||
moves = std::vector<GCodeProcessor::MoveVertex>();
|
||||
void GCodeProcessorResult::reset() {
|
||||
moves = std::vector<GCodeProcessorResult::MoveVertex>();
|
||||
bed_shape = Pointfs();
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
max_print_height = 0.0f;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
settings_ids.reset();
|
||||
extruders_count = 0;
|
||||
extruder_colors = std::vector<std::string>();
|
||||
|
@ -731,14 +729,12 @@ void GCodeProcessor::Result::reset() {
|
|||
time = 0;
|
||||
}
|
||||
#else
|
||||
void GCodeProcessor::Result::reset() {
|
||||
void GCodeProcessorResult::reset() {
|
||||
|
||||
moves.clear();
|
||||
lines_ends.clear();
|
||||
bed_shape = Pointfs();
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
max_print_height = 0.0f;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
settings_ids.reset();
|
||||
extruders_count = 0;
|
||||
extruder_colors = std::vector<std::string>();
|
||||
|
@ -752,9 +748,7 @@ const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProces
|
|||
{ EProducer::PrusaSlicer, "generated by PrusaSlicer" },
|
||||
{ EProducer::Slic3rPE, "generated by Slic3r Prusa Edition" },
|
||||
{ EProducer::Slic3r, "generated by Slic3r" },
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
{ EProducer::SuperSlicer, "generated by SuperSlicer" },
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
{ EProducer::Cura, "Cura_SteamEngine" },
|
||||
{ EProducer::Simplify3D, "G-Code generated by Simplify3D(R)" },
|
||||
{ EProducer::CraftWare, "CraftWare" },
|
||||
|
@ -817,9 +811,7 @@ bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned i
|
|||
}
|
||||
|
||||
GCodeProcessor::GCodeProcessor()
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
: m_options_z_corrector(m_result)
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
{
|
||||
reset();
|
||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n";
|
||||
|
@ -889,9 +881,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
if (first_layer_height != nullptr)
|
||||
m_first_layer_height = std::abs(first_layer_height->value);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_result.max_print_height = config.max_print_height;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
|
@ -1122,11 +1112,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
if (first_layer_height != nullptr)
|
||||
m_first_layer_height = std::abs(first_layer_height->value);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
const ConfigOptionFloat* max_print_height = config.option<ConfigOptionFloat>("max_print_height");
|
||||
if (max_print_height != nullptr)
|
||||
m_result.max_print_height = max_print_height->value;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
|
||||
|
@ -1187,9 +1175,7 @@ void GCodeProcessor::reset()
|
|||
m_use_volumetric_e = false;
|
||||
m_last_default_color_id = 0;
|
||||
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.reset();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_mm3_per_mm_compare.reset();
|
||||
|
@ -1246,17 +1232,15 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
|
|||
}
|
||||
else if (m_producer == EProducer::Simplify3D)
|
||||
apply_config_simplify3d(filename);
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
else if (m_producer == EProducer::SuperSlicer)
|
||||
apply_config_superslicer(filename);
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
}
|
||||
|
||||
// process gcode
|
||||
m_result.filename = filename;
|
||||
m_result.id = ++s_result_id;
|
||||
// 1st move must be a dummy move
|
||||
m_result.moves.emplace_back(MoveVertex());
|
||||
m_result.moves.emplace_back(GCodeProcessorResult::MoveVertex());
|
||||
size_t parse_line_callback_cntr = 10000;
|
||||
m_parser.parse_file(filename, [this, cancel_callback, &parse_line_callback_cntr](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
if (-- parse_line_callback_cntr == 0) {
|
||||
|
@ -1284,7 +1268,7 @@ void GCodeProcessor::initialize(const std::string& filename)
|
|||
m_result.filename = filename;
|
||||
m_result.id = ++s_result_id;
|
||||
// 1st move must be a dummy move
|
||||
m_result.moves.emplace_back(MoveVertex());
|
||||
m_result.moves.emplace_back(GCodeProcessorResult::MoveVertex());
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_buffer(const std::string &buffer)
|
||||
|
@ -1298,7 +1282,7 @@ void GCodeProcessor::process_buffer(const std::string &buffer)
|
|||
void GCodeProcessor::finalize(bool post_process)
|
||||
{
|
||||
// update width/height of wipe moves
|
||||
for (MoveVertex& move : m_result.moves) {
|
||||
for (GCodeProcessorResult::MoveVertex& move : m_result.moves) {
|
||||
if (move.type == EMoveType::Wipe) {
|
||||
move.width = Wipe_Width;
|
||||
move.height = Wipe_Height;
|
||||
|
@ -1383,7 +1367,6 @@ std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(Prin
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
ConfigSubstitutions load_from_superslicer_gcode_file(const std::string& filename, DynamicPrintConfig& config, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||
{
|
||||
// for reference, see: ConfigBase::load_from_gcode_file()
|
||||
|
@ -1416,7 +1399,6 @@ void GCodeProcessor::apply_config_superslicer(const std::string& filename)
|
|||
load_from_superslicer_gcode_file(filename, config, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
apply_config(config);
|
||||
}
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
|
||||
std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const
|
||||
{
|
||||
|
@ -1853,9 +1835,7 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
store_move_vertex(EMoveType::Color_change);
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::ColorChange, extruder_id + 1, color, "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.set();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
process_custom_gcode_time(CustomGCode::ColorChange);
|
||||
process_filaments(CustomGCode::ColorChange);
|
||||
}
|
||||
|
@ -1868,9 +1848,7 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
store_move_vertex(EMoveType::Pause_Print);
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::PausePrint, m_extruder_id + 1, "", "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.set();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
process_custom_gcode_time(CustomGCode::PausePrint);
|
||||
return;
|
||||
}
|
||||
|
@ -1880,9 +1858,7 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
store_move_vertex(EMoveType::Custom_GCode);
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::Custom, m_extruder_id + 1, "", "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.set();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1908,9 +1884,7 @@ bool GCodeProcessor::process_producers_tags(const std::string_view comment)
|
|||
{
|
||||
case EProducer::Slic3rPE:
|
||||
case EProducer::Slic3r:
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
case EProducer::SuperSlicer:
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
case EProducer::PrusaSlicer: { return process_prusaslicer_tags(comment); }
|
||||
case EProducer::Cura: { return process_cura_tags(comment); }
|
||||
case EProducer::Simplify3D: { return process_simplify3d_tags(comment); }
|
||||
|
@ -2463,12 +2437,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
if (m_forced_height > 0.0f)
|
||||
m_height = m_forced_height;
|
||||
else {
|
||||
if (m_end_position[Z] > m_extruded_last_z + EPSILON) {
|
||||
if (m_end_position[Z] > m_extruded_last_z + EPSILON)
|
||||
m_height = m_end_position[Z] - m_extruded_last_z;
|
||||
#if !ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_extruded_last_z = m_end_position[Z];
|
||||
#endif // !ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
}
|
||||
}
|
||||
|
||||
if (m_height == 0.0f)
|
||||
|
@ -2477,10 +2447,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
if (m_end_position[Z] == 0.0f)
|
||||
m_end_position[Z] = m_height;
|
||||
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_extruded_last_z = m_end_position[Z];
|
||||
m_options_z_corrector.update(m_height);
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_height_compare.update(m_height, m_extrusion_role);
|
||||
|
|
|
@ -76,6 +76,63 @@ namespace Slic3r {
|
|||
}
|
||||
};
|
||||
|
||||
struct GCodeProcessorResult
|
||||
{
|
||||
struct SettingsIds
|
||||
{
|
||||
std::string print;
|
||||
std::vector<std::string> filament;
|
||||
std::string printer;
|
||||
|
||||
void reset() {
|
||||
print.clear();
|
||||
filament.clear();
|
||||
printer.clear();
|
||||
}
|
||||
};
|
||||
|
||||
struct MoveVertex
|
||||
{
|
||||
unsigned int gcode_id{ 0 };
|
||||
EMoveType type{ EMoveType::Noop };
|
||||
ExtrusionRole extrusion_role{ erNone };
|
||||
unsigned char extruder_id{ 0 };
|
||||
unsigned char cp_color_id{ 0 };
|
||||
Vec3f position{ Vec3f::Zero() }; // mm
|
||||
float delta_extruder{ 0.0f }; // mm
|
||||
float feedrate{ 0.0f }; // mm/s
|
||||
float width{ 0.0f }; // mm
|
||||
float height{ 0.0f }; // mm
|
||||
float mm3_per_mm{ 0.0f };
|
||||
float fan_speed{ 0.0f }; // percentage
|
||||
float temperature{ 0.0f }; // Celsius degrees
|
||||
float time{ 0.0f }; // s
|
||||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
};
|
||||
|
||||
std::string filename;
|
||||
unsigned int id;
|
||||
std::vector<MoveVertex> moves;
|
||||
// Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code.
|
||||
std::vector<size_t> lines_ends;
|
||||
Pointfs bed_shape;
|
||||
float max_print_height;
|
||||
SettingsIds settings_ids;
|
||||
size_t extruders_count;
|
||||
std::vector<std::string> extruder_colors;
|
||||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
PrintEstimatedStatistics print_statistics;
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
int64_t time{ 0 };
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
void reset();
|
||||
};
|
||||
|
||||
|
||||
class GCodeProcessor
|
||||
{
|
||||
static const std::vector<std::string> Reserved_Tags;
|
||||
|
@ -190,26 +247,6 @@ namespace Slic3r {
|
|||
float time() const;
|
||||
};
|
||||
|
||||
struct MoveVertex
|
||||
{
|
||||
unsigned int gcode_id{ 0 };
|
||||
EMoveType type{ EMoveType::Noop };
|
||||
ExtrusionRole extrusion_role{ erNone };
|
||||
unsigned char extruder_id{ 0 };
|
||||
unsigned char cp_color_id{ 0 };
|
||||
Vec3f position{ Vec3f::Zero() }; // mm
|
||||
float delta_extruder{ 0.0f }; // mm
|
||||
float feedrate{ 0.0f }; // mm/s
|
||||
float width{ 0.0f }; // mm
|
||||
float height{ 0.0f }; // mm
|
||||
float mm3_per_mm{ 0.0f };
|
||||
float fan_speed{ 0.0f }; // percentage
|
||||
float temperature{ 0.0f }; // Celsius degrees
|
||||
float time{ 0.0f }; // s
|
||||
|
||||
float volumetric_rate() const { return feedrate * mm3_per_mm; }
|
||||
};
|
||||
|
||||
private:
|
||||
struct TimeMachine
|
||||
{
|
||||
|
@ -304,7 +341,7 @@ namespace Slic3r {
|
|||
|
||||
// post process the file with the given filename to add remaining time lines M73
|
||||
// and updates moves' gcode ids accordingly
|
||||
void post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends);
|
||||
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends);
|
||||
};
|
||||
|
||||
struct UsedFilaments // filaments per ColorChange
|
||||
|
@ -331,43 +368,6 @@ namespace Slic3r {
|
|||
};
|
||||
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
struct SettingsIds
|
||||
{
|
||||
std::string print;
|
||||
std::vector<std::string> filament;
|
||||
std::string printer;
|
||||
|
||||
void reset() {
|
||||
print.clear();
|
||||
filament.clear();
|
||||
printer.clear();
|
||||
}
|
||||
};
|
||||
std::string filename;
|
||||
unsigned int id;
|
||||
std::vector<MoveVertex> moves;
|
||||
// Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code.
|
||||
std::vector<size_t> lines_ends;
|
||||
Pointfs bed_shape;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
float max_print_height;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
SettingsIds settings_ids;
|
||||
size_t extruders_count;
|
||||
std::vector<std::string> extruder_colors;
|
||||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
PrintEstimatedStatistics print_statistics;
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
int64_t time{ 0 };
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
void reset();
|
||||
};
|
||||
|
||||
class SeamsDetector
|
||||
{
|
||||
bool m_active{ false };
|
||||
|
@ -389,17 +389,16 @@ namespace Slic3r {
|
|||
bool has_first_vertex() const { return m_first_vertex.has_value(); }
|
||||
};
|
||||
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
// Helper class used to fix the z for color change, pause print and
|
||||
// custom gcode markes
|
||||
class OptionsZCorrector
|
||||
{
|
||||
Result& m_result;
|
||||
GCodeProcessorResult& m_result;
|
||||
std::optional<size_t> m_move_id;
|
||||
std::optional<size_t> m_custom_gcode_per_print_z_id;
|
||||
|
||||
public:
|
||||
explicit OptionsZCorrector(Result& result) : m_result(result) {
|
||||
explicit OptionsZCorrector(GCodeProcessorResult& result) : m_result(result) {
|
||||
}
|
||||
|
||||
void set() {
|
||||
|
@ -413,7 +412,7 @@ namespace Slic3r {
|
|||
|
||||
const Vec3f position = m_result.moves.back().position;
|
||||
|
||||
MoveVertex& move = m_result.moves.emplace_back(m_result.moves[*m_move_id]);
|
||||
GCodeProcessorResult::MoveVertex& move = m_result.moves.emplace_back(m_result.moves[*m_move_id]);
|
||||
move.position = position;
|
||||
move.height = height;
|
||||
m_result.moves.erase(m_result.moves.begin() + *m_move_id);
|
||||
|
@ -426,7 +425,6 @@ namespace Slic3r {
|
|||
m_custom_gcode_per_print_z_id.reset();
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
struct DataChecker
|
||||
|
@ -532,9 +530,7 @@ namespace Slic3r {
|
|||
CpColor m_cp_color;
|
||||
bool m_use_volumetric_e;
|
||||
SeamsDetector m_seams_detector;
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
OptionsZCorrector m_options_z_corrector;
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
size_t m_last_default_color_id;
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
|
||||
|
@ -546,9 +542,7 @@ namespace Slic3r {
|
|||
PrusaSlicer,
|
||||
Slic3rPE,
|
||||
Slic3r,
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
SuperSlicer,
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
Cura,
|
||||
Simplify3D,
|
||||
CraftWare,
|
||||
|
@ -562,7 +556,7 @@ namespace Slic3r {
|
|||
TimeProcessor m_time_processor;
|
||||
UsedFilaments m_used_filaments;
|
||||
|
||||
Result m_result;
|
||||
GCodeProcessorResult m_result;
|
||||
static unsigned int s_result_id;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
@ -582,8 +576,8 @@ namespace Slic3r {
|
|||
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
|
||||
void reset();
|
||||
|
||||
const Result& get_result() const { return m_result; }
|
||||
Result&& extract_result() { return std::move(m_result); }
|
||||
const GCodeProcessorResult& get_result() const { return m_result; }
|
||||
GCodeProcessorResult&& extract_result() { return std::move(m_result); }
|
||||
|
||||
// Load a G-code into a stand-alone G-code viewer.
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
|
@ -605,9 +599,7 @@ namespace Slic3r {
|
|||
private:
|
||||
void apply_config(const DynamicPrintConfig& config);
|
||||
void apply_config_simplify3d(const std::string& filename);
|
||||
#if ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
void apply_config_superslicer(const std::string& filename);
|
||||
#endif // ENABLE_FIX_SUPERSLICER_GCODE_IMPORT
|
||||
void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled);
|
||||
|
||||
// Process tags embedded into comments
|
||||
|
|
|
@ -533,10 +533,9 @@ std::string GCodeWriter::set_fan(unsigned int speed) const
|
|||
return GCodeWriter::set_fan(this->config.gcode_flavor, this->config.gcode_comments, speed);
|
||||
}
|
||||
|
||||
|
||||
void GCodeFormatter::emit_axis(const char axis, const double v, size_t digits) {
|
||||
assert(digits <= 6);
|
||||
static constexpr const std::array<int, 7> pow_10{1, 10, 100, 1000, 10000, 100000, 1000000};
|
||||
assert(digits <= 9);
|
||||
static constexpr const std::array<int, 10> pow_10{1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
|
||||
*ptr_err.ptr++ = ' '; *ptr_err.ptr++ = axis;
|
||||
|
||||
char *base_ptr = this->ptr_err.ptr;
|
||||
|
@ -576,6 +575,21 @@ void GCodeFormatter::emit_axis(const char axis, const double v, size_t digits) {
|
|||
if ((this->ptr_err.ptr + 1) == base_ptr || *this->ptr_err.ptr == '-')
|
||||
*(++this->ptr_err.ptr) = '0';
|
||||
this->ptr_err.ptr++;
|
||||
|
||||
#if 0 // #ifndef NDEBUG
|
||||
{
|
||||
// Verify that the optimized formatter produces the same result as the standard sprintf().
|
||||
double v1 = atof(std::string(base_ptr, this->ptr_err.ptr).c_str());
|
||||
char buf[2048];
|
||||
sprintf(buf, "%.*lf", int(digits), v);
|
||||
double v2 = atof(buf);
|
||||
// Numbers may differ when rounding at exactly or very close to 0.5 due to numerical issues when scaling the double to an integer.
|
||||
// Thus the complex assert.
|
||||
// assert(v1 == v2);
|
||||
assert(std::abs(v1 - v) * pow_10[digits] < 0.50001);
|
||||
assert(std::abs(v2 - v) * pow_10[digits] < 0.50001);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -104,8 +104,22 @@ public:
|
|||
GCodeFormatter(const GCodeFormatter&) = delete;
|
||||
GCodeFormatter& operator=(const GCodeFormatter&) = delete;
|
||||
|
||||
// At layer height 0.15mm, extrusion width 0.2mm and filament diameter 1.75mm,
|
||||
// the crossection of extrusion is 0.4 * 0.15 = 0.06mm2
|
||||
// and the filament crossection is 1.75^2 = 3.063mm2
|
||||
// thus the filament moves 3.063 / 0.6 = 51x slower than the XY axes
|
||||
// and we need roughly two decimal digits more on extruder than on XY.
|
||||
#if 1
|
||||
static constexpr const int XYZF_EXPORT_DIGITS = 3;
|
||||
static constexpr const int E_EXPORT_DIGITS = 5;
|
||||
#else
|
||||
// order of magnitude smaller extrusion rate erros
|
||||
static constexpr const int XYZF_EXPORT_DIGITS = 4;
|
||||
static constexpr const int E_EXPORT_DIGITS = 6;
|
||||
// excessive accuracy
|
||||
// static constexpr const int XYZF_EXPORT_DIGITS = 6;
|
||||
// static constexpr const int E_EXPORT_DIGITS = 9;
|
||||
#endif
|
||||
|
||||
void emit_axis(const char axis, const double v, size_t digits);
|
||||
|
||||
|
|
|
@ -24,107 +24,8 @@
|
|||
#define BOOST_NO_CXX17_HDR_STRING_VIEW
|
||||
#endif
|
||||
|
||||
#include <boost/multiprecision/integer.hpp>
|
||||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
|
||||
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
||||
Polygon convex_hull(Points pts)
|
||||
{
|
||||
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()); });
|
||||
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a.x() == b.x() && a.y() == b.y(); }), pts.end());
|
||||
|
||||
Polygon hull;
|
||||
int n = (int)pts.size();
|
||||
if (n >= 3) {
|
||||
int k = 0;
|
||||
hull.points.resize(2 * n);
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++ i) {
|
||||
while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
// Build upper hull
|
||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||
while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
hull.points.resize(k);
|
||||
assert(hull.points.front() == hull.points.back());
|
||||
hull.points.pop_back();
|
||||
}
|
||||
return hull;
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points)
|
||||
{
|
||||
assert(points.size() >= 3);
|
||||
// sort input points
|
||||
std::sort(points.begin(), points.end(), [](const Vec3d &a, const Vec3d &b){ return a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()); });
|
||||
|
||||
int n = points.size(), k = 0;
|
||||
Pointf3s hull;
|
||||
|
||||
if (n >= 3)
|
||||
{
|
||||
hull.resize(2 * n);
|
||||
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
Point p = Point::new_scale(points[i](0), points[i](1));
|
||||
while (k >= 2)
|
||||
{
|
||||
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
|
||||
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
|
||||
|
||||
if (p.ccw(k2, k1) <= 0)
|
||||
--k;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
// Build upper hull
|
||||
for (int i = n - 2, t = k + 1; i >= 0; --i)
|
||||
{
|
||||
Point p = Point::new_scale(points[i](0), points[i](1));
|
||||
while (k >= t)
|
||||
{
|
||||
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
|
||||
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
|
||||
|
||||
if (p.ccw(k2, k1) <= 0)
|
||||
--k;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
hull.resize(k);
|
||||
|
||||
assert(hull.front() == hull.back());
|
||||
hull.pop_back();
|
||||
}
|
||||
|
||||
return hull;
|
||||
}
|
||||
|
||||
Polygon convex_hull(const Polygons &polygons)
|
||||
{
|
||||
Points pp;
|
||||
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
|
||||
pp.insert(pp.end(), p->points.begin(), p->points.end());
|
||||
}
|
||||
return convex_hull(std::move(pp));
|
||||
}
|
||||
|
||||
bool directions_parallel(double angle1, double angle2, double max_diff)
|
||||
{
|
||||
double diff = fabs(angle1 - angle2);
|
||||
|
@ -132,14 +33,12 @@ bool directions_parallel(double angle1, double angle2, double max_diff)
|
|||
return diff < max_diff || fabs(diff - PI) < max_diff;
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool directions_perpendicular(double angle1, double angle2, double max_diff)
|
||||
{
|
||||
double diff = fabs(angle1 - angle2);
|
||||
max_diff += EPSILON;
|
||||
return fabs(diff - 0.5 * PI) < max_diff || fabs(diff - 1.5 * PI) < max_diff;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
template<class T>
|
||||
bool contains(const std::vector<T> &vector, const Point &point)
|
||||
|
@ -761,205 +660,4 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
|
|||
return (axis.z() < 0) ? -angle : angle;
|
||||
}
|
||||
|
||||
namespace rotcalip {
|
||||
|
||||
using int256_t = boost::multiprecision::int256_t;
|
||||
using int128_t = boost::multiprecision::int128_t;
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar magnsq(const Point &p)
|
||||
{
|
||||
return Scalar(p.x()) * p.x() + Scalar(p.y()) * p.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dot(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.x() + Scalar(a.y()) * b.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dotperp(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.y() - Scalar(a.y()) * b.x();
|
||||
}
|
||||
|
||||
using boost::multiprecision::abs;
|
||||
|
||||
// Compares the angle enclosed by vectors dir and dirA (alpha) with the angle
|
||||
// enclosed by -dir and dirB (beta). Returns -1 if alpha is less than beta, 0
|
||||
// if they are equal and 1 if alpha is greater than beta. Note that dir is
|
||||
// reversed for beta, because it represents the opposite side of a caliper.
|
||||
int cmp_angles(const Point &dir, const Point &dirA, const Point &dirB) {
|
||||
int128_t dotA = dot(dir, dirA);
|
||||
int128_t dotB = dot(-dir, dirB);
|
||||
int256_t dcosa = int256_t(magnsq(dirB)) * int256_t(abs(dotA)) * dotA;
|
||||
int256_t dcosb = int256_t(magnsq(dirA)) * int256_t(abs(dotB)) * dotB;
|
||||
int256_t diff = dcosa - dcosb;
|
||||
|
||||
return diff > 0? -1 : (diff < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// A helper class to navigate on a polygon. Given a vertex index, one can
|
||||
// get the edge belonging to that vertex, the coordinates of the vertex, the
|
||||
// next and previous edges. Stuff that is needed in the rotating calipers algo.
|
||||
class Idx
|
||||
{
|
||||
size_t m_idx;
|
||||
const Polygon *m_poly;
|
||||
public:
|
||||
explicit Idx(const Polygon &p): m_idx{0}, m_poly{&p} {}
|
||||
explicit Idx(size_t idx, const Polygon &p): m_idx{idx}, m_poly{&p} {}
|
||||
|
||||
size_t idx() const { return m_idx; }
|
||||
void set_idx(size_t i) { m_idx = i; }
|
||||
size_t next() const { return (m_idx + 1) % m_poly->size(); }
|
||||
size_t inc() { return m_idx = (m_idx + 1) % m_poly->size(); }
|
||||
Point prev_dir() const {
|
||||
return pt() - (*m_poly)[(m_idx + m_poly->size() - 1) % m_poly->size()];
|
||||
}
|
||||
|
||||
const Point &pt() const { return (*m_poly)[m_idx]; }
|
||||
const Point dir() const { return (*m_poly)[next()] - pt(); }
|
||||
const Point next_dir() const
|
||||
{
|
||||
return (*m_poly)[(m_idx + 2) % m_poly->size()] - (*m_poly)[next()];
|
||||
}
|
||||
const Polygon &poly() const { return *m_poly; }
|
||||
};
|
||||
|
||||
enum class AntipodalVisitMode { Full, EdgesOnly };
|
||||
|
||||
// Visit all antipodal pairs starting from the initial ia, ib pair which
|
||||
// has to be a valid antipodal pair (not checked). fn is called for every
|
||||
// antipodal pair encountered including the initial one.
|
||||
// The callback Fn has a signiture of bool(size_t i, size_t j, const Point &dir)
|
||||
// where i,j are the vertex indices of the antipodal pair and dir is the
|
||||
// direction of the calipers touching the i vertex.
|
||||
template<AntipodalVisitMode mode = AntipodalVisitMode::Full, class Fn>
|
||||
void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn)
|
||||
{
|
||||
// Set current caliper direction to be the lower edge angle from X axis
|
||||
int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir());
|
||||
Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia;
|
||||
Idx *initial = current;
|
||||
bool visitor_continue = true;
|
||||
|
||||
size_t start = initial->idx();
|
||||
bool finished = false;
|
||||
|
||||
while (visitor_continue && !finished) {
|
||||
Point current_dir_a = current == &ia ? current->dir() : -current->dir();
|
||||
visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a);
|
||||
|
||||
// Parallel edges encountered. An additional pair of antipodals
|
||||
// can be yielded.
|
||||
if constexpr (mode == AntipodalVisitMode::Full)
|
||||
if (cmp == 0 && visitor_continue) {
|
||||
visitor_continue = fn(current == &ia ? ia.idx() : ia.next(),
|
||||
current == &ib ? ib.idx() : ib.next(),
|
||||
current_dir_a);
|
||||
}
|
||||
|
||||
cmp = cmp_angles(current->dir(), current->next_dir(), other->dir());
|
||||
|
||||
current->inc();
|
||||
if (cmp > 0) {
|
||||
std::swap(current, other);
|
||||
}
|
||||
|
||||
if (initial->idx() == start) finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rotcalip
|
||||
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B)
|
||||
{
|
||||
using namespace rotcalip;
|
||||
|
||||
// Establish starting antipodals as extremes in XY plane. Use the
|
||||
// easily obtainable bounding boxes to check if A and B is disjoint
|
||||
// and return false if the are.
|
||||
struct BB
|
||||
{
|
||||
size_t xmin = 0, xmax = 0, ymin = 0, ymax = 0;
|
||||
const Polygon &P;
|
||||
static bool cmpy(const Point &l, const Point &u)
|
||||
{
|
||||
return l.y() < u.y() || (l.y() == u.y() && l.x() < u.x());
|
||||
}
|
||||
|
||||
BB(const Polygon &poly): P{poly}
|
||||
{
|
||||
for (size_t i = 0; i < P.size(); ++i) {
|
||||
if (P[i] < P[xmin]) xmin = i;
|
||||
if (P[xmax] < P[i]) xmax = i;
|
||||
if (cmpy(P[i], P[ymin])) ymin = i;
|
||||
if (cmpy(P[ymax], P[i])) ymax = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BB bA{A}, bB{B};
|
||||
BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}};
|
||||
BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}};
|
||||
|
||||
// if (!bbA.overlap(bbB))
|
||||
// return false;
|
||||
|
||||
// Establish starting antipodals as extreme vertex pairs in X or Y direction
|
||||
// which reside on different polygons. If no such pair is found, the two
|
||||
// polygons are certainly not disjoint.
|
||||
Idx imin{bA.xmin, A}, imax{bB.xmax, B};
|
||||
if (B[bB.xmin] < imin.pt()) imin = Idx{bB.xmin, B};
|
||||
if (imax.pt() < A[bA.xmax]) imax = Idx{bA.xmax, A};
|
||||
if (&imin.poly() == &imax.poly()) {
|
||||
imin = Idx{bA.ymin, A};
|
||||
imax = Idx{bB.ymax, B};
|
||||
if (B[bB.ymin] < imin.pt()) imin = Idx{bB.ymin, B};
|
||||
if (imax.pt() < A[bA.ymax]) imax = Idx{bA.ymax, A};
|
||||
}
|
||||
|
||||
if (&imin.poly() == &imax.poly())
|
||||
return true;
|
||||
|
||||
bool found_divisor = false;
|
||||
visit_antipodals<AntipodalVisitMode::EdgesOnly>(
|
||||
imin, imax,
|
||||
[&imin, &imax, &found_divisor](size_t ia, size_t ib, const Point &dir) {
|
||||
// std::cout << "A" << ia << " B" << ib << " dir " <<
|
||||
// dir.x() << " " << dir.y() << std::endl;
|
||||
const Polygon &A = imin.poly(), &B = imax.poly();
|
||||
|
||||
Point ref_a = A[(ia + 2) % A.size()], ref_b = B[(ib + 2) % B.size()];
|
||||
|
||||
bool is_left_a = dotperp( dir, ref_a - A[ia]) > 0;
|
||||
bool is_left_b = dotperp(-dir, ref_b - B[ib]) > 0;
|
||||
|
||||
// If both reference points are on the left (or right) of their
|
||||
// respective support lines and the opposite support line is to
|
||||
// the right (or left), the divisor line is found. We only test
|
||||
// the reference point, as by definition, if that is on one side,
|
||||
// all the other points must be on the same side of a support
|
||||
// line. If the support lines are collinear, the polygons must be
|
||||
// on the same side of their respective support lines.
|
||||
|
||||
auto d = dotperp(dir, B[ib] - A[ia]);
|
||||
if (d == 0) {
|
||||
// The caliper lines are collinear, not just parallel
|
||||
found_divisor = (is_left_a && is_left_b) || (!is_left_a && !is_left_b);
|
||||
} else if (d > 0) { // B is to the left of (A, A+1)
|
||||
found_divisor = !is_left_a && !is_left_b;
|
||||
} else { // B is to the right of (A, A+1)
|
||||
found_divisor = is_left_a && is_left_b;
|
||||
}
|
||||
|
||||
return !found_divisor;
|
||||
});
|
||||
|
||||
// Intersects if the divisor was not found
|
||||
return !found_divisor;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::Geometry
|
||||
|
|
|
@ -72,32 +72,6 @@ static inline bool is_ccw(const Polygon &poly)
|
|||
return o == ORIENTATION_CCW;
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// returns true if the given polygons are identical
|
||||
static inline bool are_approx(const Polygon& lhs, const Polygon& rhs)
|
||||
{
|
||||
if (lhs.points.size() != rhs.points.size())
|
||||
return false;
|
||||
|
||||
size_t rhs_id = 0;
|
||||
while (rhs_id < rhs.points.size()) {
|
||||
if (rhs.points[rhs_id].isApprox(lhs.points.front()))
|
||||
break;
|
||||
++rhs_id;
|
||||
}
|
||||
|
||||
if (rhs_id == rhs.points.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < lhs.points.size(); ++i) {
|
||||
if (!lhs.points[i].isApprox(rhs.points[(i + rhs_id) % lhs.points.size()]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
inline bool ray_ray_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res)
|
||||
{
|
||||
double denom = v1(0) * v2(1) - v2(0) * v1(1);
|
||||
|
@ -313,14 +287,8 @@ bool liang_barsky_line_clipping(
|
|||
return liang_barsky_line_clipping(x0clip, x1clip, bbox);
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points);
|
||||
Polygon convex_hull(Points points);
|
||||
Polygon convex_hull(const Polygons &polygons);
|
||||
|
||||
bool directions_parallel(double angle1, double angle2, double max_diff = 0);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool directions_perpendicular(double angle1, double angle2, double max_diff = 0);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
template<class T> bool contains(const std::vector<T> &vector, const Point &point);
|
||||
template<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); }
|
||||
double rad2deg_dir(double angle);
|
||||
|
@ -479,10 +447,6 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
|
|||
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
|
||||
}
|
||||
|
||||
// Returns true if the intersection of the two convex polygons A and B
|
||||
// is not an empty set.
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B);
|
||||
|
||||
} } // namespace Slicer::Geometry
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "../Polygon.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
|
@ -94,4 +95,44 @@ Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_begin, con
|
|||
return center + centroid;
|
||||
}
|
||||
|
||||
Circled circle_taubin_newton(const Vec2ds& input, size_t cycles)
|
||||
{
|
||||
Circled out;
|
||||
if (input.size() < 3) {
|
||||
out = Circled::make_invalid();
|
||||
} else {
|
||||
out.center = circle_center_taubin_newton(input, cycles);
|
||||
out.radius = std::accumulate(input.begin(), input.end(), 0., [&out](double acc, const Vec2d& pt) { return (pt - out.center).norm() + acc; });
|
||||
out.radius /= double(input.size());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Circled circle_ransac(const Vec2ds& input, size_t iterations)
|
||||
{
|
||||
if (input.size() < 3)
|
||||
return Circled::make_invalid();
|
||||
|
||||
std::mt19937 rng;
|
||||
std::vector<Vec2d> samples;
|
||||
Circled circle_best = Circled::make_invalid();
|
||||
double err_min = std::numeric_limits<double>::max();
|
||||
for (size_t iter = 0; iter < iterations; ++ iter) {
|
||||
samples.clear();
|
||||
std::sample(input.begin(), input.end(), std::back_inserter(samples), 3, rng);
|
||||
Circled c;
|
||||
c.center = Geometry::circle_center(samples[0], samples[1], samples[2], EPSILON);
|
||||
c.radius = std::accumulate(input.begin(), input.end(), 0., [&c](double acc, const Vec2d& pt) { return (pt - c.center).norm() + acc; });
|
||||
c.radius /= double(input.size());
|
||||
double err = 0;
|
||||
for (const Vec2d &pt : input)
|
||||
err = std::max(err, std::abs((pt - c.center).norm() - c.radius));
|
||||
if (err < err_min) {
|
||||
err_min = err;
|
||||
circle_best = c;
|
||||
}
|
||||
}
|
||||
return circle_best;
|
||||
}
|
||||
|
||||
} } // namespace Slic3r::Geometry
|
||||
|
|
|
@ -7,14 +7,6 @@
|
|||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Points as an arc.
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Point circle_center_taubin_newton(const Points& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Pointfs as an arc.
|
||||
Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
// https://en.wikipedia.org/wiki/Circumscribed_circle
|
||||
// Circumcenter coordinates, Cartesian coordinates
|
||||
template<typename Vector>
|
||||
|
@ -100,6 +92,18 @@ using Circled = Circle<Vec2d>;
|
|||
using CircleSqf = CircleSq<Vec2f>;
|
||||
using CircleSqd = CircleSq<Vec2d>;
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Points as an arc.
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Point circle_center_taubin_newton(const Points& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Pointfs as an arc.
|
||||
Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_start, const Vec2ds::const_iterator& input_end, size_t cycles = 20);
|
||||
inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20) { return circle_center_taubin_newton(input.cbegin(), input.cend(), cycles); }
|
||||
Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20);
|
||||
|
||||
// Find circle using RANSAC randomized algorithm.
|
||||
Circled circle_ransac(const Vec2ds& input, size_t iterations = 20);
|
||||
|
||||
// Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon.
|
||||
template<typename Vector, typename Points>
|
||||
CircleSq<Vector> smallest_enclosing_circle2_welzl(const Points &points, const typename Vector::Scalar epsilon)
|
||||
|
|
399
src/libslic3r/Geometry/ConvexHull.cpp
Normal file
399
src/libslic3r/Geometry/ConvexHull.cpp
Normal file
|
@ -0,0 +1,399 @@
|
|||
#include "libslic3r.h"
|
||||
#include "ConvexHull.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
|
||||
#include <boost/multiprecision/integer.hpp>
|
||||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
|
||||
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
||||
Polygon convex_hull(Points pts)
|
||||
{
|
||||
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()); });
|
||||
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a.x() == b.x() && a.y() == b.y(); }), pts.end());
|
||||
|
||||
Polygon hull;
|
||||
int n = (int)pts.size();
|
||||
if (n >= 3) {
|
||||
int k = 0;
|
||||
hull.points.resize(2 * n);
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++ i) {
|
||||
while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
// Build upper hull
|
||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||
while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
hull.points.resize(k);
|
||||
assert(hull.points.front() == hull.points.back());
|
||||
hull.points.pop_back();
|
||||
}
|
||||
return hull;
|
||||
}
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points)
|
||||
{
|
||||
assert(points.size() >= 3);
|
||||
// sort input points
|
||||
std::sort(points.begin(), points.end(), [](const Vec3d &a, const Vec3d &b){ return a.x() < b.x() || (a.x() == b.x() && a.y() < b.y()); });
|
||||
|
||||
int n = points.size(), k = 0;
|
||||
Pointf3s hull;
|
||||
|
||||
if (n >= 3)
|
||||
{
|
||||
hull.resize(2 * n);
|
||||
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
Point p = Point::new_scale(points[i](0), points[i](1));
|
||||
while (k >= 2)
|
||||
{
|
||||
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
|
||||
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
|
||||
|
||||
if (p.ccw(k2, k1) <= 0)
|
||||
--k;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
// Build upper hull
|
||||
for (int i = n - 2, t = k + 1; i >= 0; --i)
|
||||
{
|
||||
Point p = Point::new_scale(points[i](0), points[i](1));
|
||||
while (k >= t)
|
||||
{
|
||||
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
|
||||
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
|
||||
|
||||
if (p.ccw(k2, k1) <= 0)
|
||||
--k;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
hull[k++] = points[i];
|
||||
}
|
||||
|
||||
hull.resize(k);
|
||||
|
||||
assert(hull.front() == hull.back());
|
||||
hull.pop_back();
|
||||
}
|
||||
|
||||
return hull;
|
||||
}
|
||||
|
||||
Polygon convex_hull(const Polygons &polygons)
|
||||
{
|
||||
Points pp;
|
||||
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
|
||||
pp.insert(pp.end(), p->points.begin(), p->points.end());
|
||||
}
|
||||
return convex_hull(std::move(pp));
|
||||
}
|
||||
|
||||
|
||||
namespace rotcalip {
|
||||
|
||||
using int256_t = boost::multiprecision::int256_t;
|
||||
using int128_t = boost::multiprecision::int128_t;
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar magnsq(const Point &p)
|
||||
{
|
||||
return Scalar(p.x()) * p.x() + Scalar(p.y()) * p.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dot(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.x() + Scalar(a.y()) * b.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dotperp(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.y() - Scalar(a.y()) * b.x();
|
||||
}
|
||||
|
||||
using boost::multiprecision::abs;
|
||||
|
||||
// Compares the angle enclosed by vectors dir and dirA (alpha) with the angle
|
||||
// enclosed by -dir and dirB (beta). Returns -1 if alpha is less than beta, 0
|
||||
// if they are equal and 1 if alpha is greater than beta. Note that dir is
|
||||
// reversed for beta, because it represents the opposite side of a caliper.
|
||||
int cmp_angles(const Point &dir, const Point &dirA, const Point &dirB) {
|
||||
int128_t dotA = dot(dir, dirA);
|
||||
int128_t dotB = dot(-dir, dirB);
|
||||
int256_t dcosa = int256_t(magnsq(dirB)) * int256_t(abs(dotA)) * dotA;
|
||||
int256_t dcosb = int256_t(magnsq(dirA)) * int256_t(abs(dotB)) * dotB;
|
||||
int256_t diff = dcosa - dcosb;
|
||||
|
||||
return diff > 0? -1 : (diff < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// A helper class to navigate on a polygon. Given a vertex index, one can
|
||||
// get the edge belonging to that vertex, the coordinates of the vertex, the
|
||||
// next and previous edges. Stuff that is needed in the rotating calipers algo.
|
||||
class Idx
|
||||
{
|
||||
size_t m_idx;
|
||||
const Polygon *m_poly;
|
||||
public:
|
||||
explicit Idx(const Polygon &p): m_idx{0}, m_poly{&p} {}
|
||||
explicit Idx(size_t idx, const Polygon &p): m_idx{idx}, m_poly{&p} {}
|
||||
|
||||
size_t idx() const { return m_idx; }
|
||||
void set_idx(size_t i) { m_idx = i; }
|
||||
size_t next() const { return (m_idx + 1) % m_poly->size(); }
|
||||
size_t inc() { return m_idx = (m_idx + 1) % m_poly->size(); }
|
||||
Point prev_dir() const {
|
||||
return pt() - (*m_poly)[(m_idx + m_poly->size() - 1) % m_poly->size()];
|
||||
}
|
||||
|
||||
const Point &pt() const { return (*m_poly)[m_idx]; }
|
||||
const Point dir() const { return (*m_poly)[next()] - pt(); }
|
||||
const Point next_dir() const
|
||||
{
|
||||
return (*m_poly)[(m_idx + 2) % m_poly->size()] - (*m_poly)[next()];
|
||||
}
|
||||
const Polygon &poly() const { return *m_poly; }
|
||||
};
|
||||
|
||||
enum class AntipodalVisitMode { Full, EdgesOnly };
|
||||
|
||||
// Visit all antipodal pairs starting from the initial ia, ib pair which
|
||||
// has to be a valid antipodal pair (not checked). fn is called for every
|
||||
// antipodal pair encountered including the initial one.
|
||||
// The callback Fn has a signiture of bool(size_t i, size_t j, const Point &dir)
|
||||
// where i,j are the vertex indices of the antipodal pair and dir is the
|
||||
// direction of the calipers touching the i vertex.
|
||||
template<AntipodalVisitMode mode = AntipodalVisitMode::Full, class Fn>
|
||||
void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn)
|
||||
{
|
||||
// Set current caliper direction to be the lower edge angle from X axis
|
||||
int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir());
|
||||
Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia;
|
||||
Idx *initial = current;
|
||||
bool visitor_continue = true;
|
||||
|
||||
size_t start = initial->idx();
|
||||
bool finished = false;
|
||||
|
||||
while (visitor_continue && !finished) {
|
||||
Point current_dir_a = current == &ia ? current->dir() : -current->dir();
|
||||
visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a);
|
||||
|
||||
// Parallel edges encountered. An additional pair of antipodals
|
||||
// can be yielded.
|
||||
if constexpr (mode == AntipodalVisitMode::Full)
|
||||
if (cmp == 0 && visitor_continue) {
|
||||
visitor_continue = fn(current == &ia ? ia.idx() : ia.next(),
|
||||
current == &ib ? ib.idx() : ib.next(),
|
||||
current_dir_a);
|
||||
}
|
||||
|
||||
cmp = cmp_angles(current->dir(), current->next_dir(), other->dir());
|
||||
|
||||
current->inc();
|
||||
if (cmp > 0) {
|
||||
std::swap(current, other);
|
||||
}
|
||||
|
||||
if (initial->idx() == start) finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rotcalip
|
||||
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B)
|
||||
{
|
||||
using namespace rotcalip;
|
||||
|
||||
// Establish starting antipodals as extremes in XY plane. Use the
|
||||
// easily obtainable bounding boxes to check if A and B is disjoint
|
||||
// and return false if the are.
|
||||
struct BB
|
||||
{
|
||||
size_t xmin = 0, xmax = 0, ymin = 0, ymax = 0;
|
||||
const Polygon &P;
|
||||
static bool cmpy(const Point &l, const Point &u)
|
||||
{
|
||||
return l.y() < u.y() || (l.y() == u.y() && l.x() < u.x());
|
||||
}
|
||||
|
||||
BB(const Polygon &poly): P{poly}
|
||||
{
|
||||
for (size_t i = 0; i < P.size(); ++i) {
|
||||
if (P[i] < P[xmin]) xmin = i;
|
||||
if (P[xmax] < P[i]) xmax = i;
|
||||
if (cmpy(P[i], P[ymin])) ymin = i;
|
||||
if (cmpy(P[ymax], P[i])) ymax = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BB bA{A}, bB{B};
|
||||
BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}};
|
||||
BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}};
|
||||
|
||||
// if (!bbA.overlap(bbB))
|
||||
// return false;
|
||||
|
||||
// Establish starting antipodals as extreme vertex pairs in X or Y direction
|
||||
// which reside on different polygons. If no such pair is found, the two
|
||||
// polygons are certainly not disjoint.
|
||||
Idx imin{bA.xmin, A}, imax{bB.xmax, B};
|
||||
if (B[bB.xmin] < imin.pt()) imin = Idx{bB.xmin, B};
|
||||
if (imax.pt() < A[bA.xmax]) imax = Idx{bA.xmax, A};
|
||||
if (&imin.poly() == &imax.poly()) {
|
||||
imin = Idx{bA.ymin, A};
|
||||
imax = Idx{bB.ymax, B};
|
||||
if (B[bB.ymin] < imin.pt()) imin = Idx{bB.ymin, B};
|
||||
if (imax.pt() < A[bA.ymax]) imax = Idx{bA.ymax, A};
|
||||
}
|
||||
|
||||
if (&imin.poly() == &imax.poly())
|
||||
return true;
|
||||
|
||||
bool found_divisor = false;
|
||||
visit_antipodals<AntipodalVisitMode::EdgesOnly>(
|
||||
imin, imax,
|
||||
[&imin, &imax, &found_divisor](size_t ia, size_t ib, const Point &dir) {
|
||||
// std::cout << "A" << ia << " B" << ib << " dir " <<
|
||||
// dir.x() << " " << dir.y() << std::endl;
|
||||
const Polygon &A = imin.poly(), &B = imax.poly();
|
||||
|
||||
Point ref_a = A[(ia + 2) % A.size()], ref_b = B[(ib + 2) % B.size()];
|
||||
|
||||
bool is_left_a = dotperp( dir, ref_a - A[ia]) > 0;
|
||||
bool is_left_b = dotperp(-dir, ref_b - B[ib]) > 0;
|
||||
|
||||
// If both reference points are on the left (or right) of their
|
||||
// respective support lines and the opposite support line is to
|
||||
// the right (or left), the divisor line is found. We only test
|
||||
// the reference point, as by definition, if that is on one side,
|
||||
// all the other points must be on the same side of a support
|
||||
// line. If the support lines are collinear, the polygons must be
|
||||
// on the same side of their respective support lines.
|
||||
|
||||
auto d = dotperp(dir, B[ib] - A[ia]);
|
||||
if (d == 0) {
|
||||
// The caliper lines are collinear, not just parallel
|
||||
found_divisor = (is_left_a && is_left_b) || (!is_left_a && !is_left_b);
|
||||
} else if (d > 0) { // B is to the left of (A, A+1)
|
||||
found_divisor = !is_left_a && !is_left_b;
|
||||
} else { // B is to the right of (A, A+1)
|
||||
found_divisor = is_left_a && is_left_b;
|
||||
}
|
||||
|
||||
return !found_divisor;
|
||||
});
|
||||
|
||||
// Intersects if the divisor was not found
|
||||
return !found_divisor;
|
||||
}
|
||||
|
||||
// Decompose source convex hull points into a top / bottom chains with monotonically increasing x,
|
||||
// creating an implicit trapezoidal decomposition of the source convex polygon.
|
||||
// The source convex polygon has to be CCW oriented. O(n) time complexity.
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> decompose_convex_polygon_top_bottom(const std::vector<Vec2d> &src)
|
||||
{
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> out;
|
||||
std::vector<Vec2d> &bottom = out.first;
|
||||
std::vector<Vec2d> &top = out.second;
|
||||
|
||||
// Find the minimum point.
|
||||
auto left_bottom = std::min_element(src.begin(), src.end(), [](const auto &l, const auto &r) { return l.x() < r.x() || (l.x() == r.x() && l.y() < r.y()); });
|
||||
auto right_top = std::max_element(src.begin(), src.end(), [](const auto &l, const auto &r) { return l.x() < r.x() || (l.x() == r.x() && l.y() < r.y()); });
|
||||
if (left_bottom != src.end() && left_bottom != right_top) {
|
||||
// Produce the bottom and bottom chains.
|
||||
if (left_bottom < right_top) {
|
||||
bottom.assign(left_bottom, right_top + 1);
|
||||
size_t cnt = (src.end() - right_top) + (left_bottom + 1 - src.begin());
|
||||
top.reserve(cnt);
|
||||
top.assign(right_top, src.end());
|
||||
top.insert(top.end(), src.begin(), left_bottom + 1);
|
||||
} else {
|
||||
size_t cnt = (src.end() - left_bottom) + (right_top + 1 - src.begin());
|
||||
bottom.reserve(cnt);
|
||||
bottom.assign(left_bottom, src.end());
|
||||
bottom.insert(bottom.end(), src.begin(), right_top + 1);
|
||||
top.assign(right_top, left_bottom + 1);
|
||||
}
|
||||
// Remove strictly vertical segments at the end.
|
||||
if (bottom.size() > 1) {
|
||||
auto it = bottom.end();
|
||||
for (-- it; it != bottom.begin() && (it - 1)->x() == bottom.back().x(); -- it) ;
|
||||
bottom.erase(it + 1, bottom.end());
|
||||
}
|
||||
if (top.size() > 1) {
|
||||
auto it = top.end();
|
||||
for (-- it; it != top.begin() && (it - 1)->x() == top.back().x(); -- it) ;
|
||||
top.erase(it + 1, top.end());
|
||||
}
|
||||
std::reverse(top.begin(), top.end());
|
||||
}
|
||||
|
||||
if (top.size() < 2 || bottom.size() < 2) {
|
||||
// invalid
|
||||
top.clear();
|
||||
bottom.clear();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Convex polygon check using a top / bottom chain decomposition with O(log n) time complexity.
|
||||
bool inside_convex_polygon(const std::pair<std::vector<Vec2d>, std::vector<Vec2d>> &top_bottom_decomposition, const Vec2d &pt)
|
||||
{
|
||||
auto it_bottom = std::lower_bound(top_bottom_decomposition.first.begin(), top_bottom_decomposition.first.end(), pt, [](const auto &l, const auto &r){ return l.x() < r.x(); });
|
||||
auto it_top = std::lower_bound(top_bottom_decomposition.second.begin(), top_bottom_decomposition.second.end(), pt, [](const auto &l, const auto &r){ return l.x() < r.x(); });
|
||||
if (it_bottom == top_bottom_decomposition.first.end()) {
|
||||
// Above max x.
|
||||
assert(it_top == top_bottom_decomposition.second.end());
|
||||
return false;
|
||||
}
|
||||
if (it_bottom == top_bottom_decomposition.first.begin()) {
|
||||
// Below or at min x.
|
||||
if (pt.x() < it_bottom->x()) {
|
||||
// Below min x.
|
||||
assert(pt.x() < it_top->x());
|
||||
return false;
|
||||
}
|
||||
// At min x.
|
||||
assert(pt.x() == it_bottom->x());
|
||||
assert(pt.x() == it_top->x());
|
||||
assert(it_bottom->y() <= pt.y() <= it_top->y());
|
||||
return pt.y() >= it_bottom->y() && pt.y() <= it_top->y();
|
||||
}
|
||||
|
||||
// Trapezoid or a triangle.
|
||||
assert(it_bottom != top_bottom_decomposition.first .begin() && it_bottom != top_bottom_decomposition.first .end());
|
||||
assert(it_top != top_bottom_decomposition.second.begin() && it_top != top_bottom_decomposition.second.end());
|
||||
assert(pt.x() <= it_bottom->x());
|
||||
assert(pt.x() <= it_top->x());
|
||||
auto it_top_prev = it_top - 1;
|
||||
auto it_bottom_prev = it_bottom - 1;
|
||||
assert(pt.x() >= it_top_prev->x());
|
||||
assert(pt.x() >= it_bottom_prev->x());
|
||||
double det = cross2(*it_bottom - *it_bottom_prev, pt - *it_bottom_prev);
|
||||
if (det < 0)
|
||||
return false;
|
||||
det = cross2(*it_top - *it_top_prev, pt - *it_top_prev);
|
||||
return det <= 0;
|
||||
}
|
||||
|
||||
} // namespace Geometry
|
||||
} // namespace Slic3r
|
||||
|
27
src/libslic3r/Geometry/ConvexHull.hpp
Normal file
27
src/libslic3r/Geometry/ConvexHull.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef slic3r_Geometry_ConvexHull_hpp_
|
||||
#define slic3r_Geometry_ConvexHull_hpp_
|
||||
|
||||
#include "../Polygon.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Geometry {
|
||||
|
||||
Pointf3s convex_hull(Pointf3s points);
|
||||
Polygon convex_hull(Points points);
|
||||
Polygon convex_hull(const Polygons &polygons);
|
||||
|
||||
// Returns true if the intersection of the two convex polygons A and B
|
||||
// is not an empty set.
|
||||
bool convex_polygons_intersect(const Polygon &A, const Polygon &B);
|
||||
|
||||
// Decompose source convex hull points into top / bottom chains with monotonically increasing x,
|
||||
// creating an implicit trapezoidal decomposition of the source convex polygon.
|
||||
// The source convex polygon has to be CCW oriented. O(n) time complexity.
|
||||
std::pair<std::vector<Vec2d>, std::vector<Vec2d>> decompose_convex_polygon_top_bottom(const std::vector<Vec2d> &src);
|
||||
|
||||
// Convex polygon check using a top / bottom chain decomposition with O(log n) time complexity.
|
||||
bool inside_convex_polygon(const std::pair<std::vector<Vec2d>, std::vector<Vec2d>> &top_bottom_decomposition, const Vec2d &pt);
|
||||
|
||||
} } // namespace Slicer::Geometry
|
||||
|
||||
#endif
|
|
@ -70,7 +70,6 @@ bool Line::parallel_to(const Line& line) const
|
|||
return sqr(cross2(v1, v2)) < sqr(EPSILON) * v1.squaredNorm() * v2.squaredNorm();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool Line::perpendicular_to(double angle) const
|
||||
{
|
||||
return Slic3r::Geometry::directions_perpendicular(this->direction(), angle);
|
||||
|
@ -82,7 +81,6 @@ bool Line::perpendicular_to(const Line& line) const
|
|||
const Vec2d v2 = (line.b - line.a).cast<double>();
|
||||
return sqr(v1.dot(v2)) < sqr(EPSILON) * v1.squaredNorm() * v2.squaredNorm();
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
bool Line::intersection(const Line &l2, Point *intersection) const
|
||||
{
|
||||
|
|
|
@ -105,10 +105,8 @@ public:
|
|||
double perp_distance_to(const Point &point) const;
|
||||
bool parallel_to(double angle) const;
|
||||
bool parallel_to(const Line& line) const;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool perpendicular_to(double angle) const;
|
||||
bool perpendicular_to(const Line& line) const;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); }
|
||||
double orientation() const;
|
||||
double direction() const;
|
||||
|
|
|
@ -12,11 +12,6 @@
|
|||
#include <CGAL/Polygon_mesh_processing/corefinement.h>
|
||||
#include <CGAL/Exact_integer.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
|
||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||
#include <CGAL/Polygon_mesh_processing/remesh.h>
|
||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||
#include <CGAL/Polygon_mesh_processing/orientation.h>
|
||||
#include <CGAL/Cartesian_converter.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include "Model.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "BuildVolume.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "ModelArrange.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
#include "TriangleMeshSlicer.hpp"
|
||||
#include "TriangleSelector.hpp"
|
||||
|
@ -26,39 +28,6 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// Using rotating callipers to check for collision of two convex polygons. Thus both printbed_shape and obj_hull_2d are convex polygons.
|
||||
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z)
|
||||
{
|
||||
if (!Geometry::convex_polygons_intersect(printbed_shape, obj_hull_2d))
|
||||
return ModelInstancePVS_Fully_Outside;
|
||||
|
||||
bool contained_xy = true;
|
||||
for (const Point& p : obj_hull_2d) {
|
||||
if (!printbed_shape.contains(p)) {
|
||||
contained_xy = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const bool contained_z = -1e10 < obj_min_z && obj_max_z <= print_volume_height;
|
||||
return (contained_xy && contained_z) ? ModelInstancePVS_Inside : ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
|
||||
/*
|
||||
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box)
|
||||
{
|
||||
const Polygon box_hull_2d({
|
||||
{ scale_(box.min.x()), scale_(box.min.y()) },
|
||||
{ scale_(box.max.x()), scale_(box.min.y()) },
|
||||
{ scale_(box.max.x()), scale_(box.max.y()) },
|
||||
{ scale_(box.min.x()), scale_(box.max.y()) }
|
||||
});
|
||||
return printbed_collision_state(printbed_shape, print_volume_height, box_hull_2d, box.min.z(), box.max.z());
|
||||
}
|
||||
*/
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
Model& Model::assign_copy(const Model &rhs)
|
||||
{
|
||||
this->copy_id(rhs);
|
||||
|
@ -191,13 +160,7 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
|
|||
if (!result)
|
||||
throw Slic3r::RuntimeError("Loading of a model file failed.");
|
||||
|
||||
#if !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
if (model.objects.empty())
|
||||
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
|
||||
#endif // !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
|
||||
for (ModelObject *o : model.objects)
|
||||
{
|
||||
for (ModelObject *o : model.objects) {
|
||||
// if (boost::algorithm::iends_with(input_file, ".zip.amf"))
|
||||
// {
|
||||
// // we remove the .zip part of the extension to avoid it be added to filenames when exporting
|
||||
|
@ -213,6 +176,8 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
|
|||
CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
|
||||
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
|
||||
|
||||
handle_legacy_sla(*config);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
|
@ -363,24 +328,13 @@ BoundingBoxf3 Model::bounding_box() const
|
|||
return bb;
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// printbed_shape is convex polygon
|
||||
unsigned int Model::update_print_volume_state(const Polygon& printbed_shape, double print_volume_height)
|
||||
unsigned int Model::update_print_volume_state(const BuildVolume &build_volume)
|
||||
{
|
||||
unsigned int num_printable = 0;
|
||||
for (ModelObject* model_object : this->objects)
|
||||
num_printable += model_object->check_instances_print_volume_state(printbed_shape, print_volume_height);
|
||||
num_printable += model_object->update_instances_print_volume_state(build_volume);
|
||||
return num_printable;
|
||||
}
|
||||
#else
|
||||
unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume)
|
||||
{
|
||||
unsigned int num_printable = 0;
|
||||
for (ModelObject *model_object : this->objects)
|
||||
num_printable += model_object->check_instances_print_volume_state(print_volume);
|
||||
return num_printable;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
bool Model::center_instances_around_point(const Vec2d &point)
|
||||
{
|
||||
|
@ -1572,9 +1526,7 @@ double ModelObject::get_instance_max_z(size_t instance_idx) const
|
|||
return max_z + inst->get_offset(Z);
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// printbed_shape is convex polygon
|
||||
unsigned int ModelObject::check_instances_print_volume_state(const Polygon& printbed_shape, double print_volume_height)
|
||||
unsigned int ModelObject::update_instances_print_volume_state(const BuildVolume &build_volume)
|
||||
{
|
||||
unsigned int num_printable = 0;
|
||||
enum {
|
||||
|
@ -1586,57 +1538,28 @@ unsigned int ModelObject::check_instances_print_volume_state(const Polygon& prin
|
|||
for (const ModelVolume* vol : this->volumes)
|
||||
if (vol->is_model_part()) {
|
||||
const Transform3d matrix = model_instance->get_matrix() * vol->get_matrix();
|
||||
const BoundingBoxf3 bb = vol->mesh().transformed_bounding_box(matrix, 0.0);
|
||||
if (!bb.defined) {
|
||||
// this may happen if the part is fully below the printbed, leading to a crash in the following call to its_convex_hull_2d_above()
|
||||
continue;
|
||||
}
|
||||
const Polygon volume_hull_2d = its_convex_hull_2d_above(vol->mesh().its, matrix.cast<float>(), 0.0f);
|
||||
ModelInstanceEPrintVolumeState state = printbed_collision_state(printbed_shape, print_volume_height, volume_hull_2d, bb.min.z(), bb.max.z());
|
||||
if (state == ModelInstancePVS_Inside)
|
||||
BuildVolume::ObjectState state = build_volume.object_state(vol->mesh().its, matrix.cast<float>(), true /* may be below print bed */);
|
||||
if (state == BuildVolume::ObjectState::Inside)
|
||||
// Volume is completely inside.
|
||||
inside_outside |= INSIDE;
|
||||
else if (state == ModelInstancePVS_Fully_Outside)
|
||||
else if (state == BuildVolume::ObjectState::Outside)
|
||||
// Volume is completely outside.
|
||||
inside_outside |= OUTSIDE;
|
||||
else
|
||||
else if (state == BuildVolume::ObjectState::Below) {
|
||||
// Volume below the print bed, thus it is completely outside, however this does not prevent the object to be printable
|
||||
// if some of its volumes are still inside the build volume.
|
||||
} else
|
||||
// Volume colliding with the build volume.
|
||||
inside_outside |= INSIDE | OUTSIDE;
|
||||
}
|
||||
model_instance->print_volume_state =
|
||||
(inside_outside == (INSIDE | OUTSIDE)) ? ModelInstancePVS_Partly_Outside :
|
||||
(inside_outside == INSIDE) ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside;
|
||||
inside_outside == (INSIDE | OUTSIDE) ? ModelInstancePVS_Partly_Outside :
|
||||
inside_outside == INSIDE ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside;
|
||||
if (inside_outside == INSIDE)
|
||||
++num_printable;
|
||||
}
|
||||
return num_printable;
|
||||
}
|
||||
#else
|
||||
unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
|
||||
{
|
||||
unsigned int num_printable = 0;
|
||||
enum {
|
||||
INSIDE = 1,
|
||||
OUTSIDE = 2
|
||||
};
|
||||
for (ModelInstance *model_instance : this->instances) {
|
||||
unsigned int inside_outside = 0;
|
||||
for (const ModelVolume *vol : this->volumes)
|
||||
if (vol->is_model_part()) {
|
||||
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix());
|
||||
if (print_volume.contains(bb))
|
||||
inside_outside |= INSIDE;
|
||||
else if (print_volume.intersects(bb))
|
||||
inside_outside |= INSIDE | OUTSIDE;
|
||||
else
|
||||
inside_outside |= OUTSIDE;
|
||||
}
|
||||
model_instance->print_volume_state =
|
||||
(inside_outside == (INSIDE | OUTSIDE)) ? ModelInstancePVS_Partly_Outside :
|
||||
(inside_outside == INSIDE) ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside;
|
||||
if (inside_outside == INSIDE)
|
||||
++ num_printable;
|
||||
}
|
||||
return num_printable;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void ModelObject::print_info() const
|
||||
{
|
||||
|
@ -1701,9 +1624,6 @@ std::string ModelObject::get_export_filename() const
|
|||
|
||||
TriangleMeshStats ModelObject::get_object_stl_stats() const
|
||||
{
|
||||
if (this->volumes.size() == 1)
|
||||
return this->volumes[0]->mesh().stats();
|
||||
|
||||
TriangleMeshStats full_stats;
|
||||
full_stats.volume = 0.f;
|
||||
|
||||
|
@ -1718,7 +1638,8 @@ TriangleMeshStats ModelObject::get_object_stl_stats() const
|
|||
|
||||
// another used satistics value
|
||||
if (volume->is_model_part()) {
|
||||
full_stats.volume += stats.volume;
|
||||
Transform3d trans = instances.empty() ? volume->get_matrix() : (volume->get_matrix() * instances[0]->get_matrix());
|
||||
full_stats.volume += stats.volume * std::fabs(trans.matrix().block(0, 0, 3, 3).determinant());
|
||||
full_stats.number_of_parts += stats.number_of_parts;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace cereal {
|
|||
namespace Slic3r {
|
||||
enum class ConversionType;
|
||||
|
||||
class BuildVolume;
|
||||
class Model;
|
||||
class ModelInstance;
|
||||
class ModelMaterial;
|
||||
|
@ -366,13 +367,6 @@ public:
|
|||
double get_instance_min_z(size_t instance_idx) const;
|
||||
double get_instance_max_z(size_t instance_idx) const;
|
||||
|
||||
// Called by Print::validate() from the UI thread.
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
unsigned int check_instances_print_volume_state(const Polygon& printbed_shape, double print_volume_height);
|
||||
#else
|
||||
unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// Print object statistics to console.
|
||||
void print_info() const;
|
||||
|
||||
|
@ -505,6 +499,9 @@ private:
|
|||
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation,
|
||||
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid);
|
||||
}
|
||||
|
||||
// Called by Print::validate() from the UI thread.
|
||||
unsigned int update_instances_print_volume_state(const BuildVolume &build_volume);
|
||||
};
|
||||
|
||||
enum class EnforcerBlockerType : int8_t {
|
||||
|
@ -908,17 +905,6 @@ enum ModelInstanceEPrintVolumeState : unsigned char
|
|||
ModelInstanceNum_BedStates
|
||||
};
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// return the state of the given object's volume (extrusion along z of obj_hull_2d from obj_min_z to obj_max_z)
|
||||
// with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height)
|
||||
// Using rotating callipers to check for collision of two convex polygons.
|
||||
ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z);
|
||||
// return the state of the given box
|
||||
// with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height)
|
||||
// Commented out, using rotating callipers is quite expensive for a bounding box test.
|
||||
//ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// A single instance of a ModelObject.
|
||||
// Knows the affine transformation of an object.
|
||||
class ModelInstance final : public ObjectBase
|
||||
|
@ -1123,12 +1109,7 @@ public:
|
|||
BoundingBoxf3 bounding_box() const;
|
||||
// Set the print_volume_state of PrintObject::instances,
|
||||
// return total number of printable objects.
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// printbed_shape is convex polygon
|
||||
unsigned int update_print_volume_state(const Polygon& printbed_shape, double print_volume_height);
|
||||
#else
|
||||
unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
unsigned int update_print_volume_state(const BuildVolume &build_volume);
|
||||
// Returns true if any ModelObject was modified.
|
||||
bool center_instances_around_point(const Vec2d &point);
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "ModelArrange.hpp"
|
||||
|
||||
#include <libslic3r/Model.hpp>
|
||||
#include <libslic3r/Geometry/ConvexHull.hpp>
|
||||
#include "MTUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
|
@ -1723,10 +1723,21 @@ std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(con
|
|||
}); // end of parallel_for
|
||||
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - slices preparation in parallel - end";
|
||||
|
||||
std::vector<BoundingBox> layer_bboxes(num_layers);
|
||||
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
BoundingBox bbox(get_extents(layers[layer_idx]->regions()));
|
||||
bbox.merge(get_extents(input_expolygons[layer_idx]));
|
||||
layer_bboxes[layer_idx] = get_extents(layers[layer_idx]->regions());
|
||||
layer_bboxes[layer_idx].merge(get_extents(input_expolygons[layer_idx]));
|
||||
}
|
||||
|
||||
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
|
||||
throw_on_cancel_callback();
|
||||
BoundingBox bbox = layer_bboxes[layer_idx];
|
||||
// Projected triangles could, in rare cases (as in GH issue #7299), belongs to polygons printed in the previous or the next layer.
|
||||
// Let's merge the bounding box of the current layer with bounding boxes of the previous and the next layer to ensure that
|
||||
// every projected triangle will be inside the resulting bounding box.
|
||||
if (layer_idx > 1) bbox.merge(layer_bboxes[layer_idx - 1]);
|
||||
if (layer_idx < num_layers - 1) bbox.merge(layer_bboxes[layer_idx + 1]);
|
||||
// Projected triangles may slightly exceed the input polygons.
|
||||
bbox.offset(20 * SCALED_EPSILON);
|
||||
edge_grids[layer_idx].set_bbox(bbox);
|
||||
|
|
|
@ -201,6 +201,14 @@ BoundingBox get_extents(const std::vector<Points> &pts)
|
|||
return bbox;
|
||||
}
|
||||
|
||||
BoundingBoxf get_extents(const std::vector<Vec2d> &pts)
|
||||
{
|
||||
BoundingBoxf bbox;
|
||||
for (const Vec2d &p : pts)
|
||||
bbox.merge(p);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf)
|
||||
{
|
||||
return stm << pointf(0) << "," << pointf(1);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
namespace Slic3r {
|
||||
|
||||
class BoundingBox;
|
||||
class BoundingBoxf;
|
||||
class Line;
|
||||
class MultiPoint;
|
||||
class Point;
|
||||
|
@ -133,6 +134,7 @@ public:
|
|||
Point(const Eigen::MatrixBase<OtherDerived> &other) : Vec2crd(other) {}
|
||||
static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); }
|
||||
static Point new_scale(const Vec2d &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); }
|
||||
static Point new_scale(const Vec2f &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); }
|
||||
|
||||
// This method allows you to assign Eigen expressions to MyVectorType
|
||||
template<typename OtherDerived>
|
||||
|
@ -213,6 +215,7 @@ inline Point lerp(const Point &a, const Point &b, double t)
|
|||
|
||||
BoundingBox get_extents(const Points &pts);
|
||||
BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
BoundingBoxf get_extents(const std::vector<Vec2d> &pts);
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// The points are copied, sorted and checked for duplicates globally.
|
||||
|
|
|
@ -334,6 +334,25 @@ extern std::vector<BoundingBox> get_extents_vector(const Polygons &polygons)
|
|||
return out;
|
||||
}
|
||||
|
||||
// Polygon must be valid (at least three points), collinear points and duplicate points removed.
|
||||
bool polygon_is_convex(const Points &poly)
|
||||
{
|
||||
if (poly.size() < 3)
|
||||
return false;
|
||||
|
||||
Point p0 = poly[poly.size() - 2];
|
||||
Point p1 = poly[poly.size() - 1];
|
||||
for (size_t i = 0; i < poly.size(); ++ i) {
|
||||
Point p2 = poly[i];
|
||||
auto det = cross2((p1 - p0).cast<int64_t>(), (p2 - p1).cast<int64_t>());
|
||||
if (det < 0)
|
||||
return false;
|
||||
p0 = p1;
|
||||
p1 = p2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_duplicate_points(const Polygons &polys)
|
||||
{
|
||||
#if 1
|
||||
|
|
|
@ -84,6 +84,10 @@ BoundingBox get_extents_rotated(const Polygon &poly, double angle);
|
|||
BoundingBox get_extents_rotated(const Polygons &polygons, double angle);
|
||||
std::vector<BoundingBox> get_extents_vector(const Polygons &polygons);
|
||||
|
||||
// Polygon must be valid (at least three points), collinear points and duplicate points removed.
|
||||
bool polygon_is_convex(const Points &poly);
|
||||
inline bool polygon_is_convex(const Polygon &poly) { return polygon_is_convex(poly.points); }
|
||||
|
||||
// Test for duplicate points. The points are copied, sorted and checked for duplicates globally.
|
||||
inline bool has_duplicate_points(Polygon &&poly) { return has_duplicate_points(std::move(poly.points)); }
|
||||
inline bool has_duplicate_points(const Polygon &poly) { return has_duplicate_points(poly.points); }
|
||||
|
|
|
@ -303,6 +303,8 @@ void Preset::normalize(DynamicPrintConfig &config)
|
|||
first_layer_height->value = first_layer_height->get_abs_value(layer_height->value);
|
||||
first_layer_height->percent = false;
|
||||
}
|
||||
|
||||
handle_legacy_sla(config);
|
||||
}
|
||||
|
||||
std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config)
|
||||
|
@ -544,8 +546,12 @@ static std::vector<std::string> s_Preset_sla_material_options {
|
|||
"exposure_time",
|
||||
"initial_exposure_time",
|
||||
"material_correction",
|
||||
"material_correction_x",
|
||||
"material_correction_y",
|
||||
"material_correction_z",
|
||||
"material_notes",
|
||||
"material_vendor",
|
||||
"material_print_speed",
|
||||
"default_sla_material_profile",
|
||||
"compatible_prints", "compatible_prints_condition",
|
||||
"compatible_printers", "compatible_printers_condition", "inherits"
|
||||
|
@ -559,6 +565,9 @@ static std::vector<std::string> s_Preset_sla_printer_options {
|
|||
"display_orientation",
|
||||
"fast_tilt_time", "slow_tilt_time", "area_fill",
|
||||
"relative_correction",
|
||||
"relative_correction_x",
|
||||
"relative_correction_y",
|
||||
"relative_correction_z",
|
||||
"absolute_correction",
|
||||
"elefant_foot_compensation",
|
||||
"elefant_foot_min_width",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "ClipperUtils.hpp"
|
||||
#include "Extruder.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
|
@ -850,7 +850,7 @@ void Print::process()
|
|||
// The export_gcode may die for various reasons (fails to process output_filename_format,
|
||||
// write error into the G-code, cannot execute post-processing scripts).
|
||||
// It is up to the caller to show an error message.
|
||||
std::string Print::export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
std::string Print::export_gcode(const std::string& path_template, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
{
|
||||
// output everything to a G-code file
|
||||
// The following call may die if the output_filename_format template substitution fails.
|
||||
|
|
|
@ -521,7 +521,7 @@ public:
|
|||
void process() override;
|
||||
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
|
||||
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
|
||||
std::string export_gcode(const std::string& path_template, GCodeProcessor::Result* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
std::string export_gcode(const std::string& path_template, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||
|
||||
// methods for handling state
|
||||
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
|
||||
|
|
|
@ -604,12 +604,36 @@ void print_objects_regions_invalidate_keep_some_volumes(PrintObjectRegions &prin
|
|||
print_object_regions.cached_volume_ids.erase(print_object_regions.cached_volume_ids.begin() + last_cached_volume, print_object_regions.cached_volume_ids.end());
|
||||
}
|
||||
|
||||
// Find a bounding box of a volume's part intersecting layer_range. Such a bounding box will likely be smaller in XY than the full bounding box,
|
||||
// thus it will intersect with lower number of other volumes.
|
||||
const PrintObjectRegions::BoundingBox* find_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const ModelVolume &volume)
|
||||
{
|
||||
auto it = lower_bound_by_predicate(layer_range.volumes.begin(), layer_range.volumes.end(), [&volume](const PrintObjectRegions::VolumeExtents &l){ return l.volume_id < volume.id(); });
|
||||
return it != layer_range.volumes.end() && it->volume_id == volume.id() ? &it->bbox : nullptr;
|
||||
}
|
||||
|
||||
// Find a bounding box of a topmost printable volume referenced by this modifier given this_region_id.
|
||||
PrintObjectRegions::BoundingBox find_modifier_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const int this_region_id)
|
||||
{
|
||||
// Find the top-most printable volume of this modifier, or the printable volume itself.
|
||||
const PrintObjectRegions::VolumeRegion &this_region = layer_range.volume_regions[this_region_id];
|
||||
const PrintObjectRegions::BoundingBox *this_extents = find_volume_extents(layer_range, *this_region.model_volume);
|
||||
assert(this_extents);
|
||||
PrintObjectRegions::BoundingBox out { *this_extents };
|
||||
if (! this_region.model_volume->is_model_part())
|
||||
for (int parent_region_id = this_region.parent;;) {
|
||||
assert(parent_region_id >= 0);
|
||||
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
|
||||
const PrintObjectRegions::BoundingBox *parent_extents = find_volume_extents(layer_range, *parent_region.model_volume);
|
||||
assert(parent_extents);
|
||||
out.extend(*parent_extents);
|
||||
if (parent_region.model_volume->is_model_part())
|
||||
break;
|
||||
parent_region_id = parent_region.parent;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders);
|
||||
|
||||
void print_region_ref_inc(PrintRegion &r) { ++ r.m_ref_cnt; }
|
||||
|
@ -634,11 +658,49 @@ bool verify_update_print_object_regions(
|
|||
print_region_ref_reset(*region);
|
||||
|
||||
// Verify and / or update PrintRegions produced by ModelVolumes, layer range modifiers, modifier volumes.
|
||||
for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges)
|
||||
for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) {
|
||||
// Each modifier ModelVolume intersecting this layer_range shall be referenced here at least once if it intersects some
|
||||
// printable ModelVolume at this layer_range even if it does not modify its overlapping printable ModelVolume configuration yet.
|
||||
// VolumeRegions reference ModelVolumes in layer_range.volume_regions the order they are stored in ModelObject volumes.
|
||||
// Remember whether a given modifier ModelVolume was visited already.
|
||||
auto it_model_volume_modifier_last = model_volumes.end();
|
||||
for (PrintObjectRegions::VolumeRegion ®ion : layer_range.volume_regions)
|
||||
if (region.model_volume->is_model_part() || region.model_volume->is_modifier()) {
|
||||
auto it_model_volume = lower_bound_by_predicate(model_volumes.begin(), model_volumes.end(), [®ion](const ModelVolume *l){ return l->id() < region.model_volume->id(); });
|
||||
assert(it_model_volume != model_volumes.end() && (*it_model_volume)->id() == region.model_volume->id());
|
||||
if (region.model_volume->is_modifier() && it_model_volume != it_model_volume_modifier_last) {
|
||||
// A modifier ModelVolume is visited for the first time.
|
||||
// A visited modifier may not have had parent volume_regions created overlapping with some model parts or modifiers,
|
||||
// if the visited modifier did not modify their properties. Now the visited modifier's configuration may have changed,
|
||||
// which may require new regions to be created.
|
||||
it_model_volume_modifier_last = it_model_volume;
|
||||
int next_region_id = int(®ion - layer_range.volume_regions.data());
|
||||
const PrintObjectRegions::BoundingBox *bbox = find_volume_extents(layer_range, *region.model_volume);
|
||||
assert(bbox);
|
||||
for (int parent_region_id = next_region_id - 1; parent_region_id >= 0; -- parent_region_id) {
|
||||
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
|
||||
assert(parent_region.model_volume != region.model_volume);
|
||||
if (parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) {
|
||||
// volume_regions are produced in decreasing order of parent volume_regions ids.
|
||||
// Some regions may not have been generated the last time by generate_print_object_regions().
|
||||
assert(next_region_id == int(layer_range.volume_regions.size()) ||
|
||||
layer_range.volume_regions[next_region_id].model_volume != region.model_volume ||
|
||||
layer_range.volume_regions[next_region_id].parent <= parent_region_id);
|
||||
if (next_region_id < int(layer_range.volume_regions.size()) &&
|
||||
layer_range.volume_regions[next_region_id].model_volume == region.model_volume &&
|
||||
layer_range.volume_regions[next_region_id].parent == parent_region_id) {
|
||||
// A parent region is already overridden.
|
||||
++ next_region_id;
|
||||
} else if (PrintObjectRegions::BoundingBox parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); parent_bbox.intersects(*bbox))
|
||||
// Such parent region does not exist. If it is needed, then we need to reslice.
|
||||
// Only create new region for a modifier, which actually modifies config of it's parent.
|
||||
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders);
|
||||
config != parent_region.region->config())
|
||||
// This modifier newly overrides a region, which it did not before. We need to reslice.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
PrintRegionConfig cfg = region.parent == -1 ?
|
||||
region_config_from_model_volume(default_region_config, layer_range.config, **it_model_volume, num_extruders) :
|
||||
region_config_from_model_volume(layer_range.volume_regions[region.parent].region->config(), nullptr, **it_model_volume, num_extruders);
|
||||
|
@ -657,6 +719,7 @@ bool verify_update_print_object_regions(
|
|||
}
|
||||
print_region_ref_inc(*region.region);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify and / or update PrintRegions produced by color painting.
|
||||
for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges)
|
||||
|
@ -851,17 +914,26 @@ static PrintObjectRegions* generate_print_object_regions(
|
|||
} else {
|
||||
assert(volume.is_modifier());
|
||||
// Modifiers may be chained one over the other. Check for overlap, merge DynamicPrintConfigs.
|
||||
for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id)
|
||||
if (const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
|
||||
parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) {
|
||||
const PrintObjectRegions::BoundingBox *parent_bbox = find_volume_extents(layer_range, *parent_region.model_volume);
|
||||
assert(parent_bbox != nullptr);
|
||||
if (parent_bbox->intersects(*bbox))
|
||||
bool added = false;
|
||||
int parent_model_part_id = -1;
|
||||
for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id) {
|
||||
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
|
||||
const ModelVolume &parent_volume = *parent_region.model_volume;
|
||||
if (parent_volume.is_model_part() || parent_volume.is_modifier())
|
||||
if (PrintObjectRegions::BoundingBox parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); parent_bbox.intersects(*bbox)) {
|
||||
// Only create new region for a modifier, which actually modifies config of it's parent.
|
||||
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders);
|
||||
config != parent_region.region->config())
|
||||
config != parent_region.region->config()) {
|
||||
added = true;
|
||||
layer_range.volume_regions.push_back({ &volume, parent_region_id, get_create_region(std::move(config)), bbox });
|
||||
} else if (parent_model_part_id == -1 && parent_volume.is_model_part())
|
||||
parent_model_part_id = parent_region_id;
|
||||
}
|
||||
}
|
||||
if (! added && parent_model_part_id >= 0)
|
||||
// This modifier does not override any printable volume's configuration, however it may in the future.
|
||||
// Store it so that verify_update_print_object_regions() will handle this modifier correctly if its configuration changes.
|
||||
layer_range.volume_regions.push_back({ &volume, parent_model_part_id, layer_range.volume_regions[parent_model_part_id].region, bbox });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,8 @@ static t_config_enum_values s_keys_map_PrintHostType {
|
|||
{ "duet", htDuet },
|
||||
{ "flashair", htFlashAir },
|
||||
{ "astrobox", htAstroBox },
|
||||
{ "repetier", htRepetier }
|
||||
{ "repetier", htRepetier },
|
||||
{ "mks", htMKS }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintHostType)
|
||||
|
||||
|
@ -165,6 +166,12 @@ static const t_config_enum_values s_keys_map_SLAPillarConnectionMode = {
|
|||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAPillarConnectionMode)
|
||||
|
||||
static const t_config_enum_values s_keys_map_SLAMaterialSpeed = {
|
||||
{"slow", slamsSlow},
|
||||
{"fast", slamsFast}
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAMaterialSpeed);
|
||||
|
||||
static const t_config_enum_values s_keys_map_BrimType = {
|
||||
{"no_brim", btNoBrim},
|
||||
{"outer_only", btOuterOnly},
|
||||
|
@ -374,6 +381,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path.");
|
||||
def->sidetext = L("mm or % (zero to disable)");
|
||||
def->min = 0;
|
||||
def->max_literal = 1000;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
|
||||
|
||||
|
@ -720,6 +728,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"If expressed as percentage (for example 200%), it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
|
@ -831,6 +840,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max = 1000;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
|
@ -1181,6 +1191,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "first_layer_height";
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(200, true));
|
||||
|
||||
|
@ -1200,6 +1211,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"(for example: 40%) it will scale the default speeds.");
|
||||
def->sidetext = L("mm/s or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 20;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(30, false));
|
||||
|
||||
|
@ -1374,6 +1386,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"Set this parameter to zero to disable anchoring perimeters connected to a single infill line.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "infill_extrusion_width";
|
||||
def->max_literal = 1000;
|
||||
def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
||||
def->enum_values.push_back("0");
|
||||
def->enum_values.push_back("1");
|
||||
|
@ -1401,6 +1414,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"Set this parameter to zero to disable anchoring.");
|
||||
def->sidetext = def_infill_anchor_min->sidetext;
|
||||
def->ratio_over = def_infill_anchor_min->ratio_over;
|
||||
def->max_literal = def_infill_anchor_min->max_literal;
|
||||
def->gui_type = def_infill_anchor_min->gui_type;
|
||||
def->enum_values = def_infill_anchor_min->enum_values;
|
||||
def->enum_labels.push_back(L("0 (not anchored)"));
|
||||
|
@ -1429,6 +1443,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
|
@ -1840,12 +1855,14 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("flashair");
|
||||
def->enum_values.push_back("astrobox");
|
||||
def->enum_values.push_back("repetier");
|
||||
def->enum_values.push_back("mks");
|
||||
def->enum_labels.push_back("PrusaLink");
|
||||
def->enum_labels.push_back("OctoPrint");
|
||||
def->enum_labels.push_back("Duet");
|
||||
def->enum_labels.push_back("FlashAir");
|
||||
def->enum_labels.push_back("AstroBox");
|
||||
def->enum_labels.push_back("Repetier");
|
||||
def->enum_labels.push_back("MKS");
|
||||
def->mode = comAdvanced;
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htOctoPrint));
|
||||
|
@ -1928,6 +1945,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm or %");
|
||||
def->aliases = { "perimeters_extrusion_width" };
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
|
@ -2300,6 +2318,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
|
@ -2474,6 +2493,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "external_perimeter_extrusion_width";
|
||||
def->min = 0;
|
||||
def->max_literal = 10;
|
||||
def->mode = comAdvanced;
|
||||
// Default is half the external perimeter width.
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
|
||||
|
@ -2560,6 +2580,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
|
@ -2670,7 +2691,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Concentric"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear));
|
||||
def->set_default_value(new ConfigOptionEnum<SupportMaterialInterfacePattern>(smipRectilinear));
|
||||
|
||||
def = this->add("support_material_spacing", coFloat);
|
||||
def->label = L("Pattern spacing");
|
||||
|
@ -2793,6 +2814,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max_literal = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
|
@ -3133,7 +3155,31 @@ void PrintConfigDef::init_sla_params()
|
|||
def->tooltip = L("Printer scaling correction");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloats( { 1., 1. } ));
|
||||
def->set_default_value(new ConfigOptionFloats( { 1., 1.} ));
|
||||
|
||||
def = this->add("relative_correction_x", coFloat);
|
||||
def->label = L("Printer scaling correction in X axis");
|
||||
def->full_label = L("Printer scaling X axis correction");
|
||||
def->tooltip = L("Printer scaling correction in X axis");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(1.));
|
||||
|
||||
def = this->add("relative_correction_y", coFloat);
|
||||
def->label = L("Printer scaling correction in Y axis");
|
||||
def->full_label = L("Printer scaling X axis correction");
|
||||
def->tooltip = L("Printer scaling correction in Y axis");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(1.));
|
||||
|
||||
def = this->add("relative_correction_z", coFloat);
|
||||
def->label = L("Printer scaling correction in Z axis");
|
||||
def->full_label = L("Printer scaling X axis correction");
|
||||
def->tooltip = L("Printer scaling correction in Z axis");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(1.));
|
||||
|
||||
def = this->add("absolute_correction", coFloat);
|
||||
def->label = L("Printer absolute correction");
|
||||
|
@ -3279,7 +3325,28 @@ void PrintConfigDef::init_sla_params()
|
|||
def->tooltip = L("Correction for expansion");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloats( { 1. , 1. } ));
|
||||
def->set_default_value(new ConfigOptionFloats( { 1., 1., 1. } ));
|
||||
|
||||
def = this->add("material_correction_x", coFloat);
|
||||
def->full_label = L("Correction for expansion in X axis");
|
||||
def->tooltip = L("Correction for expansion in X axis");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(1.));
|
||||
|
||||
def = this->add("material_correction_y", coFloat);
|
||||
def->full_label = L("Correction for expansion in Y axis");
|
||||
def->tooltip = L("Correction for expansion in Y axis");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(1.));
|
||||
|
||||
def = this->add("material_correction_z", coFloat);
|
||||
def->full_label = L("Correction for expansion in Z axis");
|
||||
def->tooltip = L("Correction for expansion in Z axis");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(1.));
|
||||
|
||||
def = this->add("material_notes", coString);
|
||||
def->label = L("SLA print material notes");
|
||||
|
@ -3672,6 +3739,19 @@ void PrintConfigDef::init_sla_params()
|
|||
def->max = 10;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(2.0));
|
||||
|
||||
def = this->add("material_print_speed", coEnum);
|
||||
def->label = L("Print speed");
|
||||
def->tooltip = L(
|
||||
"A slower printing profile might be necessary when using materials with higher viscosity "
|
||||
"or with some hollowed parts. It slows down the tilt movement and adds a delay before exposure.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<SLAMaterialSpeed>::get_enum_values();
|
||||
def->enum_values.push_back("slow");
|
||||
def->enum_values.push_back("fast");
|
||||
def->enum_labels.push_back(L("Slow"));
|
||||
def->enum_labels.push_back(L("Fast"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<SLAMaterialSpeed>(slamsSlow));
|
||||
}
|
||||
|
||||
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
|
||||
|
@ -3736,7 +3816,16 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
opt_key = "printhost_apikey";
|
||||
} else if (opt_key == "preset_name") {
|
||||
opt_key = "preset_names";
|
||||
}
|
||||
} /*else if (opt_key == "material_correction" || opt_key == "relative_correction") {
|
||||
ConfigOptionFloats p;
|
||||
p.deserialize(value);
|
||||
|
||||
if (p.values.size() < 3) {
|
||||
double firstval = p.values.front();
|
||||
p.values.emplace(p.values.begin(), firstval);
|
||||
value = p.serialize();
|
||||
}
|
||||
}*/
|
||||
|
||||
// Ignore the following obsolete configuration keys:
|
||||
static std::set<std::string> ignore = {
|
||||
|
@ -3853,6 +3942,28 @@ void DynamicPrintConfig::normalize_fdm()
|
|||
}
|
||||
}
|
||||
|
||||
void handle_legacy_sla(DynamicPrintConfig &config)
|
||||
{
|
||||
for (std::string corr : {"relative_correction", "material_correction"}) {
|
||||
if (config.has(corr)) {
|
||||
if (std::string corr_x = corr + "_x"; !config.has(corr_x)) {
|
||||
auto* opt = config.opt<ConfigOptionFloat>(corr_x, true);
|
||||
opt->value = config.opt<ConfigOptionFloats>(corr)->values[0];
|
||||
}
|
||||
|
||||
if (std::string corr_y = corr + "_y"; !config.has(corr_y)) {
|
||||
auto* opt = config.opt<ConfigOptionFloat>(corr_y, true);
|
||||
opt->value = config.opt<ConfigOptionFloats>(corr)->values[0];
|
||||
}
|
||||
|
||||
if (std::string corr_z = corr + "_z"; !config.has(corr_z)) {
|
||||
auto* opt = config.opt<ConfigOptionFloat>(corr_z, true);
|
||||
opt->value = config.opt<ConfigOptionFloats>(corr)->values[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPrintConfig::set_num_extruders(unsigned int num_extruders)
|
||||
{
|
||||
const auto &defaults = FullPrintConfig::defaults();
|
||||
|
|
|
@ -44,7 +44,7 @@ enum class MachineLimitsUsage {
|
|||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htPrusaLink, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier
|
||||
htPrusaLink, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
|
||||
};
|
||||
|
||||
enum AuthorizationType {
|
||||
|
@ -224,6 +224,8 @@ public:
|
|||
{ PrintConfigDef::handle_legacy(opt_key, value); }
|
||||
};
|
||||
|
||||
void handle_legacy_sla(DynamicPrintConfig &config);
|
||||
|
||||
class StaticPrintConfig : public StaticConfig
|
||||
{
|
||||
public:
|
||||
|
@ -913,6 +915,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionFloat, hollowing_closing_distance))
|
||||
)
|
||||
|
||||
enum SLAMaterialSpeed { slamsSlow, slamsFast };
|
||||
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
SLAMaterialConfig,
|
||||
|
||||
|
@ -924,6 +928,10 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionFloat, exposure_time))
|
||||
((ConfigOptionFloat, initial_exposure_time))
|
||||
((ConfigOptionFloats, material_correction))
|
||||
((ConfigOptionFloat, material_correction_x))
|
||||
((ConfigOptionFloat, material_correction_y))
|
||||
((ConfigOptionFloat, material_correction_z))
|
||||
((ConfigOptionEnum<SLAMaterialSpeed>, material_print_speed))
|
||||
)
|
||||
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
|
@ -940,6 +948,9 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionBool, display_mirror_x))
|
||||
((ConfigOptionBool, display_mirror_y))
|
||||
((ConfigOptionFloats, relative_correction))
|
||||
((ConfigOptionFloat, relative_correction_x))
|
||||
((ConfigOptionFloat, relative_correction_y))
|
||||
((ConfigOptionFloat, relative_correction_z))
|
||||
((ConfigOptionFloat, absolute_correction))
|
||||
((ConfigOptionFloat, elefant_foot_compensation))
|
||||
((ConfigOptionFloat, elefant_foot_min_width))
|
||||
|
|
|
@ -379,11 +379,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||
int j = i;
|
||||
bool merged = false;
|
||||
ExPolygons &expolygons = temp_slices[i].expolygons;
|
||||
for (++ j;
|
||||
j < int(temp_slices.size()) &&
|
||||
temp_slices[i].region_id == temp_slices[j].region_id &&
|
||||
(clip_multipart_objects || temp_slices[i].volume_id == temp_slices[j].volume_id);
|
||||
++ j)
|
||||
for (++ j; j < int(temp_slices.size()) && temp_slices[i].region_id == temp_slices[j].region_id; ++ j)
|
||||
if (ExPolygons &expolygons2 = temp_slices[j].expolygons; ! expolygons2.empty()) {
|
||||
if (expolygons.empty()) {
|
||||
expolygons = std::move(expolygons2);
|
||||
|
@ -392,7 +388,10 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||
merged = true;
|
||||
}
|
||||
}
|
||||
if (merged)
|
||||
// Don't unite the regions if ! clip_multipart_objects. In that case it is user's responsibility
|
||||
// to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters
|
||||
// for example.
|
||||
if (merged && clip_multipart_objects)
|
||||
expolygons = closing_ex(expolygons, float(scale_(EPSILON)));
|
||||
slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons);
|
||||
i = j;
|
||||
|
|
|
@ -807,7 +807,13 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
|||
static std::unordered_set<std::string> steps_full = {
|
||||
"initial_layer_height",
|
||||
"material_correction",
|
||||
"material_correction_x",
|
||||
"material_correction_y",
|
||||
"material_correction_z",
|
||||
"relative_correction",
|
||||
"relative_correction_x",
|
||||
"relative_correction_y",
|
||||
"relative_correction_z",
|
||||
"absolute_correction",
|
||||
"elefant_foot_compensation",
|
||||
"elefant_foot_min_width",
|
||||
|
@ -1047,15 +1053,15 @@ Vec3d SLAPrint::relative_correction() const
|
|||
Vec3d corr(1., 1., 1.);
|
||||
|
||||
if(printer_config().relative_correction.values.size() >= 2) {
|
||||
corr.x() = printer_config().relative_correction.values[0];
|
||||
corr.y() = corr.x();
|
||||
corr.z() = printer_config().relative_correction.values[1];
|
||||
corr.x() = printer_config().relative_correction_x.value;
|
||||
corr.y() = printer_config().relative_correction_y.value;
|
||||
corr.z() = printer_config().relative_correction_z.value;
|
||||
}
|
||||
|
||||
if(material_config().material_correction.values.size() >= 2) {
|
||||
corr.x() *= material_config().material_correction.values[0];
|
||||
corr.y() = corr.x();
|
||||
corr.z() *= material_config().material_correction.values[1];
|
||||
corr.x() *= material_config().material_correction_x.value;
|
||||
corr.y() *= material_config().material_correction_y.value;
|
||||
corr.z() *= material_config().material_correction_z.value;
|
||||
}
|
||||
|
||||
return corr;
|
||||
|
|
|
@ -2354,8 +2354,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
|
|||
Polygons &layer_support_area = layer_support_areas[layer_id];
|
||||
Polygons *layer_buildplate_covered = buildplate_covered.empty() ? nullptr : &buildplate_covered[layer_id];
|
||||
// Filtering the propagated support columns to two extrusions, overlapping by maximum 20%.
|
||||
float column_propagation_filtering_radius = scaled<float>(0.8 * 0.5 * (m_support_params.support_material_flow.spacing() + m_support_params.support_material_flow.width()));
|
||||
task_group.run([&grid_params, &overhangs_projection, &overhangs_projection_raw, &layer, &layer_support_area, layer_buildplate_covered, column_propagation_filtering_radius
|
||||
// float column_propagation_filtering_radius = scaled<float>(0.8 * 0.5 * (m_support_params.support_material_flow.spacing() + m_support_params.support_material_flow.width()));
|
||||
task_group.run([&grid_params, &overhangs_projection, &overhangs_projection_raw, &layer, &layer_support_area, layer_buildplate_covered /* , column_propagation_filtering_radius */
|
||||
#ifdef SLIC3R_DEBUG
|
||||
, iRun, layer_id
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
|
|
@ -36,43 +36,6 @@
|
|||
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS 1
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.alpha1 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA1 1
|
||||
|
||||
// Enable implementation of retract acceleration in gcode processor
|
||||
#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable rendering seams (and other options) in preview using models
|
||||
#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects
|
||||
#define ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED (1 && ENABLE_2_4_0_ALPHA1)
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.alpha2 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA2 1
|
||||
|
||||
// Enable rendering seams (and other options) in preview using batched models on systems not supporting OpenGL 3.3
|
||||
#define ENABLE_SEAMS_USING_BATCHED_MODELS (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable fixing the z position of color change, pause print and custom gcode markers in preview
|
||||
#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable replacing a missing file during reload from disk command
|
||||
#define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable fixing the synchronization of seams with the horizontal slider in preview
|
||||
#define ENABLE_FIX_SEAMS_SYNCH (1 && ENABLE_2_4_0_ALPHA2)
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.alpha3 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA3 1
|
||||
|
||||
// Enable fixing loading of gcode files generated with SuperSlicer in GCodeViewer
|
||||
#define ENABLE_FIX_SUPERSLICER_GCODE_IMPORT (1 && ENABLE_2_4_0_ALPHA3)
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.beta1 techs
|
||||
//====================
|
||||
|
@ -80,9 +43,6 @@
|
|||
|
||||
// Enable rendering modifiers and similar objects always as transparent
|
||||
#define ENABLE_MODIFIERS_ALWAYS_TRANSPARENT (1 && ENABLE_2_4_0_BETA1)
|
||||
// Enable the fix for the detection of the out of bed state for sinking objects
|
||||
// and detection of out of bed using the bed perimeter
|
||||
#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_BETA1)
|
||||
|
||||
|
||||
//====================
|
||||
|
@ -94,11 +54,21 @@
|
|||
// an additional button can be used to set the keyboard focus into the slider
|
||||
// to allow the user to type in the desired value
|
||||
#define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2)
|
||||
// Enable fit print volume command for circular printbeds
|
||||
#define ENABLE_ENHANCED_PRINT_VOLUME_FIT (1 && ENABLE_2_4_0_BETA2)
|
||||
|
||||
|
||||
//====================
|
||||
// 2.5.0.alpha1 techs
|
||||
//====================
|
||||
#define ENABLE_2_5_0_ALPHA1 1
|
||||
|
||||
// Enable editing volumes transformation in world coordinates and instances in local coordinates
|
||||
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_4_0_BETA2)
|
||||
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable showing world coordinates of volumes' offset relative to the instance containing them
|
||||
#define ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET (0 && ENABLE_WORLD_COORDINATE)
|
||||
// Enable editing instance coordinates of volumes
|
||||
#define ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES (1 && ENABLE_WORLD_COORDINATE)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "MeshSplitImpl.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Execution/ExecutionTBB.hpp"
|
||||
#include "Execution/ExecutionSeq.hpp"
|
||||
|
@ -435,30 +436,56 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c
|
|||
return bbox;
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, double world_min_z) const
|
||||
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafod, double world_min_z) const
|
||||
{
|
||||
BoundingBoxf3 bbox;
|
||||
const Transform3f ftrafo = trafo.cast<float>();
|
||||
for (const stl_triangle_vertex_indices& tri : its.indices) {
|
||||
const Vec3f pts[3] = { ftrafo * its.vertices[tri(0)], ftrafo * its.vertices[tri(1)], ftrafo * its.vertices[tri(2)] };
|
||||
int iprev = 2;
|
||||
for (int iedge = 0; iedge < 3; ++iedge) {
|
||||
const Vec3f& p1 = pts[iprev];
|
||||
const Vec3f& p2 = pts[iedge];
|
||||
if ((p1.z() < world_min_z && p2.z() > world_min_z) || (p2.z() < world_min_z && p1.z() > world_min_z)) {
|
||||
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
|
||||
bbox.merge(Vec3f(p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z).cast<double>());
|
||||
}
|
||||
if (p2.z() >= world_min_z)
|
||||
bbox.merge(p2.cast<double>());
|
||||
iprev = iedge;
|
||||
// 1) Allocate transformed vertices with their position with respect to print bed surface.
|
||||
std::vector<char> sides;
|
||||
size_t num_above = 0;
|
||||
Eigen::AlignedBox<float, 3> bbox;
|
||||
Transform3f trafo = trafod.cast<float>();
|
||||
sides.reserve(its.vertices.size());
|
||||
for (const stl_vertex &v : this->its.vertices) {
|
||||
const stl_vertex pt = trafo * v;
|
||||
const int sign = pt.z() > world_min_z ? 1 : pt.z() < world_min_z ? -1 : 0;
|
||||
sides.emplace_back(sign);
|
||||
if (sign >= 0) {
|
||||
// Vertex above or on print bed surface. Test whether it is inside the build volume.
|
||||
++ num_above;
|
||||
bbox.extend(pt);
|
||||
}
|
||||
}
|
||||
return bbox;
|
||||
|
||||
// 2) Calculate intersections of triangle edges with the build surface.
|
||||
if (num_above < its.vertices.size()) {
|
||||
// Not completely above the build surface and status may still change by testing edges intersecting the build platform.
|
||||
for (const stl_triangle_vertex_indices &tri : its.indices) {
|
||||
const int s[3] = { sides[tri(0)], sides[tri(1)], sides[tri(2)] };
|
||||
if (std::min(s[0], std::min(s[1], s[2])) < 0 && std::max(s[0], std::max(s[1], s[2])) > 0) {
|
||||
// Some edge of this triangle intersects the build platform. Calculate the intersection.
|
||||
int iprev = 2;
|
||||
for (int iedge = 0; iedge < 3; ++ iedge) {
|
||||
if (s[iprev] * s[iedge] == -1) {
|
||||
// edge intersects the build surface. Calculate intersection point.
|
||||
const stl_vertex p1 = trafo * its.vertices[tri(iprev)];
|
||||
const stl_vertex p2 = trafo * its.vertices[tri(iedge)];
|
||||
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
|
||||
bbox.extend(Vec3f(p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z));
|
||||
}
|
||||
iprev = iedge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoundingBoxf3 out;
|
||||
if (! bbox.isEmpty()) {
|
||||
out.min = bbox.min().cast<double>();
|
||||
out.max = bbox.max().cast<double>();
|
||||
out.defined = true;
|
||||
};
|
||||
return out;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
{
|
||||
|
@ -1063,7 +1090,7 @@ indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts)
|
|||
centroid += pt;
|
||||
centroid /= float(pts.size());
|
||||
#endif // NDEBUG
|
||||
for (const orgQhull::QhullFacet facet : qhull.facetList()) {
|
||||
for (const orgQhull::QhullFacet &facet : qhull.facetList()) {
|
||||
// Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID.
|
||||
Vec3i indices;
|
||||
int cnt = 0;
|
||||
|
|
|
@ -125,10 +125,8 @@ public:
|
|||
BoundingBoxf3 bounding_box() const;
|
||||
// Returns the bbox of this TriangleMesh transformed by the given transformation
|
||||
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// Variant returning the bbox of the part of this TriangleMesh above the given world_min_z
|
||||
BoundingBoxf3 transformed_bounding_box(const Transform3d& trafo, double world_min_z) const;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// Return the size of the mesh in coordinates.
|
||||
Vec3d size() const { return m_stats.size.cast<double>(); }
|
||||
/// Return the center of the related bounding box.
|
||||
|
|
|
@ -560,9 +560,9 @@ void slice_facet_with_slabs(
|
|||
// Save the open edge for sure.
|
||||
type = FacetSliceType::Slicing;
|
||||
} else {
|
||||
#ifndef NDEBUG
|
||||
const stl_triangle_vertex_indices &neighbor = mesh_triangles[neighbor_idx];
|
||||
float z = *it;
|
||||
#ifndef NDEBUG
|
||||
int num_on_plane = (mesh_vertices[neighbor(0)].z() == z) + (mesh_vertices[neighbor(1)].z() == z) + (mesh_vertices[neighbor(2)].z() == z);
|
||||
assert(num_on_plane == 2 || num_on_plane == 3);
|
||||
#endif // NDEBUG
|
||||
|
@ -1866,9 +1866,13 @@ std::vector<ExPolygons> slice_mesh_ex(
|
|||
//FIXME simplify
|
||||
if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour)
|
||||
keep_largest_contour_only(expolygons);
|
||||
if (resolution != 0.)
|
||||
for (ExPolygon &ex : expolygons)
|
||||
ex.simplify(resolution);
|
||||
if (resolution != 0.) {
|
||||
ExPolygons simplified;
|
||||
simplified.reserve(expolygons.size());
|
||||
for (const ExPolygon &ex : expolygons)
|
||||
append(simplified, ex.simplify(resolution));
|
||||
expolygons = std::move(simplified);
|
||||
}
|
||||
}
|
||||
});
|
||||
// BOOST_LOG_TRIVIAL(debug) << "slice_mesh make_expolygons in parallel - end";
|
||||
|
|
|
@ -165,11 +165,6 @@ typedef struct NSVGimage
|
|||
// Parses SVG file from a file, returns SVG image as paths.
|
||||
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
|
||||
|
||||
// Parses SVG file from a file, returns SVG image as paths.
|
||||
// And makes replases befor parsing
|
||||
// replace_map containes old_value->new_value
|
||||
NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replace_map);
|
||||
|
||||
// Parses SVG file from a null terminated string, returns SVG image as paths.
|
||||
// Important note: changes the string.
|
||||
NSVGimage* nsvgParse(char* input, const char* units, float dpi);
|
||||
|
@ -2908,12 +2903,6 @@ NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
|||
|
||||
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
||||
{
|
||||
return nsvgParseFromFileWithReplace(filename, units, dpi, { {} });
|
||||
}
|
||||
|
||||
NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces)
|
||||
{
|
||||
std::string str;
|
||||
FILE* fp = NULL;
|
||||
size_t size;
|
||||
char* data = NULL;
|
||||
|
@ -2930,14 +2919,7 @@ NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units,
|
|||
data[size] = '\0'; // Must be null terminated.
|
||||
fclose(fp);
|
||||
|
||||
if(replaces.empty())
|
||||
image = nsvgParse(data, units, dpi);
|
||||
else {
|
||||
str.assign(data);
|
||||
for (auto val : replaces)
|
||||
boost::replace_all(str, val.first, val.second);
|
||||
image = nsvgParse(str.data(), units, dpi);
|
||||
}
|
||||
image = nsvgParse(data, units, dpi);
|
||||
free(data);
|
||||
return image;
|
||||
|
||||
|
|
|
@ -191,6 +191,7 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Mouse3DController.hpp
|
||||
GUI/DoubleSlider.cpp
|
||||
GUI/DoubleSlider.hpp
|
||||
GUI/DoubleSlider_Utils.hpp
|
||||
GUI/Notebook.cpp
|
||||
GUI/Notebook.hpp
|
||||
GUI/ObjectDataViewModel.cpp
|
||||
|
@ -238,6 +239,10 @@ set(SLIC3R_GUI_SOURCES
|
|||
Utils/UndoRedo.hpp
|
||||
Utils/HexFile.cpp
|
||||
Utils/HexFile.hpp
|
||||
Utils/TCPConsole.cpp
|
||||
Utils/TCPConsole.hpp
|
||||
Utils/MKS.cpp
|
||||
Utils/MKS.hpp
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
|
@ -251,7 +256,6 @@ if (APPLE)
|
|||
GUI/InstanceCheckMac.h
|
||||
)
|
||||
FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration)
|
||||
|
||||
endif ()
|
||||
|
||||
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
|
||||
|
@ -260,8 +264,12 @@ encoding_check(libslic3r_gui)
|
|||
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES})
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
if (MSVC)
|
||||
target_link_libraries(libslic3r_gui Setupapi.lib)
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES})
|
||||
elseif (APPLE)
|
||||
target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (SLIC3R_STATIC)
|
||||
|
@ -270,10 +278,6 @@ if (SLIC3R_STATIC)
|
|||
target_compile_definitions(libslic3r_gui PUBLIC -DwxDEBUG_LEVEL=0)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (SLIC3R_STATIC AND NOT SLIC3R_STATIC_EXCLUDE_CURL AND UNIX AND NOT APPLE)
|
||||
target_compile_definitions(libslic3r_gui PRIVATE OPENSSL_CERT_OVERRIDE)
|
||||
endif ()
|
||||
|
|
|
@ -137,7 +137,7 @@ void Bed3D::Axes::render() const
|
|||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
}
|
||||
|
||||
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
|
||||
bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
|
||||
{
|
||||
auto check_texture = [](const std::string& texture) {
|
||||
boost::system::error_code ec; // so the exists call does not throw (e.g. after a permission problem)
|
||||
|
@ -149,17 +149,13 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
|
|||
return !model.empty() && boost::algorithm::iends_with(model, ".stl") && boost::filesystem::exists(model, ec);
|
||||
};
|
||||
|
||||
EType type;
|
||||
Type type;
|
||||
std::string model;
|
||||
std::string texture;
|
||||
if (force_as_custom)
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
type = EType::Custom;
|
||||
#else
|
||||
type = Custom;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
type = Type::Custom;
|
||||
else {
|
||||
auto [new_type, system_model, system_texture] = detect_type(shape);
|
||||
auto [new_type, system_model, system_texture] = detect_type(bed_shape);
|
||||
type = new_type;
|
||||
model = system_model;
|
||||
texture = system_texture;
|
||||
|
@ -177,29 +173,18 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
|
|||
model_filename.clear();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
EShapeType shape_type = detect_shape_type(shape);
|
||||
if (m_shape == shape && m_type == type && m_shape_type == shape_type && m_texture_filename == texture_filename && m_model_filename == model_filename)
|
||||
#else
|
||||
if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
if (m_build_volume.bed_shape() == bed_shape && m_build_volume.max_print_height() == max_print_height && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
|
||||
// No change, no need to update the UI.
|
||||
return false;
|
||||
|
||||
m_shape = shape;
|
||||
m_type = type;
|
||||
m_build_volume = BuildVolume { bed_shape, max_print_height };
|
||||
m_texture_filename = texture_filename;
|
||||
m_model_filename = model_filename;
|
||||
m_type = type;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_shape_type = shape_type;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_extended_bounding_box = this->calc_extended_bounding_box();
|
||||
|
||||
calc_bounding_boxes();
|
||||
|
||||
ExPolygon poly;
|
||||
for (const Vec2d& p : m_shape) {
|
||||
poly.contour.append({ scale_(p(0)), scale_(p(1)) });
|
||||
}
|
||||
ExPolygon poly{ Polygon::new_scale(bed_shape) };
|
||||
|
||||
calc_triangles(poly);
|
||||
|
||||
|
@ -208,13 +193,13 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
|
|||
|
||||
m_polygon = offset(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0];
|
||||
|
||||
reset();
|
||||
this->release_VBOs();
|
||||
m_texture.reset();
|
||||
m_model.reset();
|
||||
|
||||
// Set the origin and size for rendering the coordinate system axes.
|
||||
m_axes.set_origin({ 0.0, 0.0, static_cast<double>(GROUND_Z) });
|
||||
m_axes.set_stem_length(0.1f * static_cast<float>(m_bounding_box.max_size()));
|
||||
m_axes.set_stem_length(0.1f * static_cast<float>(m_build_volume.bounding_volume().max_size()));
|
||||
|
||||
// Let the calee to update the UI.
|
||||
return true;
|
||||
|
@ -240,85 +225,6 @@ void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_fact
|
|||
render_internal(canvas, bottom, scale_factor, false, false, true);
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool Bed3D::is_rectangle(const Pointfs& shape, Vec2d* min, Vec2d* max)
|
||||
{
|
||||
const Lines lines = Polygon::new_scale(shape).lines();
|
||||
bool ret = lines.size() == 4 && lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3]) && lines[0].perpendicular_to(lines[1]);
|
||||
if (ret) {
|
||||
if (min != nullptr) {
|
||||
*min = shape.front();
|
||||
for (const Vec2d& pt : shape) {
|
||||
min->x() = std::min(min->x(), pt.x());
|
||||
min->y() = std::min(min->y(), pt.y());
|
||||
}
|
||||
}
|
||||
if (max != nullptr) {
|
||||
*max = shape.front();
|
||||
for (const Vec2d& pt : shape) {
|
||||
max->x() = std::max(max->x(), pt.x());
|
||||
max->y() = std::max(max->y(), pt.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Bed3D::is_circle(const Pointfs& shape, Vec2d* center, double* radius)
|
||||
{
|
||||
if (shape.size() < 3)
|
||||
return false;
|
||||
|
||||
// Analyze the array of points.
|
||||
// Do they reside on a circle ?
|
||||
const Vec2d box_center = Geometry::circle_center_taubin_newton(shape);
|
||||
|
||||
std::vector<double> vertex_distances;
|
||||
double avg_dist = 0.0;
|
||||
for (const Vec2d& pt : shape) {
|
||||
double distance = (pt - box_center).norm();
|
||||
vertex_distances.push_back(distance);
|
||||
avg_dist += distance;
|
||||
}
|
||||
|
||||
avg_dist /= vertex_distances.size();
|
||||
|
||||
double tolerance = avg_dist * 0.01;
|
||||
|
||||
bool defined_value = true;
|
||||
for (double el : vertex_distances) {
|
||||
if (fabs(el - avg_dist) > tolerance)
|
||||
defined_value = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (center != nullptr)
|
||||
*center = box_center;
|
||||
|
||||
if (radius != nullptr)
|
||||
*radius = avg_dist;
|
||||
|
||||
return defined_value;
|
||||
}
|
||||
|
||||
bool Bed3D::is_convex(const Pointfs& shape)
|
||||
{
|
||||
return Polygon::new_scale(shape).convex_points().size() == shape.size();
|
||||
}
|
||||
|
||||
Bed3D::EShapeType Bed3D::detect_shape_type(const Pointfs& shape)
|
||||
{
|
||||
if (shape.size() < 3)
|
||||
return EShapeType::Invalid;
|
||||
else if (is_rectangle(shape))
|
||||
return EShapeType::Rectangle;
|
||||
else if (is_circle(shape))
|
||||
return EShapeType::Circle;
|
||||
else
|
||||
return EShapeType::Custom;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture, bool picking)
|
||||
{
|
||||
|
@ -334,41 +240,35 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
|||
|
||||
switch (m_type)
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
case EType::System: { render_system(canvas, bottom, show_texture); break; }
|
||||
case Type::System: { render_system(canvas, bottom, show_texture); break; }
|
||||
default:
|
||||
case EType::Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
|
||||
#else
|
||||
case System: { render_system(canvas, bottom, show_texture); break; }
|
||||
default:
|
||||
case Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
case Type::Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
|
||||
}
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
}
|
||||
|
||||
void Bed3D::calc_bounding_boxes() const
|
||||
// Calculate an extended bounding box from axes and current model for visualization purposes.
|
||||
BoundingBoxf3 Bed3D::calc_extended_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3* bounding_box = const_cast<BoundingBoxf3*>(&m_bounding_box);
|
||||
*bounding_box = BoundingBoxf3();
|
||||
for (const Vec2d& p : m_shape) {
|
||||
bounding_box->merge({ p.x(), p.y(), 0.0 });
|
||||
}
|
||||
|
||||
BoundingBoxf3* extended_bounding_box = const_cast<BoundingBoxf3*>(&m_extended_bounding_box);
|
||||
*extended_bounding_box = m_bounding_box;
|
||||
|
||||
BoundingBoxf3 out { m_build_volume.bounding_volume() };
|
||||
const Vec3d size = out.size();
|
||||
// ensures that the bounding box is set as defined or the following calls to merge() will not work as intented
|
||||
if (size.x() > 0.0 && size.y() > 0.0 && !out.defined)
|
||||
out.defined = true;
|
||||
// Reset the build volume Z, we don't want to zoom to the top of the build volume if it is empty.
|
||||
out.min.z() = 0.0;
|
||||
out.max.z() = 0.0;
|
||||
// extend to contain axes
|
||||
extended_bounding_box->merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
|
||||
extended_bounding_box->merge(extended_bounding_box->min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, extended_bounding_box->max(2)));
|
||||
|
||||
out.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
|
||||
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max.z()));
|
||||
// extend to contain model, if any
|
||||
BoundingBoxf3 model_bb = m_model.get_bounding_box();
|
||||
if (model_bb.defined) {
|
||||
model_bb.translate(m_model_offset);
|
||||
extended_bounding_box->merge(model_bb);
|
||||
out.merge(model_bb);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void Bed3D::calc_triangles(const ExPolygon& poly)
|
||||
|
@ -404,8 +304,9 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
|
|||
BOOST_LOG_TRIVIAL(error) << "Unable to create bed grid lines\n";
|
||||
}
|
||||
|
||||
|
||||
std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Pointfs& shape) const
|
||||
// Try to match the print bed shape with the shape of an active profile. If such a match exists,
|
||||
// return the print bed model.
|
||||
std::tuple<Bed3D::Type, std::string, std::string> Bed3D::detect_type(const Pointfs& shape)
|
||||
{
|
||||
auto bundle = wxGetApp().preset_bundle;
|
||||
if (bundle != nullptr) {
|
||||
|
@ -416,11 +317,7 @@ std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Poin
|
|||
std::string model_filename = PresetUtils::system_printer_bed_model(*curr);
|
||||
std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr);
|
||||
if (!model_filename.empty() && !texture_filename.empty())
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return { EType::System, model_filename, texture_filename };
|
||||
#else
|
||||
return { System, model_filename, texture_filename };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return { Type::System, model_filename, texture_filename };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -428,16 +325,12 @@ std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Poin
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return { EType::Custom, "", "" };
|
||||
#else
|
||||
return { Custom, "", "" };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return { Type::Custom, {}, {} };
|
||||
}
|
||||
|
||||
void Bed3D::render_axes() const
|
||||
{
|
||||
if (!m_shape.empty())
|
||||
if (m_build_volume.valid())
|
||||
m_axes.render();
|
||||
}
|
||||
|
||||
|
@ -596,12 +489,10 @@ void Bed3D::render_model() const
|
|||
model->set_color(-1, DEFAULT_MODEL_COLOR);
|
||||
|
||||
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
|
||||
Vec3d shift = m_bounding_box.center();
|
||||
shift(2) = -0.03;
|
||||
*const_cast<Vec3d*>(&m_model_offset) = shift;
|
||||
*const_cast<Vec3d*>(&m_model_offset) = to_3d(m_build_volume.bounding_volume2d().center(), -0.03);
|
||||
|
||||
// update extended bounding box
|
||||
calc_bounding_boxes();
|
||||
const_cast<BoundingBoxf3&>(m_extended_bounding_box) = this->calc_extended_bounding_box();
|
||||
}
|
||||
|
||||
if (!model->get_filename().empty()) {
|
||||
|
@ -673,7 +564,7 @@ void Bed3D::render_default(bool bottom, bool picking) const
|
|||
}
|
||||
}
|
||||
|
||||
void Bed3D::reset()
|
||||
void Bed3D::release_VBOs()
|
||||
{
|
||||
if (m_vbo_id > 0) {
|
||||
glsafe(::glDeleteBuffers(1, &m_vbo_id));
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "3DScene.hpp"
|
||||
#include "GLModel.hpp"
|
||||
|
||||
#include <libslic3r/BuildVolume.hpp>
|
||||
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
|
||||
|
@ -62,41 +64,22 @@ class Bed3D
|
|||
};
|
||||
|
||||
public:
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
enum class EType : unsigned char
|
||||
enum class Type : unsigned char
|
||||
{
|
||||
// The print bed model and texture are available from some printer preset.
|
||||
System,
|
||||
// The print bed model is unknown, thus it is rendered procedurally.
|
||||
Custom
|
||||
};
|
||||
|
||||
enum class EShapeType : unsigned char
|
||||
{
|
||||
Rectangle,
|
||||
Circle,
|
||||
Custom,
|
||||
Invalid
|
||||
};
|
||||
#else
|
||||
enum EType : unsigned char
|
||||
{
|
||||
System,
|
||||
Custom,
|
||||
Num_Types
|
||||
};
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
private:
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
EType m_type{ EType::Custom };
|
||||
EShapeType m_shape_type{ EShapeType::Invalid };
|
||||
#else
|
||||
EType m_type{ Custom };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Pointfs m_shape;
|
||||
BuildVolume m_build_volume;
|
||||
Type m_type{ Type::Custom };
|
||||
std::string m_texture_filename;
|
||||
std::string m_model_filename;
|
||||
BoundingBoxf3 m_bounding_box;
|
||||
// Print volume bounding box exteded with axes and model.
|
||||
BoundingBoxf3 m_extended_bounding_box;
|
||||
// Slightly expanded print bed polygon, for collision detection.
|
||||
Polygon m_polygon;
|
||||
GeometryBuffer m_triangles;
|
||||
GeometryBuffer m_gridlines;
|
||||
|
@ -112,42 +95,39 @@ private:
|
|||
|
||||
public:
|
||||
Bed3D() = default;
|
||||
~Bed3D() { reset(); }
|
||||
~Bed3D() { release_VBOs(); }
|
||||
|
||||
EType get_type() const { return m_type; }
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
EShapeType get_shape_type() const { return m_shape_type; }
|
||||
bool is_custom() const { return m_type == EType::Custom; }
|
||||
#else
|
||||
bool is_custom() const { return m_type == Custom; }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
const Pointfs& get_shape() const { return m_shape; }
|
||||
// Update print bed model from configuration.
|
||||
// Return true if the bed shape changed, so the calee will update the UI.
|
||||
bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
|
||||
//FIXME if the build volume max print height is updated, this function still returns zero
|
||||
// as this class does not use it, thus there is no need to update the UI.
|
||||
bool set_shape(const Pointfs& bed_shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false);
|
||||
|
||||
const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; }
|
||||
// Build volume geometry for various collision detection tasks.
|
||||
const BuildVolume& build_volume() const { return m_build_volume; }
|
||||
|
||||
// Was the model provided, or was it generated procedurally?
|
||||
Type get_type() const { return m_type; }
|
||||
// Was the model generated procedurally?
|
||||
bool is_custom() const { return m_type == Type::Custom; }
|
||||
|
||||
// Bounding box around the print bed, axes and model, for rendering.
|
||||
const BoundingBoxf3& extended_bounding_box() const { return m_extended_bounding_box; }
|
||||
|
||||
// Check against an expanded 2d bounding box.
|
||||
//FIXME shall one check against the real build volume?
|
||||
bool contains(const Point& point) const;
|
||||
Point point_projection(const Point& point) const;
|
||||
|
||||
void render(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture);
|
||||
|
||||
void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture);
|
||||
void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
static bool is_rectangle(const Pointfs& shape, Vec2d* min = nullptr, Vec2d* max = nullptr);
|
||||
static bool is_circle(const Pointfs& shape, Vec2d* center = nullptr, double* radius = nullptr);
|
||||
static bool is_convex(const Pointfs& shape);
|
||||
static EShapeType detect_shape_type(const Pointfs& shape);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
private:
|
||||
void calc_bounding_boxes() const;
|
||||
// Calculate an extended bounding box from axes and current model for visualization purposes.
|
||||
BoundingBoxf3 calc_extended_bounding_box() const;
|
||||
void calc_triangles(const ExPolygon& poly);
|
||||
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
|
||||
std::tuple<EType, std::string, std::string> detect_type(const Pointfs& shape) const;
|
||||
static std::tuple<Type, std::string, std::string> detect_type(const Pointfs& shape);
|
||||
void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
bool show_axes, bool show_texture, bool picking);
|
||||
void render_axes() const;
|
||||
|
@ -156,7 +136,7 @@ private:
|
|||
void render_model() const;
|
||||
void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bool picking) const;
|
||||
void render_default(bool bottom, bool picking) const;
|
||||
void reset();
|
||||
void release_VBOs();
|
||||
};
|
||||
|
||||
} // GUI
|
||||
|
|
|
@ -11,10 +11,8 @@
|
|||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#include "3DBed.hpp"
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
|
@ -27,6 +25,7 @@
|
|||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -264,11 +263,9 @@ void GLIndexedVertexArray::render(
|
|||
const std::pair<size_t, size_t>& tverts_range,
|
||||
const std::pair<size_t, size_t>& qverts_range) const
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// this method has been called before calling finalize() ?
|
||||
if (this->vertices_and_normals_interleaved_VBO_id == 0 && !this->vertices_and_normals_interleaved.empty())
|
||||
return;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
assert(this->vertices_and_normals_interleaved_VBO_id != 0);
|
||||
assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0);
|
||||
|
@ -526,7 +523,6 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &
|
|||
bounding_box().transformed(trafo);
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
BoundingBoxf3 GLVolume::transformed_non_sinking_bounding_box(const Transform3d& trafo) const
|
||||
{
|
||||
return GUI::wxGetApp().plater()->model().objects[object_idx()]->volumes[volume_idx()]->mesh().transformed_bounding_box(trafo, 0.0);
|
||||
|
@ -541,7 +537,6 @@ const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const
|
|||
}
|
||||
return *m_transformed_non_sinking_bounding_box;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void GLVolume::set_range(double min_z, double max_z)
|
||||
{
|
||||
|
@ -617,22 +612,6 @@ void GLVolume::render_sinking_contours()
|
|||
m_sinking_contours.render();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLVolume::calc_convex_hull_3d()
|
||||
{
|
||||
const std::vector<float> &src = this->indexed_vertex_array.vertices_and_normals_interleaved;
|
||||
std::vector<Vec3f> pts;
|
||||
assert(src.size() % 6 == 0);
|
||||
pts.reserve(src.size() / 6);
|
||||
for (auto it = src.begin(); it != src.end(); ) {
|
||||
it += 3;
|
||||
pts.push_back({ *it, *(it + 1), *(it + 2) });
|
||||
it += 3;
|
||||
}
|
||||
this->set_convex_hull(TriangleMesh(its_convex_hull(pts)));
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
std::vector<int> GLVolumeCollection::load_object(
|
||||
const ModelObject *model_object,
|
||||
int obj_idx,
|
||||
|
@ -791,9 +770,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
volumes.emplace_back(new GLVolume(color));
|
||||
GLVolume& v = *volumes.back();
|
||||
v.indexed_vertex_array.load_mesh(mesh);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
v.set_convex_hull(mesh.convex_hull_3d());
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
v.indexed_vertex_array.finalize_geometry(opengl_initialized);
|
||||
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
|
||||
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
|
||||
|
@ -900,17 +877,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
|||
shader->set_uniform("uniform_color", volume.first->render_color);
|
||||
shader->set_uniform("z_range", m_z_range, 2);
|
||||
shader->set_uniform("clipping_plane", m_clipping_plane, 4);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type));
|
||||
shader->set_uniform("print_volume.xy_data", m_print_volume.data);
|
||||
shader->set_uniform("print_volume.z_data", m_print_volume.zs);
|
||||
shader->set_uniform("volume_world_matrix", volume.first->world_matrix());
|
||||
#else
|
||||
shader->set_uniform("print_box.min", m_print_box_min, 3);
|
||||
shader->set_uniform("print_box.max", m_print_box_max, 3);
|
||||
shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled);
|
||||
shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
|
||||
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
|
||||
shader->set_uniform("slope.normal_z", m_slope.normal_z);
|
||||
|
@ -959,136 +929,58 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
|||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state, bool as_toolpaths) const
|
||||
#else
|
||||
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const
|
||||
{
|
||||
if (config == nullptr)
|
||||
return false;
|
||||
|
||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
|
||||
if (opt == nullptr)
|
||||
return false;
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast<float>(scale_(BedEpsilon))).front();
|
||||
const float bed_height = config->opt_float("max_print_height");
|
||||
const BoundingBox bed_box_2D = get_extents(bed_poly);
|
||||
BoundingBoxf3 print_volume({ unscale<double>(bed_box_2D.min.x()), unscale<double>(bed_box_2D.min.y()), -1e10 },
|
||||
{ unscale<double>(bed_box_2D.max.x()), unscale<double>(bed_box_2D.max.y()), bed_height });
|
||||
|
||||
auto check_against_rectangular_bed = [&print_volume](GLVolume& volume, ModelInstanceEPrintVolumeState& state) {
|
||||
const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
|
||||
volume.is_outside = !print_volume.contains(*bb);
|
||||
if (volume.printable) {
|
||||
if (state == ModelInstancePVS_Inside && volume.is_outside)
|
||||
state = ModelInstancePVS_Fully_Outside;
|
||||
if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && print_volume.intersects(*bb))
|
||||
state = ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
};
|
||||
|
||||
auto check_against_circular_bed = [bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state, const Vec2d& center, double radius) {
|
||||
const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
|
||||
const double sq_radius = sqr(radius);
|
||||
size_t outside_count = 0;
|
||||
size_t valid_count = 0;
|
||||
for (const Vec3f& v : mesh->its.vertices) {
|
||||
const Vec3f world_v = volume.world_matrix().cast<float>() * v;
|
||||
if (0.0f <= world_v.z()) {
|
||||
++valid_count;
|
||||
if (sq_radius < sqr(world_v.x() - center.x()) + sqr(world_v.y() - center.y()) || bed_height < world_v.z())
|
||||
++outside_count;
|
||||
}
|
||||
}
|
||||
volume.is_outside = outside_count > 0;
|
||||
if (volume.printable) {
|
||||
if (state == ModelInstancePVS_Inside && volume.is_outside)
|
||||
state = ModelInstancePVS_Fully_Outside;
|
||||
if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && outside_count < valid_count)
|
||||
state = ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
};
|
||||
|
||||
auto check_against_convex_bed = [&bed_poly, bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state) {
|
||||
const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull();
|
||||
const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast<float>(), 0.0f);
|
||||
const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box();
|
||||
// Using rotating callipers to check for collision of two convex polygons.
|
||||
ModelInstanceEPrintVolumeState volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb->min.z(), bb->max.z());
|
||||
bool contained = (volume_state == ModelInstancePVS_Inside);
|
||||
bool intersects = (volume_state == ModelInstancePVS_Partly_Outside);
|
||||
|
||||
volume.is_outside = !contained;
|
||||
if (volume.printable) {
|
||||
if (state == ModelInstancePVS_Inside && volume.is_outside)
|
||||
state = ModelInstancePVS_Fully_Outside;
|
||||
|
||||
if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && intersects)
|
||||
state = ModelInstancePVS_Partly_Outside;
|
||||
}
|
||||
};
|
||||
#else
|
||||
const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
|
||||
BoundingBoxf3 print_volume({ unscale<double>(bed_box_2D.min.x()), unscale<double>(bed_box_2D.min.y()), 0.0 },
|
||||
{ unscale<double>(bed_box_2D.max.x()), unscale<double>(bed_box_2D.max.y()), config->opt_float("max_print_height") });
|
||||
// Allow the objects to protrude below the print bed
|
||||
print_volume.min.z() = -1e10;
|
||||
print_volume.min.x() -= BedEpsilon;
|
||||
print_volume.min.y() -= BedEpsilon;
|
||||
print_volume.max.x() += BedEpsilon;
|
||||
print_volume.max.y() += BedEpsilon;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
const Model& model = GUI::wxGetApp().plater()->model();
|
||||
auto volume_below = [](GLVolume& volume) -> bool
|
||||
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); };
|
||||
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
|
||||
auto volume_sinking = [](GLVolume& volume) -> bool
|
||||
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); };
|
||||
// Cached bounding box of a volume above the print bed.
|
||||
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
|
||||
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
|
||||
// Cached 3D convex hull of a volume above the print bed.
|
||||
auto volume_convex_mesh = [volume_sinking, &model](GLVolume& volume) -> const TriangleMesh&
|
||||
{ return volume_sinking(volume) ? model.objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
|
||||
|
||||
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
|
||||
bool contained_min_one = false;
|
||||
|
||||
enum class BedShape { Rectangle, Circle, Convex, NonConvex };
|
||||
Vec2d center;
|
||||
double radius;
|
||||
BedShape bed_shape =
|
||||
GUI::Bed3D::is_rectangle(opt->values) ? BedShape::Rectangle :
|
||||
GUI::Bed3D::is_circle(opt->values, ¢er, &radius) ? BedShape::Circle :
|
||||
GUI::Bed3D::is_convex(opt->values) ? BedShape::Convex : BedShape::NonConvex;
|
||||
|
||||
for (GLVolume* volume : this->volumes) {
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (as_toolpaths && !volume->is_extrusion_path)
|
||||
continue;
|
||||
else if (!as_toolpaths && (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0))))
|
||||
continue;
|
||||
|
||||
switch (bed_shape) {
|
||||
case BedShape::Rectangle: check_against_rectangular_bed(*volume, overall_state); break;
|
||||
case BedShape::Circle: check_against_circular_bed(*volume, overall_state, center, radius); break;
|
||||
case BedShape::Convex: check_against_convex_bed(*volume, overall_state); break;
|
||||
default: break;
|
||||
for (GLVolume* volume : this->volumes)
|
||||
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
|
||||
BuildVolume::ObjectState state;
|
||||
if (volume_below(*volume))
|
||||
state = BuildVolume::ObjectState::Below;
|
||||
else {
|
||||
switch (build_volume.type()) {
|
||||
case BuildVolume::Type::Rectangle:
|
||||
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
|
||||
state = build_volume.volume_state_bbox(volume_bbox(*volume));
|
||||
break;
|
||||
case BuildVolume::Type::Circle:
|
||||
case BuildVolume::Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case BuildVolume::Type::Custom:
|
||||
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
|
||||
break;
|
||||
default:
|
||||
// Ignore, don't produce any collision.
|
||||
state = BuildVolume::ObjectState::Inside;
|
||||
break;
|
||||
}
|
||||
assert(state != BuildVolume::ObjectState::Below);
|
||||
}
|
||||
volume->is_outside = state != BuildVolume::ObjectState::Inside;
|
||||
if (volume->printable) {
|
||||
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
|
||||
overall_state = ModelInstancePVS_Fully_Outside;
|
||||
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
|
||||
overall_state = ModelInstancePVS_Partly_Outside;
|
||||
contained_min_one |= !volume->is_outside;
|
||||
}
|
||||
}
|
||||
|
||||
contained_min_one |= !volume->is_outside;
|
||||
#else
|
||||
if (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0)))
|
||||
continue;
|
||||
|
||||
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
|
||||
bool contained = print_volume.contains(bb);
|
||||
|
||||
volume->is_outside = !contained;
|
||||
if (!volume->printable)
|
||||
continue;
|
||||
|
||||
contained_min_one |= contained;
|
||||
|
||||
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
|
||||
overall_state = ModelInstancePVS_Fully_Outside;
|
||||
|
||||
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb))
|
||||
overall_state = ModelInstancePVS_Partly_Outside;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
if (out_state != nullptr)
|
||||
*out_state = overall_state;
|
||||
|
||||
|
@ -1136,7 +1028,9 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
|||
|
||||
if (static_cast<PrinterTechnology>(config->opt_int("printer_technology")) == ptSLA)
|
||||
{
|
||||
const std::string& txt_color = config->opt_string("material_colour");
|
||||
const std::string& txt_color = config->opt_string("material_colour").empty() ?
|
||||
print_config_def.get("material_colour")->get_default_value<ConfigOptionString>()->value :
|
||||
config->opt_string("material_colour");
|
||||
if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) {
|
||||
colors.resize(1);
|
||||
colors[0].set(txt_color, rgb);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
namespace Slic3r {
|
||||
class SLAPrintObject;
|
||||
enum SLAPrintObjectStep : unsigned int;
|
||||
class BuildVolume;
|
||||
class DynamicPrintConfig;
|
||||
class ExtrusionPath;
|
||||
class ExtrusionMultiPath;
|
||||
|
@ -281,10 +282,8 @@ private:
|
|||
std::shared_ptr<const TriangleMesh> m_convex_hull;
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
std::optional<BoundingBoxf3> m_transformed_convex_hull_bounding_box;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// Bounding box of the non sinking part of this volume, in unscaled coordinates.
|
||||
std::optional<BoundingBoxf3> m_transformed_non_sinking_bounding_box;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
class SinkingContours
|
||||
{
|
||||
|
@ -475,12 +474,10 @@ public:
|
|||
BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const;
|
||||
// caching variant
|
||||
const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// non-caching variant
|
||||
BoundingBoxf3 transformed_non_sinking_bounding_box(const Transform3d& trafo) const;
|
||||
// caching variant
|
||||
const BoundingBoxf3& transformed_non_sinking_bounding_box() const;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// convex hull
|
||||
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
|
||||
|
||||
|
@ -493,15 +490,11 @@ public:
|
|||
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
|
||||
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void set_bounding_boxes_as_dirty() {
|
||||
m_transformed_bounding_box.reset();
|
||||
m_transformed_convex_hull_bounding_box.reset();
|
||||
m_transformed_non_sinking_bounding_box.reset();
|
||||
}
|
||||
#else
|
||||
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); m_transformed_convex_hull_bounding_box.reset(); }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
bool is_sla_support() const;
|
||||
bool is_sla_pad() const;
|
||||
|
@ -518,12 +511,6 @@ public:
|
|||
// Return an estimate of the memory held by GPU vertex buffers.
|
||||
size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); }
|
||||
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// calculates the 3D convex hull from indexed_vertex_array.vertices_and_normals_interleaved
|
||||
// must be called before calling indexed_vertex_array.finalize_geometry();
|
||||
void calc_convex_hull_3d();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
};
|
||||
|
||||
typedef std::vector<GLVolume*> GLVolumePtrs;
|
||||
|
@ -540,7 +527,6 @@ public:
|
|||
All
|
||||
};
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
struct PrintVolume
|
||||
{
|
||||
// see: Bed3D::EShapeType
|
||||
|
@ -554,16 +540,9 @@ public:
|
|||
// [0] = min z, [1] = max z
|
||||
std::array<float, 2> zs;
|
||||
};
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
private:
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
PrintVolume m_print_volume;
|
||||
#else
|
||||
// min and max vertex of the print box volume
|
||||
float m_print_box_min[3];
|
||||
float m_print_box_max[3];
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// z range for clipping in shaders
|
||||
float m_z_range[2];
|
||||
|
@ -635,14 +614,7 @@ public:
|
|||
bool empty() const { return volumes.empty(); }
|
||||
void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); }
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; }
|
||||
#else
|
||||
void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) {
|
||||
m_print_box_min[0] = min_x; m_print_box_min[1] = min_y; m_print_box_min[2] = min_z;
|
||||
m_print_box_max[0] = max_x; m_print_box_max[1] = max_y; m_print_box_max[2] = max_z;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; }
|
||||
void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; }
|
||||
|
@ -657,11 +629,7 @@ public:
|
|||
|
||||
// returns true if all the volumes are completely contained in the print volume
|
||||
// returns the containment state in the given out_state, if non-null
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state, bool as_toolpaths = false) const;
|
||||
#else
|
||||
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
|
||||
void reset_outside_state();
|
||||
|
||||
void update_colors_by_extruder(const DynamicPrintConfig* config);
|
||||
|
@ -699,8 +667,6 @@ struct _3DScene
|
|||
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
|
||||
};
|
||||
|
||||
static constexpr float BedEpsilon = 3.f * float(EPSILON);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -86,7 +86,7 @@ public:
|
|||
void set_fff_print(Print *print) { m_fff_print = print; }
|
||||
void set_sla_print(SLAPrint *print) { m_sla_print = print; m_sla_print->set_printer(&m_sla_archive); }
|
||||
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
|
||||
void set_gcode_result(GCodeProcessor::Result* result) { m_gcode_result = result; }
|
||||
void set_gcode_result(GCodeProcessorResult* result) { m_gcode_result = result; }
|
||||
|
||||
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished
|
||||
// and the background processing will transition into G-code export.
|
||||
|
@ -216,7 +216,7 @@ private:
|
|||
Print *m_fff_print = nullptr;
|
||||
SLAPrint *m_sla_print = nullptr;
|
||||
// Data structure, to which the G-code export writes its annotations.
|
||||
GCodeProcessor::Result *m_gcode_result = nullptr;
|
||||
GCodeProcessorResult *m_gcode_result = nullptr;
|
||||
// Callback function, used to write thumbnails into gcode.
|
||||
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||
SL1Archive m_sla_archive;
|
||||
|
|
|
@ -22,98 +22,7 @@ namespace GUI {
|
|||
|
||||
BedShape::BedShape(const ConfigOptionPoints& points)
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (points.size() < 3) {
|
||||
m_type = Bed3D::EShapeType::Invalid;
|
||||
return;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// is this a rectangle ?
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Vec2d min;
|
||||
Vec2d max;
|
||||
if (Bed3D::is_rectangle(points.values, &min, &max)) {
|
||||
m_type = Bed3D::EShapeType::Rectangle;
|
||||
m_rectSize = max - min;
|
||||
m_rectOrigin = -min;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
Polygon polygon = Polygon::new_scale(points.values);
|
||||
if (points.size() == 4) {
|
||||
auto lines = polygon.lines();
|
||||
if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) {
|
||||
// okay, it's a rectangle
|
||||
// find origin
|
||||
coordf_t x_min, x_max, y_min, y_max;
|
||||
x_max = x_min = points.values[0](0);
|
||||
y_max = y_min = points.values[0](1);
|
||||
for (auto pt : points.values)
|
||||
{
|
||||
x_min = std::min(x_min, pt(0));
|
||||
x_max = std::max(x_max, pt(0));
|
||||
y_min = std::min(y_min, pt(1));
|
||||
y_max = std::max(y_max, pt(1));
|
||||
}
|
||||
|
||||
m_type = Type::Rectangular;
|
||||
m_rectSize = Vec2d(x_max - x_min, y_max - y_min);
|
||||
m_rectOrigin = Vec2d(-x_min, -y_min);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
// is this a circle ?
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Vec2d center;
|
||||
double radius;
|
||||
if (Bed3D::is_circle(points.values, ¢er, &radius)) {
|
||||
m_type = Bed3D::EShapeType::Circle;
|
||||
m_diameter = 2.0 * radius;
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a custom bed shape, use the polygon provided.
|
||||
m_type = Bed3D::EShapeType::Custom;
|
||||
#else
|
||||
{
|
||||
// Analyze the array of points.Do they reside on a circle ?
|
||||
auto center = polygon.bounding_box().center();
|
||||
std::vector<double> vertex_distances;
|
||||
double avg_dist = 0;
|
||||
for (auto pt : polygon.points)
|
||||
{
|
||||
double distance = (pt - center).cast<double>().norm();
|
||||
vertex_distances.push_back(distance);
|
||||
avg_dist += distance;
|
||||
}
|
||||
|
||||
avg_dist /= vertex_distances.size();
|
||||
bool defined_value = true;
|
||||
for (auto el : vertex_distances)
|
||||
{
|
||||
if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
|
||||
defined_value = false;
|
||||
break;
|
||||
}
|
||||
if (defined_value) {
|
||||
// all vertices are equidistant to center
|
||||
m_type = Type::Circular;
|
||||
m_diameter = unscale<double>(avg_dist * 2);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (points.size() < 3)
|
||||
return;
|
||||
|
||||
// This is a custom bed shape, use the polygon provided.
|
||||
m_type = Type::Custom;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_build_volume = { points.values, 0. };
|
||||
}
|
||||
|
||||
static std::string get_option_label(BedShape::Parameter param)
|
||||
|
@ -122,119 +31,101 @@ static std::string get_option_label(BedShape::Parameter param)
|
|||
case BedShape::Parameter::RectSize : return L("Size");
|
||||
case BedShape::Parameter::RectOrigin: return L("Origin");
|
||||
case BedShape::Parameter::Diameter : return L("Diameter");
|
||||
default: return "";
|
||||
default: assert(false); return {};
|
||||
}
|
||||
}
|
||||
|
||||
void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter param)
|
||||
{
|
||||
ConfigOptionDef def;
|
||||
|
||||
if (param == Parameter::RectSize) {
|
||||
t_config_option_key key;
|
||||
switch (param) {
|
||||
case Parameter::RectSize:
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) });
|
||||
def.min = 0;
|
||||
def.max = 1200;
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Size in X and Y of the rectangular plate.");
|
||||
|
||||
Option option(def, "rect_size");
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
else if (param == Parameter::RectOrigin) {
|
||||
key = "rect_size";
|
||||
break;
|
||||
case Parameter::RectOrigin:
|
||||
def.type = coPoints;
|
||||
def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
|
||||
def.min = -600;
|
||||
def.max = 600;
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
||||
|
||||
Option option(def, "rect_origin");
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
else if (param == Parameter::Diameter) {
|
||||
key = "rect_origin";
|
||||
break;
|
||||
case Parameter::Diameter:
|
||||
def.type = coFloat;
|
||||
def.set_default_value(new ConfigOptionFloat(200));
|
||||
def.sidetext = L("mm");
|
||||
def.label = get_option_label(param);
|
||||
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
|
||||
|
||||
Option option(def, "diameter");
|
||||
optgroup->append_single_option_line(option);
|
||||
key = "diameter";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
optgroup->append_single_option_line({ def, std::move(key) });
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
wxString BedShape::get_name(Bed3D::EShapeType type)
|
||||
wxString BedShape::get_name(PageType type)
|
||||
{
|
||||
switch (type) {
|
||||
case Bed3D::EShapeType::Rectangle: { return _L("Rectangular"); }
|
||||
case Bed3D::EShapeType::Circle: { return _L("Circular"); }
|
||||
case Bed3D::EShapeType::Custom: { return _L("Custom"); }
|
||||
case Bed3D::EShapeType::Invalid:
|
||||
default: return _L("Invalid");
|
||||
case PageType::Rectangle: return _L("Rectangular");
|
||||
case PageType::Circle: return _L("Circular");
|
||||
case PageType::Custom: return _L("Custom");
|
||||
}
|
||||
// make visual studio happy
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
#else
|
||||
wxString BedShape::get_name(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case Type::Rectangular: return _L("Rectangular");
|
||||
case Type::Circular: return _L("Circular");
|
||||
case Type::Custom: return _L("Custom");
|
||||
case Type::Invalid:
|
||||
default: return _L("Invalid");
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
size_t BedShape::get_type()
|
||||
BedShape::PageType BedShape::get_page_type()
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
return static_cast<size_t>(m_type == Bed3D::EShapeType::Invalid ? Bed3D::EShapeType::Rectangle : m_type);
|
||||
#else
|
||||
return static_cast<size_t>(m_type == Type::Invalid ? Type::Rectangular : m_type);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
switch (m_build_volume.type()) {
|
||||
case BuildVolume::Type::Rectangle:
|
||||
case BuildVolume::Type::Invalid: return PageType::Rectangle;
|
||||
case BuildVolume::Type::Circle: return PageType::Circle;
|
||||
case BuildVolume::Type::Convex:
|
||||
case BuildVolume::Type::Custom: return PageType::Custom;
|
||||
}
|
||||
// make visual studio happy
|
||||
assert(false);
|
||||
return PageType::Rectangle;
|
||||
}
|
||||
|
||||
wxString BedShape::get_full_name_with_params()
|
||||
{
|
||||
wxString out = _L("Shape") + ": " + get_name(m_type);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (m_type == Bed3D::EShapeType::Rectangle) {
|
||||
#else
|
||||
if (m_type == Type::Rectangular) {
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(m_rectSize).serialize() + "]";
|
||||
out += "\n" + _(get_option_label(Parameter::RectOrigin))+ ": [" + ConfigOptionPoint(m_rectOrigin).serialize() + "]";
|
||||
wxString out = _L("Shape") + ": " + get_name(this->get_page_type());
|
||||
switch (m_build_volume.type()) {
|
||||
case BuildVolume::Type::Circle:
|
||||
out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(2. * unscaled<double>(m_build_volume.circle().radius)) + "]";
|
||||
break;
|
||||
default:
|
||||
// rectangle, convex, concave...
|
||||
out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(to_2d(m_build_volume.bounding_volume().size())).serialize() + "]";
|
||||
out += "\n" + _(get_option_label(Parameter::RectOrigin)) + ": [" + ConfigOptionPoint(to_2d(m_build_volume.bounding_volume().min)).serialize() + "]";
|
||||
break;
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (m_type == Bed3D::EShapeType::Circle)
|
||||
#else
|
||||
else if (m_type == Type::Circular)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(m_diameter) + "]";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup)
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (m_type == Bed3D::EShapeType::Rectangle || m_type == Bed3D::EShapeType::Invalid) {
|
||||
#else
|
||||
if (m_type == Type::Rectangular || m_type == Type::Invalid) {
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup->set_value("rect_size" , new ConfigOptionPoints{ m_rectSize });
|
||||
optgroup->set_value("rect_origin" , new ConfigOptionPoints{ m_rectOrigin });
|
||||
switch (m_build_volume.type()) {
|
||||
case BuildVolume::Type::Circle:
|
||||
optgroup->set_value("diameter", double_to_string(2. * unscaled<double>(m_build_volume.circle().radius)));
|
||||
break;
|
||||
default:
|
||||
// rectangle, convex, concave...
|
||||
optgroup->set_value("rect_size" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().size()) });
|
||||
optgroup->set_value("rect_origin" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().min) });
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (m_type == Bed3D::EShapeType::Circle)
|
||||
#else
|
||||
else if (m_type == Type::Circular)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup->set_value("diameter", double_to_string(m_diameter));
|
||||
}
|
||||
|
||||
void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
|
||||
|
@ -295,28 +186,16 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
|
|||
|
||||
sbsizer->Add(m_shape_options_book);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
auto optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Rectangle));
|
||||
#else
|
||||
auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Rectangular));
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Rectangle));
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize);
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin);
|
||||
activate_options_page(optgroup);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Circle));
|
||||
#else
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Circular));
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Circle));
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter);
|
||||
activate_options_page(optgroup);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Custom));
|
||||
#else
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Custom));
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Custom));
|
||||
|
||||
Line line{ "", "" };
|
||||
line.full_width = 1;
|
||||
|
@ -538,8 +417,8 @@ void BedShapePanel::set_shape(const ConfigOptionPoints& points)
|
|||
{
|
||||
BedShape shape(points);
|
||||
|
||||
m_shape_options_book->SetSelection(shape.get_type());
|
||||
shape.apply_optgroup_values(m_optgroups[shape.get_type()]);
|
||||
m_shape_options_book->SetSelection(int(shape.get_page_type()));
|
||||
shape.apply_optgroup_values(m_optgroups[int(shape.get_page_type())]);
|
||||
|
||||
// Copy the polygon to the canvas, make a copy of the array, if custom shape is selected
|
||||
if (shape.is_custom())
|
||||
|
@ -562,17 +441,9 @@ void BedShapePanel::update_shape()
|
|||
auto page_idx = m_shape_options_book->GetSelection();
|
||||
auto opt_group = m_optgroups[page_idx];
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Bed3D::EShapeType page_type = static_cast<Bed3D::EShapeType>(page_idx);
|
||||
#else
|
||||
BedShape::Type page_type = static_cast<BedShape::Type>(page_idx);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (page_type == Bed3D::EShapeType::Rectangle) {
|
||||
#else
|
||||
if (page_type == BedShape::Type::Rectangular) {
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
switch (static_cast<BedShape::PageType>(page_idx)) {
|
||||
case BedShape::PageType::Rectangle:
|
||||
{
|
||||
Vec2d rect_size(Vec2d::Zero());
|
||||
Vec2d rect_origin(Vec2d::Zero());
|
||||
|
||||
|
@ -602,12 +473,10 @@ void BedShapePanel::update_shape()
|
|||
Vec2d(x1, y0),
|
||||
Vec2d(x1, y1),
|
||||
Vec2d(x0, y1) };
|
||||
break;
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (page_type == Bed3D::EShapeType::Circle) {
|
||||
#else
|
||||
else if (page_type == BedShape::Type::Circular) {
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
case BedShape::PageType::Circle:
|
||||
{
|
||||
double diameter;
|
||||
try { diameter = boost::any_cast<double>(opt_group->get_value("diameter")); }
|
||||
catch (const std::exception & /* e */) { return; }
|
||||
|
@ -615,6 +484,7 @@ void BedShapePanel::update_shape()
|
|||
if (diameter == 0.0) return ;
|
||||
auto r = diameter / 2;
|
||||
auto twopi = 2 * PI;
|
||||
// Don't change this value without adjusting BuildVolume constructor detecting circle diameter!
|
||||
auto edges = 72;
|
||||
std::vector<Vec2d> points;
|
||||
for (int i = 1; i <= edges; ++i) {
|
||||
|
@ -622,13 +492,12 @@ void BedShapePanel::update_shape()
|
|||
points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
|
||||
}
|
||||
m_shape = points;
|
||||
break;
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
else if (page_type == Bed3D::EShapeType::Custom)
|
||||
#else
|
||||
else if (page_type == BedShape::Type::Custom)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
case BedShape::PageType::Custom:
|
||||
m_shape = m_loaded_shape;
|
||||
break;
|
||||
}
|
||||
|
||||
update_preview();
|
||||
}
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "2DBed.hpp"
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#include "3DBed.hpp"
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <libslic3r/BuildVolume.hpp>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/choicebk.h>
|
||||
|
||||
|
@ -22,14 +21,11 @@ using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
|||
|
||||
struct BedShape
|
||||
{
|
||||
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
enum class Type {
|
||||
Rectangular = 0,
|
||||
Circular,
|
||||
Custom,
|
||||
Invalid
|
||||
enum class PageType {
|
||||
Rectangle,
|
||||
Circle,
|
||||
Custom
|
||||
};
|
||||
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
enum class Parameter {
|
||||
RectSize,
|
||||
|
@ -39,34 +35,18 @@ struct BedShape
|
|||
|
||||
BedShape(const ConfigOptionPoints& points);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool is_custom() { return m_type == Bed3D::EShapeType::Custom; }
|
||||
#else
|
||||
bool is_custom() { return m_type == Type::Custom; }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool is_custom() { return m_build_volume.type() == BuildVolume::Type::Convex || m_build_volume.type() == BuildVolume::Type::Custom; }
|
||||
|
||||
static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
static wxString get_name(Bed3D::EShapeType type);
|
||||
#else
|
||||
static wxString get_name(Type type);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
static wxString get_name(PageType type);
|
||||
|
||||
// convert Type to size_t
|
||||
size_t get_type();
|
||||
PageType get_page_type();
|
||||
|
||||
wxString get_full_name_with_params();
|
||||
void apply_optgroup_values(ConfigOptionsGroupShp optgroup);
|
||||
|
||||
private:
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Bed3D::EShapeType m_type{ Bed3D::EShapeType::Invalid };
|
||||
#else
|
||||
Type m_type {Type::Invalid};
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Vec2d m_rectSize {200, 200};
|
||||
Vec2d m_rectOrigin {0, 0};
|
||||
double m_diameter {0};
|
||||
BuildVolume m_build_volume;
|
||||
};
|
||||
|
||||
class BedShapePanel : public wxPanel
|
||||
|
|
|
@ -260,6 +260,43 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width,
|
|||
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
|
||||
}
|
||||
|
||||
NSVGimage* BitmapCache::nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces)
|
||||
{
|
||||
std::string str;
|
||||
FILE* fp = NULL;
|
||||
size_t size;
|
||||
char* data = NULL;
|
||||
NSVGimage* image = NULL;
|
||||
|
||||
fp = boost::nowide::fopen(filename, "rb");
|
||||
if (!fp) goto error;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
data = (char*)malloc(size + 1);
|
||||
if (data == NULL) goto error;
|
||||
if (fread(data, 1, size, fp) != size) goto error;
|
||||
data[size] = '\0'; // Must be null terminated.
|
||||
fclose(fp);
|
||||
|
||||
if (replaces.empty())
|
||||
image = nsvgParse(data, units, dpi);
|
||||
else {
|
||||
str.assign(data);
|
||||
for (auto val : replaces)
|
||||
boost::replace_all(str, val.first, val.second);
|
||||
image = nsvgParse(str.data(), units, dpi);
|
||||
}
|
||||
free(data);
|
||||
return image;
|
||||
|
||||
error:
|
||||
if (fp) fclose(fp);
|
||||
if (data) free(data);
|
||||
if (image) nsvgDelete(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height,
|
||||
const bool grayscale/* = false*/, const bool dark_mode/* = false*/, const std::string& new_color /*= ""*/)
|
||||
{
|
||||
|
@ -278,11 +315,11 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
|
|||
// map of color replaces
|
||||
std::map<std::string, std::string> replaces;
|
||||
if (dark_mode)
|
||||
replaces["#808080"] = "#FFFFFF";
|
||||
replaces["\"#808080\""] = "\"#FFFFFF\"";
|
||||
if (!new_color.empty())
|
||||
replaces["#ED6B21"] = new_color;
|
||||
replaces["\"#ED6B21\""] = "\"" + new_color + "\"";
|
||||
|
||||
NSVGimage *image = ::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces);
|
||||
NSVGimage *image = nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces);
|
||||
if (image == nullptr)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
struct NSVGimage;
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class BitmapCache
|
||||
|
@ -32,6 +34,11 @@ public:
|
|||
|
||||
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
|
||||
wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false);
|
||||
|
||||
// Parses SVG file from a file, returns SVG image as paths.
|
||||
// And makes replases befor parsing
|
||||
// replace_map containes old_value->new_value
|
||||
static NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces);
|
||||
// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
|
||||
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = "");
|
||||
|
||||
|
|
|
@ -167,7 +167,12 @@ int BitmapComboBox::Append(const wxString& item)
|
|||
//3. Set this empty bitmap to the at list one item and BitmapCombobox will be recreated correct
|
||||
|
||||
wxBitmap bitmap(1, int(1.6 * wxGetApp().em_unit() + 1));
|
||||
bitmap.SetWidth(0);
|
||||
{
|
||||
// bitmap.SetWidth(0); is depricated now
|
||||
// so, use next code
|
||||
bitmap.UnShare();// AllocExclusive();
|
||||
bitmap.GetGDIImageData()->m_width = 0;
|
||||
}
|
||||
|
||||
OnAddBitmap(bitmap);
|
||||
const int n = wxComboBox::Append(item);
|
||||
|
|
|
@ -21,7 +21,13 @@ void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWi
|
|||
ScalableBitmap bmp_delete_focus = ScalableBitmap(parent, "cross_focus");
|
||||
|
||||
auto add_color = [grid_sizer, parent](wxColourPickerCtrl** color_picker, const wxColour& color, const wxColour& def_color, wxString label_text) {
|
||||
//
|
||||
// wrap the label_text to the max 80 characters
|
||||
if (label_text.Len() > 80) {
|
||||
size_t brack_pos = label_text.find_last_of(" ", 79);
|
||||
if (brack_pos > 0 && brack_pos < 80)
|
||||
label_text.insert(brack_pos + 1, "\n");
|
||||
}
|
||||
|
||||
auto sys_label = new wxStaticText(parent, wxID_ANY, label_text);
|
||||
sys_label->SetForegroundColour(color);
|
||||
|
||||
|
|
|
@ -162,8 +162,8 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
|||
|
||||
if (config->opt_bool("support_material")) {
|
||||
// Ask only once.
|
||||
if (!support_material_overhangs_queried) {
|
||||
support_material_overhangs_queried = true;
|
||||
if (!m_support_material_overhangs_queried) {
|
||||
m_support_material_overhangs_queried = true;
|
||||
if (!config->opt_bool("overhangs")/* != 1*/) {
|
||||
wxString msg_text = _(L("Supports work better, if the following feature is enabled:\n"
|
||||
"- Detect bridging perimeters"));
|
||||
|
@ -182,7 +182,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
|||
}
|
||||
}
|
||||
else {
|
||||
support_material_overhangs_queried = false;
|
||||
m_support_material_overhangs_queried = false;
|
||||
}
|
||||
|
||||
if (config->option<ConfigOptionPercent>("fill_density")->value == 100) {
|
||||
|
|
|
@ -17,15 +17,12 @@ class ModelConfig;
|
|||
|
||||
namespace GUI {
|
||||
|
||||
// This variable have to be static because of use its value from Preset configuration
|
||||
// and from object/parts configuration from the Settings in sidebar
|
||||
static bool support_material_overhangs_queried {false};
|
||||
|
||||
class ConfigManipulation
|
||||
{
|
||||
bool is_msg_dlg_already_exist{ false };
|
||||
|
||||
bool m_is_initialized_support_material_overhangs_queried{ false };
|
||||
bool m_support_material_overhangs_queried{ false };
|
||||
|
||||
// function to loading of changed configuration
|
||||
std::function<void()> load_config = nullptr;
|
||||
|
@ -66,7 +63,7 @@ public:
|
|||
void initialize_support_material_overhangs_queried(bool queried)
|
||||
{
|
||||
m_is_initialized_support_material_overhangs_queried = true;
|
||||
support_material_overhangs_queried = queried;
|
||||
m_support_material_overhangs_queried = queried;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "DoubleSlider.hpp"
|
||||
#include "DoubleSlider_Utils.hpp"
|
||||
#include "libslic3r/GCode.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
@ -25,6 +26,7 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <random>
|
||||
#include "Field.hpp"
|
||||
#include "format.hpp"
|
||||
|
@ -1445,6 +1447,18 @@ wxString Control::get_tooltip(int tick/*=-1*/)
|
|||
std::string space = " ";
|
||||
tooltip = space;
|
||||
auto format_gcode = [space](std::string gcode) {
|
||||
// when the tooltip is too long, it starts to flicker, see: https://github.com/prusa3d/PrusaSlicer/issues/7368
|
||||
// so we limit the number of lines shown
|
||||
std::vector<std::string> lines;
|
||||
boost::split(lines, gcode, boost::is_any_of("\n"), boost::token_compress_off);
|
||||
static const size_t MAX_LINES = 10;
|
||||
if (lines.size() > MAX_LINES) {
|
||||
gcode = lines.front() + '\n';
|
||||
for (size_t i = 1; i < MAX_LINES; ++i) {
|
||||
gcode += lines[i] + '\n';
|
||||
}
|
||||
gcode += "[" + into_u8(_L("continue")) + "]\n";
|
||||
}
|
||||
boost::replace_all(gcode, "\n", "\n" + space);
|
||||
return gcode;
|
||||
};
|
||||
|
@ -2546,12 +2560,70 @@ bool Control::check_ticks_changed_event(Type type)
|
|||
std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int extruder)
|
||||
{
|
||||
if (mode == SingleExtruder && type == ColorChange && m_use_default_colors) {
|
||||
#if 1
|
||||
if (ticks.empty())
|
||||
return get_opposite_color((*m_colors)[0]);
|
||||
|
||||
auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick);
|
||||
if (before_tick_it == ticks.end())
|
||||
{
|
||||
while (before_tick_it != ticks.begin())
|
||||
if (--before_tick_it; before_tick_it->type == ColorChange)
|
||||
break;
|
||||
if (before_tick_it->type == ColorChange)
|
||||
return get_opposite_color(before_tick_it->color);
|
||||
return get_opposite_color((*m_colors)[0]);
|
||||
}
|
||||
|
||||
if (before_tick_it == ticks.begin())
|
||||
{
|
||||
const std::string& frst_color = (*m_colors)[0];
|
||||
if (before_tick_it->type == ColorChange)
|
||||
return get_opposite_color(frst_color, before_tick_it->color);
|
||||
|
||||
auto next_tick_it = before_tick_it;
|
||||
while (next_tick_it != ticks.end())
|
||||
if (++next_tick_it; next_tick_it->type == ColorChange)
|
||||
break;
|
||||
if (next_tick_it->type == ColorChange)
|
||||
return get_opposite_color(frst_color, next_tick_it->color);
|
||||
|
||||
return get_opposite_color(frst_color);
|
||||
}
|
||||
|
||||
std::string frst_color = "";
|
||||
if (before_tick_it->type == ColorChange)
|
||||
frst_color = before_tick_it->color;
|
||||
else {
|
||||
auto next_tick_it = before_tick_it;
|
||||
while (next_tick_it != ticks.end())
|
||||
if (++next_tick_it; next_tick_it->type == ColorChange) {
|
||||
frst_color = next_tick_it->color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (before_tick_it != ticks.begin())
|
||||
if (--before_tick_it; before_tick_it->type == ColorChange)
|
||||
break;
|
||||
|
||||
if (before_tick_it->type == ColorChange) {
|
||||
if (frst_color.empty())
|
||||
return get_opposite_color(before_tick_it->color);
|
||||
return get_opposite_color(before_tick_it->color, frst_color);
|
||||
}
|
||||
|
||||
if (frst_color.empty())
|
||||
return get_opposite_color((*m_colors)[0]);
|
||||
return get_opposite_color((*m_colors)[0], frst_color);
|
||||
#else
|
||||
const std::vector<std::string>& colors = ColorPrintColors::get();
|
||||
if (ticks.empty())
|
||||
return colors[0];
|
||||
m_default_color_idx++;
|
||||
|
||||
return colors[m_default_color_idx % colors.size()];
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string color = (*m_colors)[extruder - 1];
|
||||
|
|
|
@ -115,7 +115,7 @@ class TickCodeInfo
|
|||
bool m_suppress_plus = false;
|
||||
bool m_suppress_minus = false;
|
||||
bool m_use_default_colors= false;
|
||||
int m_default_color_idx = 0;
|
||||
// int m_default_color_idx = 0;
|
||||
|
||||
std::vector<std::string>* m_colors {nullptr};
|
||||
|
||||
|
|
173
src/slic3r/GUI/DoubleSlider_Utils.hpp
Normal file
173
src/slic3r/GUI/DoubleSlider_Utils.hpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
#include <stdio.h>
|
||||
#include "wx/colour.h"
|
||||
|
||||
// next code is borrowed from https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both
|
||||
|
||||
typedef struct {
|
||||
double r; // a fraction between 0 and 1
|
||||
double g; // a fraction between 0 and 1
|
||||
double b; // a fraction between 0 and 1
|
||||
} rgb;
|
||||
|
||||
typedef struct {
|
||||
double h; // angle in degrees
|
||||
double s; // a fraction between 0 and 1
|
||||
double v; // a fraction between 0 and 1
|
||||
} hsv;
|
||||
|
||||
static hsv rgb2hsv(rgb in);
|
||||
static rgb hsv2rgb(hsv in);
|
||||
|
||||
hsv rgb2hsv(rgb in)
|
||||
{
|
||||
hsv out;
|
||||
double min, max, delta;
|
||||
|
||||
min = in.r < in.g ? in.r : in.g;
|
||||
min = min < in.b ? min : in.b;
|
||||
|
||||
max = in.r > in.g ? in.r : in.g;
|
||||
max = max > in.b ? max : in.b;
|
||||
|
||||
out.v = max; // v
|
||||
delta = max - min;
|
||||
if (delta < 0.00001)
|
||||
{
|
||||
out.s = 0;
|
||||
out.h = 0; // undefined, maybe nan?
|
||||
return out;
|
||||
}
|
||||
if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
|
||||
out.s = (delta / max); // s
|
||||
} else {
|
||||
// if max is 0, then r = g = b = 0
|
||||
// s = 0, h is undefined
|
||||
out.s = 0.0;
|
||||
out.h = NAN; // its now undefined
|
||||
return out;
|
||||
}
|
||||
if( in.r >= max ) // > is bogus, just keeps compilor happy
|
||||
out.h = ( in.g - in.b ) / delta; // between yellow & magenta
|
||||
else
|
||||
if( in.g >= max )
|
||||
out.h = 2.0 + ( in.b - in.r ) / delta; // between cyan & yellow
|
||||
else
|
||||
out.h = 4.0 + ( in.r - in.g ) / delta; // between magenta & cyan
|
||||
|
||||
out.h *= 60.0; // degrees
|
||||
|
||||
if( out.h < 0.0 )
|
||||
out.h += 360.0;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
hsv rgb2hsv(const std::string& str_clr_in)
|
||||
{
|
||||
wxColour clr(str_clr_in);
|
||||
rgb in = { clr.Red() / 255.0, clr.Green() / 255.0, clr.Blue() / 255.0 };
|
||||
return rgb2hsv(in);
|
||||
}
|
||||
|
||||
|
||||
rgb hsv2rgb(hsv in)
|
||||
{
|
||||
double hh, p, q, t, ff;
|
||||
long i;
|
||||
rgb out;
|
||||
|
||||
if(in.s <= 0.0) { // < is bogus, just shuts up warnings
|
||||
out.r = in.v;
|
||||
out.g = in.v;
|
||||
out.b = in.v;
|
||||
return out;
|
||||
}
|
||||
hh = in.h;
|
||||
if (hh >= 360.0) hh -= 360.0;//hh = 0.0;
|
||||
hh /= 60.0;
|
||||
i = (long)hh;
|
||||
ff = hh - i;
|
||||
p = in.v * (1.0 - in.s);
|
||||
q = in.v * (1.0 - (in.s * ff));
|
||||
t = in.v * (1.0 - (in.s * (1.0 - ff)));
|
||||
|
||||
switch(i) {
|
||||
case 0:
|
||||
out.r = in.v;
|
||||
out.g = t;
|
||||
out.b = p;
|
||||
break;
|
||||
case 1:
|
||||
out.r = q;
|
||||
out.g = in.v;
|
||||
out.b = p;
|
||||
break;
|
||||
case 2:
|
||||
out.r = p;
|
||||
out.g = in.v;
|
||||
out.b = t;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
out.r = p;
|
||||
out.g = q;
|
||||
out.b = in.v;
|
||||
break;
|
||||
case 4:
|
||||
out.r = t;
|
||||
out.g = p;
|
||||
out.b = in.v;
|
||||
break;
|
||||
case 5:
|
||||
default:
|
||||
out.r = in.v;
|
||||
out.g = p;
|
||||
out.b = q;
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
double rand_val()
|
||||
{
|
||||
return 0.1 * (10 - rand() % 8);
|
||||
}
|
||||
|
||||
std::string get_opposite_color(const std::string& color)
|
||||
{
|
||||
std::string opp_color = "";
|
||||
|
||||
hsv hsv_clr = rgb2hsv(color);
|
||||
hsv_clr.h += 65; // 65 instead 60 to avoid circle values
|
||||
hsv_clr.s = rand_val();
|
||||
hsv_clr.v = rand_val();
|
||||
|
||||
rgb rgb_opp_color = hsv2rgb(hsv_clr);
|
||||
|
||||
wxString clr_str = wxString::Format(wxT("#%02X%02X%02X"), (unsigned char)(rgb_opp_color.r * 255), (unsigned char)(rgb_opp_color.g * 255), (unsigned char)(rgb_opp_color.b * 255));
|
||||
opp_color = clr_str.ToStdString();
|
||||
|
||||
return opp_color;
|
||||
}
|
||||
|
||||
std::string get_opposite_color(const std::string& color_frst, const std::string& color_scnd)
|
||||
{
|
||||
std::string opp_color = "";
|
||||
|
||||
hsv hsv_frst = rgb2hsv(color_frst);
|
||||
hsv hsv_scnd = rgb2hsv(color_scnd);
|
||||
|
||||
double delta_h = fabs(hsv_frst.h - hsv_scnd.h);
|
||||
double start_h = delta_h > 180 ? std::min<double>(hsv_scnd.h, hsv_frst.h) : std::max<double>(hsv_scnd.h, hsv_frst.h);
|
||||
start_h += 5; // to avoid circle change of colors for 120 deg
|
||||
if (delta_h < 180)
|
||||
delta_h = 360 - delta_h;
|
||||
|
||||
hsv hsv_opp = hsv{ start_h + 0.5 * delta_h, rand_val(), rand_val() };
|
||||
rgb rgb_opp_color = hsv2rgb(hsv_opp);
|
||||
|
||||
wxString clr_str = wxString::Format(wxT("#%02X%02X%02X"), (unsigned char)(rgb_opp_color.r * 255), (unsigned char)(rgb_opp_color.g * 255), (unsigned char)(rgb_opp_color.b * 255));
|
||||
opp_color = clr_str.ToStdString();
|
||||
|
||||
return opp_color;
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#include "GUI.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "BitmapComboBox.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
||||
#include <wx/dc.h>
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
|
@ -222,14 +223,9 @@ bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value
|
|||
if (!text_editor || text_editor->GetValue().IsEmpty())
|
||||
return false;
|
||||
|
||||
std::string chosen_name = into_u8(text_editor->GetValue());
|
||||
const char* unusable_symbols = "<>:/\\|?*\"";
|
||||
for (size_t i = 0; i < std::strlen(unusable_symbols); i++) {
|
||||
if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) {
|
||||
m_was_unusable_symbol = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_was_unusable_symbol = Slic3r::GUI::Plater::has_illegal_filename_characters(text_editor->GetValue());
|
||||
if (m_was_unusable_symbol)
|
||||
return false;
|
||||
|
||||
// The icon can't be edited so get its old value and reuse it.
|
||||
wxVariant valueOld;
|
||||
|
|
|
@ -315,8 +315,8 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
set_value(double_to_string(val), true);
|
||||
}
|
||||
else if (((m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max) ||
|
||||
(m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1)) &&
|
||||
(m_value.empty() || std::string(str.ToUTF8().data()) != boost::any_cast<std::string>(m_value)))
|
||||
(m_opt.sidetext.rfind("mm ") != std::string::npos && val > /*1*/m_opt.max_literal)) &&
|
||||
(m_value.empty() || into_u8(str) != boost::any_cast<std::string>(m_value)))
|
||||
{
|
||||
if (!check_value) {
|
||||
m_value.clear();
|
||||
|
@ -330,7 +330,6 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
const wxString msg_text = from_u8((boost::format(_utf8(L("Do you mean %s%% instead of %s %s?\n"
|
||||
"Select YES if you want to change this value to %s%%, \n"
|
||||
"or NO if you are sure that %s %s is a correct value."))) % stVal % stVal % sidetext % stVal % stVal % sidetext).str());
|
||||
// wxMessageDialog dialog(m_parent, msg_text, _(L("Parameter validation")) + ": " + m_opt_id , wxICON_WARNING | wxYES | wxNO);
|
||||
WarningDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxYES | wxNO);
|
||||
if ((!infill_anchors || val > 100) && dialog.ShowModal() == wxID_YES) {
|
||||
set_value(from_u8((boost::format("%s%%") % stVal).str()), false/*true*/);
|
||||
|
@ -341,7 +340,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
}
|
||||
}
|
||||
|
||||
m_value = std::string(str.ToUTF8().data());
|
||||
m_value = into_u8(str);
|
||||
break; }
|
||||
|
||||
case coPoints: {
|
||||
|
@ -1287,7 +1286,7 @@ void Choice::msw_rescale()
|
|||
size_t counter = 0;
|
||||
bool labels = ! m_opt.enum_labels.empty();
|
||||
for (const std::string &el : labels ? m_opt.enum_labels : m_opt.enum_values) {
|
||||
wxString text = labels ? _(el) : wxString::FromUTF8(el.c_str());
|
||||
wxString text = labels ? _(el) : from_u8(el);
|
||||
field->Append(text);
|
||||
if (text == selection)
|
||||
idx = counter;
|
||||
|
@ -1575,7 +1574,7 @@ void StaticText::BUILD()
|
|||
if (m_opt.height >= 0) size.SetHeight(m_opt.height*m_em_unit);
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit);
|
||||
|
||||
const wxString legend = wxString::FromUTF8(m_opt.get_default_value<ConfigOptionString>()->value.c_str());
|
||||
const wxString legend = from_u8(m_opt.get_default_value<ConfigOptionString>()->value);
|
||||
auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size, wxST_ELLIPSIZE_MIDDLE);
|
||||
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,13 +27,9 @@ class GCodeViewer
|
|||
using MultiVertexBuffer = std::vector<VertexBuffer>;
|
||||
using IndexBuffer = std::vector<IBufferType>;
|
||||
using MultiIndexBuffer = std::vector<IndexBuffer>;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
using InstanceBuffer = std::vector<float>;
|
||||
using InstanceIdBuffer = std::vector<size_t>;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
using InstancesOffsets = std::vector<Vec3f>;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
static const std::vector<Color> Extrusion_Role_Colors;
|
||||
static const std::vector<Color> Options_Colors;
|
||||
|
@ -107,17 +103,10 @@ class GCodeViewer
|
|||
void reset();
|
||||
};
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
// buffer containing instances data used to render a toolpaths using instanced or batched models
|
||||
// instance record format:
|
||||
// instanced models: 5 floats -> position.x|position.y|position.z|width|height (which are sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced())
|
||||
// batched models: 3 floats -> position.x|position.y|position.z
|
||||
#else
|
||||
// buffer containing instances data used to render a toolpaths using instanced models
|
||||
// instance record format: 5 floats -> position.x|position.y|position.z|width|height
|
||||
// which is sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced()
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
struct InstanceVBuffer
|
||||
{
|
||||
// ranges used to render only subparts of the intances
|
||||
|
@ -140,7 +129,6 @@ class GCodeViewer
|
|||
void reset();
|
||||
};
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
enum class EFormat : unsigned char
|
||||
{
|
||||
InstancedModel,
|
||||
|
@ -148,21 +136,17 @@ class GCodeViewer
|
|||
};
|
||||
|
||||
EFormat format;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
|
||||
// cpu-side buffer containing all instances data
|
||||
InstanceBuffer buffer;
|
||||
// indices of the moves for all instances
|
||||
std::vector<size_t> s_ids;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
// position offsets, used to show the correct value of the tool position
|
||||
InstancesOffsets offsets;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
Ranges render_ranges;
|
||||
|
||||
size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); }
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t instance_size_floats() const {
|
||||
switch (format)
|
||||
{
|
||||
|
@ -171,14 +155,10 @@ class GCodeViewer
|
|||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
#else
|
||||
size_t instance_size_floats() const { return 5; }
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t instance_size_bytes() const { return instance_size_floats() * sizeof(float); }
|
||||
|
||||
void reset();
|
||||
};
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
// ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type
|
||||
struct IBuffer
|
||||
|
@ -233,7 +213,7 @@ class GCodeViewer
|
|||
unsigned char cp_color_id{ 0 };
|
||||
std::vector<Sub_Path> sub_paths;
|
||||
|
||||
bool matches(const GCodeProcessor::MoveVertex& move) const;
|
||||
bool matches(const GCodeProcessorResult::MoveVertex& move) const;
|
||||
size_t vertices_count() const {
|
||||
return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1;
|
||||
}
|
||||
|
@ -251,7 +231,7 @@ class GCodeViewer
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
void add_sub_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) {
|
||||
void add_sub_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id) {
|
||||
Endpoint endpoint = { b_id, i_id, s_id, move.position };
|
||||
sub_paths.push_back({ endpoint , endpoint });
|
||||
}
|
||||
|
@ -313,17 +293,9 @@ class GCodeViewer
|
|||
{
|
||||
Point,
|
||||
Line,
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
Triangle,
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
InstancedModel,
|
||||
BatchedModel
|
||||
#else
|
||||
Model
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#else
|
||||
Triangle
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
};
|
||||
|
||||
ERenderPrimitiveType render_primitive_type;
|
||||
|
@ -332,22 +304,18 @@ class GCodeViewer
|
|||
VBuffer vertices;
|
||||
std::vector<IBuffer> indices;
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
struct Model
|
||||
{
|
||||
GLModel model;
|
||||
Color color;
|
||||
InstanceVBuffer instances;
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
GLModel::InitializationData data;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
// contain the buffer for model primitive types
|
||||
Model model;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
std::string shader;
|
||||
std::vector<Path> paths;
|
||||
|
@ -361,7 +329,7 @@ class GCodeViewer
|
|||
// b_id index of buffer contained in this->indices
|
||||
// i_id index of first index contained in this->indices[b_id]
|
||||
// s_id index of first vertex contained in this->vertices
|
||||
void add_path(const GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
|
||||
void add_path(const GCodeProcessorResult::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
|
||||
|
||||
unsigned int max_vertices_per_segment() const {
|
||||
switch (render_primitive_type)
|
||||
|
@ -396,7 +364,6 @@ class GCodeViewer
|
|||
}
|
||||
size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); }
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
bool has_data() const {
|
||||
switch (render_primitive_type)
|
||||
{
|
||||
|
@ -405,23 +372,14 @@ class GCodeViewer
|
|||
case ERenderPrimitiveType::Triangle: {
|
||||
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||
}
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
case ERenderPrimitiveType::InstancedModel: { return model.model.is_initialized() && !model.instances.buffer.empty(); }
|
||||
case ERenderPrimitiveType::BatchedModel: {
|
||||
return model.data.vertices_count() > 0 && model.data.indices_count() &&
|
||||
!vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||
}
|
||||
#else
|
||||
case ERenderPrimitiveType::Model: { return model.model.is_initialized() && !model.instances.buffer.empty(); }
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
default: { return false; }
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool has_data() const {
|
||||
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
};
|
||||
|
||||
// helper to render shells
|
||||
|
@ -569,36 +527,24 @@ class GCodeViewer
|
|||
int64_t gl_multi_lines_calls_count{ 0 };
|
||||
int64_t gl_multi_triangles_calls_count{ 0 };
|
||||
int64_t gl_triangles_calls_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t gl_instanced_models_calls_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
int64_t gl_batched_models_calls_count{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
// memory
|
||||
int64_t results_size{ 0 };
|
||||
int64_t total_vertices_gpu_size{ 0 };
|
||||
int64_t total_indices_gpu_size{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t total_instances_gpu_size{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
int64_t max_vbuffer_gpu_size{ 0 };
|
||||
int64_t max_ibuffer_gpu_size{ 0 };
|
||||
int64_t paths_size{ 0 };
|
||||
int64_t render_paths_size{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t models_instances_size{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
// other
|
||||
int64_t travel_segments_count{ 0 };
|
||||
int64_t wipe_segments_count{ 0 };
|
||||
int64_t extrude_segments_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t instances_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
int64_t batched_count{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
int64_t vbuffers_count{ 0 };
|
||||
int64_t ibuffers_count{ 0 };
|
||||
|
||||
|
@ -624,40 +570,28 @@ class GCodeViewer
|
|||
gl_multi_lines_calls_count = 0;
|
||||
gl_multi_triangles_calls_count = 0;
|
||||
gl_triangles_calls_count = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
gl_instanced_models_calls_count = 0;
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
gl_batched_models_calls_count = 0;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
|
||||
void reset_sizes() {
|
||||
results_size = 0;
|
||||
total_vertices_gpu_size = 0;
|
||||
total_indices_gpu_size = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
total_instances_gpu_size = 0;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
max_vbuffer_gpu_size = 0;
|
||||
max_ibuffer_gpu_size = 0;
|
||||
paths_size = 0;
|
||||
render_paths_size = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
models_instances_size = 0;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
|
||||
void reset_others() {
|
||||
travel_segments_count = 0;
|
||||
wipe_segments_count = 0;
|
||||
extrude_segments_count = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
extrude_segments_count = 0;
|
||||
instances_count = 0;
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
batched_count = 0;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
vbuffers_count = 0;
|
||||
ibuffers_count = 0;
|
||||
}
|
||||
|
@ -672,12 +606,10 @@ public:
|
|||
GLModel m_model;
|
||||
Vec3f m_world_position;
|
||||
Transform3f m_world_transform;
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
// for seams, the position of the marker is on the last endpoint of the toolpath containing it
|
||||
// the offset is used to show the correct value of tool position in the "ToolPosition" window
|
||||
// see implementation of render() method
|
||||
Vec3f m_world_offset;
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
float m_z_offset{ 0.5f };
|
||||
bool m_visible{ true };
|
||||
|
||||
|
@ -687,9 +619,7 @@ public:
|
|||
const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); }
|
||||
|
||||
void set_world_position(const Vec3f& position);
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
void set_world_offset(const Vec3f& offset) { m_world_offset = offset; }
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
|
||||
bool is_visible() const { return m_visible; }
|
||||
void set_visible(bool visible) { m_visible = visible; }
|
||||
|
@ -743,13 +673,9 @@ public:
|
|||
Endpoints endpoints;
|
||||
Endpoints current;
|
||||
Endpoints last_current;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
Endpoints global;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
Vec3f current_position{ Vec3f::Zero() };
|
||||
#if ENABLE_FIX_SEAMS_SYNCH
|
||||
Vec3f current_offset{ Vec3f::Zero() };
|
||||
#endif // ENABLE_FIX_SEAMS_SYNCH
|
||||
Marker marker;
|
||||
GCodeWindow gcode_window;
|
||||
std::vector<unsigned int> gcode_ids;
|
||||
|
@ -780,9 +706,7 @@ private:
|
|||
BoundingBoxf3 m_paths_bounding_box;
|
||||
// bounding box of toolpaths + marker tools
|
||||
BoundingBoxf3 m_max_bounding_box;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
float m_max_print_height{ 0.0f };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
std::vector<Color> m_tool_colors;
|
||||
Layers m_layers;
|
||||
std::array<unsigned int, 2> m_layers_z_range;
|
||||
|
@ -802,27 +726,23 @@ private:
|
|||
Statistics m_statistics;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
std::array<float, 2> m_detected_point_sizes = { 0.0f, 0.0f };
|
||||
GCodeProcessor::Result::SettingsIds m_settings_ids;
|
||||
GCodeProcessorResult::SettingsIds m_settings_ids;
|
||||
std::array<SequentialRangeCap, 2> m_sequential_range_caps;
|
||||
|
||||
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool m_contained_in_bed{ true };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
public:
|
||||
GCodeViewer();
|
||||
~GCodeViewer() { reset(); }
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void init();
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
// extract rendering data from the given parameters
|
||||
void load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized);
|
||||
void load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized);
|
||||
// recalculate ranges in dependence of what is visible and sets tool/print colors
|
||||
void refresh(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void refresh_render_paths();
|
||||
void update_shells_color_by_extruder(const DynamicPrintConfig* config);
|
||||
|
||||
|
@ -839,9 +759,7 @@ public:
|
|||
const SequentialView& get_sequential_view() const { return m_sequential_view; }
|
||||
void update_sequential_view_current(unsigned int first, unsigned int last);
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool is_contained_in_bed() const { return m_contained_in_bed; }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
EViewType get_view_type() const { return m_view_type; }
|
||||
void set_view_type(EViewType type) {
|
||||
|
@ -870,7 +788,7 @@ public:
|
|||
size_t get_extruders_count() { return m_extruders_count; }
|
||||
|
||||
private:
|
||||
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
|
||||
void load_toolpaths(const GCodeProcessorResult& gcode_result);
|
||||
void load_shells(const Print& print, bool initialized);
|
||||
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
|
||||
void render_toolpaths();
|
||||
|
|
|
@ -3,16 +3,18 @@
|
|||
|
||||
#include <igl/unproject.h>
|
||||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Technologies.hpp"
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/3DBed.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp"
|
||||
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
|
||||
#include "slic3r/GUI/GLShader.hpp"
|
||||
|
@ -20,7 +22,6 @@
|
|||
#include "slic3r/GUI/Tab.hpp"
|
||||
#include "slic3r/GUI/GUI_Preview.hpp"
|
||||
#include "slic3r/GUI/OpenGLManager.hpp"
|
||||
#include "slic3r/GUI/3DBed.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
|
@ -94,6 +95,11 @@ RetinaHelper::~RetinaHelper() {}
|
|||
float RetinaHelper::get_scale_factor() { return float(m_window->GetContentScaleFactor()); }
|
||||
#endif // __WXGTK3__
|
||||
|
||||
// Fixed the collision between BuildVolume::Type::Convex and macro Convex defined inside /usr/include/X11/X.h that is included by WxWidgets 3.0.
|
||||
#if defined(__linux__) && defined(Convex)
|
||||
#undef Convex
|
||||
#endif
|
||||
|
||||
Size::Size()
|
||||
: m_width(0)
|
||||
, m_height(0)
|
||||
|
@ -270,8 +276,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
|
|||
ImGui::SetCursorPosX(widget_align);
|
||||
ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
|
||||
int radius = (int)m_smooth_params.radius;
|
||||
if (ImGui::SliderInt("##1", &radius, 1, 10))
|
||||
if (ImGui::SliderInt("##1", &radius, 1, 10)) {
|
||||
radius = std::clamp(radius, 1, 10);
|
||||
m_smooth_params.radius = (unsigned int)radius;
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosX(text_align);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
|
@ -957,9 +965,10 @@ PrinterTechnology GLCanvas3D::current_printer_technology() const
|
|||
return m_process->current_printer_technology();
|
||||
}
|
||||
|
||||
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
||||
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
|
||||
: m_canvas(canvas)
|
||||
, m_context(nullptr)
|
||||
, m_bed(bed)
|
||||
#if ENABLE_RETINA_GL
|
||||
, m_retina_helper(nullptr)
|
||||
#endif
|
||||
|
@ -1115,18 +1124,12 @@ void GLCanvas3D::reset_volumes()
|
|||
_set_warning_notification(EWarning::ObjectOutside, false);
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state(bool as_toolpaths) const
|
||||
#else
|
||||
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
{
|
||||
assert(m_initialized);
|
||||
|
||||
ModelInstanceEPrintVolumeState state;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_volumes.check_outside_state(m_config, &state, as_toolpaths);
|
||||
#else
|
||||
m_volumes.check_outside_state(m_config, &state);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_volumes.check_outside_state(m_bed.build_volume(), &state);
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -1250,13 +1253,11 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
|
|||
BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb = volumes_bounding_box();
|
||||
bb.merge(wxGetApp().plater()->get_bed().get_bounding_box(true));
|
||||
if (m_config != nullptr) {
|
||||
double h = m_config->opt_float("max_print_height");
|
||||
bb.min(2) = std::min(bb.min(2), -h);
|
||||
bb.max(2) = std::max(bb.max(2), h);
|
||||
}
|
||||
|
||||
bb.merge(m_bed.extended_bounding_box());
|
||||
double h = m_bed.build_volume().max_print_height();
|
||||
//FIXME why -h?
|
||||
bb.min.z() = std::min(bb.min.z(), -h);
|
||||
bb.max.z() = std::max(bb.max.z(), h);
|
||||
return bb;
|
||||
}
|
||||
|
||||
|
@ -1362,7 +1363,10 @@ void GLCanvas3D::allow_multisample(bool allow)
|
|||
|
||||
void GLCanvas3D::zoom_to_bed()
|
||||
{
|
||||
_zoom_to_box(wxGetApp().plater()->get_bed().get_bounding_box(false));
|
||||
BoundingBoxf3 box = m_bed.build_volume().bounding_volume();
|
||||
box.min.z() = 0.0;
|
||||
box.max.z() = 0.0;
|
||||
_zoom_to_box(box);
|
||||
}
|
||||
|
||||
void GLCanvas3D::zoom_to_volumes()
|
||||
|
@ -1418,12 +1422,10 @@ void GLCanvas3D::render()
|
|||
if (!is_initialized() && !init())
|
||||
return;
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
if (!m_main_toolbar.is_enabled())
|
||||
m_gcode_viewer.init();
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
if (wxGetApp().plater()->get_bed().get_shape().empty()) {
|
||||
if (! m_bed.build_volume().valid()) {
|
||||
// this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE));
|
||||
return;
|
||||
|
@ -2057,7 +2059,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
// checks for geometry outside the print volume to render it accordingly
|
||||
if (!m_volumes.empty()) {
|
||||
ModelInstanceEPrintVolumeState state;
|
||||
const bool contained_min_one = m_volumes.check_outside_state(m_config, &state);
|
||||
const bool contained_min_one = m_volumes.check_outside_state(m_bed.build_volume(), &state);
|
||||
const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside);
|
||||
const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside);
|
||||
|
||||
|
@ -2109,7 +2111,7 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume&
|
|||
vol_old.finalize_geometry(gl_initialized);
|
||||
}
|
||||
|
||||
void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors)
|
||||
{
|
||||
m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized);
|
||||
|
||||
|
@ -2138,10 +2140,6 @@ void GLCanvas3D::load_sla_preview()
|
|||
// Release OpenGL data before generating new data.
|
||||
reset_volumes();
|
||||
_load_sla_shells();
|
||||
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
|
||||
m_volumes.set_print_box(float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, 0.0f, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon, (float)m_config->opt_float("max_print_height"));
|
||||
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
_update_sla_shells_outside_state();
|
||||
_set_warning_notification_if_needed(EWarning::SlaSupportsOutside);
|
||||
}
|
||||
|
@ -2158,20 +2156,12 @@ void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, c
|
|||
// Release OpenGL data before generating new data.
|
||||
this->reset_volumes();
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool requires_convex_hulls = wxGetApp().plater()->get_bed().get_shape_type() != Bed3D::EShapeType::Rectangle;
|
||||
_load_print_toolpaths(requires_convex_hulls);
|
||||
_load_wipe_tower_toolpaths(str_tool_colors, requires_convex_hulls);
|
||||
const BuildVolume &build_volume = m_bed.build_volume();
|
||||
_load_print_toolpaths(build_volume);
|
||||
_load_wipe_tower_toolpaths(build_volume, str_tool_colors);
|
||||
for (const PrintObject* object : print->objects())
|
||||
_load_print_object_toolpaths(*object, str_tool_colors, color_print_values, requires_convex_hulls);
|
||||
#else
|
||||
_load_print_toolpaths();
|
||||
_load_wipe_tower_toolpaths(str_tool_colors);
|
||||
for (const PrintObject* object : print->objects())
|
||||
_load_print_object_toolpaths(*object, str_tool_colors, color_print_values);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
_load_print_object_toolpaths(*object, build_volume, str_tool_colors, color_print_values);
|
||||
|
||||
_update_toolpath_volumes_outside_state();
|
||||
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
|
||||
}
|
||||
|
||||
|
@ -2605,9 +2595,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
displacement = multiplier * direction;
|
||||
|
||||
m_selection.translate(displacement);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_dirty = true;
|
||||
}
|
||||
);
|
||||
|
@ -2706,9 +2694,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
|||
auto do_rotate = [this](double angle_z_rad) {
|
||||
m_selection.start_dragging();
|
||||
m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint));
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
m_dirty = true;
|
||||
// wxGetApp().obj_manipul()->set_dirty();
|
||||
};
|
||||
|
@ -3262,10 +3248,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
}
|
||||
}
|
||||
else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) {
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (evt.LeftUp())
|
||||
m_selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
if (m_layers_editing.state != LayersEditing::Unknown) {
|
||||
m_layers_editing.state = LayersEditing::Unknown;
|
||||
|
@ -3770,7 +3754,8 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos)
|
|||
|
||||
double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const
|
||||
{
|
||||
return factor * wxGetApp().plater()->get_bed().get_bounding_box(false).max_size();
|
||||
const BoundingBoxf& bbox = m_bed.build_volume().bounding_volume2d();
|
||||
return factor * std::max(bbox.size()[0], bbox.size()[1]);
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_cursor(ECursorType type)
|
||||
|
@ -4145,24 +4130,15 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
|||
}
|
||||
}
|
||||
|
||||
#if !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
if (visible_volumes.empty())
|
||||
return;
|
||||
#endif // !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
|
||||
BoundingBoxf3 volumes_box;
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
if (!visible_volumes.empty()) {
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
for (const GLVolume* vol : visible_volumes) {
|
||||
volumes_box.merge(vol->transformed_bounding_box());
|
||||
}
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
}
|
||||
else
|
||||
// This happens for empty projects
|
||||
volumes_box = wxGetApp().plater()->get_bed().get_bounding_box(true);
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
volumes_box = m_bed.extended_bounding_box();
|
||||
|
||||
Camera camera;
|
||||
camera.set_type(camera_type);
|
||||
|
@ -4178,7 +4154,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
|||
// extends the near and far z of the frustrum to avoid the bed being clipped
|
||||
|
||||
// box in eye space
|
||||
BoundingBoxf3 t_bed_box = wxGetApp().plater()->get_bed().get_bounding_box(true).transformed(camera.get_view_matrix());
|
||||
BoundingBoxf3 t_bed_box = m_bed.extended_bounding_box().transformed(camera.get_view_matrix());
|
||||
near_z = -t_bed_box.max.z();
|
||||
far_z = -t_bed_box.min.z();
|
||||
}
|
||||
|
@ -4744,7 +4720,7 @@ bool GLCanvas3D::_init_undoredo_toolbar()
|
|||
std::string curr_additional_tooltip;
|
||||
m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip);
|
||||
|
||||
std::string new_additional_tooltip = "";
|
||||
std::string new_additional_tooltip;
|
||||
if (can_undo) {
|
||||
std::string action;
|
||||
wxGetApp().plater()->undo_redo_topmost_string_getter(true, action);
|
||||
|
@ -4782,7 +4758,7 @@ bool GLCanvas3D::_init_undoredo_toolbar()
|
|||
std::string curr_additional_tooltip;
|
||||
m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip);
|
||||
|
||||
std::string new_additional_tooltip = "";
|
||||
std::string new_additional_tooltip;
|
||||
if (can_redo) {
|
||||
std::string action;
|
||||
wxGetApp().plater()->undo_redo_topmost_string_getter(false, action);
|
||||
|
@ -4861,7 +4837,7 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be
|
|||
bb.merge(BoundingBoxf3(sel_bb_center - extend_by, sel_bb_center + extend_by));
|
||||
}
|
||||
|
||||
bb.merge(wxGetApp().plater()->get_bed().get_bounding_box(include_bed_model));
|
||||
bb.merge(include_bed_model ? m_bed.extended_bounding_box() : m_bed.build_volume().bounding_volume());
|
||||
|
||||
if (!m_main_toolbar.is_enabled())
|
||||
bb.merge(m_gcode_viewer.get_max_bounding_box());
|
||||
|
@ -5035,25 +5011,6 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
|
|||
_update_volumes_hover_state();
|
||||
}
|
||||
|
||||
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
static BoundingBoxf3 print_volume(const DynamicPrintConfig& config)
|
||||
{
|
||||
// tolerance to avoid false detection at bed edges
|
||||
const double tolerance_x = 0.05;
|
||||
const double tolerance_y = 0.05;
|
||||
|
||||
BoundingBoxf3 ret;
|
||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config.option("bed_shape"));
|
||||
if (opt != nullptr) {
|
||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
|
||||
ret = BoundingBoxf3(Vec3d(unscale<double>(bed_box_2D.min(0)) - tolerance_x, unscale<double>(bed_box_2D.min(1)) - tolerance_y, 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)) + tolerance_x, unscale<double>(bed_box_2D.max(1)) + tolerance_y, config.opt_float("max_print_height")));
|
||||
// Allow the objects to protrude below the print bed
|
||||
ret.min(2) = -1e10;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
void GLCanvas3D::_render_background() const
|
||||
{
|
||||
bool use_error_color = false;
|
||||
|
@ -5064,15 +5021,7 @@ void GLCanvas3D::_render_background() const
|
|||
if (!m_volumes.empty())
|
||||
use_error_color &= _is_any_volume_outside();
|
||||
else
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
use_error_color &= m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed();
|
||||
#else
|
||||
{
|
||||
const BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
|
||||
const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box();
|
||||
use_error_color &= (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) ? !test_volume.contains(paths_volume) : false;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
|
@ -5123,7 +5072,7 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes)
|
|||
&& m_gizmos.get_current_type() != GLGizmosManager::Seam
|
||||
&& m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation);
|
||||
|
||||
wxGetApp().plater()->get_bed().render(*this, bottom, scale_factor, show_axes, show_texture);
|
||||
m_bed.render(*this, bottom, scale_factor, show_axes, show_texture);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_bed_for_picking(bool bottom)
|
||||
|
@ -5133,7 +5082,7 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
|
|||
scale_factor = m_retina_helper->get_scale_factor();
|
||||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
wxGetApp().plater()->get_bed().render_for_picking(*this, bottom, scale_factor);
|
||||
m_bed.render_for_picking(*this, bottom, scale_factor);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
||||
|
@ -5145,58 +5094,39 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
|||
|
||||
m_camera_clipping_plane = m_gizmos.get_clipping_plane();
|
||||
|
||||
if (m_picking_enabled) {
|
||||
if (m_picking_enabled)
|
||||
// Update the layer editing selection to the first object selected, update the current object maximum Z.
|
||||
m_layers_editing.select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
if (m_config != nullptr) {
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
Bed3D::EShapeType type = wxGetApp().plater()->get_bed().get_shape_type();
|
||||
switch (type)
|
||||
{
|
||||
case Bed3D::EShapeType::Circle: {
|
||||
Vec2d center;
|
||||
double radius;
|
||||
if (Bed3D::is_circle(wxGetApp().plater()->get_bed().get_shape(), ¢er, &radius)) {
|
||||
m_volumes.set_print_volume({ static_cast<int>(type),
|
||||
{ float(center.x()), float(center.y()), float(radius) + BedEpsilon, 0.0f },
|
||||
{ 0.0f, float(m_config->opt_float("max_print_height")) } });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Bed3D::EShapeType::Rectangle: {
|
||||
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
|
||||
m_volumes.set_print_volume({ static_cast<int>(type),
|
||||
{ float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon },
|
||||
{ 0.0f, float(m_config->opt_float("max_print_height")) } });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case Bed3D::EShapeType::Custom: {
|
||||
m_volumes.set_print_volume({ static_cast<int>(type),
|
||||
{ 0.0f, 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f } });
|
||||
}
|
||||
}
|
||||
#else
|
||||
const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false);
|
||||
m_volumes.set_print_box((float)bed_bb.min.x() - BedEpsilon, (float)bed_bb.min.y() - BedEpsilon, 0.0f, (float)bed_bb.max.x() + BedEpsilon, (float)bed_bb.max.y() + BedEpsilon, (float)m_config->opt_float("max_print_height"));
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (m_requires_check_outside_state) {
|
||||
m_volumes.check_outside_state(m_config, nullptr);
|
||||
m_requires_check_outside_state = false;
|
||||
}
|
||||
#else
|
||||
m_volumes.check_outside_state(m_config, nullptr);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (const BuildVolume &build_volume = m_bed.build_volume(); build_volume.valid()) {
|
||||
switch (build_volume.type()) {
|
||||
case BuildVolume::Type::Rectangle: {
|
||||
const BoundingBox3Base<Vec3d> bed_bb = build_volume.bounding_volume().inflated(BuildVolume::SceneEpsilon);
|
||||
m_volumes.set_print_volume({ 0, // circle
|
||||
{ float(bed_bb.min.x()), float(bed_bb.min.y()), float(bed_bb.max.x()), float(bed_bb.max.y()) },
|
||||
{ 0.0f, float(build_volume.max_print_height()) } });
|
||||
break;
|
||||
}
|
||||
case BuildVolume::Type::Circle: {
|
||||
m_volumes.set_print_volume({ 1, // rectangle
|
||||
{ unscaled<float>(build_volume.circle().center.x()), unscaled<float>(build_volume.circle().center.y()), unscaled<float>(build_volume.circle().radius + BuildVolume::SceneEpsilon), 0.0f },
|
||||
{ 0.0f, float(build_volume.max_print_height() + BuildVolume::SceneEpsilon) } });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case BuildVolume::Type::Convex:
|
||||
case BuildVolume::Type::Custom: {
|
||||
m_volumes.set_print_volume({ static_cast<int>(type),
|
||||
{ -FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX },
|
||||
{ -FLT_MAX, FLT_MAX } }
|
||||
);
|
||||
}
|
||||
}
|
||||
if (m_requires_check_outside_state) {
|
||||
m_volumes.check_outside_state(build_volume, nullptr);
|
||||
m_requires_check_outside_state = false;
|
||||
}
|
||||
#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
if (m_use_clipping_planes)
|
||||
m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
|
||||
|
@ -5206,11 +5136,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
|||
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
|
||||
m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances());
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_mod");
|
||||
#else
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (shader != nullptr) {
|
||||
shader->start_using();
|
||||
|
||||
|
@ -5832,11 +5758,7 @@ void GLCanvas3D::_stop_timer()
|
|||
m_timer.Stop();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_print_toolpaths(bool generate_convex_hulls)
|
||||
#else
|
||||
void GLCanvas3D::_load_print_toolpaths()
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_print_toolpaths(const BuildVolume &build_volume)
|
||||
{
|
||||
const Print *print = this->fff_print();
|
||||
if (print == nullptr)
|
||||
|
@ -5889,18 +5811,11 @@ void GLCanvas3D::_load_print_toolpaths()
|
|||
reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized);
|
||||
}
|
||||
}
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (generate_convex_hulls)
|
||||
volume->calc_convex_hull_3d();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
volume->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(volume->indexed_vertex_array.vertices_and_normals_interleaved, volume->indexed_vertex_array.bounding_box());
|
||||
volume->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values, bool generate_convex_hulls)
|
||||
#else
|
||||
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
|
||||
{
|
||||
std::vector<std::array<float, 4>> tool_colors = _parse_colors(str_tool_colors);
|
||||
|
||||
|
@ -6187,26 +6102,16 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
|||
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
|
||||
[](const GLVolume *volume) { return volume->empty(); }),
|
||||
m_volumes.volumes.end());
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
|
||||
GLVolume* v = m_volumes.volumes[i];
|
||||
if (generate_convex_hulls)
|
||||
v->calc_convex_hull_3d();
|
||||
v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box());
|
||||
v->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
}
|
||||
#else
|
||||
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
|
||||
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
|
||||
}
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors, bool generate_convex_hulls)
|
||||
#else
|
||||
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors)
|
||||
{
|
||||
const Print *print = this->fff_print();
|
||||
if (print == nullptr || print->wipe_tower_data().tool_changes.empty())
|
||||
|
@ -6357,17 +6262,11 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
|
|||
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
|
||||
[](const GLVolume *volume) { return volume->empty(); }),
|
||||
m_volumes.volumes.end());
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
|
||||
GLVolume* v = m_volumes.volumes[i];
|
||||
if (generate_convex_hulls)
|
||||
v->calc_convex_hull_3d();
|
||||
v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box());
|
||||
v->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
}
|
||||
#else
|
||||
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
|
||||
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
|
||||
}
|
||||
|
@ -6427,28 +6326,9 @@ void GLCanvas3D::_load_sla_shells()
|
|||
update_volumes_colors_by_extruder();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_update_toolpath_volumes_outside_state()
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
check_volumes_outside_state(true);
|
||||
#else
|
||||
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
volume->is_outside = (test_volume.radius() > 0.0 && volume->is_extrusion_path) ? !test_volume.contains(volume->bounding_box()) : false;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
void GLCanvas3D::_update_sla_shells_outside_state()
|
||||
{
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
check_volumes_outside_state();
|
||||
#else
|
||||
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
volume->is_outside = (test_volume.radius() > 0.0 && volume->shader_outside_printer_detection_enabled) ? !test_volume.contains(volume->transformed_convex_hull_bounding_box()) : false;
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
|
||||
void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
||||
|
@ -6459,15 +6339,8 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
|||
show = _is_any_volume_outside();
|
||||
else {
|
||||
if (wxGetApp().is_editor()) {
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (current_printer_technology() != ptSLA)
|
||||
show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed();
|
||||
#else
|
||||
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
|
||||
const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box();
|
||||
if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0)
|
||||
show = !test_volume.contains(paths_volume);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ class wxGLContext;
|
|||
namespace Slic3r {
|
||||
|
||||
class BackgroundSlicingProcess;
|
||||
class BuildVolume;
|
||||
struct ThumbnailData;
|
||||
struct ThumbnailsParams;
|
||||
class ModelObject;
|
||||
|
@ -50,6 +51,8 @@ namespace CustomGCode { struct Item; }
|
|||
|
||||
namespace GUI {
|
||||
|
||||
class Bed3D;
|
||||
|
||||
#if ENABLE_RETINA_GL
|
||||
class RetinaHelper;
|
||||
#endif
|
||||
|
@ -446,6 +449,7 @@ public:
|
|||
private:
|
||||
wxGLCanvas* m_canvas;
|
||||
wxGLContext* m_context;
|
||||
Bed3D &m_bed;
|
||||
#if ENABLE_RETINA_GL
|
||||
std::unique_ptr<RetinaHelper> m_retina_helper;
|
||||
#endif
|
||||
|
@ -475,9 +479,7 @@ private:
|
|||
const DynamicPrintConfig* m_config;
|
||||
Model* m_model;
|
||||
BackgroundSlicingProcess *m_process;
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
bool m_requires_check_outside_state{ false };
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
std::array<unsigned int, 2> m_old_size{ 0, 0 };
|
||||
|
||||
|
@ -600,7 +602,7 @@ private:
|
|||
m_gizmo_highlighter;
|
||||
|
||||
public:
|
||||
explicit GLCanvas3D(wxGLCanvas* canvas);
|
||||
explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed);
|
||||
~GLCanvas3D();
|
||||
|
||||
bool is_initialized() const { return m_initialized; }
|
||||
|
@ -614,22 +616,14 @@ public:
|
|||
void post_event(wxEvent &&event);
|
||||
|
||||
void set_as_dirty();
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
void requires_check_outside_state() { m_requires_check_outside_state = true; }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
unsigned int get_volumes_count() const;
|
||||
const GLVolumeCollection& get_volumes() const { return m_volumes; }
|
||||
void reset_volumes();
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
ModelInstanceEPrintVolumeState check_volumes_outside_state(bool as_toolpaths = false) const;
|
||||
#else
|
||||
ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void init_gcode_viewer() { m_gcode_viewer.init(); }
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
|
||||
const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); }
|
||||
void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); }
|
||||
|
@ -736,7 +730,7 @@ public:
|
|||
|
||||
void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
|
||||
|
||||
void load_gcode_preview(const GCodeProcessor::Result& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
|
||||
void refresh_gcode_preview_render_paths();
|
||||
void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); }
|
||||
GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); }
|
||||
|
@ -816,6 +810,7 @@ public:
|
|||
|
||||
void schedule_extra_frame(int miliseconds);
|
||||
|
||||
float get_main_toolbar_height() { return m_main_toolbar.get_height(); }
|
||||
int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); }
|
||||
void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); }
|
||||
void force_main_toolbar_right_action(int item_id) { m_main_toolbar.force_right_action(item_id, *this); }
|
||||
|
@ -955,33 +950,19 @@ private:
|
|||
void _start_timer();
|
||||
void _stop_timer();
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// Create 3D thick extrusion lines for a skirt and brim.
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to volumes.
|
||||
void _load_print_toolpaths(bool generate_convex_hulls = false);
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to volumes, updates collision with the build_volume.
|
||||
void _load_print_toolpaths(const BuildVolume &build_volume);
|
||||
// Create 3D thick extrusion lines for object forming extrusions.
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
|
||||
// one for perimeters, one for infill and one for supports.
|
||||
void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors,
|
||||
const std::vector<CustomGCode::Item>& color_print_values, bool generate_convex_hulls = false);
|
||||
// Create 3D thick extrusion lines for wipe tower extrusions
|
||||
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors, bool generate_convex_hulls = false);
|
||||
#else
|
||||
// Create 3D thick extrusion lines for a skirt and brim.
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to volumes.
|
||||
void _load_print_toolpaths();
|
||||
// Create 3D thick extrusion lines for object forming extrusions.
|
||||
// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
|
||||
// one for perimeters, one for infill and one for supports.
|
||||
void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors,
|
||||
const std::vector<CustomGCode::Item>& color_print_values);
|
||||
// Create 3D thick extrusion lines for wipe tower extrusions
|
||||
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// one for perimeters, one for infill and one for supports, updates collision with the build_volume.
|
||||
void _load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume &build_volume,
|
||||
const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values);
|
||||
// Create 3D thick extrusion lines for wipe tower extrusions, updates collision with the build_volume.
|
||||
void _load_wipe_tower_toolpaths(const BuildVolume &build_volume, const std::vector<std::string>& str_tool_colors);
|
||||
|
||||
// Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished.
|
||||
void _load_sla_shells();
|
||||
void _update_toolpath_volumes_outside_state();
|
||||
void _update_sla_shells_outside_state();
|
||||
void _set_warning_notification_if_needed(EWarning warning);
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t GLModel::InitializationData::vertices_count() const
|
||||
{
|
||||
size_t ret = 0;
|
||||
|
@ -35,7 +34,6 @@ size_t GLModel::InitializationData::indices_count() const
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
|
||||
void GLModel::init_from(const InitializationData& data)
|
||||
{
|
||||
|
@ -231,7 +229,6 @@ void GLModel::render() const
|
|||
}
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) const
|
||||
{
|
||||
if (instances_vbo == 0)
|
||||
|
@ -308,7 +305,6 @@ void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instance
|
|||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
void GLModel::send_to_gpu(RenderData& data, const std::vector<float>& vertices, const std::vector<unsigned int>& indices)
|
||||
{
|
||||
|
|
|
@ -49,14 +49,12 @@ namespace GUI {
|
|||
|
||||
std::vector<Entity> entities;
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t vertices_count() const;
|
||||
size_t vertices_size_floats() const { return vertices_count() * 6; }
|
||||
size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); }
|
||||
|
||||
size_t indices_count() const;
|
||||
size_t indices_size_bytes() const { return indices_count() * sizeof(unsigned int); }
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -80,9 +78,7 @@ namespace GUI {
|
|||
|
||||
void reset();
|
||||
void render() const;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const;
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
bool is_initialized() const { return !m_render_data.empty(); }
|
||||
|
||||
|
|
|
@ -33,40 +33,17 @@ std::pair<bool, std::string> GLShadersManager::init()
|
|||
|
||||
bool valid = true;
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview
|
||||
#else
|
||||
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" });
|
||||
// used to render printbed
|
||||
valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" });
|
||||
// used to render options in gcode preview
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3))
|
||||
valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" });
|
||||
#else
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3))
|
||||
valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" });
|
||||
else {
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" });
|
||||
if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20))
|
||||
valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" });
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
// used to render extrusion and travel paths as lines in gcode preview
|
||||
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
|
||||
// used to render objects in 3d editor
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
// When setting this technology to default rename the following from "gouraud_mod" to "gouraud"
|
||||
valid &= append_shader("gouraud_mod", { "gouraud_mod.vs", "gouraud_mod.fs" }
|
||||
#else
|
||||
valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
, { "ENABLE_ENVIRONMENT_MAP"sv }
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <exception>
|
||||
#include <cstdlib>
|
||||
#include <regex>
|
||||
#include <string_view>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
@ -97,6 +98,8 @@
|
|||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
@ -431,85 +434,126 @@ bool static check_old_linux_datadir(const wxString& app_name) {
|
|||
static bool run_updater_win()
|
||||
{
|
||||
// find updater exe
|
||||
boost::filesystem::path path_to_binary = boost::dll::program_location();
|
||||
for (const auto& dir_entry : boost::filesystem::directory_iterator(path_to_binary.parent_path())) {
|
||||
if (dir_entry.path().filename() == "prusaslicer-updater.exe") {
|
||||
// run updater. Original args: /silent -restartapp prusa-slicer.exe -startappfirst
|
||||
boost::filesystem::path path_updater = boost::dll::program_location().parent_path() / "prusaslicer-updater.exe";
|
||||
if (boost::filesystem::exists(path_updater)) {
|
||||
// run updater. Original args: /silent -restartapp prusa-slicer.exe -startappfirst
|
||||
|
||||
// Using quoted string as mentioned in CreateProcessW docs.
|
||||
std::wstring wcmd = L"\"" + dir_entry.path().wstring() + L"\"";
|
||||
wcmd += L" /silent";
|
||||
// Using quoted string as mentioned in CreateProcessW docs, silent execution parameter.
|
||||
std::wstring wcmd = L"\"" + path_updater.wstring() + L"\" /silent";
|
||||
|
||||
// additional information
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
// additional information
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
// set the size of the structures
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
// set the size of the structures
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
// start the program up
|
||||
if (CreateProcessW(NULL, // the path
|
||||
wcmd.data(), // Command line
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&si, // Pointer to STARTUPINFO structure
|
||||
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
|
||||
)) {
|
||||
// Close process and thread handles.
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return true;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to start prusaslicer-updater.exe with command " << wcmd;
|
||||
}
|
||||
break;
|
||||
// start the program up
|
||||
if (CreateProcessW(NULL, // the path
|
||||
wcmd.data(), // Command line
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&si, // Pointer to STARTUPINFO structure
|
||||
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
|
||||
)) {
|
||||
// Close process and thread handles.
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return true;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to start prusaslicer-updater.exe with command " << wcmd;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif //_WIN32
|
||||
|
||||
struct FileWildcards {
|
||||
std::string_view title;
|
||||
std::vector<std::string_view> file_extensions;
|
||||
};
|
||||
|
||||
static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
|
||||
/* FT_STL */ { "STL files"sv, { ".stl"sv } },
|
||||
/* FT_OBJ */ { "OBJ files"sv, { ".obj"sv } },
|
||||
/* FT_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"sv } },
|
||||
/* FT_3MF */ { "3MF files"sv, { ".3mf"sv } },
|
||||
/* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".g"sv, ".ngc"sv } },
|
||||
/* FT_MODEL */ { "Known files"sv, { ".stl"sv, ".obj"sv, ".3mf"sv, ".amf"sv, ".zip.amf"sv, ".xml"sv } },
|
||||
/* FT_PROJECT */ { "Project files"sv, { ".3mf"sv, ".amf"sv, ".zip.amf"sv } },
|
||||
/* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } },
|
||||
|
||||
/* FT_INI */ { "INI files"sv, { ".ini"sv } },
|
||||
/* FT_SVG */ { "SVG files"sv, { ".svg"sv } },
|
||||
|
||||
/* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } },
|
||||
|
||||
/* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv } },
|
||||
};
|
||||
|
||||
// This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms.
|
||||
// The function accepts a custom extension parameter. If the parameter is provided, the custom extension
|
||||
// will be added as a fist to the list. This is important for a "file save" dialog on OSX, which strips
|
||||
// an extension from the provided initial file name and substitutes it with the default extension (the first one in the template).
|
||||
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
{
|
||||
static const std::string defaults[FT_SIZE] = {
|
||||
/* FT_STL */ "STL files (*.stl)|*.stl;*.STL",
|
||||
/* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ",
|
||||
/* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML",
|
||||
/* FT_3MF */ "3MF files (*.3mf)|*.3mf;*.3MF;",
|
||||
/* FT_GCODE */ "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC",
|
||||
/* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF",
|
||||
/* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF",
|
||||
/* FT_GALLERY */ "Known files (*.stl, *.obj)|*.stl;*.STL;*.obj;*.OBJ",
|
||||
const FileWildcards data = file_wildcards_by_type[file_type];
|
||||
std::string title;
|
||||
std::string mask;
|
||||
std::string custom_ext_lower;
|
||||
|
||||
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
|
||||
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
|
||||
|
||||
/* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG",
|
||||
|
||||
/* FT_SL1 */ "Masked SLA files (*.sl1, *.sl1s)|*.sl1;*.SL1;*.sl1s;*.SL1S",
|
||||
// Workaround for OSX file picker, for some reason it always saves with the 1st extension.
|
||||
/* FT_SL1S */ "Masked SLA files (*.sl1s, *.sl1)|*.sl1s;*.SL1S;*.sl1;*.SL1",
|
||||
};
|
||||
|
||||
std::string out = defaults[file_type];
|
||||
if (! custom_extension.empty()) {
|
||||
// Find the custom extension in the template.
|
||||
if (out.find(std::string("*") + custom_extension + ",") == std::string::npos && out.find(std::string("*") + custom_extension + ")") == std::string::npos) {
|
||||
// The custom extension was not found in the template.
|
||||
// Append the custom extension to the wildcards, so that the file dialog would not add the default extension to it.
|
||||
boost::replace_first(out, ")|", std::string(", *") + custom_extension + ")|");
|
||||
out += std::string(";*") + custom_extension;
|
||||
// Generate an extension into the title mask and into the list of extensions.
|
||||
custom_ext_lower = custom_extension;
|
||||
boost::to_lower(custom_ext_lower);
|
||||
std::string custom_ext_upper = custom_extension;
|
||||
boost::to_upper(custom_ext_upper);
|
||||
if (custom_ext_lower == custom_extension) {
|
||||
// Add a lower case version.
|
||||
title = std::string("*") + custom_ext_lower;
|
||||
mask = title;
|
||||
// Add an upper case version.
|
||||
mask += ";*";
|
||||
mask += custom_ext_upper;
|
||||
} else if (custom_ext_upper == custom_extension) {
|
||||
// Add an upper case version.
|
||||
title = std::string("*") + custom_ext_upper;
|
||||
mask = title;
|
||||
// Add a lower case version.
|
||||
mask += ";*";
|
||||
mask += custom_ext_lower;
|
||||
} else {
|
||||
// Add the mixed case version only.
|
||||
title = std::string("*") + custom_extension;
|
||||
mask = title;
|
||||
}
|
||||
}
|
||||
return from_u8(out);
|
||||
|
||||
for (const std::string_view &ext : data.file_extensions)
|
||||
// Only add an extension if it was not added first as the custom extension.
|
||||
if (ext != custom_ext_lower) {
|
||||
if (title.empty()) {
|
||||
title = "*";
|
||||
title += ext;
|
||||
mask = title;
|
||||
} else {
|
||||
title += ", *";
|
||||
title += ext;
|
||||
mask += ";*";
|
||||
mask += ext;
|
||||
}
|
||||
mask += ";*";
|
||||
std::string ext_upper{ ext };
|
||||
boost::to_upper(ext_upper);
|
||||
mask += ext_upper;
|
||||
}
|
||||
return GUI::format("%s (%s)|%s", data.title, title, mask);
|
||||
}
|
||||
|
||||
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
|
||||
|
@ -738,14 +782,11 @@ void GUI_App::post_init()
|
|||
// sees something else than "we want something" on the first start.
|
||||
show_send_system_info_dialog_if_needed();
|
||||
}
|
||||
bool updater_running =
|
||||
#ifdef _WIN32
|
||||
// Run external updater on Windows.
|
||||
run_updater_win();
|
||||
#else
|
||||
false;
|
||||
if (! run_updater_win())
|
||||
// "prusaslicer-updater.exe" was not started, run our own update check.
|
||||
#endif // _WIN32
|
||||
if (!updater_running)
|
||||
this->preset_updater->slic3r_update_notify();
|
||||
});
|
||||
}
|
||||
|
@ -992,22 +1033,13 @@ bool GUI_App::OnInit()
|
|||
|
||||
bool GUI_App::on_init_inner()
|
||||
{
|
||||
// win32 build on win64 and viceversa
|
||||
#ifdef _WIN64
|
||||
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "") {
|
||||
RichMessageDialog dlg(nullptr,
|
||||
_L("You have started PrusaSlicer for 64-bit architecture on 32-bit system."
|
||||
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
|
||||
"\nDo you wish to continue?"),
|
||||
"PrusaSlicer", wxICON_QUESTION | wxYES_NO);
|
||||
if (dlg.ShowModal() != wxID_YES)
|
||||
return false;
|
||||
}
|
||||
#elif _WIN32
|
||||
#if defined(_WIN32) && ! defined(_WIN64)
|
||||
// Win32 32bit build.
|
||||
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "64") {
|
||||
RichMessageDialog dlg(nullptr,
|
||||
_L("You have started PrusaSlicer for 32-bit architecture on 64-bit system."
|
||||
"\nPlease download and install correct version at https://www.prusa3d.cz/prusaslicer/."
|
||||
_L("You are running a 32 bit build of PrusaSlicer on 64-bit Windows."
|
||||
"\n32 bit build of PrusaSlicer will likely not be able to utilize all the RAM available in the system."
|
||||
"\nPlease download and install a 64 bit build of PrusaSlice from https://www.prusa3d.cz/prusaslicer/."
|
||||
"\nDo you wish to continue?"),
|
||||
"PrusaSlicer", wxICON_QUESTION | wxYES_NO);
|
||||
if (dlg.ShowModal() != wxID_YES)
|
||||
|
@ -2652,6 +2684,11 @@ Plater* GUI_App::plater()
|
|||
return plater_;
|
||||
}
|
||||
|
||||
const Plater* GUI_App::plater() const
|
||||
{
|
||||
return plater_;
|
||||
}
|
||||
|
||||
Model& GUI_App::model()
|
||||
{
|
||||
return plater_->model();
|
||||
|
|
|
@ -66,13 +66,11 @@ enum FileType
|
|||
FT_TEX,
|
||||
|
||||
FT_SL1,
|
||||
// Workaround for OSX file picker, for some reason it always saves with the 1st extension.
|
||||
FT_SL1S,
|
||||
|
||||
FT_SIZE,
|
||||
};
|
||||
|
||||
extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = std::string());
|
||||
extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = std::string{});
|
||||
|
||||
enum ConfigMenuIDs {
|
||||
ConfigMenuWizard,
|
||||
|
@ -281,6 +279,7 @@ public:
|
|||
ObjectList* obj_list();
|
||||
ObjectLayers* obj_layers();
|
||||
Plater* plater();
|
||||
const Plater* plater() const;
|
||||
Model& model();
|
||||
NotificationManager * notification_manager();
|
||||
|
||||
|
|
|
@ -484,6 +484,10 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu)
|
|||
menu->Destroy(settings_id);
|
||||
}
|
||||
|
||||
// Update "Height range Modifier" item (delete old & create new)
|
||||
if (const auto range_id = menu->FindItem(_L("Height range Modifier")); range_id != wxNOT_FOUND)
|
||||
menu->Destroy(range_id);
|
||||
|
||||
const ConfigOptionMode mode = wxGetApp().get_mode();
|
||||
|
||||
if (mode == comAdvanced) {
|
||||
|
@ -513,6 +517,8 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu)
|
|||
append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second,
|
||||
[]() { return obj_list()->is_instance_or_object_selected(); }, m_parent);
|
||||
}
|
||||
|
||||
append_menu_item_layers_editing(menu);
|
||||
}
|
||||
|
||||
wxMenuItem* MenuFactory::append_menu_item_layers_editing(wxMenu* menu)
|
||||
|
@ -786,8 +792,14 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu)
|
|||
|
||||
void MenuFactory::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu)
|
||||
{
|
||||
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
|
||||
append_menu_item(menu, wxID_ANY, _L("Scale to print volume"), _L("Scale the selected object to fit the print volume"),
|
||||
[](wxCommandEvent&) { plater()->scale_selection_to_fit_print_volume(); }, "", menu,
|
||||
[]() { return plater()->can_scale_to_print_volume(); }, m_parent);
|
||||
#else
|
||||
append_menu_item(menu, wxID_ANY, _L("Scale to print volume"), _L("Scale the selected object to fit the print volume"),
|
||||
[](wxCommandEvent&) { plater()->scale_selection_to_fit_print_volume(); }, "", menu);
|
||||
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/* = 1*/)
|
||||
|
@ -938,11 +950,7 @@ void MenuFactory::create_object_menu()
|
|||
[]() { return plater()->can_split(true) && wxGetApp().get_mode() > comSimple; }, m_parent);
|
||||
m_object_menu.AppendSeparator();
|
||||
|
||||
// Layers Editing for object
|
||||
append_menu_item_layers_editing(&m_object_menu);
|
||||
m_object_menu.AppendSeparator();
|
||||
|
||||
// "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume()
|
||||
// "Height range Modifier" and "Add (volumes)" menu items will be added later in append_menu_items_add_volume()
|
||||
}
|
||||
|
||||
void MenuFactory::create_sla_object_menu()
|
||||
|
|
|
@ -911,21 +911,14 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me
|
|||
toggle_printable_state();
|
||||
else if (title == _("Editing"))
|
||||
show_context_menu(evt_context_menu);
|
||||
else if (title == _("Name"))
|
||||
{
|
||||
if (wxOSX)
|
||||
show_context_menu(evt_context_menu); // return context menu under OSX (related to #2909)
|
||||
|
||||
if (is_windows10())
|
||||
{
|
||||
int obj_idx, vol_idx;
|
||||
get_selected_item_indexes(obj_idx, vol_idx, item);
|
||||
|
||||
if (m_objects_model->HasWarningIcon(item) &&
|
||||
mouse_pos.x > 2 * wxGetApp().em_unit() && mouse_pos.x < 4 * wxGetApp().em_unit())
|
||||
fix_through_netfabb();
|
||||
}
|
||||
}
|
||||
else if (title == _("Name"))
|
||||
{
|
||||
if (is_windows10() && m_objects_model->HasWarningIcon(item) &&
|
||||
mouse_pos.x > 2 * wxGetApp().em_unit() && mouse_pos.x < 4 * wxGetApp().em_unit())
|
||||
fix_through_netfabb();
|
||||
else if (evt_context_menu)
|
||||
show_context_menu(evt_context_menu); // show context menu for "Name" column too
|
||||
}
|
||||
// workaround for extruder editing under OSX
|
||||
else if (wxOSX && evt_context_menu && title == _("Extruder"))
|
||||
extruder_editing();
|
||||
|
@ -1758,12 +1751,9 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
|
|||
new_object->invalidate_bounding_box();
|
||||
new_object->translate(-bb.center());
|
||||
|
||||
if (center) {
|
||||
const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb();
|
||||
new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation.z()));
|
||||
} else {
|
||||
new_object->instances[0]->set_offset(bb.center());
|
||||
}
|
||||
new_object->instances[0]->set_offset(center ?
|
||||
to_3d(wxGetApp().plater()->build_volume().bounding_volume2d().center(), -new_object->origin_translation.z()) :
|
||||
bb.center());
|
||||
|
||||
new_object->ensure_on_bed();
|
||||
|
||||
|
@ -4036,18 +4026,8 @@ void ObjectList::rename_item()
|
|||
if (new_name.IsEmpty())
|
||||
return;
|
||||
|
||||
bool is_unusable_symbol = false;
|
||||
std::string chosen_name = Slic3r::normalize_utf8_nfc(new_name.ToUTF8());
|
||||
const char* unusable_symbols = "<>:/\\|?*\"";
|
||||
for (size_t i = 0; i < std::strlen(unusable_symbols); i++) {
|
||||
if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) {
|
||||
is_unusable_symbol = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_unusable_symbol) {
|
||||
show_error(this, _(L("The supplied name is not valid;")) + "\n" +
|
||||
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
|
||||
if (Plater::has_illegal_filename_characters(new_name)) {
|
||||
Plater::show_illegal_characters_warning(this);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4267,10 +4247,7 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event)
|
|||
const auto renderer = dynamic_cast<BitmapTextRenderer*>(GetColumn(colName)->GetRenderer());
|
||||
|
||||
if (renderer->WasCanceled())
|
||||
wxTheApp->CallAfter([this]{
|
||||
show_error(this, _(L("The supplied name is not valid;")) + "\n" +
|
||||
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
|
||||
});
|
||||
wxTheApp->CallAfter([this]{ Plater::show_illegal_characters_warning(this); });
|
||||
|
||||
#ifdef __WXMSW__
|
||||
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
|
||||
|
|
|
@ -841,14 +841,10 @@ void ObjectManipulation::update_if_dirty()
|
|||
else
|
||||
m_og->disable();
|
||||
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
if (!selection.is_dragging()) {
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
update_reset_buttons_visibility();
|
||||
update_mirror_buttons_visibility();
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
update_reset_buttons_visibility();
|
||||
update_mirror_buttons_visibility();
|
||||
}
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
|
||||
m_dirty = false;
|
||||
}
|
||||
|
@ -1048,9 +1044,7 @@ void ObjectManipulation::change_position_value(int axis, double value)
|
|||
#else
|
||||
selection.translate(position - m_cache.position, selection.requires_local_axes());
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
canvas->do_move(L("Set Position"));
|
||||
|
||||
m_cache.position = position;
|
||||
|
@ -1097,9 +1091,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
|
|||
selection.rotate(
|
||||
(M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation),
|
||||
transformation_type);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
canvas->do_rotate(L("Set Orientation"));
|
||||
|
||||
m_cache.rotation = rotation;
|
||||
|
@ -1215,9 +1207,7 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const
|
|||
|
||||
selection.start_dragging();
|
||||
selection.scale(scaling_factor, transformation_type);
|
||||
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
selection.stop_dragging();
|
||||
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||
wxGetApp().plater()->canvas3D()->do_scale(L("Set Scale"));
|
||||
}
|
||||
|
||||
|
|
|
@ -181,17 +181,16 @@ bool ObjectSettings::update_settings_list()
|
|||
|
||||
bool ObjectSettings::add_missed_options(ModelConfig* config_to, const DynamicPrintConfig& config_from)
|
||||
{
|
||||
const DynamicPrintConfig& print_config = wxGetApp().plater()->printer_technology() == ptFFF ?
|
||||
wxGetApp().preset_bundle->prints.get_edited_preset().config :
|
||||
wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
bool is_added = false;
|
||||
if (wxGetApp().plater()->printer_technology() == ptFFF)
|
||||
{
|
||||
if (config_to->has("fill_density") && !config_to->has("fill_pattern"))
|
||||
{
|
||||
if (config_from.option<ConfigOptionPercent>("fill_density")->value == 100) {
|
||||
config_to->set_key_value("fill_pattern", config_from.option("fill_pattern")->clone());
|
||||
is_added = true;
|
||||
}
|
||||
|
||||
for (auto opt_key : config_from.diff(print_config))
|
||||
if (!config_to->has(opt_key)) {
|
||||
config_to->set_key_value(opt_key, config_from.option(opt_key)->clone());
|
||||
is_added = true;
|
||||
}
|
||||
}
|
||||
|
||||
return is_added;
|
||||
}
|
||||
|
|
|
@ -37,11 +37,11 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
View3D::View3D(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
: m_canvas_widget(nullptr)
|
||||
, m_canvas(nullptr)
|
||||
{
|
||||
init(parent, model, config, process);
|
||||
init(parent, bed, model, config, process);
|
||||
}
|
||||
|
||||
View3D::~View3D()
|
||||
|
@ -50,7 +50,7 @@ View3D::~View3D()
|
|||
delete m_canvas_widget;
|
||||
}
|
||||
|
||||
bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
{
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
|
||||
return false;
|
||||
|
@ -59,7 +59,7 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
|
|||
if (m_canvas_widget == nullptr)
|
||||
return false;
|
||||
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget);
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget, bed);
|
||||
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
|
||||
|
||||
m_canvas->allow_multisample(OpenGLManager::can_multisample());
|
||||
|
@ -169,18 +169,18 @@ void View3D::render()
|
|||
}
|
||||
|
||||
Preview::Preview(
|
||||
wxWindow* parent, Model* model, DynamicPrintConfig* config,
|
||||
BackgroundSlicingProcess* process, GCodeProcessor::Result* gcode_result, std::function<void()> schedule_background_process_func)
|
||||
wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config,
|
||||
BackgroundSlicingProcess* process, GCodeProcessorResult* gcode_result, std::function<void()> schedule_background_process_func)
|
||||
: m_config(config)
|
||||
, m_process(process)
|
||||
, m_gcode_result(gcode_result)
|
||||
, m_schedule_background_process(schedule_background_process_func)
|
||||
{
|
||||
if (init(parent, model))
|
||||
if (init(parent, bed, model))
|
||||
load_print();
|
||||
}
|
||||
|
||||
bool Preview::init(wxWindow* parent, Model* model)
|
||||
bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model)
|
||||
{
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
|
||||
return false;
|
||||
|
@ -196,7 +196,7 @@ bool Preview::init(wxWindow* parent, Model* model)
|
|||
if (m_canvas_widget == nullptr)
|
||||
return false;
|
||||
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget);
|
||||
m_canvas = new GLCanvas3D(m_canvas_widget, bed);
|
||||
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
|
||||
m_canvas->allow_multisample(OpenGLManager::can_multisample());
|
||||
m_canvas->set_config(m_config);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue