mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-25 17:51:10 -06:00
Fixed conflicts after merge with master
This commit is contained in:
commit
6bddacee44
90 changed files with 4276 additions and 4185 deletions
|
|
@ -5,7 +5,7 @@ use warnings;
|
||||||
require Exporter;
|
require Exporter;
|
||||||
our @ISA = qw(Exporter);
|
our @ISA = qw(Exporter);
|
||||||
our @EXPORT_OK = qw(
|
our @EXPORT_OK = qw(
|
||||||
offset offset2
|
offset
|
||||||
offset_ex offset2_ex
|
offset_ex offset2_ex
|
||||||
diff_ex diff union_ex intersection_ex
|
diff_ex diff union_ex intersection_ex
|
||||||
JT_ROUND JT_MITER JT_SQUARE
|
JT_ROUND JT_MITER JT_SQUARE
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use List::Util qw(min max sum first);
|
||||||
use Slic3r::Flow ':roles';
|
use Slic3r::Flow ':roles';
|
||||||
use Slic3r::Geometry qw(scale epsilon);
|
use Slic3r::Geometry qw(scale epsilon);
|
||||||
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
|
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
|
||||||
offset offset2 offset_ex offset2_ex JT_MITER);
|
offset offset_ex offset2_ex JT_MITER);
|
||||||
use Slic3r::Print::State ':steps';
|
use Slic3r::Print::State ':steps';
|
||||||
use Slic3r::Surface ':types';
|
use Slic3r::Surface ':types';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1163,7 +1163,7 @@ before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]
|
||||||
default_filament_profile = Generic PLA @MEGA
|
default_filament_profile = Generic PLA @MEGA
|
||||||
default_print_profile = 0.15mm QUALITY @MEGA
|
default_print_profile = 0.15mm QUALITY @MEGA
|
||||||
deretract_speed = 40
|
deretract_speed = 40
|
||||||
end_gcode = G1 E-1.0 F2100 ; retract\nG92 E0.0\nG1{if max_layer_z < max_print_height} Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; move print head up\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y105 F3000 ; park print head\nM84 ; disable motors
|
end_gcode = G1 E-1.0 F2100 ; retract\nG92 E0.0\nG1{if max_layer_z < max_print_height} Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} E-34.0 F720 ; move print head up & retract filament\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y105 F3000 ; park print head\nM84 ; disable motors
|
||||||
extruder_colour = #808080
|
extruder_colour = #808080
|
||||||
gcode_flavor = marlin
|
gcode_flavor = marlin
|
||||||
layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]
|
layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]
|
||||||
|
|
@ -1178,7 +1178,7 @@ retract_lift = 0.2
|
||||||
retract_lift_below = 204
|
retract_lift_below = 204
|
||||||
retract_speed = 70
|
retract_speed = 70
|
||||||
silent_mode = 0
|
silent_mode = 0
|
||||||
start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nG28 ; home all\nG1 Y2.0 Z0.2 F1000 ; move print head up\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG92 E0.0\nG1 X60.0 E9.0 F1000 ; intro line\nG1 X100.0 E12.5 F1000 ; intro line\nG92 E0.0
|
start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nG28 ; home all\nG1 Y1.0 Z0.3 F1000 ; move print head up\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG92 E0.0\n; initial load\nG1 X205.0 E19 F1000\nG1 Y1.6\nG1 X5.0 E19 F1000\nG92 E0.0\n; intro line\nG1 Y2.0 Z0.2 F1000\nG1 X65.0 E9.0 F1000\nG1 X105.0 E12.5 F1000\nG92 E0.0
|
||||||
thumbnails = 16x16,220x124
|
thumbnails = 16x16,220x124
|
||||||
use_relative_e_distances = 1
|
use_relative_e_distances = 1
|
||||||
wipe = 1
|
wipe = 1
|
||||||
|
|
@ -1892,4 +1892,4 @@ default_print_profile = 0.24mm 0.8 nozzle DETAILED QUALITY @PREDATOR
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
########## end printer presets ##########
|
########## end printer presets ##########
|
||||||
#########################################"do not" cause ' is bad for syntax highlighting
|
#########################################
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ const vec3 ZERO = vec3(0.0, 0.0, 0.0);
|
||||||
const vec3 GREEN = vec3(0.0, 0.7, 0.0);
|
const vec3 GREEN = vec3(0.0, 0.7, 0.0);
|
||||||
const vec3 YELLOW = vec3(0.5, 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 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 EPSILON = 0.0001;
|
||||||
|
const float BANDS_WIDTH = 10.0;
|
||||||
|
|
||||||
struct SlopeDetection
|
struct SlopeDetection
|
||||||
{
|
{
|
||||||
|
|
@ -15,6 +17,7 @@ struct SlopeDetection
|
||||||
|
|
||||||
uniform vec4 uniform_color;
|
uniform vec4 uniform_color;
|
||||||
uniform SlopeDetection slope;
|
uniform SlopeDetection slope;
|
||||||
|
uniform bool sinking;
|
||||||
|
|
||||||
#ifdef ENABLE_ENVIRONMENT_MAP
|
#ifdef ENABLE_ENVIRONMENT_MAP
|
||||||
uniform sampler2D environment_tex;
|
uniform sampler2D environment_tex;
|
||||||
|
|
@ -23,27 +26,38 @@ uniform SlopeDetection slope;
|
||||||
|
|
||||||
varying vec3 clipping_planes_dots;
|
varying vec3 clipping_planes_dots;
|
||||||
|
|
||||||
// x = tainted, y = specular;
|
// x = diffuse, y = specular;
|
||||||
varying vec2 intensity;
|
varying vec2 intensity;
|
||||||
|
|
||||||
varying vec3 delta_box_min;
|
varying vec3 delta_box_min;
|
||||||
varying vec3 delta_box_max;
|
varying vec3 delta_box_max;
|
||||||
|
|
||||||
|
varying vec4 model_pos;
|
||||||
|
varying float world_pos_z;
|
||||||
varying float world_normal_z;
|
varying float world_normal_z;
|
||||||
varying vec3 eye_normal;
|
varying vec3 eye_normal;
|
||||||
|
|
||||||
|
vec3 sinking_color(vec3 color)
|
||||||
|
{
|
||||||
|
return (mod(model_pos.x + model_pos.y + model_pos.z, BANDS_WIDTH) < (0.5 * BANDS_WIDTH)) ? mix(color, ZERO, 0.6666) : color;
|
||||||
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
if (any(lessThan(clipping_planes_dots, ZERO)))
|
if (any(lessThan(clipping_planes_dots, ZERO)))
|
||||||
discard;
|
discard;
|
||||||
vec3 color = uniform_color.rgb;
|
vec3 color = uniform_color.rgb;
|
||||||
float alpha = uniform_color.a;
|
float alpha = uniform_color.a;
|
||||||
if (slope.actived && world_normal_z < slope.normal_z - EPSILON) {
|
if (slope.actived && world_normal_z < slope.normal_z - EPSILON)
|
||||||
|
{
|
||||||
color = vec3(0.7, 0.7, 1.0);
|
color = vec3(0.7, 0.7, 1.0);
|
||||||
alpha = 1.0;
|
alpha = 1.0;
|
||||||
}
|
}
|
||||||
// if the fragment is outside the print volume -> use darker color
|
// 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;
|
color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(color, ZERO, 0.3333) : color;
|
||||||
|
// if the object is sinking, shade it with inclined bands or white around world z = 0
|
||||||
|
if (sinking)
|
||||||
|
color = (abs(world_pos_z) < 0.05) ? WHITE : sinking_color(color);
|
||||||
#ifdef ENABLE_ENVIRONMENT_MAP
|
#ifdef ENABLE_ENVIRONMENT_MAP
|
||||||
if (use_environment_tex)
|
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);
|
gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha);
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ uniform vec2 z_range;
|
||||||
// Clipping plane - general orientation. Used by the SLA gizmo.
|
// Clipping plane - general orientation. Used by the SLA gizmo.
|
||||||
uniform vec4 clipping_plane;
|
uniform vec4 clipping_plane;
|
||||||
|
|
||||||
// x = tainted, y = specular;
|
// x = diffuse, y = specular;
|
||||||
varying vec2 intensity;
|
varying vec2 intensity;
|
||||||
|
|
||||||
varying vec3 delta_box_min;
|
varying vec3 delta_box_min;
|
||||||
|
|
@ -49,6 +49,8 @@ varying vec3 delta_box_max;
|
||||||
|
|
||||||
varying vec3 clipping_planes_dots;
|
varying vec3 clipping_planes_dots;
|
||||||
|
|
||||||
|
varying vec4 model_pos;
|
||||||
|
varying float world_pos_z;
|
||||||
varying float world_normal_z;
|
varying float world_normal_z;
|
||||||
varying vec3 eye_normal;
|
varying vec3 eye_normal;
|
||||||
|
|
||||||
|
|
@ -69,12 +71,16 @@ void main()
|
||||||
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
|
NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
|
||||||
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
|
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)
|
// compute deltas for out of print volume detection (world coordinates)
|
||||||
if (print_box.actived)
|
if (print_box.actived)
|
||||||
{
|
{
|
||||||
vec3 v = (print_box.volume_world_matrix * gl_Vertex).xyz;
|
delta_box_min = world_pos.xyz - print_box.min;
|
||||||
delta_box_min = v - print_box.min;
|
delta_box_max = world_pos.xyz - print_box.max;
|
||||||
delta_box_max = v - print_box.max;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -86,8 +92,6 @@ void main()
|
||||||
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0;
|
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0;
|
||||||
|
|
||||||
gl_Position = ftransform();
|
gl_Position = ftransform();
|
||||||
// Point in homogenous coordinates.
|
|
||||||
vec4 world_pos = print_box.volume_world_matrix * gl_Vertex;
|
|
||||||
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
|
// 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);
|
clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ void main()
|
||||||
float z_texture_col = object_z_row - z_texture_row;
|
float z_texture_col = object_z_row - z_texture_row;
|
||||||
float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25;
|
float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25;
|
||||||
// Calculate level of detail from the object Z coordinate.
|
// Calculate level of detail from the object Z coordinate.
|
||||||
// This makes the slowly sloping surfaces to be show with high detail (with stripes),
|
// This makes the slowly sloping surfaces to be shown with high detail (with stripes),
|
||||||
// and the vertical surfaces to be shown with low detail (no stripes)
|
// and the vertical surfaces to be shown with low detail (no stripes)
|
||||||
float z_in_cells = object_z_row * 190.;
|
float z_in_cells = object_z_row * 190.;
|
||||||
// Gradient of Z projected on the screen.
|
// Gradient of Z projected on the screen.
|
||||||
|
|
@ -32,9 +32,10 @@ void main()
|
||||||
float dy_vtc = dFdy(z_in_cells);
|
float dy_vtc = dFdy(z_in_cells);
|
||||||
float lod = clamp(0.5 * log2(max(dx_vtc * dx_vtc, dy_vtc * dy_vtc)), 0., 1.);
|
float lod = clamp(0.5 * log2(max(dx_vtc * dx_vtc, dy_vtc * dy_vtc)), 0., 1.);
|
||||||
// Sample the Z texture. Texture coordinates are normalized to <0, 1>.
|
// Sample the Z texture. Texture coordinates are normalized to <0, 1>.
|
||||||
vec4 color = mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.),
|
vec4 color = vec4(0.25, 0.25, 0.25, 1.0);
|
||||||
texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod);
|
if (z_texture_row >= 0.0)
|
||||||
|
color = mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.),
|
||||||
|
texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod);
|
||||||
// Mix the final color.
|
// Mix the final color.
|
||||||
gl_FragColor = vec4(intensity.y, intensity.y, intensity.y, 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend);
|
gl_FragColor = vec4(vec3(intensity.y), 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ add_library(libslic3r STATIC
|
||||||
AppConfig.hpp
|
AppConfig.hpp
|
||||||
Print.cpp
|
Print.cpp
|
||||||
Print.hpp
|
Print.hpp
|
||||||
|
PrintApply.cpp
|
||||||
PrintBase.cpp
|
PrintBase.cpp
|
||||||
PrintBase.hpp
|
PrintBase.hpp
|
||||||
PrintConfig.cpp
|
PrintConfig.cpp
|
||||||
|
|
|
||||||
|
|
@ -546,7 +546,7 @@ bool EdgeGrid::Grid::inside(const Point &pt_src)
|
||||||
return false;
|
return false;
|
||||||
coord_t ix = p(0) / m_resolution;
|
coord_t ix = p(0) / m_resolution;
|
||||||
coord_t iy = p(1) / m_resolution;
|
coord_t iy = p(1) / m_resolution;
|
||||||
if (ix >= this->m_cols || iy >= this->m_rows)
|
if (ix >= m_cols || iy >= m_rows)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
size_t i_closest = (size_t)-1;
|
size_t i_closest = (size_t)-1;
|
||||||
|
|
|
||||||
|
|
@ -122,10 +122,10 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||||
if (surface.surface_type == stInternalVoid)
|
if (surface.surface_type == stInternalVoid)
|
||||||
has_internal_voids = true;
|
has_internal_voids = true;
|
||||||
else {
|
else {
|
||||||
const PrintRegionConfig ®ion_config = layerm.region()->config();
|
const PrintRegionConfig ®ion_config = layerm.region().config();
|
||||||
FlowRole extrusion_role = surface.is_top() ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
|
FlowRole extrusion_role = surface.is_top() ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
|
||||||
bool is_bridge = layer.id() > 0 && surface.is_bridge();
|
bool is_bridge = layer.id() > 0 && surface.is_bridge();
|
||||||
params.extruder = layerm.region()->extruder(extrusion_role);
|
params.extruder = layerm.region().extruder(extrusion_role);
|
||||||
params.pattern = region_config.fill_pattern.value;
|
params.pattern = region_config.fill_pattern.value;
|
||||||
params.density = float(region_config.fill_density);
|
params.density = float(region_config.fill_density);
|
||||||
|
|
||||||
|
|
@ -162,7 +162,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||||
} else {
|
} else {
|
||||||
// Internal infill. Calculating infill line spacing independent of the current layer height and 1st layer status,
|
// Internal infill. Calculating infill line spacing independent of the current layer height and 1st layer status,
|
||||||
// so that internall infill will be aligned over all layers of the current region.
|
// so that internall infill will be aligned over all layers of the current region.
|
||||||
params.spacing = layerm.region()->flow(*layer.object(), frInfill, layer.object()->config().layer_height, false).spacing();
|
params.spacing = layerm.region().flow(*layer.object(), frInfill, layer.object()->config().layer_height, false).spacing();
|
||||||
// Anchor a sparse infill to inner perimeters with the following anchor length:
|
// Anchor a sparse infill to inner perimeters with the following anchor length:
|
||||||
params.anchor_length = float(region_config.infill_anchor);
|
params.anchor_length = float(region_config.infill_anchor);
|
||||||
if (region_config.infill_anchor.percent)
|
if (region_config.infill_anchor.percent)
|
||||||
|
|
@ -274,11 +274,11 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||||
}
|
}
|
||||||
if (internal_solid_fill == nullptr) {
|
if (internal_solid_fill == nullptr) {
|
||||||
// Produce another solid fill.
|
// Produce another solid fill.
|
||||||
params.extruder = layerm.region()->extruder(frSolidInfill);
|
params.extruder = layerm.region().extruder(frSolidInfill);
|
||||||
params.pattern = layerm.region()->config().top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear;
|
params.pattern = layerm.region().config().top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear;
|
||||||
params.density = 100.f;
|
params.density = 100.f;
|
||||||
params.extrusion_role = erInternalInfill;
|
params.extrusion_role = erInternalInfill;
|
||||||
params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
params.angle = float(Geometry::deg2rad(layerm.region().config().fill_angle.value));
|
||||||
// calculate the actual flow we'll be using for this infill
|
// calculate the actual flow we'll be using for this infill
|
||||||
params.flow = layerm.flow(frSolidInfill);
|
params.flow = layerm.flow(frSolidInfill);
|
||||||
params.spacing = params.flow.spacing();
|
params.spacing = params.flow.spacing();
|
||||||
|
|
@ -501,7 +501,7 @@ void Layer::make_ironing()
|
||||||
for (LayerRegion *layerm : m_regions)
|
for (LayerRegion *layerm : m_regions)
|
||||||
if (! layerm->slices.empty()) {
|
if (! layerm->slices.empty()) {
|
||||||
IroningParams ironing_params;
|
IroningParams ironing_params;
|
||||||
const PrintRegionConfig &config = layerm->region()->config();
|
const PrintRegionConfig &config = layerm->region().config();
|
||||||
if (config.ironing &&
|
if (config.ironing &&
|
||||||
(config.ironing_type == IroningType::AllSolid ||
|
(config.ironing_type == IroningType::AllSolid ||
|
||||||
(config.top_solid_layers > 0 &&
|
(config.top_solid_layers > 0 &&
|
||||||
|
|
@ -556,7 +556,7 @@ void Layer::make_ironing()
|
||||||
Polygons infills;
|
Polygons infills;
|
||||||
for (size_t k = i; k < j; ++ k) {
|
for (size_t k = i; k < j; ++ k) {
|
||||||
const IroningParams &ironing_params = by_extruder[k];
|
const IroningParams &ironing_params = by_extruder[k];
|
||||||
const PrintRegionConfig ®ion_config = ironing_params.layerm->region()->config();
|
const PrintRegionConfig ®ion_config = ironing_params.layerm->region().config();
|
||||||
bool iron_everything = region_config.ironing_type == IroningType::AllSolid;
|
bool iron_everything = region_config.ironing_type == IroningType::AllSolid;
|
||||||
bool iron_completely = iron_everything;
|
bool iron_completely = iron_everything;
|
||||||
if (iron_everything) {
|
if (iron_everything) {
|
||||||
|
|
|
||||||
|
|
@ -291,13 +291,13 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
|
||||||
double extrusion_width;
|
double extrusion_width;
|
||||||
};
|
};
|
||||||
std::vector<RegionFillData> region_fill_data;
|
std::vector<RegionFillData> region_fill_data;
|
||||||
region_fill_data.reserve(print_object.print()->regions().size());
|
region_fill_data.reserve(print_object.num_printing_regions());
|
||||||
bool build_octree = false;
|
bool build_octree = false;
|
||||||
const std::vector<double> &nozzle_diameters = print_object.print()->config().nozzle_diameter.values;
|
const std::vector<double> &nozzle_diameters = print_object.print()->config().nozzle_diameter.values;
|
||||||
double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
|
double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
|
||||||
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
|
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
|
||||||
for (const PrintRegion *region : print_object.print()->regions()) {
|
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
|
||||||
const PrintRegionConfig &config = region->config();
|
const PrintRegionConfig &config = print_object.printing_region(region_id).config();
|
||||||
bool nonempty = config.fill_density > 0;
|
bool nonempty = config.fill_density > 0;
|
||||||
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
|
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
|
||||||
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
|
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
|
||||||
|
|
|
||||||
|
|
@ -611,12 +611,47 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
||||||
|
|
||||||
// free functions called by GCode::do_export()
|
// free functions called by GCode::do_export()
|
||||||
namespace DoExport {
|
namespace DoExport {
|
||||||
static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics)
|
// static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics)
|
||||||
|
// {
|
||||||
|
// const GCodeProcessor::Result& 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";
|
||||||
|
// }
|
||||||
|
|
||||||
|
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 GCodeProcessor::Result& result = processor.get_result();
|
||||||
print_statistics.estimated_normal_print_time = get_time_dhms(result.time_statistics.modes[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].time);
|
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() ?
|
print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
|
||||||
get_time_dhms(result.time_statistics.modes[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].time) : "N/A";
|
get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
|
||||||
|
|
||||||
|
// update filament statictics
|
||||||
|
double total_extruded_volume = 0.0;
|
||||||
|
double total_used_filament = 0.0;
|
||||||
|
double total_weight = 0.0;
|
||||||
|
double total_cost = 0.0;
|
||||||
|
for (auto volume : result.print_statistics.volumes_per_extruder) {
|
||||||
|
total_extruded_volume += volume.second;
|
||||||
|
|
||||||
|
size_t extruder_id = volume.first;
|
||||||
|
auto extruder = std::find_if(extruders.begin(), extruders.end(), [extruder_id](const Extruder& extr) { return extr.id() == extruder_id; });
|
||||||
|
if (extruder == extruders.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
double s = PI * sqr(0.5* extruder->filament_diameter());
|
||||||
|
double weight = volume.second * extruder->filament_density() * 0.001;
|
||||||
|
total_used_filament += volume.second/s;
|
||||||
|
total_weight += weight;
|
||||||
|
total_cost += weight * extruder->filament_cost() * 0.001;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_statistics.total_extruded_volume = total_extruded_volume;
|
||||||
|
print_statistics.total_used_filament = total_used_filament;
|
||||||
|
print_statistics.total_weight = total_weight;
|
||||||
|
print_statistics.total_cost = total_cost;
|
||||||
|
|
||||||
|
print_statistics.filament_stats = result.print_statistics.volumes_per_extruder;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||||
|
|
@ -754,7 +789,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
|
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
|
||||||
m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); });
|
m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); });
|
||||||
DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
|
// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
|
||||||
|
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
|
||||||
#if ENABLE_GCODE_WINDOW
|
#if ENABLE_GCODE_WINDOW
|
||||||
if (result != nullptr) {
|
if (result != nullptr) {
|
||||||
*result = std::move(m_processor.extract_result());
|
*result = std::move(m_processor.extract_result());
|
||||||
|
|
@ -796,19 +832,19 @@ namespace DoExport {
|
||||||
// get the minimum cross-section used in the print
|
// get the minimum cross-section used in the print
|
||||||
std::vector<double> mm3_per_mm;
|
std::vector<double> mm3_per_mm;
|
||||||
for (auto object : print.objects()) {
|
for (auto object : print.objects()) {
|
||||||
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) {
|
||||||
const PrintRegion* region = print.regions()[region_id];
|
const PrintRegion ®ion = object->printing_region(region_id);
|
||||||
for (auto layer : object->layers()) {
|
for (auto layer : object->layers()) {
|
||||||
const LayerRegion* layerm = layer->regions()[region_id];
|
const LayerRegion* layerm = layer->regions()[region_id];
|
||||||
if (region->config().get_abs_value("perimeter_speed") == 0 ||
|
if (region.config().get_abs_value("perimeter_speed") == 0 ||
|
||||||
region->config().get_abs_value("small_perimeter_speed") == 0 ||
|
region.config().get_abs_value("small_perimeter_speed") == 0 ||
|
||||||
region->config().get_abs_value("external_perimeter_speed") == 0 ||
|
region.config().get_abs_value("external_perimeter_speed") == 0 ||
|
||||||
region->config().get_abs_value("bridge_speed") == 0)
|
region.config().get_abs_value("bridge_speed") == 0)
|
||||||
mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm());
|
mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm());
|
||||||
if (region->config().get_abs_value("infill_speed") == 0 ||
|
if (region.config().get_abs_value("infill_speed") == 0 ||
|
||||||
region->config().get_abs_value("solid_infill_speed") == 0 ||
|
region.config().get_abs_value("solid_infill_speed") == 0 ||
|
||||||
region->config().get_abs_value("top_solid_infill_speed") == 0 ||
|
region.config().get_abs_value("top_solid_infill_speed") == 0 ||
|
||||||
region->config().get_abs_value("bridge_speed") == 0)
|
region.config().get_abs_value("bridge_speed") == 0)
|
||||||
{
|
{
|
||||||
// Minimal volumetric flow should not be calculated over ironing extrusions.
|
// Minimal volumetric flow should not be calculated over ironing extrusions.
|
||||||
// Use following lambda instead of the built-it method.
|
// Use following lambda instead of the built-it method.
|
||||||
|
|
@ -957,7 +993,6 @@ namespace DoExport {
|
||||||
dst.first += buf;
|
dst.first += buf;
|
||||||
++ dst.second;
|
++ dst.second;
|
||||||
};
|
};
|
||||||
print_statistics.filament_stats.insert(std::pair<size_t, float>{extruder.id(), (float)used_filament});
|
|
||||||
append(out_filament_used_mm, "%.2lf", used_filament);
|
append(out_filament_used_mm, "%.2lf", used_filament);
|
||||||
append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001);
|
append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001);
|
||||||
if (filament_weight > 0.) {
|
if (filament_weight > 0.) {
|
||||||
|
|
@ -1112,16 +1147,17 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
||||||
const double layer_height = first_object->config().layer_height.value;
|
const double layer_height = first_object->config().layer_height.value;
|
||||||
assert(! print.config().first_layer_height.percent);
|
assert(! print.config().first_layer_height.percent);
|
||||||
const double first_layer_height = print.config().first_layer_height.value;
|
const double first_layer_height = print.config().first_layer_height.value;
|
||||||
for (const PrintRegion* region : print.regions()) {
|
for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
|
||||||
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width());
|
const PrintRegion ®ion = print.get_print_region(region_id);
|
||||||
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width());
|
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
|
||||||
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(*first_object, frInfill, layer_height).width());
|
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
|
||||||
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(*first_object, frSolidInfill, layer_height).width());
|
_write_format(file, "; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
|
||||||
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(*first_object, frTopSolidInfill, layer_height).width());
|
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
|
||||||
|
_write_format(file, "; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
|
||||||
if (print.has_support_material())
|
if (print.has_support_material())
|
||||||
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
|
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
|
||||||
if (print.config().first_layer_extrusion_width.value > 0)
|
if (print.config().first_layer_extrusion_width.value > 0)
|
||||||
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, first_layer_height, true).width());
|
_write_format(file, "; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width());
|
||||||
_write_format(file, "\n");
|
_write_format(file, "\n");
|
||||||
}
|
}
|
||||||
print.throw_if_canceled();
|
print.throw_if_canceled();
|
||||||
|
|
@ -1935,7 +1971,7 @@ void GCode::process_layer(
|
||||||
bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt());
|
bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt());
|
||||||
if (enable) {
|
if (enable) {
|
||||||
for (const LayerRegion *layer_region : layer.regions())
|
for (const LayerRegion *layer_region : layer.regions())
|
||||||
if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() ||
|
if (size_t(layer_region->region().config().bottom_solid_layers.value) > layer.id() ||
|
||||||
layer_region->perimeters.items_count() > 1u ||
|
layer_region->perimeters.items_count() > 1u ||
|
||||||
layer_region->fills.items_count() > 0) {
|
layer_region->fills.items_count() > 0) {
|
||||||
enable = false;
|
enable = false;
|
||||||
|
|
@ -2109,7 +2145,9 @@ void GCode::process_layer(
|
||||||
const LayerRegion *layerm = layer.regions()[region_id];
|
const LayerRegion *layerm = layer.regions()[region_id];
|
||||||
if (layerm == nullptr)
|
if (layerm == nullptr)
|
||||||
continue;
|
continue;
|
||||||
const PrintRegion ®ion = *print.regions()[region_id];
|
// PrintObjects own the PrintRegions, thus the pointer to PrintRegion would be unique to a PrintObject, they would not
|
||||||
|
// identify the content of PrintRegion accross the whole print uniquely. Translate to a Print specific PrintRegion.
|
||||||
|
const PrintRegion ®ion = print.get_print_region(layerm->region().print_region_id());
|
||||||
|
|
||||||
// Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
|
// Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
|
||||||
// It is also necessary to save which extrusions are part of MM wiping and which are not.
|
// It is also necessary to save which extrusions are part of MM wiping and which are not.
|
||||||
|
|
@ -2167,8 +2205,8 @@ void GCode::process_layer(
|
||||||
// extrusions->first_point fits inside ith slice
|
// extrusions->first_point fits inside ith slice
|
||||||
point_inside_surface(island_idx, extrusions->first_point())) {
|
point_inside_surface(island_idx, extrusions->first_point())) {
|
||||||
if (islands[island_idx].by_region.empty())
|
if (islands[island_idx].by_region.empty())
|
||||||
islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
|
islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region());
|
||||||
islands[island_idx].by_region[region_id].append(entity_type, extrusions, entity_overrides);
|
islands[island_idx].by_region[region.print_region_id()].append(entity_type, extrusions, entity_overrides);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2570,7 +2608,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
|
||||||
std::string gcode;
|
std::string gcode;
|
||||||
for (const ObjectByExtruder::Island::Region ®ion : by_region)
|
for (const ObjectByExtruder::Island::Region ®ion : by_region)
|
||||||
if (! region.perimeters.empty()) {
|
if (! region.perimeters.empty()) {
|
||||||
m_config.apply(print.regions()[®ion - &by_region.front()]->config());
|
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
||||||
for (const ExtrusionEntity *ee : region.perimeters)
|
for (const ExtrusionEntity *ee : region.perimeters)
|
||||||
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
|
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
|
||||||
}
|
}
|
||||||
|
|
@ -2591,7 +2629,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
|
||||||
if ((ee->role() == erIroning) == ironing)
|
if ((ee->role() == erIroning) == ironing)
|
||||||
extrusions.emplace_back(ee);
|
extrusions.emplace_back(ee);
|
||||||
if (! extrusions.empty()) {
|
if (! extrusions.empty()) {
|
||||||
m_config.apply(print.regions()[®ion - &by_region.front()]->config());
|
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
||||||
chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
|
chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
|
||||||
for (const ExtrusionEntity *fill : extrusions) {
|
for (const ExtrusionEntity *fill : extrusions) {
|
||||||
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
|
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,72 @@ void GCodeProcessor::TimeMachine::CustomGCodeTime::reset()
|
||||||
times = std::vector<std::pair<CustomGCode::Type, float>>();
|
times = std::vector<std::pair<CustomGCode::Type, float>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCodeProcessor::UsedFilaments::reset()
|
||||||
|
{
|
||||||
|
color_change_cache = 0.0f;
|
||||||
|
volumes_per_color_change = std::vector<double>();
|
||||||
|
|
||||||
|
tool_change_cache = 0.0f;
|
||||||
|
volumes_per_extruder.clear();
|
||||||
|
|
||||||
|
role_cache = 0.0f;
|
||||||
|
filaments_per_role.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeProcessor::UsedFilaments::increase_caches(double extruded_volume)
|
||||||
|
{
|
||||||
|
color_change_cache += extruded_volume;
|
||||||
|
tool_change_cache += extruded_volume;
|
||||||
|
role_cache += extruded_volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeProcessor::UsedFilaments::process_color_change_cache()
|
||||||
|
{
|
||||||
|
if (color_change_cache != 0.0f) {
|
||||||
|
volumes_per_color_change.push_back(color_change_cache);
|
||||||
|
color_change_cache = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeProcessor::UsedFilaments::process_extruder_cache(GCodeProcessor* processor)
|
||||||
|
{
|
||||||
|
size_t active_extruder_id = processor->m_extruder_id;
|
||||||
|
if (tool_change_cache != 0.0f) {
|
||||||
|
if (volumes_per_extruder.find(active_extruder_id) != volumes_per_extruder.end())
|
||||||
|
volumes_per_extruder[active_extruder_id] += tool_change_cache;
|
||||||
|
else
|
||||||
|
volumes_per_extruder[active_extruder_id] = tool_change_cache;
|
||||||
|
tool_change_cache = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeProcessor::UsedFilaments::process_role_cache(GCodeProcessor* processor)
|
||||||
|
{
|
||||||
|
if (role_cache != 0.0f) {
|
||||||
|
std::pair<double, double> filament = { 0.0f, 0.0f };
|
||||||
|
|
||||||
|
double s = PI * sqr(0.5 * processor->m_filament_diameters[processor->m_extruder_id]);
|
||||||
|
filament.first = role_cache/s * 0.001;
|
||||||
|
filament.second = role_cache * processor->m_filament_densities[processor->m_extruder_id] * 0.001;
|
||||||
|
|
||||||
|
ExtrusionRole active_role = processor->m_extrusion_role;
|
||||||
|
if (filaments_per_role.find(active_role) != filaments_per_role.end()) {
|
||||||
|
filaments_per_role[active_role].first += filament.first;
|
||||||
|
filaments_per_role[active_role].second += filament.second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
filaments_per_role[active_role] = filament;
|
||||||
|
role_cache = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodeProcessor::UsedFilaments::process_caches(GCodeProcessor* processor)
|
||||||
|
{
|
||||||
|
process_color_change_cache();
|
||||||
|
process_extruder_cache(processor);
|
||||||
|
process_role_cache(processor);
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeProcessor::TimeMachine::reset()
|
void GCodeProcessor::TimeMachine::reset()
|
||||||
{
|
{
|
||||||
enabled = false;
|
enabled = false;
|
||||||
|
|
@ -348,10 +414,10 @@ void GCodeProcessor::TimeProcessor::reset()
|
||||||
machine_limits = MachineEnvelopeConfig();
|
machine_limits = MachineEnvelopeConfig();
|
||||||
filament_load_times = std::vector<float>();
|
filament_load_times = std::vector<float>();
|
||||||
filament_unload_times = std::vector<float>();
|
filament_unload_times = std::vector<float>();
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
machines[i].reset();
|
machines[i].reset();
|
||||||
}
|
}
|
||||||
machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].enabled = true;
|
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||||
|
|
@ -416,19 +482,19 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
||||||
size_t g1_lines_counter = 0;
|
size_t g1_lines_counter = 0;
|
||||||
// keeps track of last exported pair <percent, remaining time>
|
// keeps track of last exported pair <percent, remaining time>
|
||||||
#if ENABLE_EXTENDED_M73_LINES
|
#if ENABLE_EXTENDED_M73_LINES
|
||||||
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported_main;
|
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_main;
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
last_exported_main[i] = { 0, time_in_minutes(machines[i].time) };
|
last_exported_main[i] = { 0, time_in_minutes(machines[i].time) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// keeps track of last exported remaining time to next printer stop
|
// keeps track of last exported remaining time to next printer stop
|
||||||
std::array<int, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported_stop;
|
std::array<int, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_stop;
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
last_exported_stop[i] = time_in_minutes(machines[i].time);
|
last_exported_stop[i] = time_in_minutes(machines[i].time);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> last_exported;
|
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported;
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
last_exported[i] = { 0, time_in_minutes(machines[i].time) };
|
last_exported[i] = { 0, time_in_minutes(machines[i].time) };
|
||||||
}
|
}
|
||||||
#endif // ENABLE_EXTENDED_M73_LINES
|
#endif // ENABLE_EXTENDED_M73_LINES
|
||||||
|
|
@ -451,7 +517,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
||||||
line = line.substr(1);
|
line = line.substr(1);
|
||||||
if (export_remaining_time_enabled &&
|
if (export_remaining_time_enabled &&
|
||||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder))) {
|
(line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder))) {
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
const TimeMachine& machine = machines[i];
|
const TimeMachine& machine = machines[i];
|
||||||
if (machine.enabled) {
|
if (machine.enabled) {
|
||||||
#if ENABLE_EXTENDED_M73_LINES
|
#if ENABLE_EXTENDED_M73_LINES
|
||||||
|
|
@ -486,7 +552,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
||||||
else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
|
else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
|
||||||
#else
|
#else
|
||||||
if (export_remaining_time_enabled && (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag)) {
|
if (export_remaining_time_enabled && (line == First_Line_M73_Placeholder_Tag || line == Last_Line_M73_Placeholder_Tag)) {
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
const TimeMachine& machine = machines[i];
|
const TimeMachine& machine = machines[i];
|
||||||
if (machine.enabled) {
|
if (machine.enabled) {
|
||||||
ret += format_line_M73(machine.line_m73_mask.c_str(),
|
ret += format_line_M73(machine.line_m73_mask.c_str(),
|
||||||
|
|
@ -497,13 +563,13 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
||||||
}
|
}
|
||||||
else if (line == Estimated_Printing_Time_Placeholder_Tag) {
|
else if (line == Estimated_Printing_Time_Placeholder_Tag) {
|
||||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
const TimeMachine& machine = machines[i];
|
const TimeMachine& machine = machines[i];
|
||||||
PrintEstimatedTimeStatistics::ETimeMode mode = static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i);
|
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
|
||||||
if (mode == PrintEstimatedTimeStatistics::ETimeMode::Normal || machine.enabled) {
|
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
sprintf(buf, "; estimated printing time (%s mode) = %s\n",
|
sprintf(buf, "; estimated printing time (%s mode) = %s\n",
|
||||||
(mode == PrintEstimatedTimeStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||||
get_time_dhms(machine.time).c_str());
|
get_time_dhms(machine.time).c_str());
|
||||||
ret += buf;
|
ret += buf;
|
||||||
}
|
}
|
||||||
|
|
@ -545,7 +611,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
||||||
unsigned int exported_lines_count = 0;
|
unsigned int exported_lines_count = 0;
|
||||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||||
if (export_remaining_time_enabled) {
|
if (export_remaining_time_enabled) {
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
const TimeMachine& machine = machines[i];
|
const TimeMachine& machine = machines[i];
|
||||||
if (machine.enabled) {
|
if (machine.enabled) {
|
||||||
// export pair <percent, remaining time>
|
// export pair <percent, remaining time>
|
||||||
|
|
@ -789,13 +855,13 @@ GCodeProcessor::GCodeProcessor()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
#if ENABLE_EXTENDED_M73_LINES
|
#if ENABLE_EXTENDED_M73_LINES
|
||||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n";
|
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n";
|
||||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_stop_mask = "M73 C%s\n";
|
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_stop_mask = "M73 C%s\n";
|
||||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_main_mask = "M73 Q%s S%s\n";
|
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_main_mask = "M73 Q%s S%s\n";
|
||||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n";
|
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n";
|
||||||
#else
|
#else
|
||||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n";
|
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_mask = "M73 P%s R%s\n";
|
||||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n";
|
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_mask = "M73 Q%s S%s\n";
|
||||||
#endif // ENABLE_EXTENDED_M73_LINES
|
#endif // ENABLE_EXTENDED_M73_LINES
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -826,6 +892,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||||
m_filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
|
m_filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_filament_densities.resize(config.filament_density.values.size());
|
||||||
|
for (size_t i = 0; i < config.filament_density.values.size(); ++i) {
|
||||||
|
m_filament_densities[i] = static_cast<float>(config.filament_density.values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {
|
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {
|
||||||
m_time_processor.machine_limits = reinterpret_cast<const MachineEnvelopeConfig&>(config);
|
m_time_processor.machine_limits = reinterpret_cast<const MachineEnvelopeConfig&>(config);
|
||||||
if (m_flavor == gcfMarlinLegacy) {
|
if (m_flavor == gcfMarlinLegacy) {
|
||||||
|
|
@ -846,7 +917,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||||
m_time_processor.filament_unload_times[i] = static_cast<float>(config.filament_unload_time.values[i]);
|
m_time_processor.filament_unload_times[i] = static_cast<float>(config.filament_unload_time.values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
|
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
|
||||||
m_time_processor.machines[i].max_acceleration = max_acceleration;
|
m_time_processor.machines[i].max_acceleration = max_acceleration;
|
||||||
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
|
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
|
||||||
|
|
@ -896,6 +967,13 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ConfigOptionFloats* filament_densities = config.option<ConfigOptionFloats>("filament_density");
|
||||||
|
if (filament_densities != nullptr) {
|
||||||
|
for (double dens : filament_densities->values) {
|
||||||
|
m_filament_densities.push_back(static_cast<float>(dens));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_result.extruders_count = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
m_result.extruders_count = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||||
|
|
||||||
const ConfigOptionPoints* extruder_offset = config.option<ConfigOptionPoints>("extruder_offset");
|
const ConfigOptionPoints* extruder_offset = config.option<ConfigOptionPoints>("extruder_offset");
|
||||||
|
|
@ -1026,7 +1104,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||||
m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values;
|
m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
|
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
|
||||||
m_time_processor.machines[i].max_acceleration = max_acceleration;
|
m_time_processor.machines[i].max_acceleration = max_acceleration;
|
||||||
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
|
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
|
||||||
|
|
@ -1051,7 +1129,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||||
|
|
||||||
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
|
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
|
||||||
{
|
{
|
||||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled = enabled;
|
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodeProcessor::reset()
|
void GCodeProcessor::reset()
|
||||||
|
|
@ -1096,6 +1174,7 @@ void GCodeProcessor::reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_filament_diameters = std::vector<float>(Min_Extruder_Count, 1.75f);
|
m_filament_diameters = std::vector<float>(Min_Extruder_Count, 1.75f);
|
||||||
|
m_filament_densities = std::vector<float>(Min_Extruder_Count, 1.245f);
|
||||||
m_extruded_last_z = 0.0f;
|
m_extruded_last_z = 0.0f;
|
||||||
#if ENABLE_START_GCODE_VISUALIZATION
|
#if ENABLE_START_GCODE_VISUALIZATION
|
||||||
m_first_layer_height = 0.0f;
|
m_first_layer_height = 0.0f;
|
||||||
|
|
@ -1109,6 +1188,7 @@ void GCodeProcessor::reset()
|
||||||
m_producers_enabled = false;
|
m_producers_enabled = false;
|
||||||
|
|
||||||
m_time_processor.reset();
|
m_time_processor.reset();
|
||||||
|
m_used_filaments.reset();
|
||||||
|
|
||||||
m_result.reset();
|
m_result.reset();
|
||||||
m_result.id = ++s_result_id;
|
m_result.id = ++s_result_id;
|
||||||
|
|
@ -1186,7 +1266,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
||||||
}
|
}
|
||||||
|
|
||||||
// process the time blocks
|
// process the time blocks
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
TimeMachine& machine = m_time_processor.machines[i];
|
TimeMachine& machine = m_time_processor.machines[i];
|
||||||
TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time;
|
TimeMachine::CustomGCodeTime& gcode_time = machine.gcode_time;
|
||||||
machine.calculate_time();
|
machine.calculate_time();
|
||||||
|
|
@ -1194,6 +1274,8 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
||||||
gcode_time.times.push_back({ CustomGCode::ColorChange, gcode_time.cache });
|
gcode_time.times.push_back({ CustomGCode::ColorChange, gcode_time.cache });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_used_filaments.process_caches(this);
|
||||||
|
|
||||||
update_estimated_times_stats();
|
update_estimated_times_stats();
|
||||||
|
|
||||||
// post-process to add M73 lines into the gcode
|
// post-process to add M73 lines into the gcode
|
||||||
|
|
@ -1216,20 +1298,20 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
||||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||||
}
|
}
|
||||||
|
|
||||||
float GCodeProcessor::get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const
|
float GCodeProcessor::get_time(PrintEstimatedStatistics::ETimeMode mode) const
|
||||||
{
|
{
|
||||||
return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].time : 0.0f;
|
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].time : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GCodeProcessor::get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const
|
std::string GCodeProcessor::get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const
|
||||||
{
|
{
|
||||||
return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].time)) : std::string("N/A");
|
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].time)) : std::string("N/A");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const
|
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const
|
||||||
{
|
{
|
||||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> ret;
|
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> ret;
|
||||||
if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) {
|
if (mode < PrintEstimatedStatistics::ETimeMode::Count) {
|
||||||
const TimeMachine& machine = m_time_processor.machines[static_cast<size_t>(mode)];
|
const TimeMachine& machine = m_time_processor.machines[static_cast<size_t>(mode)];
|
||||||
float total_time = 0.0f;
|
float total_time = 0.0f;
|
||||||
for (const auto& [type, time] : machine.gcode_time.times) {
|
for (const auto& [type, time] : machine.gcode_time.times) {
|
||||||
|
|
@ -1241,10 +1323,10 @@ std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcesso
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<EMoveType, float>> GCodeProcessor::get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const
|
std::vector<std::pair<EMoveType, float>> GCodeProcessor::get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const
|
||||||
{
|
{
|
||||||
std::vector<std::pair<EMoveType, float>> ret;
|
std::vector<std::pair<EMoveType, float>> ret;
|
||||||
if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) {
|
if (mode < PrintEstimatedStatistics::ETimeMode::Count) {
|
||||||
for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].moves_time.size(); ++i) {
|
for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].moves_time.size(); ++i) {
|
||||||
float time = m_time_processor.machines[static_cast<size_t>(mode)].moves_time[i];
|
float time = m_time_processor.machines[static_cast<size_t>(mode)].moves_time[i];
|
||||||
if (time > 0.0f)
|
if (time > 0.0f)
|
||||||
|
|
@ -1254,10 +1336,10 @@ std::vector<std::pair<EMoveType, float>> GCodeProcessor::get_moves_time(PrintEst
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const
|
std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const
|
||||||
{
|
{
|
||||||
std::vector<std::pair<ExtrusionRole, float>> ret;
|
std::vector<std::pair<ExtrusionRole, float>> ret;
|
||||||
if (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) {
|
if (mode < PrintEstimatedStatistics::ETimeMode::Count) {
|
||||||
for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].roles_time.size(); ++i) {
|
for (size_t i = 0; i < m_time_processor.machines[static_cast<size_t>(mode)].roles_time.size(); ++i) {
|
||||||
float time = m_time_processor.machines[static_cast<size_t>(mode)].roles_time[i];
|
float time = m_time_processor.machines[static_cast<size_t>(mode)].roles_time[i];
|
||||||
if (time > 0.0f)
|
if (time > 0.0f)
|
||||||
|
|
@ -1267,9 +1349,9 @@ std::vector<std::pair<ExtrusionRole, float>> GCodeProcessor::get_roles_time(Prin
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const
|
std::vector<float> GCodeProcessor::get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const
|
||||||
{
|
{
|
||||||
return (mode < PrintEstimatedTimeStatistics::ETimeMode::Count) ?
|
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ?
|
||||||
m_time_processor.machines[static_cast<size_t>(mode)].layers_time :
|
m_time_processor.machines[static_cast<size_t>(mode)].layers_time :
|
||||||
std::vector<float>();
|
std::vector<float>();
|
||||||
}
|
}
|
||||||
|
|
@ -1461,6 +1543,7 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
||||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||||
// extrusion role tag
|
// extrusion role tag
|
||||||
if (boost::starts_with(comment, reserved_tag(ETags::Role))) {
|
if (boost::starts_with(comment, reserved_tag(ETags::Role))) {
|
||||||
|
m_used_filaments.process_role_cache(this);
|
||||||
m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length()));
|
m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length()));
|
||||||
#if ENABLE_SEAMS_VISUALIZATION
|
#if ENABLE_SEAMS_VISUALIZATION
|
||||||
if (m_extrusion_role == erExternalPerimeter)
|
if (m_extrusion_role == erExternalPerimeter)
|
||||||
|
|
@ -1546,7 +1629,8 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
||||||
extruder_id = static_cast<unsigned char>(eid);
|
extruder_id = static_cast<unsigned char>(eid);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
|
if (extruder_id < m_extruder_colors.size())
|
||||||
|
m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
|
||||||
++m_cp_color.counter;
|
++m_cp_color.counter;
|
||||||
if (m_cp_color.counter == UCHAR_MAX)
|
if (m_cp_color.counter == UCHAR_MAX)
|
||||||
m_cp_color.counter = 0;
|
m_cp_color.counter = 0;
|
||||||
|
|
@ -1557,6 +1641,7 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
process_custom_gcode_time(CustomGCode::ColorChange);
|
process_custom_gcode_time(CustomGCode::ColorChange);
|
||||||
|
process_filaments(CustomGCode::ColorChange);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -2194,6 +2279,9 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||||
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
|
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
|
||||||
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
|
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
|
||||||
|
|
||||||
|
// save extruded volume to the cache
|
||||||
|
m_used_filaments.increase_caches(volume_extruded_filament);
|
||||||
|
|
||||||
// volume extruded filament / tool displacement = area toolpath cross section
|
// volume extruded filament / tool displacement = area toolpath cross section
|
||||||
m_mm3_per_mm = area_toolpath_cross_section;
|
m_mm3_per_mm = area_toolpath_cross_section;
|
||||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||||
|
|
@ -2254,7 +2342,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||||
assert(distance != 0.0f);
|
assert(distance != 0.0f);
|
||||||
float inv_distance = 1.0f / distance;
|
float inv_distance = 1.0f / distance;
|
||||||
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
TimeMachine& machine = m_time_processor.machines[i];
|
TimeMachine& machine = m_time_processor.machines[i];
|
||||||
if (!machine.enabled)
|
if (!machine.enabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -2264,8 +2352,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||||
std::vector<TimeBlock>& blocks = machine.blocks;
|
std::vector<TimeBlock>& blocks = machine.blocks;
|
||||||
|
|
||||||
curr.feedrate = (delta_pos[E] == 0.0f) ?
|
curr.feedrate = (delta_pos[E] == 0.0f) ?
|
||||||
minimum_travel_feedrate(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), m_feedrate) :
|
minimum_travel_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate) :
|
||||||
minimum_feedrate(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), m_feedrate);
|
minimum_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate);
|
||||||
|
|
||||||
TimeBlock block;
|
TimeBlock block;
|
||||||
block.move_type = type;
|
block.move_type = type;
|
||||||
|
|
@ -2283,7 +2371,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||||
|
|
||||||
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
|
curr.abs_axis_feedrate[a] = std::abs(curr.axis_feedrate[a]);
|
||||||
if (curr.abs_axis_feedrate[a] != 0.0f) {
|
if (curr.abs_axis_feedrate[a] != 0.0f) {
|
||||||
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
float axis_max_feedrate = get_axis_max_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||||
if (axis_max_feedrate != 0.0f)
|
if (axis_max_feedrate != 0.0f)
|
||||||
min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
|
min_feedrate_factor = std::min(min_feedrate_factor, axis_max_feedrate / curr.abs_axis_feedrate[a]);
|
||||||
}
|
}
|
||||||
|
|
@ -2300,13 +2388,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||||
|
|
||||||
// calculates block acceleration
|
// calculates block acceleration
|
||||||
float acceleration =
|
float acceleration =
|
||||||
(type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)) :
|
(type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
|
||||||
(is_extrusion_only_move(delta_pos) ?
|
(is_extrusion_only_move(delta_pos) ?
|
||||||
get_retract_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)) :
|
get_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
|
||||||
get_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)));
|
get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)));
|
||||||
|
|
||||||
for (unsigned char a = X; a <= E; ++a) {
|
for (unsigned char a = X; a <= E; ++a) {
|
||||||
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||||
if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration)
|
if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration)
|
||||||
acceleration = axis_max_acceleration;
|
acceleration = axis_max_acceleration;
|
||||||
}
|
}
|
||||||
|
|
@ -2317,7 +2405,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||||
curr.safe_feedrate = block.feedrate_profile.cruise;
|
curr.safe_feedrate = block.feedrate_profile.cruise;
|
||||||
|
|
||||||
for (unsigned char a = X; a <= E; ++a) {
|
for (unsigned char a = X; a <= E; ++a) {
|
||||||
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||||
if (curr.abs_axis_feedrate[a] > axis_max_jerk)
|
if (curr.abs_axis_feedrate[a] > axis_max_jerk)
|
||||||
curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk);
|
curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk);
|
||||||
}
|
}
|
||||||
|
|
@ -2365,7 +2453,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||||
// axis reversal
|
// axis reversal
|
||||||
std::max(-v_exit, v_entry));
|
std::max(-v_exit, v_entry));
|
||||||
|
|
||||||
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||||
if (jerk > axis_max_jerk) {
|
if (jerk > axis_max_jerk) {
|
||||||
v_factor *= axis_max_jerk / jerk;
|
v_factor *= axis_max_jerk / jerk;
|
||||||
limited = true;
|
limited = true;
|
||||||
|
|
@ -2650,8 +2738,8 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line)
|
||||||
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
|
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
|
||||||
float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
|
float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
|
||||||
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
|
||||||
m_time_processor.machine_envelope_processing_enabled) {
|
m_time_processor.machine_envelope_processing_enabled) {
|
||||||
if (line.has_x())
|
if (line.has_x())
|
||||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor);
|
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor);
|
||||||
|
|
@ -2678,8 +2766,8 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
|
||||||
// http://smoothieware.org/supported-g-codes
|
// http://smoothieware.org/supported-g-codes
|
||||||
float factor = (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC;
|
float factor = (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC;
|
||||||
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
|
||||||
m_time_processor.machine_envelope_processing_enabled) {
|
m_time_processor.machine_envelope_processing_enabled) {
|
||||||
if (line.has_x())
|
if (line.has_x())
|
||||||
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor);
|
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor);
|
||||||
|
|
@ -2699,27 +2787,27 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
|
||||||
void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
|
void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
|
||||||
{
|
{
|
||||||
float value;
|
float value;
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
|
||||||
m_time_processor.machine_envelope_processing_enabled) {
|
m_time_processor.machine_envelope_processing_enabled) {
|
||||||
if (line.has_value('S', value)) {
|
if (line.has_value('S', value)) {
|
||||||
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware
|
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware
|
||||||
// It is also generated by PrusaSlicer to control acceleration per extrusion type
|
// It is also generated by PrusaSlicer to control acceleration per extrusion type
|
||||||
// (perimeters, first layer etc) when 'Marlin (legacy)' flavor is used.
|
// (perimeters, first layer etc) when 'Marlin (legacy)' flavor is used.
|
||||||
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||||
set_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||||
if (line.has_value('T', value))
|
if (line.has_value('T', value))
|
||||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// New acceleration format, compatible with the upstream Marlin.
|
// New acceleration format, compatible with the upstream Marlin.
|
||||||
if (line.has_value('P', value))
|
if (line.has_value('P', value))
|
||||||
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||||
if (line.has_value('R', value))
|
if (line.has_value('R', value))
|
||||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||||
if (line.has_value('T', value))
|
if (line.has_value('T', value))
|
||||||
// Interpret the T value as the travel acceleration in the new Marlin format.
|
// Interpret the T value as the travel acceleration in the new Marlin format.
|
||||||
set_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2727,8 +2815,8 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
|
||||||
|
|
||||||
void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line)
|
void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
if (static_cast<PrintEstimatedStatistics::ETimeMode>(i) == PrintEstimatedStatistics::ETimeMode::Normal ||
|
||||||
m_time_processor.machine_envelope_processing_enabled) {
|
m_time_processor.machine_envelope_processing_enabled) {
|
||||||
if (line.has_x()) {
|
if (line.has_x()) {
|
||||||
float max_jerk = line.x();
|
float max_jerk = line.x();
|
||||||
|
|
@ -2761,7 +2849,7 @@ void GCodeProcessor::process_M221(const GCodeReader::GCodeLine& line)
|
||||||
float value_t;
|
float value_t;
|
||||||
if (line.has_value('S', value_s) && !line.has_value('T', value_t)) {
|
if (line.has_value('S', value_s) && !line.has_value('T', value_t)) {
|
||||||
value_s *= 0.01f;
|
value_s *= 0.01f;
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
m_time_processor.machines[i].extrude_factor_override_percentage = value_s;
|
m_time_processor.machines[i].extrude_factor_override_percentage = value_s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2812,7 +2900,7 @@ void GCodeProcessor::process_M402(const GCodeReader::GCodeLine& line)
|
||||||
|
|
||||||
void GCodeProcessor::process_M566(const GCodeReader::GCodeLine& line)
|
void GCodeProcessor::process_M566(const GCodeReader::GCodeLine& line)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
if (line.has_x())
|
if (line.has_x())
|
||||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, line.x() * MMMIN_TO_MMSEC);
|
set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, line.x() * MMMIN_TO_MMSEC);
|
||||||
|
|
||||||
|
|
@ -2863,6 +2951,7 @@ void GCodeProcessor::process_T(const std::string_view command)
|
||||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode.";
|
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode.";
|
||||||
else {
|
else {
|
||||||
unsigned char old_extruder_id = m_extruder_id;
|
unsigned char old_extruder_id = m_extruder_id;
|
||||||
|
process_filaments(CustomGCode::ToolChange);
|
||||||
m_extruder_id = id;
|
m_extruder_id = id;
|
||||||
m_cp_color.current = m_extruder_colors[id];
|
m_cp_color.current = m_extruder_colors[id];
|
||||||
// Specific to the MK3 MMU2:
|
// Specific to the MK3 MMU2:
|
||||||
|
|
@ -2920,7 +3009,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
|
||||||
#if ENABLE_EXTENDED_M73_LINES
|
#if ENABLE_EXTENDED_M73_LINES
|
||||||
// stores stop time placeholders for later use
|
// stores stop time placeholders for later use
|
||||||
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
|
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
TimeMachine& machine = m_time_processor.machines[i];
|
TimeMachine& machine = m_time_processor.machines[i];
|
||||||
if (!machine.enabled)
|
if (!machine.enabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -2931,7 +3020,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
|
||||||
#endif // ENABLE_EXTENDED_M73_LINES
|
#endif // ENABLE_EXTENDED_M73_LINES
|
||||||
}
|
}
|
||||||
|
|
||||||
float GCodeProcessor::minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const
|
float GCodeProcessor::minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const
|
||||||
{
|
{
|
||||||
if (m_time_processor.machine_limits.machine_min_extruding_rate.empty())
|
if (m_time_processor.machine_limits.machine_min_extruding_rate.empty())
|
||||||
return feedrate;
|
return feedrate;
|
||||||
|
|
@ -2939,7 +3028,7 @@ float GCodeProcessor::minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode m
|
||||||
return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, static_cast<size_t>(mode)));
|
return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, static_cast<size_t>(mode)));
|
||||||
}
|
}
|
||||||
|
|
||||||
float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const
|
float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const
|
||||||
{
|
{
|
||||||
if (m_time_processor.machine_limits.machine_min_travel_rate.empty())
|
if (m_time_processor.machine_limits.machine_min_travel_rate.empty())
|
||||||
return feedrate;
|
return feedrate;
|
||||||
|
|
@ -2947,7 +3036,7 @@ float GCodeProcessor::minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETim
|
||||||
return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast<size_t>(mode)));
|
return std::max(feedrate, get_option_value(m_time_processor.machine_limits.machine_min_travel_rate, static_cast<size_t>(mode)));
|
||||||
}
|
}
|
||||||
|
|
||||||
float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const
|
float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
|
||||||
{
|
{
|
||||||
switch (axis)
|
switch (axis)
|
||||||
{
|
{
|
||||||
|
|
@ -2959,7 +3048,7 @@ float GCodeProcessor::get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const
|
float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
|
||||||
{
|
{
|
||||||
switch (axis)
|
switch (axis)
|
||||||
{
|
{
|
||||||
|
|
@ -2971,7 +3060,7 @@ float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedTimeStatistics::ET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float GCodeProcessor::get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const
|
float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
|
||||||
{
|
{
|
||||||
switch (axis)
|
switch (axis)
|
||||||
{
|
{
|
||||||
|
|
@ -2983,18 +3072,18 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float GCodeProcessor::get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const
|
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
|
||||||
{
|
{
|
||||||
return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast<size_t>(mode));
|
return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast<size_t>(mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
float GCodeProcessor::get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const
|
float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
|
||||||
{
|
{
|
||||||
size_t id = static_cast<size_t>(mode);
|
size_t id = static_cast<size_t>(mode);
|
||||||
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].acceleration : DEFAULT_ACCELERATION;
|
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].acceleration : DEFAULT_ACCELERATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value)
|
void GCodeProcessor::set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
|
||||||
{
|
{
|
||||||
size_t id = static_cast<size_t>(mode);
|
size_t id = static_cast<size_t>(mode);
|
||||||
if (id < m_time_processor.machines.size()) {
|
if (id < m_time_processor.machines.size()) {
|
||||||
|
|
@ -3004,13 +3093,13 @@ void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float GCodeProcessor::get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const
|
float GCodeProcessor::get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
|
||||||
{
|
{
|
||||||
size_t id = static_cast<size_t>(mode);
|
size_t id = static_cast<size_t>(mode);
|
||||||
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
|
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodeProcessor::set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value)
|
void GCodeProcessor::set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
|
||||||
{
|
{
|
||||||
size_t id = static_cast<size_t>(mode);
|
size_t id = static_cast<size_t>(mode);
|
||||||
if (id < m_time_processor.machines.size()) {
|
if (id < m_time_processor.machines.size()) {
|
||||||
|
|
@ -3038,7 +3127,7 @@ float GCodeProcessor::get_filament_unload_time(size_t extruder_id)
|
||||||
|
|
||||||
void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code)
|
void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
TimeMachine& machine = m_time_processor.machines[i];
|
TimeMachine& machine = m_time_processor.machines[i];
|
||||||
if (!machine.enabled)
|
if (!machine.enabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -3055,17 +3144,26 @@ void GCodeProcessor::process_custom_gcode_time(CustomGCode::Type code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCodeProcessor::process_filaments(CustomGCode::Type code)
|
||||||
|
{
|
||||||
|
if (code == CustomGCode::ColorChange)
|
||||||
|
m_used_filaments.process_color_change_cache();
|
||||||
|
|
||||||
|
if (code == CustomGCode::ToolChange)
|
||||||
|
m_used_filaments.process_extruder_cache(this);
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeProcessor::simulate_st_synchronize(float additional_time)
|
void GCodeProcessor::simulate_st_synchronize(float additional_time)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||||
m_time_processor.machines[i].simulate_st_synchronize(additional_time);
|
m_time_processor.machines[i].simulate_st_synchronize(additional_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodeProcessor::update_estimated_times_stats()
|
void GCodeProcessor::update_estimated_times_stats()
|
||||||
{
|
{
|
||||||
auto update_mode = [this](PrintEstimatedTimeStatistics::ETimeMode mode) {
|
auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) {
|
||||||
PrintEstimatedTimeStatistics::Mode& data = m_result.time_statistics.modes[static_cast<size_t>(mode)];
|
PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast<size_t>(mode)];
|
||||||
data.time = get_time(mode);
|
data.time = get_time(mode);
|
||||||
data.custom_gcode_times = get_custom_gcode_times(mode, true);
|
data.custom_gcode_times = get_custom_gcode_times(mode, true);
|
||||||
data.moves_times = get_moves_time(mode);
|
data.moves_times = get_moves_time(mode);
|
||||||
|
|
@ -3073,11 +3171,15 @@ void GCodeProcessor::update_estimated_times_stats()
|
||||||
data.layers_times = get_layers_time(mode);
|
data.layers_times = get_layers_time(mode);
|
||||||
};
|
};
|
||||||
|
|
||||||
update_mode(PrintEstimatedTimeStatistics::ETimeMode::Normal);
|
update_mode(PrintEstimatedStatistics::ETimeMode::Normal);
|
||||||
if (m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled)
|
if (m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled)
|
||||||
update_mode(PrintEstimatedTimeStatistics::ETimeMode::Stealth);
|
update_mode(PrintEstimatedStatistics::ETimeMode::Stealth);
|
||||||
else
|
else
|
||||||
m_result.time_statistics.modes[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].reset();
|
m_result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].reset();
|
||||||
|
|
||||||
|
m_result.print_statistics.volumes_per_color_change = m_used_filaments.volumes_per_color_change;
|
||||||
|
m_result.print_statistics.volumes_per_extruder = m_used_filaments.volumes_per_extruder;
|
||||||
|
m_result.print_statistics.used_filaments_per_role = m_used_filaments.filaments_per_role;
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace Slic3r */
|
} /* namespace Slic3r */
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace Slic3r {
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PrintEstimatedTimeStatistics
|
struct PrintEstimatedStatistics
|
||||||
{
|
{
|
||||||
enum class ETimeMode : unsigned char
|
enum class ETimeMode : unsigned char
|
||||||
{
|
{
|
||||||
|
|
@ -62,14 +62,21 @@ namespace Slic3r {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<double> volumes_per_color_change;
|
||||||
|
std::map<size_t, double> volumes_per_extruder;
|
||||||
|
std::map<ExtrusionRole, std::pair<double, double>> used_filaments_per_role;
|
||||||
|
|
||||||
std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes;
|
std::array<Mode, static_cast<size_t>(ETimeMode::Count)> modes;
|
||||||
|
|
||||||
PrintEstimatedTimeStatistics() { reset(); }
|
PrintEstimatedStatistics() { reset(); }
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
for (auto m : modes) {
|
for (auto m : modes) {
|
||||||
m.reset();
|
m.reset();
|
||||||
}
|
}
|
||||||
|
volumes_per_color_change.clear();
|
||||||
|
volumes_per_extruder.clear();
|
||||||
|
used_filaments_per_role.clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -314,7 +321,7 @@ namespace Slic3r {
|
||||||
// Additional load / unload times for a filament exchange sequence.
|
// Additional load / unload times for a filament exchange sequence.
|
||||||
std::vector<float> filament_load_times;
|
std::vector<float> filament_load_times;
|
||||||
std::vector<float> filament_unload_times;
|
std::vector<float> filament_unload_times;
|
||||||
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count)> machines;
|
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> machines;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
|
@ -327,6 +334,30 @@ namespace Slic3r {
|
||||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct UsedFilaments // filaments per ColorChange
|
||||||
|
{
|
||||||
|
double color_change_cache;
|
||||||
|
std::vector<double> volumes_per_color_change;
|
||||||
|
|
||||||
|
double tool_change_cache;
|
||||||
|
std::map<size_t, double> volumes_per_extruder;
|
||||||
|
|
||||||
|
double role_cache;
|
||||||
|
// ExtrusionRole : <used_filament_m, used_filament_g>
|
||||||
|
std::map<ExtrusionRole, std::pair<double, double>> filaments_per_role;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void increase_caches(double extruded_volume);
|
||||||
|
|
||||||
|
void process_color_change_cache();
|
||||||
|
void process_extruder_cache(GCodeProcessor* processor);
|
||||||
|
void process_role_cache(GCodeProcessor* processor);
|
||||||
|
void process_caches(GCodeProcessor* processor);
|
||||||
|
|
||||||
|
friend class GCodeProcessor;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#if !ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
#if !ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||||
struct MoveVertex
|
struct MoveVertex
|
||||||
|
|
@ -372,7 +403,7 @@ namespace Slic3r {
|
||||||
SettingsIds settings_ids;
|
SettingsIds settings_ids;
|
||||||
size_t extruders_count;
|
size_t extruders_count;
|
||||||
std::vector<std::string> extruder_colors;
|
std::vector<std::string> extruder_colors;
|
||||||
PrintEstimatedTimeStatistics time_statistics;
|
PrintEstimatedStatistics print_statistics;
|
||||||
|
|
||||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||||
int64_t time{ 0 };
|
int64_t time{ 0 };
|
||||||
|
|
@ -519,6 +550,7 @@ namespace Slic3r {
|
||||||
ExtruderColors m_extruder_colors;
|
ExtruderColors m_extruder_colors;
|
||||||
ExtruderTemps m_extruder_temps;
|
ExtruderTemps m_extruder_temps;
|
||||||
std::vector<float> m_filament_diameters;
|
std::vector<float> m_filament_diameters;
|
||||||
|
std::vector<float> m_filament_densities;
|
||||||
float m_extruded_last_z;
|
float m_extruded_last_z;
|
||||||
#if ENABLE_START_GCODE_VISUALIZATION
|
#if ENABLE_START_GCODE_VISUALIZATION
|
||||||
float m_first_layer_height; // mm
|
float m_first_layer_height; // mm
|
||||||
|
|
@ -550,6 +582,7 @@ namespace Slic3r {
|
||||||
bool m_producers_enabled;
|
bool m_producers_enabled;
|
||||||
|
|
||||||
TimeProcessor m_time_processor;
|
TimeProcessor m_time_processor;
|
||||||
|
UsedFilaments m_used_filaments;
|
||||||
|
|
||||||
Result m_result;
|
Result m_result;
|
||||||
static unsigned int s_result_id;
|
static unsigned int s_result_id;
|
||||||
|
|
@ -566,7 +599,7 @@ namespace Slic3r {
|
||||||
void apply_config(const PrintConfig& config);
|
void apply_config(const PrintConfig& config);
|
||||||
void enable_stealth_time_estimator(bool enabled);
|
void enable_stealth_time_estimator(bool enabled);
|
||||||
bool is_stealth_time_estimator_enabled() const {
|
bool is_stealth_time_estimator_enabled() const {
|
||||||
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Stealth)].enabled;
|
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;
|
||||||
}
|
}
|
||||||
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
|
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
|
||||||
void enable_producers(bool enabled) { m_producers_enabled = enabled; }
|
void enable_producers(bool enabled) { m_producers_enabled = enabled; }
|
||||||
|
|
@ -579,13 +612,13 @@ namespace Slic3r {
|
||||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||||
void process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback = nullptr);
|
void process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback = nullptr);
|
||||||
|
|
||||||
float get_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||||
std::string get_time_dhm(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedTimeStatistics::ETimeMode mode, bool include_remaining) const;
|
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const;
|
||||||
|
|
||||||
std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||||
std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
std::vector<std::pair<ExtrusionRole, float>> get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||||
std::vector<float> get_layers_time(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
std::vector<float> get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void apply_config(const DynamicPrintConfig& config);
|
void apply_config(const DynamicPrintConfig& config);
|
||||||
|
|
@ -701,20 +734,21 @@ namespace Slic3r {
|
||||||
|
|
||||||
void store_move_vertex(EMoveType type);
|
void store_move_vertex(EMoveType type);
|
||||||
|
|
||||||
float minimum_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
|
float minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const;
|
||||||
float minimum_travel_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, float feedrate) const;
|
float minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const;
|
||||||
float get_axis_max_feedrate(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
|
||||||
float get_axis_max_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
|
||||||
float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
|
||||||
float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||||
float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||||
void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
|
void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
|
||||||
float get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||||
void set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
|
void set_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
|
||||||
float get_filament_load_time(size_t extruder_id);
|
float get_filament_load_time(size_t extruder_id);
|
||||||
float get_filament_unload_time(size_t extruder_id);
|
float get_filament_unload_time(size_t extruder_id);
|
||||||
|
|
||||||
void process_custom_gcode_time(CustomGCode::Type code);
|
void process_custom_gcode_time(CustomGCode::Type code);
|
||||||
|
void process_filaments(CustomGCode::Type code);
|
||||||
|
|
||||||
// Simulates firmware st_synchronize() call
|
// Simulates firmware st_synchronize() call
|
||||||
void simulate_st_synchronize(float additional_time = 0.0f);
|
void simulate_st_synchronize(float additional_time = 0.0f);
|
||||||
|
|
|
||||||
|
|
@ -223,11 +223,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
||||||
layer_tools.extruder_override = extruder_override;
|
layer_tools.extruder_override = extruder_override;
|
||||||
|
|
||||||
// What extruders are required to print this object layer?
|
// What extruders are required to print this object layer?
|
||||||
for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) {
|
for (const LayerRegion *layerm : layer->regions()) {
|
||||||
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
|
const PrintRegion ®ion = layerm->region();
|
||||||
if (layerm == nullptr)
|
|
||||||
continue;
|
|
||||||
const PrintRegion ®ion = *object.print()->regions()[region_id];
|
|
||||||
|
|
||||||
if (! layerm->perimeters.entities.empty()) {
|
if (! layerm->perimeters.entities.empty()) {
|
||||||
bool something_nonoverriddable = true;
|
bool something_nonoverriddable = true;
|
||||||
|
|
@ -688,16 +685,14 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
||||||
|
|
||||||
// iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
|
// iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
|
||||||
for (unsigned int copy = 0; copy < num_of_copies; ++copy) {
|
for (unsigned int copy = 0; copy < num_of_copies; ++copy) {
|
||||||
|
for (const LayerRegion *layerm : this_layer->regions()) {
|
||||||
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
|
const auto ®ion = layerm->region();
|
||||||
const auto& region = *object->print()->regions()[region_id];
|
|
||||||
|
|
||||||
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
|
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool wipe_into_infill_only = ! object->config().wipe_into_objects && region.config().wipe_into_infill;
|
bool wipe_into_infill_only = ! object->config().wipe_into_objects && region.config().wipe_into_infill;
|
||||||
if (print.config().infill_first != perimeters_done || wipe_into_infill_only) {
|
if (print.config().infill_first != perimeters_done || wipe_into_infill_only) {
|
||||||
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
|
for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections
|
||||||
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
|
|
||||||
if (!is_overriddable(*fill, print.config(), *object, region))
|
if (!is_overriddable(*fill, print.config(), *object, region))
|
||||||
|
|
@ -721,7 +716,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
||||||
// Now the same for perimeters - see comments above for explanation:
|
// Now the same for perimeters - see comments above for explanation:
|
||||||
if (object->config().wipe_into_objects && print.config().infill_first == perimeters_done)
|
if (object->config().wipe_into_objects && print.config().infill_first == perimeters_done)
|
||||||
{
|
{
|
||||||
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) {
|
for (const ExtrusionEntity* ee : layerm->perimeters.entities) {
|
||||||
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
|
if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
|
||||||
set_extruder_override(fill, copy, new_extruder, num_of_copies);
|
set_extruder_override(fill, copy, new_extruder, num_of_copies);
|
||||||
|
|
@ -762,13 +757,12 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
|
||||||
size_t num_of_copies = object->instances().size();
|
size_t num_of_copies = object->instances().size();
|
||||||
|
|
||||||
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
|
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
|
||||||
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
|
for (const LayerRegion *layerm : this_layer->regions()) {
|
||||||
const auto& region = *object->print()->regions()[region_id];
|
const auto ®ion = layerm->region();
|
||||||
|
|
||||||
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
|
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
|
for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections
|
||||||
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
|
|
||||||
if (!is_overriddable(*fill, print.config(), *object, region)
|
if (!is_overriddable(*fill, print.config(), *object, region)
|
||||||
|
|
@ -791,7 +785,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now the same for perimeters - see comments above for explanation:
|
// Now the same for perimeters - see comments above for explanation:
|
||||||
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections
|
for (const ExtrusionEntity* ee : layerm->perimeters.entities) { // iterate through all perimeter Collections
|
||||||
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy))
|
if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy))
|
||||||
set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
|
set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
|
||||||
|
|
|
||||||
|
|
@ -500,9 +500,9 @@ WipeTower::ToolChangeResult WipeTower::construct_tcr(WipeTowerWriter& writer,
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.priming = priming;
|
result.priming = priming;
|
||||||
result.initial_tool = int(old_tool);
|
result.initial_tool = int(old_tool);
|
||||||
result.new_tool = int(this->m_current_tool);
|
result.new_tool = int(m_current_tool);
|
||||||
result.print_z = this->m_z_pos;
|
result.print_z = m_z_pos;
|
||||||
result.layer_height = this->m_layer_height;
|
result.layer_height = m_layer_height;
|
||||||
result.elapsed_time = writer.elapsed_time();
|
result.elapsed_time = writer.elapsed_time();
|
||||||
result.start_pos = writer.start_pos_rotated();
|
result.start_pos = writer.start_pos_rotated();
|
||||||
result.end_pos = priming ? writer.pos() : writer.pos_rotated();
|
result.end_pos = priming ? writer.pos() : writer.pos_rotated();
|
||||||
|
|
@ -630,7 +630,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
|
||||||
bool /*last_wipe_inside_wipe_tower*/)
|
bool /*last_wipe_inside_wipe_tower*/)
|
||||||
{
|
{
|
||||||
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
|
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
|
||||||
this->m_current_tool = tools.front();
|
m_current_tool = tools.front();
|
||||||
|
|
||||||
// The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210].
|
// The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210].
|
||||||
// Due to the XYZ calibration, this working space may shrink slightly from all directions,
|
// Due to the XYZ calibration, this working space may shrink slightly from all directions,
|
||||||
|
|
|
||||||
|
|
@ -164,10 +164,9 @@ public:
|
||||||
m_current_layer_finished = false;
|
m_current_layer_finished = false;
|
||||||
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
|
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
|
||||||
if (is_first_layer) {
|
if (is_first_layer) {
|
||||||
this->m_num_layer_changes = 0;
|
m_num_layer_changes = 0;
|
||||||
this->m_num_tool_changes = 0;
|
m_num_tool_changes = 0;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
++ m_num_layer_changes;
|
++ m_num_layer_changes;
|
||||||
|
|
||||||
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
|
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
|
||||||
|
|
|
||||||
|
|
@ -1083,8 +1083,7 @@ MedialAxis::process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* pol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool MedialAxis::validate_edge(const VD::edge_type* edge)
|
||||||
MedialAxis::validate_edge(const VD::edge_type* edge)
|
|
||||||
{
|
{
|
||||||
// prevent overflows and detect almost-infinite edges
|
// prevent overflows and detect almost-infinite edges
|
||||||
#ifndef CLIPPERLIB_INT32
|
#ifndef CLIPPERLIB_INT32
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ bool Layer::empty() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerRegion* Layer::add_region(PrintRegion* print_region)
|
LayerRegion* Layer::add_region(const PrintRegion *print_region)
|
||||||
{
|
{
|
||||||
m_regions.emplace_back(new LayerRegion(this, print_region));
|
m_regions.emplace_back(new LayerRegion(this, print_region));
|
||||||
return m_regions.back();
|
return m_regions.back();
|
||||||
|
|
@ -102,7 +102,7 @@ ExPolygons Layer::merged(float offset_scaled) const
|
||||||
}
|
}
|
||||||
Polygons polygons;
|
Polygons polygons;
|
||||||
for (LayerRegion *layerm : m_regions) {
|
for (LayerRegion *layerm : m_regions) {
|
||||||
const PrintRegionConfig &config = layerm->region()->config();
|
const PrintRegionConfig &config = layerm->region().config();
|
||||||
// Our users learned to bend Slic3r to produce empty volumes to act as subtracters. Only add the region if it is non-empty.
|
// Our users learned to bend Slic3r to produce empty volumes to act as subtracters. Only add the region if it is non-empty.
|
||||||
if (config.bottom_solid_layers > 0 || config.top_solid_layers > 0 || config.fill_density > 0. || config.perimeters > 0)
|
if (config.bottom_solid_layers > 0 || config.top_solid_layers > 0 || config.fill_density > 0. || config.perimeters > 0)
|
||||||
append(polygons, offset(layerm->slices.surfaces, offset_scaled));
|
append(polygons, offset(layerm->slices.surfaces, offset_scaled));
|
||||||
|
|
@ -134,7 +134,7 @@ void Layer::make_perimeters()
|
||||||
continue;
|
continue;
|
||||||
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
|
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
|
||||||
done[region_id] = true;
|
done[region_id] = true;
|
||||||
const PrintRegionConfig &config = (*layerm)->region()->config();
|
const PrintRegionConfig &config = (*layerm)->region().config();
|
||||||
|
|
||||||
// find compatible regions
|
// find compatible regions
|
||||||
LayerRegionPtrs layerms;
|
LayerRegionPtrs layerms;
|
||||||
|
|
@ -142,7 +142,7 @@ void Layer::make_perimeters()
|
||||||
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it)
|
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it)
|
||||||
if (! (*it)->slices.empty()) {
|
if (! (*it)->slices.empty()) {
|
||||||
LayerRegion* other_layerm = *it;
|
LayerRegion* other_layerm = *it;
|
||||||
const PrintRegionConfig &other_config = other_layerm->region()->config();
|
const PrintRegionConfig &other_config = other_layerm->region().config();
|
||||||
if (config.perimeter_extruder == other_config.perimeter_extruder
|
if (config.perimeter_extruder == other_config.perimeter_extruder
|
||||||
&& config.perimeters == other_config.perimeters
|
&& config.perimeters == other_config.perimeters
|
||||||
&& config.perimeter_speed == other_config.perimeter_speed
|
&& config.perimeter_speed == other_config.perimeter_speed
|
||||||
|
|
@ -180,7 +180,7 @@ void Layer::make_perimeters()
|
||||||
for (LayerRegion *layerm : layerms) {
|
for (LayerRegion *layerm : layerms) {
|
||||||
for (Surface &surface : layerm->slices.surfaces)
|
for (Surface &surface : layerm->slices.surfaces)
|
||||||
slices[surface.extra_perimeters].emplace_back(surface);
|
slices[surface.extra_perimeters].emplace_back(surface);
|
||||||
if (layerm->region()->config().fill_density > layerm_config->region()->config().fill_density)
|
if (layerm->region().config().fill_density > layerm_config->region().config().fill_density)
|
||||||
layerm_config = layerm;
|
layerm_config = layerm;
|
||||||
}
|
}
|
||||||
// merge the surfaces assigned to each group
|
// merge the surfaces assigned to each group
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ class LayerRegion
|
||||||
public:
|
public:
|
||||||
Layer* layer() { return m_layer; }
|
Layer* layer() { return m_layer; }
|
||||||
const Layer* layer() const { return m_layer; }
|
const Layer* layer() const { return m_layer; }
|
||||||
PrintRegion* region() { return m_region; }
|
const PrintRegion& region() const { return *m_region; }
|
||||||
const PrintRegion* region() const { return m_region; }
|
|
||||||
|
|
||||||
// collection of surfaces generated by slicing the original geometry
|
// collection of surfaces generated by slicing the original geometry
|
||||||
// divided by type top/bottom/internal
|
// divided by type top/bottom/internal
|
||||||
|
|
@ -86,12 +85,12 @@ public:
|
||||||
protected:
|
protected:
|
||||||
friend class Layer;
|
friend class Layer;
|
||||||
|
|
||||||
LayerRegion(Layer *layer, PrintRegion *region) : m_layer(layer), m_region(region) {}
|
LayerRegion(Layer *layer, const PrintRegion *region) : m_layer(layer), m_region(region) {}
|
||||||
~LayerRegion() {}
|
~LayerRegion() {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Layer *m_layer;
|
Layer *m_layer;
|
||||||
PrintRegion *m_region;
|
const PrintRegion *m_region;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -126,9 +125,9 @@ public:
|
||||||
std::vector<BoundingBox> lslices_bboxes;
|
std::vector<BoundingBox> lslices_bboxes;
|
||||||
|
|
||||||
size_t region_count() const { return m_regions.size(); }
|
size_t region_count() const { return m_regions.size(); }
|
||||||
const LayerRegion* get_region(int idx) const { return m_regions.at(idx); }
|
const LayerRegion* get_region(int idx) const { return m_regions[idx]; }
|
||||||
LayerRegion* get_region(int idx) { return m_regions[idx]; }
|
LayerRegion* get_region(int idx) { return m_regions[idx]; }
|
||||||
LayerRegion* add_region(PrintRegion* print_region);
|
LayerRegion* add_region(const PrintRegion *print_region);
|
||||||
const LayerRegionPtrs& regions() const { return m_regions; }
|
const LayerRegionPtrs& regions() const { return m_regions; }
|
||||||
// Test whether whether there are any slices assigned to this layer.
|
// Test whether whether there are any slices assigned to this layer.
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,14 @@ Flow LayerRegion::flow(FlowRole role, double layer_height) const
|
||||||
|
|
||||||
Flow LayerRegion::bridging_flow(FlowRole role) const
|
Flow LayerRegion::bridging_flow(FlowRole role) const
|
||||||
{
|
{
|
||||||
const PrintRegion ®ion = *this->region();
|
const PrintRegion ®ion = this->region();
|
||||||
const PrintRegionConfig ®ion_config = region.config();
|
const PrintRegionConfig ®ion_config = region.config();
|
||||||
if (this->layer()->object()->config().thick_bridges) {
|
const PrintObject &print_object = *this->layer()->object();
|
||||||
|
if (print_object.config().thick_bridges) {
|
||||||
// The old Slic3r way (different from all other slicers): Use rounded extrusions.
|
// The old Slic3r way (different from all other slicers): Use rounded extrusions.
|
||||||
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
|
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
|
||||||
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
|
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
|
||||||
auto nozzle_diameter = float(region.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
|
auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
|
||||||
// Applies default bridge spacing.
|
// Applies default bridge spacing.
|
||||||
return Flow::bridging_flow(float(sqrt(region_config.bridge_flow_ratio)) * nozzle_diameter, nozzle_diameter);
|
return Flow::bridging_flow(float(sqrt(region_config.bridge_flow_ratio)) * nozzle_diameter, nozzle_diameter);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -69,7 +70,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
||||||
this->thin_fills.clear();
|
this->thin_fills.clear();
|
||||||
|
|
||||||
const PrintConfig &print_config = this->layer()->object()->print()->config();
|
const PrintConfig &print_config = this->layer()->object()->print()->config();
|
||||||
const PrintRegionConfig ®ion_config = this->region()->config();
|
const PrintRegionConfig ®ion_config = this->region().config();
|
||||||
// This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
|
// This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
|
||||||
bool spiral_vase = print_config.spiral_vase &&
|
bool spiral_vase = print_config.spiral_vase &&
|
||||||
//FIXME account for raft layers.
|
//FIXME account for raft layers.
|
||||||
|
|
@ -110,7 +111,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
||||||
|
|
||||||
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
|
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
|
||||||
{
|
{
|
||||||
const bool has_infill = this->region()->config().fill_density.value > 0.;
|
const bool has_infill = this->region().config().fill_density.value > 0.;
|
||||||
const float margin = float(scale_(EXTERNAL_INFILL_MARGIN));
|
const float margin = float(scale_(EXTERNAL_INFILL_MARGIN));
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
|
|
@ -284,7 +285,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_DEBUG
|
||||||
printf("Processing bridge at layer %zu:\n", this->layer()->id());
|
printf("Processing bridge at layer %zu:\n", this->layer()->id());
|
||||||
#endif
|
#endif
|
||||||
double custom_angle = Geometry::deg2rad(this->region()->config().bridge_angle.value);
|
double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value);
|
||||||
if (bd.detect_angle(custom_angle)) {
|
if (bd.detect_angle(custom_angle)) {
|
||||||
bridges[idx_last].bridge_angle = bd.angle;
|
bridges[idx_last].bridge_angle = bd.angle;
|
||||||
if (this->layer()->object()->has_support()) {
|
if (this->layer()->object()->has_support()) {
|
||||||
|
|
@ -383,21 +384,21 @@ void LayerRegion::prepare_fill_surfaces()
|
||||||
bool spiral_vase = this->layer()->object()->print()->config().spiral_vase;
|
bool spiral_vase = this->layer()->object()->print()->config().spiral_vase;
|
||||||
|
|
||||||
// if no solid layers are requested, turn top/bottom surfaces to internal
|
// if no solid layers are requested, turn top/bottom surfaces to internal
|
||||||
if (! spiral_vase && this->region()->config().top_solid_layers == 0) {
|
if (! spiral_vase && this->region().config().top_solid_layers == 0) {
|
||||||
for (Surface &surface : this->fill_surfaces.surfaces)
|
for (Surface &surface : this->fill_surfaces.surfaces)
|
||||||
if (surface.is_top())
|
if (surface.is_top())
|
||||||
surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal;
|
surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal;
|
||||||
}
|
}
|
||||||
if (this->region()->config().bottom_solid_layers == 0) {
|
if (this->region().config().bottom_solid_layers == 0) {
|
||||||
for (Surface &surface : this->fill_surfaces.surfaces)
|
for (Surface &surface : this->fill_surfaces.surfaces)
|
||||||
if (surface.is_bottom()) // (surface.surface_type == stBottom)
|
if (surface.is_bottom()) // (surface.surface_type == stBottom)
|
||||||
surface.surface_type = stInternal;
|
surface.surface_type = stInternal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn too small internal regions into solid regions according to the user setting
|
// turn too small internal regions into solid regions according to the user setting
|
||||||
if (! spiral_vase && this->region()->config().fill_density.value > 0) {
|
if (! spiral_vase && this->region().config().fill_density.value > 0) {
|
||||||
// scaling an area requires two calls!
|
// scaling an area requires two calls!
|
||||||
double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value));
|
double min_area = scale_(scale_(this->region().config().solid_infill_below_area.value));
|
||||||
for (Surface &surface : this->fill_surfaces.surfaces)
|
for (Surface &surface : this->fill_surfaces.surfaces)
|
||||||
if (surface.surface_type == stInternal && surface.area() <= min_area)
|
if (surface.surface_type == stInternal && surface.area() <= min_area)
|
||||||
surface.surface_type = stInternalSolid;
|
surface.surface_type = stInternalSolid;
|
||||||
|
|
|
||||||
|
|
@ -942,30 +942,39 @@ void ModelObject::center_around_origin(bool include_modifiers)
|
||||||
{
|
{
|
||||||
// calculate the displacements needed to
|
// calculate the displacements needed to
|
||||||
// center this object around the origin
|
// center this object around the origin
|
||||||
BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box();
|
const BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box();
|
||||||
|
|
||||||
// Shift is the vector from the center of the bounding box to the origin
|
// Shift is the vector from the center of the bounding box to the origin
|
||||||
Vec3d shift = -bb.center();
|
const Vec3d shift = -bb.center();
|
||||||
|
|
||||||
this->translate(shift);
|
this->translate(shift);
|
||||||
this->origin_translation += shift;
|
this->origin_translation += shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
void ModelObject::ensure_on_bed(bool allow_negative_z)
|
||||||
|
{
|
||||||
|
const double min_z = get_min_z();
|
||||||
|
if (!allow_negative_z || min_z > 0.0)
|
||||||
|
translate_instances({ 0.0, 0.0, -min_z });
|
||||||
|
}
|
||||||
|
#else
|
||||||
void ModelObject::ensure_on_bed()
|
void ModelObject::ensure_on_bed()
|
||||||
{
|
{
|
||||||
translate_instances(Vec3d(0.0, 0.0, -get_min_z()));
|
translate_instances({ 0.0, 0.0, -get_min_z() });
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
void ModelObject::translate_instances(const Vec3d& vector)
|
void ModelObject::translate_instances(const Vec3d& vector)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < instances.size(); ++i)
|
for (size_t i = 0; i < instances.size(); ++i) {
|
||||||
{
|
|
||||||
translate_instance(i, vector);
|
translate_instance(i, vector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector)
|
void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector)
|
||||||
{
|
{
|
||||||
|
assert(instance_idx < instances.size());
|
||||||
ModelInstance* i = instances[instance_idx];
|
ModelInstance* i = instances[instance_idx];
|
||||||
i->set_offset(i->get_offset() + vector);
|
i->set_offset(i->get_offset() + vector);
|
||||||
invalidate_bounding_box();
|
invalidate_bounding_box();
|
||||||
|
|
@ -973,8 +982,7 @@ void ModelObject::translate_instance(size_t instance_idx, const Vec3d& vector)
|
||||||
|
|
||||||
void ModelObject::translate(double x, double y, double z)
|
void ModelObject::translate(double x, double y, double z)
|
||||||
{
|
{
|
||||||
for (ModelVolume *v : this->volumes)
|
for (ModelVolume *v : this->volumes) {
|
||||||
{
|
|
||||||
v->translate(x, y, z);
|
v->translate(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -984,8 +992,7 @@ void ModelObject::translate(double x, double y, double z)
|
||||||
|
|
||||||
void ModelObject::scale(const Vec3d &versor)
|
void ModelObject::scale(const Vec3d &versor)
|
||||||
{
|
{
|
||||||
for (ModelVolume *v : this->volumes)
|
for (ModelVolume *v : this->volumes) {
|
||||||
{
|
|
||||||
v->scale(versor);
|
v->scale(versor);
|
||||||
}
|
}
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
|
|
@ -993,41 +1000,34 @@ void ModelObject::scale(const Vec3d &versor)
|
||||||
|
|
||||||
void ModelObject::rotate(double angle, Axis axis)
|
void ModelObject::rotate(double angle, Axis axis)
|
||||||
{
|
{
|
||||||
for (ModelVolume *v : this->volumes)
|
for (ModelVolume *v : this->volumes) {
|
||||||
{
|
|
||||||
v->rotate(angle, axis);
|
v->rotate(angle, axis);
|
||||||
}
|
}
|
||||||
|
|
||||||
center_around_origin();
|
center_around_origin();
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelObject::rotate(double angle, const Vec3d& axis)
|
void ModelObject::rotate(double angle, const Vec3d& axis)
|
||||||
{
|
{
|
||||||
for (ModelVolume *v : this->volumes)
|
for (ModelVolume *v : this->volumes) {
|
||||||
{
|
|
||||||
v->rotate(angle, axis);
|
v->rotate(angle, axis);
|
||||||
}
|
}
|
||||||
|
|
||||||
center_around_origin();
|
center_around_origin();
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelObject::mirror(Axis axis)
|
void ModelObject::mirror(Axis axis)
|
||||||
{
|
{
|
||||||
for (ModelVolume *v : this->volumes)
|
for (ModelVolume *v : this->volumes) {
|
||||||
{
|
|
||||||
v->mirror(axis);
|
v->mirror(axis);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||||
void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
|
void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
|
||||||
{
|
{
|
||||||
for (ModelVolume *v : this->volumes)
|
for (ModelVolume *v : this->volumes) {
|
||||||
{
|
|
||||||
v->scale_geometry_after_creation(versor);
|
v->scale_geometry_after_creation(versor);
|
||||||
v->set_offset(versor.cwiseProduct(v->get_offset()));
|
v->set_offset(versor.cwiseProduct(v->get_offset()));
|
||||||
}
|
}
|
||||||
|
|
@ -1418,11 +1418,9 @@ double ModelObject::get_min_z() const
|
||||||
{
|
{
|
||||||
if (instances.empty())
|
if (instances.empty())
|
||||||
return 0.0;
|
return 0.0;
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
double min_z = DBL_MAX;
|
double min_z = DBL_MAX;
|
||||||
for (size_t i = 0; i < instances.size(); ++i)
|
for (size_t i = 0; i < instances.size(); ++i) {
|
||||||
{
|
|
||||||
min_z = std::min(min_z, get_instance_min_z(i));
|
min_z = std::min(min_z, get_instance_min_z(i));
|
||||||
}
|
}
|
||||||
return min_z;
|
return min_z;
|
||||||
|
|
@ -1433,15 +1431,14 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
|
||||||
{
|
{
|
||||||
double min_z = DBL_MAX;
|
double min_z = DBL_MAX;
|
||||||
|
|
||||||
ModelInstance* inst = instances[instance_idx];
|
const ModelInstance* inst = instances[instance_idx];
|
||||||
const Transform3d& mi = inst->get_matrix(true);
|
const Transform3d& mi = inst->get_matrix(true);
|
||||||
|
|
||||||
for (const ModelVolume* v : volumes)
|
for (const ModelVolume* v : volumes) {
|
||||||
{
|
|
||||||
if (!v->is_model_part())
|
if (!v->is_model_part())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Transform3d mv = mi * v->get_matrix();
|
const Transform3d mv = mi * v->get_matrix();
|
||||||
const TriangleMesh& hull = v->get_convex_hull();
|
const TriangleMesh& hull = v->get_convex_hull();
|
||||||
for (const stl_facet &facet : hull.stl.facet_start)
|
for (const stl_facet &facet : hull.stl.facet_start)
|
||||||
for (int i = 0; i < 3; ++ i)
|
for (int i = 0; i < 3; ++ i)
|
||||||
|
|
@ -1813,7 +1810,7 @@ void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_le
|
||||||
this->set_mesh(std::move(mesh));
|
this->set_mesh(std::move(mesh));
|
||||||
TriangleMesh convex_hull = this->get_convex_hull();
|
TriangleMesh convex_hull = this->get_convex_hull();
|
||||||
convex_hull.transform(mesh_trafo, fix_left_handed);
|
convex_hull.transform(mesh_trafo, fix_left_handed);
|
||||||
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
|
m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
|
||||||
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
|
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
|
||||||
this->set_new_unique_id();
|
this->set_new_unique_id();
|
||||||
}
|
}
|
||||||
|
|
@ -1825,7 +1822,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand
|
||||||
this->set_mesh(std::move(mesh));
|
this->set_mesh(std::move(mesh));
|
||||||
TriangleMesh convex_hull = this->get_convex_hull();
|
TriangleMesh convex_hull = this->get_convex_hull();
|
||||||
convex_hull.transform(matrix, fix_left_handed);
|
convex_hull.transform(matrix, fix_left_handed);
|
||||||
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
|
m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
|
||||||
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
|
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
|
||||||
this->set_new_unique_id();
|
this->set_new_unique_id();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -180,8 +180,8 @@ private:
|
||||||
class LayerHeightProfile final : public ObjectWithTimestamp {
|
class LayerHeightProfile final : public ObjectWithTimestamp {
|
||||||
public:
|
public:
|
||||||
// Assign the content if the timestamp differs, don't assign an ObjectID.
|
// Assign the content if the timestamp differs, don't assign an ObjectID.
|
||||||
void assign(const LayerHeightProfile &rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = rhs.m_data; this->copy_timestamp(rhs); } }
|
void assign(const LayerHeightProfile &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
|
||||||
void assign(LayerHeightProfile &&rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
|
void assign(LayerHeightProfile &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
|
||||||
|
|
||||||
std::vector<coordf_t> get() const throw() { return m_data; }
|
std::vector<coordf_t> get() const throw() { return m_data; }
|
||||||
bool empty() const throw() { return m_data.empty(); }
|
bool empty() const throw() { return m_data.empty(); }
|
||||||
|
|
@ -306,7 +306,11 @@ public:
|
||||||
|
|
||||||
void center_around_origin(bool include_modifiers = true);
|
void center_around_origin(bool include_modifiers = true);
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
void ensure_on_bed(bool allow_negative_z = false);
|
||||||
|
#else
|
||||||
void ensure_on_bed();
|
void ensure_on_bed();
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
void translate_instances(const Vec3d& vector);
|
void translate_instances(const Vec3d& vector);
|
||||||
void translate_instance(size_t instance_idx, const Vec3d& vector);
|
void translate_instance(size_t instance_idx, const Vec3d& vector);
|
||||||
void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); }
|
void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); }
|
||||||
|
|
@ -504,8 +508,8 @@ enum class ConversionType : int {
|
||||||
class FacetsAnnotation final : public ObjectWithTimestamp {
|
class FacetsAnnotation final : public ObjectWithTimestamp {
|
||||||
public:
|
public:
|
||||||
// Assign the content if the timestamp differs, don't assign an ObjectID.
|
// Assign the content if the timestamp differs, don't assign an ObjectID.
|
||||||
void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = rhs.m_data; this->copy_timestamp(rhs); } }
|
void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
|
||||||
void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { this->m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
|
void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
|
||||||
const std::map<int, std::vector<bool>>& get_data() const throw() { return m_data; }
|
const std::map<int, std::vector<bool>>& get_data() const throw() { return m_data; }
|
||||||
bool set(const TriangleSelector& selector);
|
bool set(const TriangleSelector& selector);
|
||||||
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||||
|
|
@ -680,6 +684,7 @@ protected:
|
||||||
friend class SLAPrint;
|
friend class SLAPrint;
|
||||||
friend class Model;
|
friend class Model;
|
||||||
friend class ModelObject;
|
friend class ModelObject;
|
||||||
|
friend void model_volume_list_update_supports(ModelObject& model_object_dst, const ModelObject& model_object_new);
|
||||||
|
|
||||||
// Copies IDs of both the ModelVolume and its config.
|
// Copies IDs of both the ModelVolume and its config.
|
||||||
explicit ModelVolume(const ModelVolume &rhs) = default;
|
explicit ModelVolume(const ModelVolume &rhs) = default;
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ public:
|
||||||
PointType* operator->() const { return &m_data->at(m_idx).point; }
|
PointType* operator->() const { return &m_data->at(m_idx).point; }
|
||||||
MutablePolygon& polygon() const { assert(this->valid()); return *m_data; }
|
MutablePolygon& polygon() const { assert(this->valid()); return *m_data; }
|
||||||
IndexType size() const { assert(this->valid()); return m_data->size(); }
|
IndexType size() const { assert(this->valid()); return m_data->size(); }
|
||||||
iterator& remove() { this->m_idx = m_data->remove(*this).m_idx; return *this; }
|
iterator& remove() { m_idx = m_data->remove(*this).m_idx; return *this; }
|
||||||
iterator insert(const PointType pt) const { return m_data->insert(*this, pt); }
|
iterator insert(const PointType pt) const { return m_data->insert(*this, pt); }
|
||||||
private:
|
private:
|
||||||
iterator(MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {}
|
iterator(MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {}
|
||||||
|
|
@ -162,10 +162,10 @@ public:
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool empty() const { return this->m_size == 0; }
|
bool empty() const { return m_size == 0; }
|
||||||
size_t size() const { return this->m_size; }
|
size_t size() const { return m_size; }
|
||||||
size_t capacity() const { return this->m_data.capacity(); }
|
size_t capacity() const { return m_data.capacity(); }
|
||||||
bool valid() const { return this->m_size >= 3; }
|
bool valid() const { return m_size >= 3; }
|
||||||
void clear() { m_data.clear(); m_size = 0; m_head = IndexType(-1); m_head_free = IndexType(-1); }
|
void clear() { m_data.clear(); m_size = 0; m_head = IndexType(-1); m_head_free = IndexType(-1); }
|
||||||
|
|
||||||
iterator begin() { return { this, m_head }; }
|
iterator begin() { return { this, m_head }; }
|
||||||
|
|
|
||||||
|
|
@ -121,8 +121,8 @@ protected:
|
||||||
if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff);
|
if(!std::isnan(rel_diff)) nlopt_set_ftol_rel(nl.ptr, rel_diff);
|
||||||
if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval);
|
if(!std::isnan(stopval)) nlopt_set_stopval(nl.ptr, stopval);
|
||||||
|
|
||||||
if(this->m_stopcr.max_iterations() > 0)
|
if(m_stopcr.max_iterations() > 0)
|
||||||
nlopt_set_maxeval(nl.ptr, this->m_stopcr.max_iterations());
|
nlopt_set_maxeval(nl.ptr, m_stopcr.max_iterations());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Fn, size_t N>
|
template<class Fn, size_t N>
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
|
||||||
// Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes.
|
// Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes.
|
||||||
// Based on https://www.lemoda.net/c/write-png/
|
// Based on https://www.lemoda.net/c/write-png/
|
||||||
// png_color_type is PNG_COLOR_TYPE_RGB or PNG_COLOR_TYPE_GRAY
|
// png_color_type is PNG_COLOR_TYPE_RGB or PNG_COLOR_TYPE_GRAY
|
||||||
|
//FIXME maybe better to use tdefl_write_image_to_png_file_in_memory() instead?
|
||||||
static bool write_rgb_or_gray_to_file(const char *file_name_utf8, size_t width, size_t height, int png_color_type, const uint8_t *data)
|
static bool write_rgb_or_gray_to_file(const char *file_name_utf8, size_t width, size_t height, int png_color_type, const uint8_t *data)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
|
||||||
|
|
@ -33,32 +33,16 @@ Polyline Polygon::split_at_index(int index) const
|
||||||
return polyline;
|
return polyline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
int64_t Polygon::area2x() const
|
|
||||||
{
|
|
||||||
size_t n = poly.size();
|
|
||||||
if (n < 3)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
int64_t a = 0;
|
|
||||||
for (size_t i = 0, j = n - 1; i < n; ++i)
|
|
||||||
a += int64_t(poly[j](0) + poly[i](0)) * int64_t(poly[j](1) - poly[i](1));
|
|
||||||
j = i;
|
|
||||||
}
|
|
||||||
return -a * 0.5;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
double Polygon::area(const Points &points)
|
double Polygon::area(const Points &points)
|
||||||
{
|
{
|
||||||
size_t n = points.size();
|
|
||||||
if (n < 3)
|
|
||||||
return 0.;
|
|
||||||
|
|
||||||
double a = 0.;
|
double a = 0.;
|
||||||
for (size_t i = 0, j = n - 1; i < n; ++i) {
|
if (points.size() >= 3) {
|
||||||
a += ((double)points[j](0) + (double)points[i](0)) * ((double)points[i](1) - (double)points[j](1));
|
Vec2d p1 = points.back().cast<double>();
|
||||||
j = i;
|
for (const Point &p : points) {
|
||||||
|
Vec2d p2 = p.cast<double>();
|
||||||
|
a += cross2(p1, p2);
|
||||||
|
p1 = p2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0.5 * a;
|
return 0.5 * a;
|
||||||
}
|
}
|
||||||
|
|
@ -169,19 +153,22 @@ void Polygon::triangulate_convex(Polygons* polygons) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// center of mass
|
// center of mass
|
||||||
|
// source: https://en.wikipedia.org/wiki/Centroid
|
||||||
Point Polygon::centroid() const
|
Point Polygon::centroid() const
|
||||||
{
|
{
|
||||||
double area_temp = this->area();
|
double area_sum = 0.;
|
||||||
double x_temp = 0;
|
Vec2d c(0., 0.);
|
||||||
double y_temp = 0;
|
if (points.size() >= 3) {
|
||||||
|
Vec2d p1 = points.back().cast<double>();
|
||||||
Polyline polyline = this->split_at_first_point();
|
for (const Point &p : points) {
|
||||||
for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) {
|
Vec2d p2 = p.cast<double>();
|
||||||
x_temp += (double)( point->x() + (point+1)->x() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() );
|
double a = cross2(p1, p2);
|
||||||
y_temp += (double)( point->y() + (point+1)->y() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() );
|
area_sum += a;
|
||||||
|
c += (p1 + p2) * a;
|
||||||
|
p1 = p2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return Point(Vec2d(c / (3. * area_sum)));
|
||||||
return Point(x_temp/(6*area_temp), y_temp/(6*area_temp));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// find all concave vertices (i.e. having an internal angle greater than the supplied angle)
|
// find all concave vertices (i.e. having an internal angle greater than the supplied angle)
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,9 @@ public:
|
||||||
bool is_closed() const { return this->points.front() == this->points.back(); }
|
bool is_closed() const { return this->points.front() == this->points.back(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool operator==(const Polyline &lhs, const Polyline &rhs) { return lhs.points == rhs.points; }
|
||||||
|
inline bool operator!=(const Polyline &lhs, const Polyline &rhs) { return lhs.points != rhs.points; }
|
||||||
|
|
||||||
// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests!
|
// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests!
|
||||||
#ifdef PERL_UCHAR_MIN
|
#ifdef PERL_UCHAR_MIN
|
||||||
class PolylineCollection
|
class PolylineCollection
|
||||||
|
|
|
||||||
|
|
@ -624,11 +624,17 @@ const std::vector<std::string>& Preset::sla_printer_options()
|
||||||
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
|
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
|
||||||
m_type(type),
|
m_type(type),
|
||||||
m_edited_preset(type, "", false),
|
m_edited_preset(type, "", false),
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
m_saved_preset(type, "", false),
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
m_idx_selected(0)
|
m_idx_selected(0)
|
||||||
{
|
{
|
||||||
// Insert just the default preset.
|
// Insert just the default preset.
|
||||||
this->add_default_preset(keys, defaults, default_name);
|
this->add_default_preset(keys, defaults, default_name);
|
||||||
m_edited_preset.config.apply(m_presets.front().config);
|
m_edited_preset.config.apply(m_presets.front().config);
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
update_saved_preset_from_current_preset();
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetCollection::reset(bool delete_files)
|
void PresetCollection::reset(bool delete_files)
|
||||||
|
|
@ -805,7 +811,10 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
|
||||||
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
|
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
|
||||||
this->get_edited_preset().config.apply_only(combined_config, keys, true);
|
this->get_edited_preset().config.apply_only(combined_config, keys, true);
|
||||||
this->update_dirty();
|
this->update_dirty();
|
||||||
assert(this->get_edited_preset().is_dirty);
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
update_saved_preset_from_current_preset();
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
assert(this->get_edited_preset().is_dirty);
|
||||||
return std::make_pair(&(*it), this->get_edited_preset().is_dirty);
|
return std::make_pair(&(*it), this->get_edited_preset().is_dirty);
|
||||||
}
|
}
|
||||||
if (inherits.empty()) {
|
if (inherits.empty()) {
|
||||||
|
|
@ -1070,7 +1079,7 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl
|
||||||
size_t PresetCollection::first_visible_idx() const
|
size_t PresetCollection::first_visible_idx() const
|
||||||
{
|
{
|
||||||
size_t idx = m_default_suppressed ? m_num_default_presets : 0;
|
size_t idx = m_default_suppressed ? m_num_default_presets : 0;
|
||||||
for (; idx < this->m_presets.size(); ++ idx)
|
for (; idx < m_presets.size(); ++ idx)
|
||||||
if (m_presets[idx].is_visible)
|
if (m_presets[idx].is_visible)
|
||||||
break;
|
break;
|
||||||
if (idx == m_presets.size())
|
if (idx == m_presets.size())
|
||||||
|
|
@ -1215,6 +1224,9 @@ Preset& PresetCollection::select_preset(size_t idx)
|
||||||
idx = first_visible_idx();
|
idx = first_visible_idx();
|
||||||
m_idx_selected = idx;
|
m_idx_selected = idx;
|
||||||
m_edited_preset = m_presets[idx];
|
m_edited_preset = m_presets[idx];
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
update_saved_preset_from_current_preset();
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
|
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
|
||||||
for (size_t i = 0; i < m_num_default_presets; ++i)
|
for (size_t i = 0; i < m_num_default_presets; ++i)
|
||||||
m_presets[i].is_visible = default_visible;
|
m_presets[i].is_visible = default_visible;
|
||||||
|
|
@ -1282,7 +1294,7 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
|
||||||
assert(it != new_vendors.end());
|
assert(it != new_vendors.end());
|
||||||
preset.vendor = &it->second;
|
preset.vendor = &it->second;
|
||||||
}
|
}
|
||||||
this->m_presets.emplace(it, std::move(preset));
|
m_presets.emplace(it, std::move(preset));
|
||||||
} else
|
} else
|
||||||
duplicates.emplace_back(std::move(preset.name));
|
duplicates.emplace_back(std::move(preset.name));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -346,6 +346,11 @@ public:
|
||||||
Preset& get_edited_preset() { return m_edited_preset; }
|
Preset& get_edited_preset() { return m_edited_preset; }
|
||||||
const Preset& get_edited_preset() const { return m_edited_preset; }
|
const Preset& get_edited_preset() const { return m_edited_preset; }
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
// Return the last saved preset.
|
||||||
|
const Preset& get_saved_preset() const { return m_saved_preset; }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
|
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
|
||||||
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
|
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
|
||||||
PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); }
|
PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); }
|
||||||
|
|
@ -365,8 +370,16 @@ public:
|
||||||
// Return a preset by an index. If the preset is active, a temporary copy is returned.
|
// Return a preset by an index. If the preset is active, a temporary copy is returned.
|
||||||
Preset& preset(size_t idx) { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; }
|
Preset& preset(size_t idx) { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; }
|
||||||
const Preset& preset(size_t idx) const { return const_cast<PresetCollection*>(this)->preset(idx); }
|
const Preset& preset(size_t idx) const { return const_cast<PresetCollection*>(this)->preset(idx); }
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
void discard_current_changes() {
|
||||||
|
m_presets[m_idx_selected].reset_dirty();
|
||||||
|
m_edited_preset = m_presets[m_idx_selected];
|
||||||
|
update_saved_preset_from_current_preset();
|
||||||
|
}
|
||||||
|
#else
|
||||||
void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; }
|
void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
// Return a preset by its name. If the preset is active, a temporary copy is returned.
|
// Return a preset by its name. If the preset is active, a temporary copy is returned.
|
||||||
// If a preset is not found by its name, null is returned.
|
// If a preset is not found by its name, null is returned.
|
||||||
Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false);
|
Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false);
|
||||||
|
|
@ -440,6 +453,16 @@ public:
|
||||||
std::vector<std::string> current_different_from_parent_options(const bool deep_compare = false) const
|
std::vector<std::string> current_different_from_parent_options(const bool deep_compare = false) const
|
||||||
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
|
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
// Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ.
|
||||||
|
bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); }
|
||||||
|
// Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ.
|
||||||
|
std::vector<std::string> saved_dirty_options(const bool deep_compare = false) const
|
||||||
|
{ return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); }
|
||||||
|
// Copy edited preset into saved preset.
|
||||||
|
void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
// Return a sorted list of system preset names.
|
// Return a sorted list of system preset names.
|
||||||
// Used for validating the "inherits" flag when importing user's config bundles.
|
// Used for validating the "inherits" flag when importing user's config bundles.
|
||||||
// Returns names of all system presets including the former names of these presets.
|
// Returns names of all system presets including the former names of these presets.
|
||||||
|
|
@ -527,6 +550,11 @@ private:
|
||||||
std::map<std::string, std::string> m_map_system_profile_renamed;
|
std::map<std::string, std::string> m_map_system_profile_renamed;
|
||||||
// Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
|
// Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
|
||||||
Preset m_edited_preset;
|
Preset m_edited_preset;
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
// Contains a copy of the last saved selected preset.
|
||||||
|
Preset m_saved_preset;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
// Selected preset.
|
// Selected preset.
|
||||||
size_t m_idx_selected;
|
size_t m_idx_selected;
|
||||||
// Is the "- default -" preset suppressed?
|
// Is the "- default -" preset suppressed?
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ namespace Slic3r {
|
||||||
template class PrintState<PrintStep, psCount>;
|
template class PrintState<PrintStep, psCount>;
|
||||||
template class PrintState<PrintObjectStep, posCount>;
|
template class PrintState<PrintObjectStep, posCount>;
|
||||||
|
|
||||||
|
PrintRegion::PrintRegion(const PrintRegionConfig &config) : PrintRegion(config, config.hash()) {}
|
||||||
|
PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(config), config.hash()) {}
|
||||||
|
|
||||||
void Print::clear()
|
void Print::clear()
|
||||||
{
|
{
|
||||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||||
|
|
@ -39,24 +42,10 @@ void Print::clear()
|
||||||
for (PrintObject *object : m_objects)
|
for (PrintObject *object : m_objects)
|
||||||
delete object;
|
delete object;
|
||||||
m_objects.clear();
|
m_objects.clear();
|
||||||
for (PrintRegion *region : m_regions)
|
m_print_regions.clear();
|
||||||
delete region;
|
|
||||||
m_regions.clear();
|
|
||||||
m_model.clear_objects();
|
m_model.clear_objects();
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintRegion* Print::add_region()
|
|
||||||
{
|
|
||||||
m_regions.emplace_back(new PrintRegion(this));
|
|
||||||
return m_regions.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintRegion* Print::add_region(const PrintRegionConfig &config)
|
|
||||||
{
|
|
||||||
m_regions.emplace_back(new PrintRegion(this, config));
|
|
||||||
return m_regions.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by Print::apply().
|
// Called by Print::apply().
|
||||||
// This method only accepts PrintConfig option keys.
|
// This method only accepts PrintConfig option keys.
|
||||||
bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* new_config */, const std::vector<t_config_option_key> &opt_keys)
|
bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* new_config */, const std::vector<t_config_option_key> &opt_keys)
|
||||||
|
|
@ -273,15 +262,10 @@ bool Print::is_step_done(PrintObjectStep step) const
|
||||||
std::vector<unsigned int> Print::object_extruders() const
|
std::vector<unsigned int> Print::object_extruders() const
|
||||||
{
|
{
|
||||||
std::vector<unsigned int> extruders;
|
std::vector<unsigned int> extruders;
|
||||||
extruders.reserve(m_regions.size() * 3);
|
extruders.reserve(m_print_regions.size() * m_objects.size() * 3);
|
||||||
std::vector<unsigned char> region_used(m_regions.size(), false);
|
|
||||||
for (const PrintObject *object : m_objects)
|
for (const PrintObject *object : m_objects)
|
||||||
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : object->region_volumes)
|
for (const PrintRegion ®ion : object->all_regions())
|
||||||
if (! volumes_per_region.empty())
|
region.collect_object_printing_extruders(*this, extruders);
|
||||||
region_used[&volumes_per_region - &object->region_volumes.front()] = true;
|
|
||||||
for (size_t idx_region = 0; idx_region < m_regions.size(); ++ idx_region)
|
|
||||||
if (region_used[idx_region])
|
|
||||||
m_regions[idx_region]->collect_object_printing_extruders(extruders);
|
|
||||||
sort_remove_duplicates(extruders);
|
sort_remove_duplicates(extruders);
|
||||||
return extruders;
|
return extruders;
|
||||||
}
|
}
|
||||||
|
|
@ -345,242 +329,6 @@ double Print::max_allowed_layer_height() const
|
||||||
return nozzle_diameter_max;
|
return nozzle_diameter_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
|
|
||||||
// in the exact order and with the same IDs.
|
|
||||||
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
|
|
||||||
void Print::model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new)
|
|
||||||
{
|
|
||||||
typedef std::pair<const ModelVolume*, bool> ModelVolumeWithStatus;
|
|
||||||
std::vector<ModelVolumeWithStatus> old_volumes;
|
|
||||||
old_volumes.reserve(model_object_dst.volumes.size());
|
|
||||||
for (const ModelVolume *model_volume : model_object_dst.volumes)
|
|
||||||
old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false));
|
|
||||||
auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); };
|
|
||||||
auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); };
|
|
||||||
std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower);
|
|
||||||
model_object_dst.volumes.clear();
|
|
||||||
model_object_dst.volumes.reserve(model_object_new.volumes.size());
|
|
||||||
for (const ModelVolume *model_volume_src : model_object_new.volumes) {
|
|
||||||
ModelVolumeWithStatus key(model_volume_src, false);
|
|
||||||
auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower);
|
|
||||||
if (it != old_volumes.end() && model_volume_equal(*it, key)) {
|
|
||||||
// The volume was found in the old list. Just copy it.
|
|
||||||
assert(! it->second); // not consumed yet
|
|
||||||
it->second = true;
|
|
||||||
ModelVolume *model_volume_dst = const_cast<ModelVolume*>(it->first);
|
|
||||||
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
|
|
||||||
assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type());
|
|
||||||
model_object_dst.volumes.emplace_back(model_volume_dst);
|
|
||||||
if (model_volume_dst->is_support_modifier()) {
|
|
||||||
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
|
|
||||||
model_volume_dst->set_type(model_volume_src->type());
|
|
||||||
model_volume_dst->set_transformation(model_volume_src->get_transformation());
|
|
||||||
}
|
|
||||||
assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix()));
|
|
||||||
} else {
|
|
||||||
// The volume was not found in the old list. Create a new copy.
|
|
||||||
assert(model_volume_src->is_support_modifier());
|
|
||||||
model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src));
|
|
||||||
model_object_dst.volumes.back()->set_model_object(&model_object_dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Release the non-consumed old volumes (those were deleted from the new list).
|
|
||||||
for (ModelVolumeWithStatus &mv_with_status : old_volumes)
|
|
||||||
if (! mv_with_status.second)
|
|
||||||
delete mv_with_status.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type)
|
|
||||||
{
|
|
||||||
size_t i_src, i_dst;
|
|
||||||
for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) {
|
|
||||||
const ModelVolume &mv_src = *model_object_src.volumes[i_src];
|
|
||||||
ModelVolume &mv_dst = *model_object_dst.volumes[i_dst];
|
|
||||||
if (mv_src.type() != type) {
|
|
||||||
++ i_src;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (mv_dst.type() != type) {
|
|
||||||
++ i_dst;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
assert(mv_src.id() == mv_dst.id());
|
|
||||||
// Copy the ModelVolume data.
|
|
||||||
mv_dst.name = mv_src.name;
|
|
||||||
mv_dst.config.assign_config(mv_src.config);
|
|
||||||
assert(mv_dst.supported_facets.id() == mv_src.supported_facets.id());
|
|
||||||
mv_dst.supported_facets.assign(mv_src.supported_facets);
|
|
||||||
assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id());
|
|
||||||
mv_dst.seam_facets.assign(mv_src.seam_facets);
|
|
||||||
//FIXME what to do with the materials?
|
|
||||||
// mv_dst.m_material_id = mv_src.m_material_id;
|
|
||||||
++ i_src;
|
|
||||||
++ i_dst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
|
|
||||||
{
|
|
||||||
assert(lr_dst.size() == lr_src.size());
|
|
||||||
auto it_src = lr_src.cbegin();
|
|
||||||
for (auto &kvp_dst : lr_dst) {
|
|
||||||
const auto &kvp_src = *it_src ++;
|
|
||||||
assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
|
|
||||||
assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
|
|
||||||
// Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
|
|
||||||
// assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
|
|
||||||
kvp_dst.second = kvp_src.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
|
|
||||||
{
|
|
||||||
typedef Transform3d::Scalar T;
|
|
||||||
const T *lv = lhs.data();
|
|
||||||
const T *rv = rhs.data();
|
|
||||||
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) {
|
|
||||||
if (*lv < *rv)
|
|
||||||
return true;
|
|
||||||
else if (*lv > *rv)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
|
|
||||||
{
|
|
||||||
typedef Transform3d::Scalar T;
|
|
||||||
const T *lv = lhs.data();
|
|
||||||
const T *rv = rhs.data();
|
|
||||||
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv)
|
|
||||||
if (*lv != *rv)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PrintObjectTrafoAndInstances
|
|
||||||
{
|
|
||||||
Transform3d trafo;
|
|
||||||
PrintInstances instances;
|
|
||||||
bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
|
||||||
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
|
|
||||||
{
|
|
||||||
std::set<PrintObjectTrafoAndInstances> trafos;
|
|
||||||
PrintObjectTrafoAndInstances trafo;
|
|
||||||
for (ModelInstance *model_instance : model_object.instances)
|
|
||||||
if (model_instance->is_printable()) {
|
|
||||||
trafo.trafo = model_instance->get_matrix();
|
|
||||||
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
|
|
||||||
// Reset the XY axes of the transformation.
|
|
||||||
trafo.trafo.data()[12] = 0;
|
|
||||||
trafo.trafo.data()[13] = 0;
|
|
||||||
// Search or insert a trafo.
|
|
||||||
auto it = trafos.emplace(trafo).first;
|
|
||||||
const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
|
|
||||||
}
|
|
||||||
return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare just the layer ranges and their layer heights, not the associated configs.
|
|
||||||
// Ignore the layer heights if check_layer_heights is false.
|
|
||||||
static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
|
|
||||||
{
|
|
||||||
if (lr1.size() != lr2.size())
|
|
||||||
return false;
|
|
||||||
auto it2 = lr2.begin();
|
|
||||||
for (const auto &kvp1 : lr1) {
|
|
||||||
const auto &kvp2 = *it2 ++;
|
|
||||||
if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
|
|
||||||
std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
|
|
||||||
(check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
|
|
||||||
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<CustomGCode::Item> &va, const std::vector<CustomGCode::Item> &vb)
|
|
||||||
{
|
|
||||||
auto it_a = va.begin();
|
|
||||||
auto it_b = vb.begin();
|
|
||||||
while (it_a != va.end() || it_b != vb.end()) {
|
|
||||||
if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) {
|
|
||||||
// Skip any CustomGCode items, which are not tool changes.
|
|
||||||
++ it_a;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) {
|
|
||||||
// Skip any CustomGCode items, which are not tool changes.
|
|
||||||
++ it_b;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (it_a == va.end() || it_b == vb.end())
|
|
||||||
// va or vb contains more Tool Changes than the other.
|
|
||||||
return true;
|
|
||||||
assert(it_a->type == CustomGCode::ToolChange);
|
|
||||||
assert(it_b->type == CustomGCode::ToolChange);
|
|
||||||
if (*it_a != *it_b)
|
|
||||||
// The two Tool Changes differ.
|
|
||||||
return true;
|
|
||||||
++ it_a;
|
|
||||||
++ it_b;
|
|
||||||
}
|
|
||||||
// There is no change in custom Tool Changes.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect diffs of configuration values at various containers,
|
|
||||||
// resolve the filament rectract overrides of extruder retract values.
|
|
||||||
void Print::config_diffs(
|
|
||||||
const DynamicPrintConfig &new_full_config,
|
|
||||||
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff,
|
|
||||||
t_config_option_keys &full_config_diff,
|
|
||||||
DynamicPrintConfig &filament_overrides) const
|
|
||||||
{
|
|
||||||
// Collect changes to print config, account for overrides of extruder retract values by filament presets.
|
|
||||||
{
|
|
||||||
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
|
|
||||||
const std::string filament_prefix = "filament_";
|
|
||||||
for (const t_config_option_key &opt_key : m_config.keys()) {
|
|
||||||
const ConfigOption *opt_old = m_config.option(opt_key);
|
|
||||||
assert(opt_old != nullptr);
|
|
||||||
const ConfigOption *opt_new = new_full_config.option(opt_key);
|
|
||||||
// assert(opt_new != nullptr);
|
|
||||||
if (opt_new == nullptr)
|
|
||||||
//FIXME This may happen when executing some test cases.
|
|
||||||
continue;
|
|
||||||
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
|
|
||||||
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
|
|
||||||
// An extruder retract override is available at some of the filament presets.
|
|
||||||
if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
|
|
||||||
auto opt_copy = opt_new->clone();
|
|
||||||
opt_copy->apply_override(opt_new_filament);
|
|
||||||
if (*opt_old == *opt_copy)
|
|
||||||
delete opt_copy;
|
|
||||||
else {
|
|
||||||
filament_overrides.set_key_value(opt_key, opt_copy);
|
|
||||||
print_diff.emplace_back(opt_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (*opt_new != *opt_old)
|
|
||||||
print_diff.emplace_back(opt_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Collect changes to object and region configs.
|
|
||||||
object_diff = m_default_object_config.diff(new_full_config);
|
|
||||||
region_diff = m_default_region_config.diff(new_full_config);
|
|
||||||
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
|
|
||||||
for (const t_config_option_key &opt_key : new_full_config.keys()) {
|
|
||||||
const ConfigOption *opt_old = m_full_print_config.option(opt_key);
|
|
||||||
const ConfigOption *opt_new = new_full_config.option(opt_key);
|
|
||||||
if (opt_old == nullptr || *opt_new != *opt_old)
|
|
||||||
full_config_diff.emplace_back(opt_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ObjectID> Print::print_object_ids() const
|
std::vector<ObjectID> Print::print_object_ids() const
|
||||||
{
|
{
|
||||||
std::vector<ObjectID> out;
|
std::vector<ObjectID> out;
|
||||||
|
|
@ -591,594 +339,6 @@ std::vector<ObjectID> Print::print_object_ids() const
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config)
|
|
||||||
{
|
|
||||||
#ifdef _DEBUG
|
|
||||||
check_model_ids_validity(model);
|
|
||||||
#endif /* _DEBUG */
|
|
||||||
|
|
||||||
// Normalize the config.
|
|
||||||
new_full_config.option("print_settings_id", true);
|
|
||||||
new_full_config.option("filament_settings_id", true);
|
|
||||||
new_full_config.option("printer_settings_id", true);
|
|
||||||
new_full_config.option("physical_printer_settings_id", true);
|
|
||||||
new_full_config.normalize_fdm();
|
|
||||||
|
|
||||||
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
|
|
||||||
t_config_option_keys print_diff, object_diff, region_diff, full_config_diff;
|
|
||||||
DynamicPrintConfig filament_overrides;
|
|
||||||
this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, filament_overrides);
|
|
||||||
|
|
||||||
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
|
|
||||||
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
|
|
||||||
auto update_apply_status = [&apply_status](bool invalidated)
|
|
||||||
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
|
|
||||||
if (! (print_diff.empty() && object_diff.empty() && region_diff.empty()))
|
|
||||||
update_apply_status(false);
|
|
||||||
|
|
||||||
// Grab the lock for the Print / PrintObject milestones.
|
|
||||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
|
||||||
|
|
||||||
// The following call may stop the background processing.
|
|
||||||
if (! print_diff.empty())
|
|
||||||
update_apply_status(this->invalidate_state_by_config_options(new_full_config, print_diff));
|
|
||||||
|
|
||||||
// Apply variables to placeholder parser. The placeholder parser is used by G-code export,
|
|
||||||
// which should be stopped if print_diff is not empty.
|
|
||||||
size_t num_extruders = m_config.nozzle_diameter.size();
|
|
||||||
bool num_extruders_changed = false;
|
|
||||||
if (! full_config_diff.empty()) {
|
|
||||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
|
||||||
// Set the profile aliases for the PrintBase::output_filename()
|
|
||||||
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
|
|
||||||
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
|
|
||||||
m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
|
|
||||||
m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone());
|
|
||||||
// We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser.
|
|
||||||
// see "Placeholders do not respect filament overrides." GH issue #3649
|
|
||||||
m_placeholder_parser.apply_config(filament_overrides);
|
|
||||||
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
|
|
||||||
m_config.apply_only(new_full_config, print_diff, true);
|
|
||||||
//FIXME use move semantics once ConfigBase supports it.
|
|
||||||
m_config.apply(filament_overrides);
|
|
||||||
// Handle changes to object config defaults
|
|
||||||
m_default_object_config.apply_only(new_full_config, object_diff, true);
|
|
||||||
// Handle changes to regions config defaults
|
|
||||||
m_default_region_config.apply_only(new_full_config, region_diff, true);
|
|
||||||
m_full_print_config = std::move(new_full_config);
|
|
||||||
if (num_extruders != m_config.nozzle_diameter.size()) {
|
|
||||||
num_extruders = m_config.nozzle_diameter.size();
|
|
||||||
num_extruders_changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LayerRanges
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LayerRanges() {}
|
|
||||||
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
|
|
||||||
void assign(const t_layer_config_ranges &in) {
|
|
||||||
m_ranges.clear();
|
|
||||||
m_ranges.reserve(in.size());
|
|
||||||
// Input ranges are sorted lexicographically. First range trims the other ranges.
|
|
||||||
coordf_t last_z = 0;
|
|
||||||
for (const std::pair<const t_layer_height_range, ModelConfig> &range : in)
|
|
||||||
if (range.first.second > last_z) {
|
|
||||||
coordf_t min_z = std::max(range.first.first, 0.);
|
|
||||||
if (min_z > last_z + EPSILON) {
|
|
||||||
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
|
|
||||||
last_z = min_z;
|
|
||||||
}
|
|
||||||
if (range.first.second > last_z + EPSILON) {
|
|
||||||
const DynamicPrintConfig *cfg = &range.second.get();
|
|
||||||
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
|
|
||||||
last_z = range.first.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_ranges.empty())
|
|
||||||
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
|
|
||||||
else if (m_ranges.back().second == nullptr)
|
|
||||||
m_ranges.back().first.second = DBL_MAX;
|
|
||||||
else
|
|
||||||
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DynamicPrintConfig* config(const t_layer_height_range &range) const {
|
|
||||||
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
|
|
||||||
// #ys_FIXME_COLOR
|
|
||||||
// assert(it != m_ranges.end());
|
|
||||||
// assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
|
|
||||||
// assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
|
|
||||||
if (it == m_ranges.end() ||
|
|
||||||
std::abs(it->first.first - range.first) > EPSILON ||
|
|
||||||
std::abs(it->first.second - range.second) > EPSILON )
|
|
||||||
return nullptr; // desired range doesn't found
|
|
||||||
return (it == m_ranges.end()) ? nullptr : it->second;
|
|
||||||
}
|
|
||||||
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
|
|
||||||
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
|
|
||||||
private:
|
|
||||||
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
|
|
||||||
};
|
|
||||||
struct ModelObjectStatus {
|
|
||||||
enum Status {
|
|
||||||
Unknown,
|
|
||||||
Old,
|
|
||||||
New,
|
|
||||||
Moved,
|
|
||||||
Deleted,
|
|
||||||
};
|
|
||||||
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
|
|
||||||
ObjectID id;
|
|
||||||
Status status;
|
|
||||||
LayerRanges layer_ranges;
|
|
||||||
// Search by id.
|
|
||||||
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
|
|
||||||
};
|
|
||||||
std::set<ModelObjectStatus> model_object_status;
|
|
||||||
|
|
||||||
// 1) Synchronize model objects.
|
|
||||||
if (model.id() != m_model.id()) {
|
|
||||||
// Kill everything, initialize from scratch.
|
|
||||||
// Stop background processing.
|
|
||||||
this->call_cancel_callback();
|
|
||||||
update_apply_status(this->invalidate_all_steps());
|
|
||||||
for (PrintObject *object : m_objects) {
|
|
||||||
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
|
|
||||||
update_apply_status(object->invalidate_all_steps());
|
|
||||||
delete object;
|
|
||||||
}
|
|
||||||
m_objects.clear();
|
|
||||||
for (PrintRegion *region : m_regions)
|
|
||||||
delete region;
|
|
||||||
m_regions.clear();
|
|
||||||
m_model.assign_copy(model);
|
|
||||||
for (const ModelObject *model_object : m_model.objects)
|
|
||||||
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
|
|
||||||
} else {
|
|
||||||
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
|
|
||||||
update_apply_status(num_extruders_changed ||
|
|
||||||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
|
|
||||||
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
|
|
||||||
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
|
|
||||||
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
|
|
||||||
// The Tool Ordering and the Wipe Tower are no more valid.
|
|
||||||
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
|
|
||||||
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
|
|
||||||
this->invalidate_step(psGCodeExport));
|
|
||||||
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
|
|
||||||
}
|
|
||||||
if (model_object_list_equal(m_model, model)) {
|
|
||||||
// The object list did not change.
|
|
||||||
for (const ModelObject *model_object : m_model.objects)
|
|
||||||
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
|
||||||
} else if (model_object_list_extended(m_model, model)) {
|
|
||||||
// Add new objects. Their volumes and configs will be synchronized later.
|
|
||||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
|
||||||
for (const ModelObject *model_object : m_model.objects)
|
|
||||||
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
|
||||||
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
|
|
||||||
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
|
|
||||||
m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
|
|
||||||
m_model.objects.back()->set_model(&m_model);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Reorder the objects, add new objects.
|
|
||||||
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
|
|
||||||
this->call_cancel_callback();
|
|
||||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
|
||||||
// Second create a new list of objects.
|
|
||||||
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
|
|
||||||
m_model.objects.clear();
|
|
||||||
m_model.objects.reserve(model.objects.size());
|
|
||||||
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
|
|
||||||
std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
|
|
||||||
for (const ModelObject *mobj : model.objects) {
|
|
||||||
auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
|
|
||||||
if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
|
|
||||||
// New ModelObject added.
|
|
||||||
m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
|
|
||||||
m_model.objects.back()->set_model(&m_model);
|
|
||||||
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
|
|
||||||
} else {
|
|
||||||
// Existing ModelObject re-added (possibly moved in the list).
|
|
||||||
m_model.objects.emplace_back(*it);
|
|
||||||
model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool deleted_any = false;
|
|
||||||
for (ModelObject *&model_object : model_objects_old) {
|
|
||||||
if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
|
|
||||||
model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
|
|
||||||
deleted_any = true;
|
|
||||||
} else
|
|
||||||
// Do not delete this ModelObject instance.
|
|
||||||
model_object = nullptr;
|
|
||||||
}
|
|
||||||
if (deleted_any) {
|
|
||||||
// Delete PrintObjects of the deleted ModelObjects.
|
|
||||||
PrintObjectPtrs print_objects_old = std::move(m_objects);
|
|
||||||
m_objects.clear();
|
|
||||||
m_objects.reserve(print_objects_old.size());
|
|
||||||
for (PrintObject *print_object : print_objects_old) {
|
|
||||||
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
|
|
||||||
assert(it_status != model_object_status.end());
|
|
||||||
if (it_status->status == ModelObjectStatus::Deleted) {
|
|
||||||
update_apply_status(print_object->invalidate_all_steps());
|
|
||||||
delete print_object;
|
|
||||||
} else
|
|
||||||
m_objects.emplace_back(print_object);
|
|
||||||
}
|
|
||||||
for (ModelObject *model_object : model_objects_old)
|
|
||||||
delete model_object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) Map print objects including their transformation matrices.
|
|
||||||
struct PrintObjectStatus {
|
|
||||||
enum Status {
|
|
||||||
Unknown,
|
|
||||||
Deleted,
|
|
||||||
Reused,
|
|
||||||
New
|
|
||||||
};
|
|
||||||
PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
|
|
||||||
id(print_object->model_object()->id()),
|
|
||||||
print_object(print_object),
|
|
||||||
trafo(print_object->trafo()),
|
|
||||||
status(status) {}
|
|
||||||
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
|
|
||||||
// ID of the ModelObject & PrintObject
|
|
||||||
ObjectID id;
|
|
||||||
// Pointer to the old PrintObject
|
|
||||||
PrintObject *print_object;
|
|
||||||
// Trafo generated with model_object->world_matrix(true)
|
|
||||||
Transform3d trafo;
|
|
||||||
Status status;
|
|
||||||
// Search by id.
|
|
||||||
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
|
|
||||||
};
|
|
||||||
std::multiset<PrintObjectStatus> print_object_status;
|
|
||||||
for (PrintObject *print_object : m_objects)
|
|
||||||
print_object_status.emplace(PrintObjectStatus(print_object));
|
|
||||||
|
|
||||||
// 3) Synchronize ModelObjects & PrintObjects.
|
|
||||||
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
|
|
||||||
ModelObject &model_object = *m_model.objects[idx_model_object];
|
|
||||||
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
|
||||||
assert(it_status != model_object_status.end());
|
|
||||||
assert(it_status->status != ModelObjectStatus::Deleted);
|
|
||||||
const ModelObject& model_object_new = *model.objects[idx_model_object];
|
|
||||||
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
|
|
||||||
if (it_status->status == ModelObjectStatus::New)
|
|
||||||
// PrintObject instances will be added in the next loop.
|
|
||||||
continue;
|
|
||||||
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
|
||||||
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
|
||||||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
|
||||||
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
|
|
||||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
|
||||||
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
|
|
||||||
bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
|
|
||||||
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
|
|
||||||
if (model_parts_differ || modifiers_differ ||
|
|
||||||
model_object.origin_translation != model_object_new.origin_translation ||
|
|
||||||
! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) ||
|
|
||||||
! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
|
|
||||||
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
|
||||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
|
||||||
for (auto it = range.first; it != range.second; ++ it) {
|
|
||||||
update_apply_status(it->print_object->invalidate_all_steps());
|
|
||||||
const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
|
|
||||||
}
|
|
||||||
// Copy content of the ModelObject including its ID, do not change the parent.
|
|
||||||
model_object.assign_copy(model_object_new);
|
|
||||||
} else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) {
|
|
||||||
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
|
|
||||||
if (supports_differ) {
|
|
||||||
this->call_cancel_callback();
|
|
||||||
update_apply_status(false);
|
|
||||||
}
|
|
||||||
// Invalidate just the supports step.
|
|
||||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
|
||||||
for (auto it = range.first; it != range.second; ++ it)
|
|
||||||
update_apply_status(it->print_object->invalidate_step(posSupportMaterial));
|
|
||||||
if (supports_differ) {
|
|
||||||
// Copy just the support volumes.
|
|
||||||
model_volume_list_update_supports(model_object, model_object_new);
|
|
||||||
}
|
|
||||||
} else if (model_custom_seam_data_changed(model_object, model_object_new)) {
|
|
||||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
|
||||||
}
|
|
||||||
if (! model_parts_differ && ! modifiers_differ) {
|
|
||||||
// Synchronize Object's config.
|
|
||||||
bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
|
|
||||||
if (object_config_changed)
|
|
||||||
model_object.config.assign_config(model_object_new.config);
|
|
||||||
if (! object_diff.empty() || object_config_changed || num_extruders_changed) {
|
|
||||||
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
|
|
||||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
|
||||||
for (auto it = range.first; it != range.second; ++ it) {
|
|
||||||
t_config_option_keys diff = it->print_object->config().diff(new_config);
|
|
||||||
if (! diff.empty()) {
|
|
||||||
update_apply_status(it->print_object->invalidate_state_by_config_options(it->print_object->config(), new_config, diff));
|
|
||||||
it->print_object->config_apply_only(new_config, diff, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data).
|
|
||||||
//FIXME What to do with m_material_id?
|
|
||||||
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
|
|
||||||
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
|
|
||||||
layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
|
|
||||||
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
|
|
||||||
model_object.name = model_object_new.name;
|
|
||||||
model_object.input_file = model_object_new.input_file;
|
|
||||||
// Only refresh ModelInstances if there is any change.
|
|
||||||
if (model_object.instances.size() != model_object_new.instances.size() ||
|
|
||||||
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
|
|
||||||
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
|
|
||||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
|
||||||
model_object.clear_instances();
|
|
||||||
model_object.instances.reserve(model_object_new.instances.size());
|
|
||||||
for (const ModelInstance *model_instance : model_object_new.instances) {
|
|
||||||
model_object.instances.emplace_back(new ModelInstance(*model_instance));
|
|
||||||
model_object.instances.back()->set_model_object(&model_object);
|
|
||||||
}
|
|
||||||
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
|
|
||||||
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
|
|
||||||
l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
|
|
||||||
// If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
|
|
||||||
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
|
|
||||||
model_object.invalidate_bounding_box();
|
|
||||||
// Synchronize the content of instances.
|
|
||||||
auto new_instance = model_object_new.instances.begin();
|
|
||||||
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
|
|
||||||
(*old_instance)->set_transformation((*new_instance)->get_transformation());
|
|
||||||
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
|
|
||||||
(*old_instance)->printable = (*new_instance)->printable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4) Generate PrintObjects from ModelObjects and their instances.
|
|
||||||
{
|
|
||||||
PrintObjectPtrs print_objects_new;
|
|
||||||
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
|
|
||||||
bool new_objects = false;
|
|
||||||
// Walk over all new model objects and check, whether there are matching PrintObjects.
|
|
||||||
for (ModelObject *model_object : m_model.objects) {
|
|
||||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id()));
|
|
||||||
std::vector<const PrintObjectStatus*> old;
|
|
||||||
if (range.first != range.second) {
|
|
||||||
old.reserve(print_object_status.count(PrintObjectStatus(model_object->id())));
|
|
||||||
for (auto it = range.first; it != range.second; ++ it)
|
|
||||||
if (it->status != PrintObjectStatus::Deleted)
|
|
||||||
old.emplace_back(&(*it));
|
|
||||||
}
|
|
||||||
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
|
||||||
// Producing the config for PrintObject on demand, caching it at print_object_last.
|
|
||||||
const PrintObject *print_object_last = nullptr;
|
|
||||||
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) {
|
|
||||||
print_object->config_apply(print_object_last ?
|
|
||||||
print_object_last->config() :
|
|
||||||
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders));
|
|
||||||
print_object_last = print_object;
|
|
||||||
};
|
|
||||||
std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
|
|
||||||
if (old.empty()) {
|
|
||||||
// Simple case, just generate new instances.
|
|
||||||
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
|
|
||||||
PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
|
|
||||||
print_object_apply_config(print_object);
|
|
||||||
print_objects_new.emplace_back(print_object);
|
|
||||||
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
|
||||||
new_objects = true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Complex case, try to merge the two lists.
|
|
||||||
// Sort the old lexicographically by their trafos.
|
|
||||||
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
|
|
||||||
// Merge the old / new lists.
|
|
||||||
auto it_old = old.begin();
|
|
||||||
for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) {
|
|
||||||
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
|
|
||||||
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
|
|
||||||
// This is a new instance (or a set of instances with the same trafo). Just add it.
|
|
||||||
PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
|
|
||||||
print_object_apply_config(print_object);
|
|
||||||
print_objects_new.emplace_back(print_object);
|
|
||||||
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
|
||||||
new_objects = true;
|
|
||||||
if (it_old != old.end())
|
|
||||||
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
|
|
||||||
} else {
|
|
||||||
// The PrintObject already exists and the copies differ.
|
|
||||||
PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
|
|
||||||
if (status != PrintBase::APPLY_STATUS_UNCHANGED)
|
|
||||||
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
|
|
||||||
print_objects_new.emplace_back((*it_old)->print_object);
|
|
||||||
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Reused;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_objects != print_objects_new) {
|
|
||||||
this->call_cancel_callback();
|
|
||||||
update_apply_status(this->invalidate_all_steps());
|
|
||||||
m_objects = print_objects_new;
|
|
||||||
// Delete the PrintObjects marked as Unknown or Deleted.
|
|
||||||
bool deleted_objects = false;
|
|
||||||
for (auto &pos : print_object_status)
|
|
||||||
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
|
|
||||||
update_apply_status(pos.print_object->invalidate_all_steps());
|
|
||||||
delete pos.print_object;
|
|
||||||
deleted_objects = true;
|
|
||||||
}
|
|
||||||
if (new_objects || deleted_objects)
|
|
||||||
update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport }));
|
|
||||||
if (new_objects)
|
|
||||||
update_apply_status(false);
|
|
||||||
}
|
|
||||||
print_object_status.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5) Synchronize configs of ModelVolumes, synchronize AMF / 3MF materials (and their configs), refresh PrintRegions.
|
|
||||||
// Update reference counts of regions from the remaining PrintObjects and their volumes.
|
|
||||||
// Regions with zero references could and should be reused.
|
|
||||||
for (PrintRegion *region : m_regions)
|
|
||||||
region->m_refcnt = 0;
|
|
||||||
for (PrintObject *print_object : m_objects) {
|
|
||||||
int idx_region = 0;
|
|
||||||
for (const auto &volumes : print_object->region_volumes) {
|
|
||||||
if (! volumes.empty())
|
|
||||||
++ m_regions[idx_region]->m_refcnt;
|
|
||||||
++ idx_region;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All regions now have distinct settings.
|
|
||||||
// Check whether applying the new region config defaults we'd get different regions.
|
|
||||||
for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) {
|
|
||||||
PrintRegion ®ion = *m_regions[region_id];
|
|
||||||
PrintRegionConfig this_region_config;
|
|
||||||
bool this_region_config_set = false;
|
|
||||||
for (PrintObject *print_object : m_objects) {
|
|
||||||
const LayerRanges *layer_ranges;
|
|
||||||
{
|
|
||||||
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
|
|
||||||
assert(it_status != model_object_status.end());
|
|
||||||
assert(it_status->status != ModelObjectStatus::Deleted);
|
|
||||||
layer_ranges = &it_status->layer_ranges;
|
|
||||||
}
|
|
||||||
if (region_id < print_object->region_volumes.size()) {
|
|
||||||
for (const std::pair<t_layer_height_range, int> &volume_and_range : print_object->region_volumes[region_id]) {
|
|
||||||
const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second];
|
|
||||||
const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first);
|
|
||||||
if (this_region_config_set) {
|
|
||||||
// If the new config for this volume differs from the other
|
|
||||||
// volume configs currently associated to this region, it means
|
|
||||||
// the region subdivision does not make sense anymore.
|
|
||||||
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders)))
|
|
||||||
// Regions were split. Reset this print_object.
|
|
||||||
goto print_object_end;
|
|
||||||
} else {
|
|
||||||
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
|
|
||||||
for (size_t i = 0; i < region_id; ++ i) {
|
|
||||||
const PrintRegion ®ion_other = *m_regions[i];
|
|
||||||
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
|
|
||||||
// Regions were merged. Reset this print_object.
|
|
||||||
goto print_object_end;
|
|
||||||
}
|
|
||||||
this_region_config_set = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
print_object_end:
|
|
||||||
update_apply_status(print_object->invalidate_all_steps());
|
|
||||||
// Decrease the references to regions from this volume.
|
|
||||||
int ireg = 0;
|
|
||||||
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes : print_object->region_volumes) {
|
|
||||||
if (! volumes.empty())
|
|
||||||
-- m_regions[ireg]->m_refcnt;
|
|
||||||
++ ireg;
|
|
||||||
}
|
|
||||||
print_object->region_volumes.clear();
|
|
||||||
}
|
|
||||||
if (this_region_config_set) {
|
|
||||||
t_config_option_keys diff = region.config().diff(this_region_config);
|
|
||||||
if (! diff.empty()) {
|
|
||||||
// Stop the background process before assigning new configuration to the regions.
|
|
||||||
for (PrintObject *print_object : m_objects)
|
|
||||||
if (region_id < print_object->region_volumes.size() && ! print_object->region_volumes[region_id].empty())
|
|
||||||
update_apply_status(print_object->invalidate_state_by_config_options(region.config(), this_region_config, diff));
|
|
||||||
region.config_apply_only(this_region_config, diff, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Possibly add new regions for the newly added or resetted PrintObjects.
|
|
||||||
for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
|
|
||||||
PrintObject &print_object0 = *m_objects[idx_print_object];
|
|
||||||
const ModelObject &model_object = *print_object0.model_object();
|
|
||||||
const LayerRanges *layer_ranges;
|
|
||||||
{
|
|
||||||
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
|
||||||
assert(it_status != model_object_status.end());
|
|
||||||
assert(it_status->status != ModelObjectStatus::Deleted);
|
|
||||||
layer_ranges = &it_status->layer_ranges;
|
|
||||||
}
|
|
||||||
std::vector<int> regions_in_object;
|
|
||||||
regions_in_object.reserve(64);
|
|
||||||
for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
|
|
||||||
PrintObject &print_object = *m_objects[i];
|
|
||||||
bool fresh = print_object.region_volumes.empty();
|
|
||||||
unsigned int volume_id = 0;
|
|
||||||
unsigned int idx_region_in_object = 0;
|
|
||||||
for (const ModelVolume *volume : model_object.volumes) {
|
|
||||||
if (! volume->is_model_part() && ! volume->is_modifier()) {
|
|
||||||
++ volume_id;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Filter the layer ranges, so they do not overlap and they contain at least a single layer.
|
|
||||||
// Now insert a volume with a layer range to its own region.
|
|
||||||
for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
|
|
||||||
int region_id = -1;
|
|
||||||
if (&print_object == &print_object0) {
|
|
||||||
// Get the config applied to this volume.
|
|
||||||
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
|
|
||||||
// Find an existing print region with the same config.
|
|
||||||
int idx_empty_slot = -1;
|
|
||||||
for (int i = 0; i < (int)m_regions.size(); ++ i) {
|
|
||||||
if (m_regions[i]->m_refcnt == 0) {
|
|
||||||
if (idx_empty_slot == -1)
|
|
||||||
idx_empty_slot = i;
|
|
||||||
} else if (config.equals(m_regions[i]->config())) {
|
|
||||||
region_id = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If no region exists with the same config, create a new one.
|
|
||||||
if (region_id == -1) {
|
|
||||||
if (idx_empty_slot == -1) {
|
|
||||||
region_id = (int)m_regions.size();
|
|
||||||
this->add_region(config);
|
|
||||||
} else {
|
|
||||||
region_id = idx_empty_slot;
|
|
||||||
m_regions[region_id]->set_config(std::move(config));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
regions_in_object.emplace_back(region_id);
|
|
||||||
} else
|
|
||||||
region_id = regions_in_object[idx_region_in_object ++];
|
|
||||||
// Assign volume to a region.
|
|
||||||
if (fresh) {
|
|
||||||
if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
|
|
||||||
++ m_regions[region_id]->m_refcnt;
|
|
||||||
print_object.add_region_volume(region_id, volume_id, it_range->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++ volume_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update SlicingParameters for each object where the SlicingParameters is not valid.
|
|
||||||
// If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use
|
|
||||||
// (posSlicing and posSupportMaterial was invalidated).
|
|
||||||
for (PrintObject *object : m_objects)
|
|
||||||
object->update_slicing_parameters();
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
check_model_ids_equal(m_model, model);
|
|
||||||
#endif /* _DEBUG */
|
|
||||||
|
|
||||||
return static_cast<ApplyStatus>(apply_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Print::has_infinite_skirt() const
|
bool Print::has_infinite_skirt() const
|
||||||
{
|
{
|
||||||
return (m_config.draft_shield && m_config.skirts > 0) || (m_config.ooze_prevention && this->extruders().size() > 1);
|
return (m_config.draft_shield && m_config.skirts > 0) || (m_config.ooze_prevention && this->extruders().size() > 1);
|
||||||
|
|
@ -1289,10 +449,12 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print)
|
||||||
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
||||||
std::string Print::validate(std::string* warning) const
|
std::string Print::validate(std::string* warning) const
|
||||||
{
|
{
|
||||||
|
std::vector<unsigned int> extruders = this->extruders();
|
||||||
|
|
||||||
if (m_objects.empty())
|
if (m_objects.empty())
|
||||||
return L("All objects are outside of the print volume.");
|
return L("All objects are outside of the print volume.");
|
||||||
|
|
||||||
if (extruders().empty())
|
if (extruders.empty())
|
||||||
return L("The supplied settings will cause an empty print.");
|
return L("The supplied settings will cause an empty print.");
|
||||||
|
|
||||||
if (m_config.complete_objects) {
|
if (m_config.complete_objects) {
|
||||||
|
|
@ -1311,20 +473,16 @@ std::string Print::validate(std::string* warning) const
|
||||||
return L("Only a single object may be printed at a time in Spiral Vase mode. "
|
return L("Only a single object may be printed at a time in Spiral Vase mode. "
|
||||||
"Either remove all but the last object, or enable sequential mode by \"complete_objects\".");
|
"Either remove all but the last object, or enable sequential mode by \"complete_objects\".");
|
||||||
assert(m_objects.size() == 1);
|
assert(m_objects.size() == 1);
|
||||||
size_t num_regions = 0;
|
if (m_objects.front()->all_regions().size() > 1)
|
||||||
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : m_objects.front()->region_volumes)
|
|
||||||
if (! volumes_per_region.empty())
|
|
||||||
++ num_regions;
|
|
||||||
if (num_regions > 1)
|
|
||||||
return L("The Spiral Vase option can only be used when printing single material objects.");
|
return L("The Spiral Vase option can only be used when printing single material objects.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->has_wipe_tower() && ! m_objects.empty()) {
|
if (this->has_wipe_tower() && ! m_objects.empty()) {
|
||||||
// Make sure all extruders use same diameter filament and have the same nozzle diameter
|
// Make sure all extruders use same diameter filament and have the same nozzle diameter
|
||||||
// EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
|
// EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
|
||||||
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders().front());
|
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front());
|
||||||
double first_filament_diam = m_config.filament_diameter.get_at(extruders().front());
|
double first_filament_diam = m_config.filament_diameter.get_at(extruders.front());
|
||||||
for (const auto& extruder_idx : extruders()) {
|
for (const auto& extruder_idx : extruders) {
|
||||||
double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
|
double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
|
||||||
double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
|
double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
|
||||||
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
|
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
|
||||||
|
|
@ -1342,7 +500,7 @@ std::string Print::validate(std::string* warning) const
|
||||||
return L("Ooze prevention is currently not supported with the wipe tower enabled.");
|
return L("Ooze prevention is currently not supported with the wipe tower enabled.");
|
||||||
if (m_config.use_volumetric_e)
|
if (m_config.use_volumetric_e)
|
||||||
return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0).");
|
return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0).");
|
||||||
if (m_config.complete_objects && extruders().size() > 1)
|
if (m_config.complete_objects && extruders.size() > 1)
|
||||||
return L("The Wipe Tower is currently not supported for multimaterial sequential prints.");
|
return L("The Wipe Tower is currently not supported for multimaterial sequential prints.");
|
||||||
|
|
||||||
if (m_objects.size() > 1) {
|
if (m_objects.size() > 1) {
|
||||||
|
|
@ -1422,8 +580,6 @@ std::string Print::validate(std::string* warning) const
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::vector<unsigned int> extruders = this->extruders();
|
|
||||||
|
|
||||||
// Find the smallest used nozzle diameter and the number of unique nozzle diameters.
|
// Find the smallest used nozzle diameter and the number of unique nozzle diameters.
|
||||||
double min_nozzle_diameter = std::numeric_limits<double>::max();
|
double min_nozzle_diameter = std::numeric_limits<double>::max();
|
||||||
double max_nozzle_diameter = 0;
|
double max_nozzle_diameter = 0;
|
||||||
|
|
@ -1530,8 +686,8 @@ std::string Print::validate(std::string* warning) const
|
||||||
if ((object->has_support() || object->has_raft()) && ! validate_extrusion_width(object->config(), "support_material_extrusion_width", layer_height, err_msg))
|
if ((object->has_support() || object->has_raft()) && ! validate_extrusion_width(object->config(), "support_material_extrusion_width", layer_height, err_msg))
|
||||||
return err_msg;
|
return err_msg;
|
||||||
for (const char *opt_key : { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width" })
|
for (const char *opt_key : { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width" })
|
||||||
for (size_t i = 0; i < object->region_volumes.size(); ++ i)
|
for (const PrintRegion ®ion : object->all_regions())
|
||||||
if (! object->region_volumes[i].empty() && ! validate_extrusion_width(this->get_region(i)->config(), opt_key, layer_height, err_msg))
|
if (! validate_extrusion_width(region.config(), opt_key, layer_height, err_msg))
|
||||||
return err_msg;
|
return err_msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1606,7 +762,7 @@ Flow Print::brim_flow() const
|
||||||
{
|
{
|
||||||
ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width;
|
ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width;
|
||||||
if (width.value == 0)
|
if (width.value == 0)
|
||||||
width = m_regions.front()->config().perimeter_extrusion_width;
|
width = m_print_regions.front()->config().perimeter_extrusion_width;
|
||||||
if (width.value == 0)
|
if (width.value == 0)
|
||||||
width = m_objects.front()->config().extrusion_width;
|
width = m_objects.front()->config().extrusion_width;
|
||||||
|
|
||||||
|
|
@ -1618,7 +774,7 @@ Flow Print::brim_flow() const
|
||||||
return Flow::new_from_config_width(
|
return Flow::new_from_config_width(
|
||||||
frPerimeter,
|
frPerimeter,
|
||||||
width,
|
width,
|
||||||
(float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
|
(float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().perimeter_extruder-1),
|
||||||
(float)this->skirt_first_layer_height());
|
(float)this->skirt_first_layer_height());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1626,7 +782,7 @@ Flow Print::skirt_flow() const
|
||||||
{
|
{
|
||||||
ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width;
|
ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width;
|
||||||
if (width.value == 0)
|
if (width.value == 0)
|
||||||
width = m_regions.front()->config().perimeter_extrusion_width;
|
width = m_print_regions.front()->config().perimeter_extrusion_width;
|
||||||
if (width.value == 0)
|
if (width.value == 0)
|
||||||
width = m_objects.front()->config().extrusion_width;
|
width = m_objects.front()->config().extrusion_width;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class Print;
|
class Print;
|
||||||
|
|
@ -57,12 +60,20 @@ enum PrintObjectStep {
|
||||||
// sharing the same config (including the same assigned extruder(s))
|
// sharing the same config (including the same assigned extruder(s))
|
||||||
class PrintRegion
|
class PrintRegion
|
||||||
{
|
{
|
||||||
friend class Print;
|
public:
|
||||||
|
PrintRegion() = default;
|
||||||
|
PrintRegion(const PrintRegionConfig &config);
|
||||||
|
PrintRegion(const PrintRegionConfig &config, const size_t config_hash) : m_config(config), m_config_hash(config_hash) {}
|
||||||
|
PrintRegion(PrintRegionConfig &&config);
|
||||||
|
PrintRegion(PrintRegionConfig &&config, const size_t config_hash) : m_config(std::move(config)), m_config_hash(config_hash) {}
|
||||||
|
~PrintRegion() = default;
|
||||||
|
|
||||||
// Methods NOT modifying the PrintRegion's state:
|
// Methods NOT modifying the PrintRegion's state:
|
||||||
public:
|
public:
|
||||||
const Print* print() const { return m_print; }
|
const PrintRegionConfig& config() const throw() { return m_config; }
|
||||||
const PrintRegionConfig& config() const { return m_config; }
|
size_t config_hash() const throw() { return m_config_hash; }
|
||||||
|
// Identifier of this PrintRegion in the list of Print::m_print_regions.
|
||||||
|
int print_region_id() const throw() { return m_print_region_id; }
|
||||||
// 1-based extruder identifier for this region and role.
|
// 1-based extruder identifier for this region and role.
|
||||||
unsigned int extruder(FlowRole role) const;
|
unsigned int extruder(FlowRole role) const;
|
||||||
Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const;
|
Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const;
|
||||||
|
|
@ -72,29 +83,25 @@ public:
|
||||||
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
|
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
|
||||||
|
|
||||||
// Collect 0-based extruder indices used to print this region's object.
|
// Collect 0-based extruder indices used to print this region's object.
|
||||||
void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const;
|
void collect_object_printing_extruders(const Print &print, std::vector<unsigned int> &object_extruders) const;
|
||||||
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, const bool has_brim, std::vector<unsigned int> &object_extruders);
|
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, const bool has_brim, std::vector<unsigned int> &object_extruders);
|
||||||
|
|
||||||
// Methods modifying the PrintRegion's state:
|
// Methods modifying the PrintRegion's state:
|
||||||
public:
|
public:
|
||||||
Print* print() { return m_print; }
|
void set_config(const PrintRegionConfig &config) { m_config = config; m_config_hash = m_config.hash(); }
|
||||||
void set_config(const PrintRegionConfig &config) { m_config = config; }
|
void set_config(PrintRegionConfig &&config) { m_config = std::move(config); m_config_hash = m_config.hash(); }
|
||||||
void set_config(PrintRegionConfig &&config) { m_config = std::move(config); }
|
|
||||||
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
|
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
|
||||||
{ this->m_config.apply_only(other, keys, ignore_nonexistent); }
|
{ m_config.apply_only(other, keys, ignore_nonexistent); m_config_hash = m_config.hash(); }
|
||||||
|
|
||||||
protected:
|
|
||||||
size_t m_refcnt;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Print *m_print;
|
friend Print;
|
||||||
PrintRegionConfig m_config;
|
PrintRegionConfig m_config;
|
||||||
|
size_t m_config_hash;
|
||||||
PrintRegion(Print* print) : m_refcnt(0), m_print(print) {}
|
int m_print_region_id = -1;
|
||||||
PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {}
|
|
||||||
~PrintRegion() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool operator==(const PrintRegion &lhs, const PrintRegion &rhs) { return lhs.config_hash() == rhs.config_hash() && lhs.config() == rhs.config(); }
|
||||||
|
inline bool operator!=(const PrintRegion &lhs, const PrintRegion &rhs) { return ! (lhs == rhs); }
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class ConstVectorOfPtrsAdaptor {
|
class ConstVectorOfPtrsAdaptor {
|
||||||
public:
|
public:
|
||||||
|
|
@ -145,15 +152,40 @@ struct PrintInstance
|
||||||
|
|
||||||
typedef std::vector<PrintInstance> PrintInstances;
|
typedef std::vector<PrintInstance> PrintInstances;
|
||||||
|
|
||||||
|
// Region and its volumes (printing volumes or modifier volumes)
|
||||||
|
struct PrintRegionVolumes
|
||||||
|
{
|
||||||
|
// Single volume + Z range assigned to a region.
|
||||||
|
struct VolumeWithZRange {
|
||||||
|
// Z range to slice this ModelVolume over.
|
||||||
|
t_layer_height_range layer_height_range;
|
||||||
|
// Index of a ModelVolume inside its parent ModelObject.
|
||||||
|
int volume_idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Overriding one region with some other extruder, producing another region.
|
||||||
|
// The region is owned by PrintObject::m_all_regions.
|
||||||
|
struct ExtruderOverride {
|
||||||
|
unsigned int extruder;
|
||||||
|
// const PrintRegion *region;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The region is owned by PrintObject::m_all_regions.
|
||||||
|
// const PrintRegion *region;
|
||||||
|
// Possible overrides of the default region extruder.
|
||||||
|
std::vector<ExtruderOverride> overrides;
|
||||||
|
// List of ModelVolume indices and layer ranges of thereof.
|
||||||
|
std::vector<VolumeWithZRange> volumes;
|
||||||
|
// Is this region printing in any layer?
|
||||||
|
// bool printing { false };
|
||||||
|
};
|
||||||
|
|
||||||
class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount>
|
class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount>
|
||||||
{
|
{
|
||||||
private: // Prevents erroneous use by other classes.
|
private: // Prevents erroneous use by other classes.
|
||||||
typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited;
|
typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// vector of (layer height ranges and vectors of volume ids), indexed by region_id
|
|
||||||
std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
|
|
||||||
|
|
||||||
// Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane.
|
// Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane.
|
||||||
const Vec3crd& size() const { return m_size; }
|
const Vec3crd& size() const { return m_size; }
|
||||||
const PrintObjectConfig& config() const { return m_config; }
|
const PrintObjectConfig& config() const { return m_config; }
|
||||||
|
|
@ -180,9 +212,9 @@ public:
|
||||||
|
|
||||||
// adds region_id, too, if necessary
|
// adds region_id, too, if necessary
|
||||||
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
|
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
|
||||||
if (region_id >= region_volumes.size())
|
if (region_id >= m_region_volumes.size())
|
||||||
region_volumes.resize(region_id + 1);
|
m_region_volumes.resize(region_id + 1);
|
||||||
region_volumes[region_id].emplace_back(layer_range, volume_id);
|
m_region_volumes[region_id].volumes.push_back({ layer_range, volume_id });
|
||||||
}
|
}
|
||||||
// This is the *total* layer count (including support layers)
|
// This is the *total* layer count (including support layers)
|
||||||
// this value is not supposed to be compared with Layer::id
|
// this value is not supposed to be compared with Layer::id
|
||||||
|
|
@ -213,7 +245,7 @@ public:
|
||||||
|
|
||||||
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
|
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
|
||||||
// Returns true, if the layer_height_profile was changed.
|
// Returns true, if the layer_height_profile was changed.
|
||||||
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile);
|
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile);
|
||||||
|
|
||||||
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
|
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
|
||||||
// by the interactive layer height editor and by the printing process itself.
|
// by the interactive layer height editor and by the printing process itself.
|
||||||
|
|
@ -222,6 +254,11 @@ public:
|
||||||
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
|
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
|
||||||
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z);
|
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z);
|
||||||
|
|
||||||
|
size_t num_printing_regions() const throw() { return m_all_regions.size(); }
|
||||||
|
const PrintRegion& printing_region(size_t idx) const throw() { return *m_all_regions[idx]; }
|
||||||
|
//FIXME returing all possible regions before slicing, thus some of the regions may not be slicing at the end.
|
||||||
|
std::vector<std::reference_wrapper<const PrintRegion>> all_regions() const;
|
||||||
|
|
||||||
bool has_support() const { return m_config.support_material || m_config.support_material_enforce_layers > 0; }
|
bool has_support() const { return m_config.support_material || m_config.support_material_enforce_layers > 0; }
|
||||||
bool has_raft() const { return m_config.raft_layers > 0; }
|
bool has_raft() const { return m_config.raft_layers > 0; }
|
||||||
bool has_support_material() const { return this->has_support() || this->has_raft(); }
|
bool has_support_material() const { return this->has_support() || this->has_raft(); }
|
||||||
|
|
@ -247,8 +284,8 @@ private:
|
||||||
PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances);
|
PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances);
|
||||||
~PrintObject() = default;
|
~PrintObject() = default;
|
||||||
|
|
||||||
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
|
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); }
|
||||||
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
|
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_config.apply_only(other, keys, ignore_nonexistent); }
|
||||||
PrintBase::ApplyStatus set_instances(PrintInstances &&instances);
|
PrintBase::ApplyStatus set_instances(PrintInstances &&instances);
|
||||||
// Invalidates the step, and its depending steps in PrintObject and Print.
|
// Invalidates the step, and its depending steps in PrintObject and Print.
|
||||||
bool invalidate_step(PrintObjectStep step);
|
bool invalidate_step(PrintObjectStep step);
|
||||||
|
|
@ -296,6 +333,10 @@ private:
|
||||||
// This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system.
|
// This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system.
|
||||||
Point m_center_offset;
|
Point m_center_offset;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<PrintRegion>> m_all_regions;
|
||||||
|
// vector of (layer height ranges and vectors of volume ids), indexed by region_id
|
||||||
|
std::vector<PrintRegionVolumes> m_region_volumes;
|
||||||
|
|
||||||
SlicingParameters m_slicing_params;
|
SlicingParameters m_slicing_params;
|
||||||
LayerPtrs m_layers;
|
LayerPtrs m_layers;
|
||||||
SupportLayerPtrs m_support_layers;
|
SupportLayerPtrs m_support_layers;
|
||||||
|
|
@ -366,7 +407,7 @@ struct PrintStatistics
|
||||||
double total_weight;
|
double total_weight;
|
||||||
double total_wipe_tower_cost;
|
double total_wipe_tower_cost;
|
||||||
double total_wipe_tower_filament;
|
double total_wipe_tower_filament;
|
||||||
std::map<size_t, float> filament_stats;
|
std::map<size_t, double> filament_stats;
|
||||||
|
|
||||||
// Config with the filled in print statistics.
|
// Config with the filled in print statistics.
|
||||||
DynamicConfig config() const;
|
DynamicConfig config() const;
|
||||||
|
|
@ -395,11 +436,13 @@ class ConstPrintObjectPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintObject>
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<PrintRegion*> PrintRegionPtrs;
|
typedef std::vector<PrintRegion*> PrintRegionPtrs;
|
||||||
|
/*
|
||||||
typedef std::vector<const PrintRegion*> ConstPrintRegionPtrs;
|
typedef std::vector<const PrintRegion*> ConstPrintRegionPtrs;
|
||||||
class ConstPrintRegionPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintRegion> {
|
class ConstPrintRegionPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintRegion> {
|
||||||
friend Print;
|
friend Print;
|
||||||
ConstPrintRegionPtrsAdaptor(const PrintRegionPtrs *data) : ConstVectorOfPtrsAdaptor<PrintRegion>(data) {}
|
ConstPrintRegionPtrsAdaptor(const PrintRegionPtrs *data) : ConstVectorOfPtrsAdaptor<PrintRegion>(data) {}
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
// The complete print tray with possibly multiple objects.
|
// The complete print tray with possibly multiple objects.
|
||||||
class Print : public PrintBaseWithState<PrintStep, psCount>
|
class Print : public PrintBaseWithState<PrintStep, psCount>
|
||||||
|
|
@ -470,14 +513,14 @@ public:
|
||||||
[object_id](const PrintObject *obj) { return obj->id() == object_id; });
|
[object_id](const PrintObject *obj) { return obj->id() == object_id; });
|
||||||
return (it == m_objects.end()) ? nullptr : *it;
|
return (it == m_objects.end()) ? nullptr : *it;
|
||||||
}
|
}
|
||||||
ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); }
|
// ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); }
|
||||||
// How many of PrintObject::copies() over all print objects are there?
|
// How many of PrintObject::copies() over all print objects are there?
|
||||||
// If zero, then the print is empty and the print shall not be executed.
|
// If zero, then the print is empty and the print shall not be executed.
|
||||||
unsigned int num_object_instances() const;
|
unsigned int num_object_instances() const;
|
||||||
|
|
||||||
// For Perl bindings.
|
// For Perl bindings.
|
||||||
PrintObjectPtrs& objects_mutable() { return m_objects; }
|
PrintObjectPtrs& objects_mutable() { return m_objects; }
|
||||||
PrintRegionPtrs& regions_mutable() { return m_regions; }
|
PrintRegionPtrs& print_regions_mutable() { return m_print_regions; }
|
||||||
|
|
||||||
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
|
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
|
||||||
const ExtrusionEntityCollection& brim() const { return m_brim; }
|
const ExtrusionEntityCollection& brim() const { return m_brim; }
|
||||||
|
|
@ -498,30 +541,19 @@ public:
|
||||||
|
|
||||||
std::string output_filename(const std::string &filename_base = std::string()) const override;
|
std::string output_filename(const std::string &filename_base = std::string()) const override;
|
||||||
|
|
||||||
// Accessed by SupportMaterial
|
size_t num_print_regions() const throw() { return m_print_regions.size(); }
|
||||||
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
|
const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; }
|
||||||
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } // #ys_FIXME just for testing
|
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; }
|
||||||
|
|
||||||
#if ENABLE_SEQUENTIAL_LIMITS
|
#if ENABLE_SEQUENTIAL_LIMITS
|
||||||
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
|
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
|
||||||
#endif // ENABLE_SEQUENTIAL_LIMITS
|
#endif // ENABLE_SEQUENTIAL_LIMITS
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// methods for handling regions
|
|
||||||
PrintRegion* get_region(size_t idx) { return m_regions[idx]; }
|
|
||||||
PrintRegion* add_region();
|
|
||||||
PrintRegion* add_region(const PrintRegionConfig &config);
|
|
||||||
|
|
||||||
// Invalidates the step, and its depending steps in Print.
|
// Invalidates the step, and its depending steps in Print.
|
||||||
bool invalidate_step(PrintStep step);
|
bool invalidate_step(PrintStep step);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void config_diffs(
|
|
||||||
const DynamicPrintConfig &new_full_config,
|
|
||||||
t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff,
|
|
||||||
t_config_option_keys &full_config_diff,
|
|
||||||
DynamicPrintConfig &filament_overrides) const;
|
|
||||||
|
|
||||||
bool invalidate_state_by_config_options(const ConfigOptionResolver &new_config, const std::vector<t_config_option_key> &opt_keys);
|
bool invalidate_state_by_config_options(const ConfigOptionResolver &new_config, const std::vector<t_config_option_key> &opt_keys);
|
||||||
|
|
||||||
void _make_skirt();
|
void _make_skirt();
|
||||||
|
|
@ -533,14 +565,11 @@ private:
|
||||||
// Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim.
|
// Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim.
|
||||||
std::vector<Point> first_layer_wipe_tower_corners() const;
|
std::vector<Point> first_layer_wipe_tower_corners() const;
|
||||||
|
|
||||||
// Declared here to have access to Model / ModelObject / ModelInstance
|
|
||||||
static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src);
|
|
||||||
|
|
||||||
PrintConfig m_config;
|
PrintConfig m_config;
|
||||||
PrintObjectConfig m_default_object_config;
|
PrintObjectConfig m_default_object_config;
|
||||||
PrintRegionConfig m_default_region_config;
|
PrintRegionConfig m_default_region_config;
|
||||||
PrintObjectPtrs m_objects;
|
PrintObjectPtrs m_objects;
|
||||||
PrintRegionPtrs m_regions;
|
PrintRegionPtrs m_print_regions;
|
||||||
|
|
||||||
// Ordered collections of extrusion paths to build skirt loops and brim.
|
// Ordered collections of extrusion paths to build skirt loops and brim.
|
||||||
ExtrusionEntityCollection m_skirt;
|
ExtrusionEntityCollection m_skirt;
|
||||||
|
|
|
||||||
824
src/libslic3r/PrintApply.cpp
Normal file
824
src/libslic3r/PrintApply.cpp
Normal file
|
|
@ -0,0 +1,824 @@
|
||||||
|
#include "Model.hpp"
|
||||||
|
#include "Print.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
|
||||||
|
// in the exact order and with the same IDs.
|
||||||
|
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
|
||||||
|
// Friend to ModelVolume to allow copying.
|
||||||
|
// static is not accepted by gcc if declared as a friend of ModelObject.
|
||||||
|
/* static */ void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_new)
|
||||||
|
{
|
||||||
|
typedef std::pair<const ModelVolume*, bool> ModelVolumeWithStatus;
|
||||||
|
std::vector<ModelVolumeWithStatus> old_volumes;
|
||||||
|
old_volumes.reserve(model_object_dst.volumes.size());
|
||||||
|
for (const ModelVolume *model_volume : model_object_dst.volumes)
|
||||||
|
old_volumes.emplace_back(ModelVolumeWithStatus(model_volume, false));
|
||||||
|
auto model_volume_lower = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() < mv2.first->id(); };
|
||||||
|
auto model_volume_equal = [](const ModelVolumeWithStatus &mv1, const ModelVolumeWithStatus &mv2){ return mv1.first->id() == mv2.first->id(); };
|
||||||
|
std::sort(old_volumes.begin(), old_volumes.end(), model_volume_lower);
|
||||||
|
model_object_dst.volumes.clear();
|
||||||
|
model_object_dst.volumes.reserve(model_object_new.volumes.size());
|
||||||
|
for (const ModelVolume *model_volume_src : model_object_new.volumes) {
|
||||||
|
ModelVolumeWithStatus key(model_volume_src, false);
|
||||||
|
auto it = std::lower_bound(old_volumes.begin(), old_volumes.end(), key, model_volume_lower);
|
||||||
|
if (it != old_volumes.end() && model_volume_equal(*it, key)) {
|
||||||
|
// The volume was found in the old list. Just copy it.
|
||||||
|
assert(! it->second); // not consumed yet
|
||||||
|
it->second = true;
|
||||||
|
ModelVolume *model_volume_dst = const_cast<ModelVolume*>(it->first);
|
||||||
|
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
|
||||||
|
assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type());
|
||||||
|
model_object_dst.volumes.emplace_back(model_volume_dst);
|
||||||
|
if (model_volume_dst->is_support_modifier()) {
|
||||||
|
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
|
||||||
|
model_volume_dst->set_type(model_volume_src->type());
|
||||||
|
model_volume_dst->set_transformation(model_volume_src->get_transformation());
|
||||||
|
}
|
||||||
|
assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix()));
|
||||||
|
} else {
|
||||||
|
// The volume was not found in the old list. Create a new copy.
|
||||||
|
assert(model_volume_src->is_support_modifier());
|
||||||
|
model_object_dst.volumes.emplace_back(new ModelVolume(*model_volume_src));
|
||||||
|
model_object_dst.volumes.back()->set_model_object(&model_object_dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Release the non-consumed old volumes (those were deleted from the new list).
|
||||||
|
for (ModelVolumeWithStatus &mv_with_status : old_volumes)
|
||||||
|
if (! mv_with_status.second)
|
||||||
|
delete mv_with_status.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type)
|
||||||
|
{
|
||||||
|
size_t i_src, i_dst;
|
||||||
|
for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) {
|
||||||
|
const ModelVolume &mv_src = *model_object_src.volumes[i_src];
|
||||||
|
ModelVolume &mv_dst = *model_object_dst.volumes[i_dst];
|
||||||
|
if (mv_src.type() != type) {
|
||||||
|
++ i_src;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mv_dst.type() != type) {
|
||||||
|
++ i_dst;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(mv_src.id() == mv_dst.id());
|
||||||
|
// Copy the ModelVolume data.
|
||||||
|
mv_dst.name = mv_src.name;
|
||||||
|
mv_dst.config.assign_config(mv_src.config);
|
||||||
|
assert(mv_dst.supported_facets.id() == mv_src.supported_facets.id());
|
||||||
|
mv_dst.supported_facets.assign(mv_src.supported_facets);
|
||||||
|
assert(mv_dst.seam_facets.id() == mv_src.seam_facets.id());
|
||||||
|
mv_dst.seam_facets.assign(mv_src.seam_facets);
|
||||||
|
//FIXME what to do with the materials?
|
||||||
|
// mv_dst.m_material_id = mv_src.m_material_id;
|
||||||
|
++ i_src;
|
||||||
|
++ i_dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
|
||||||
|
{
|
||||||
|
assert(lr_dst.size() == lr_src.size());
|
||||||
|
auto it_src = lr_src.cbegin();
|
||||||
|
for (auto &kvp_dst : lr_dst) {
|
||||||
|
const auto &kvp_src = *it_src ++;
|
||||||
|
assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
|
||||||
|
assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
|
||||||
|
// Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
|
||||||
|
// assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
|
||||||
|
kvp_dst.second = kvp_src.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
|
||||||
|
{
|
||||||
|
typedef Transform3d::Scalar T;
|
||||||
|
const T *lv = lhs.data();
|
||||||
|
const T *rv = rhs.data();
|
||||||
|
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv) {
|
||||||
|
if (*lv < *rv)
|
||||||
|
return true;
|
||||||
|
else if (*lv > *rv)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
|
||||||
|
{
|
||||||
|
typedef Transform3d::Scalar T;
|
||||||
|
const T *lv = lhs.data();
|
||||||
|
const T *rv = rhs.data();
|
||||||
|
for (size_t i = 0; i < 16; ++ i, ++ lv, ++ rv)
|
||||||
|
if (*lv != *rv)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PrintObjectTrafoAndInstances
|
||||||
|
{
|
||||||
|
Transform3d trafo;
|
||||||
|
PrintInstances instances;
|
||||||
|
bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
||||||
|
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
|
||||||
|
{
|
||||||
|
std::set<PrintObjectTrafoAndInstances> trafos;
|
||||||
|
PrintObjectTrafoAndInstances trafo;
|
||||||
|
for (ModelInstance *model_instance : model_object.instances)
|
||||||
|
if (model_instance->is_printable()) {
|
||||||
|
trafo.trafo = model_instance->get_matrix();
|
||||||
|
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
|
||||||
|
// Reset the XY axes of the transformation.
|
||||||
|
trafo.trafo.data()[12] = 0;
|
||||||
|
trafo.trafo.data()[13] = 0;
|
||||||
|
// Search or insert a trafo.
|
||||||
|
auto it = trafos.emplace(trafo).first;
|
||||||
|
const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
|
||||||
|
}
|
||||||
|
return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare just the layer ranges and their layer heights, not the associated configs.
|
||||||
|
// Ignore the layer heights if check_layer_heights is false.
|
||||||
|
static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
|
||||||
|
{
|
||||||
|
if (lr1.size() != lr2.size())
|
||||||
|
return false;
|
||||||
|
auto it2 = lr2.begin();
|
||||||
|
for (const auto &kvp1 : lr1) {
|
||||||
|
const auto &kvp2 = *it2 ++;
|
||||||
|
if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
|
||||||
|
std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
|
||||||
|
(check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
|
||||||
|
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<CustomGCode::Item> &va, const std::vector<CustomGCode::Item> &vb)
|
||||||
|
{
|
||||||
|
auto it_a = va.begin();
|
||||||
|
auto it_b = vb.begin();
|
||||||
|
while (it_a != va.end() || it_b != vb.end()) {
|
||||||
|
if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) {
|
||||||
|
// Skip any CustomGCode items, which are not tool changes.
|
||||||
|
++ it_a;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) {
|
||||||
|
// Skip any CustomGCode items, which are not tool changes.
|
||||||
|
++ it_b;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (it_a == va.end() || it_b == vb.end())
|
||||||
|
// va or vb contains more Tool Changes than the other.
|
||||||
|
return true;
|
||||||
|
assert(it_a->type == CustomGCode::ToolChange);
|
||||||
|
assert(it_b->type == CustomGCode::ToolChange);
|
||||||
|
if (*it_a != *it_b)
|
||||||
|
// The two Tool Changes differ.
|
||||||
|
return true;
|
||||||
|
++ it_a;
|
||||||
|
++ it_b;
|
||||||
|
}
|
||||||
|
// There is no change in custom Tool Changes.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect changes to print config, account for overrides of extruder retract values by filament presets.
|
||||||
|
static t_config_option_keys print_config_diffs(
|
||||||
|
const PrintConfig ¤t_config,
|
||||||
|
const DynamicPrintConfig &new_full_config,
|
||||||
|
DynamicPrintConfig &filament_overrides)
|
||||||
|
{
|
||||||
|
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
|
||||||
|
const std::string filament_prefix = "filament_";
|
||||||
|
t_config_option_keys print_diff;
|
||||||
|
for (const t_config_option_key &opt_key : current_config.keys()) {
|
||||||
|
const ConfigOption *opt_old = current_config.option(opt_key);
|
||||||
|
assert(opt_old != nullptr);
|
||||||
|
const ConfigOption *opt_new = new_full_config.option(opt_key);
|
||||||
|
// assert(opt_new != nullptr);
|
||||||
|
if (opt_new == nullptr)
|
||||||
|
//FIXME This may happen when executing some test cases.
|
||||||
|
continue;
|
||||||
|
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
|
||||||
|
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
|
||||||
|
// An extruder retract override is available at some of the filament presets.
|
||||||
|
if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
|
||||||
|
auto opt_copy = opt_new->clone();
|
||||||
|
opt_copy->apply_override(opt_new_filament);
|
||||||
|
if (*opt_old == *opt_copy)
|
||||||
|
delete opt_copy;
|
||||||
|
else {
|
||||||
|
filament_overrides.set_key_value(opt_key, opt_copy);
|
||||||
|
print_diff.emplace_back(opt_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (*opt_new != *opt_old)
|
||||||
|
print_diff.emplace_back(opt_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return print_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
|
||||||
|
static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig ¤t_full_config, const DynamicPrintConfig &new_full_config)
|
||||||
|
{
|
||||||
|
t_config_option_keys full_config_diff;
|
||||||
|
for (const t_config_option_key &opt_key : new_full_config.keys()) {
|
||||||
|
const ConfigOption *opt_old = current_full_config.option(opt_key);
|
||||||
|
const ConfigOption *opt_new = new_full_config.option(opt_key);
|
||||||
|
if (opt_old == nullptr || *opt_new != *opt_old)
|
||||||
|
full_config_diff.emplace_back(opt_key);
|
||||||
|
}
|
||||||
|
return full_config_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
check_model_ids_validity(model);
|
||||||
|
#endif /* _DEBUG */
|
||||||
|
|
||||||
|
// Normalize the config.
|
||||||
|
new_full_config.option("print_settings_id", true);
|
||||||
|
new_full_config.option("filament_settings_id", true);
|
||||||
|
new_full_config.option("printer_settings_id", true);
|
||||||
|
new_full_config.option("physical_printer_settings_id", true);
|
||||||
|
new_full_config.normalize_fdm();
|
||||||
|
|
||||||
|
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
|
||||||
|
DynamicPrintConfig filament_overrides;
|
||||||
|
t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides);
|
||||||
|
t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config);
|
||||||
|
// Collect changes to object and region configs.
|
||||||
|
t_config_option_keys object_diff = m_default_object_config.diff(new_full_config);
|
||||||
|
t_config_option_keys region_diff = m_default_region_config.diff(new_full_config);
|
||||||
|
|
||||||
|
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
|
||||||
|
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
|
||||||
|
auto update_apply_status = [&apply_status](bool invalidated)
|
||||||
|
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
|
||||||
|
if (! (print_diff.empty() && object_diff.empty() && region_diff.empty()))
|
||||||
|
update_apply_status(false);
|
||||||
|
|
||||||
|
// Grab the lock for the Print / PrintObject milestones.
|
||||||
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||||
|
|
||||||
|
// The following call may stop the background processing.
|
||||||
|
if (! print_diff.empty())
|
||||||
|
update_apply_status(this->invalidate_state_by_config_options(new_full_config, print_diff));
|
||||||
|
|
||||||
|
// Apply variables to placeholder parser. The placeholder parser is used by G-code export,
|
||||||
|
// which should be stopped if print_diff is not empty.
|
||||||
|
size_t num_extruders = m_config.nozzle_diameter.size();
|
||||||
|
bool num_extruders_changed = false;
|
||||||
|
if (! full_config_diff.empty()) {
|
||||||
|
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||||
|
// Set the profile aliases for the PrintBase::output_filename()
|
||||||
|
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
|
||||||
|
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
|
||||||
|
m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
|
||||||
|
m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone());
|
||||||
|
// We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser.
|
||||||
|
// see "Placeholders do not respect filament overrides." GH issue #3649
|
||||||
|
m_placeholder_parser.apply_config(filament_overrides);
|
||||||
|
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
|
||||||
|
m_config.apply_only(new_full_config, print_diff, true);
|
||||||
|
//FIXME use move semantics once ConfigBase supports it.
|
||||||
|
m_config.apply(filament_overrides);
|
||||||
|
// Handle changes to object config defaults
|
||||||
|
m_default_object_config.apply_only(new_full_config, object_diff, true);
|
||||||
|
// Handle changes to regions config defaults
|
||||||
|
m_default_region_config.apply_only(new_full_config, region_diff, true);
|
||||||
|
m_full_print_config = std::move(new_full_config);
|
||||||
|
if (num_extruders != m_config.nozzle_diameter.size()) {
|
||||||
|
num_extruders = m_config.nozzle_diameter.size();
|
||||||
|
num_extruders_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LayerRanges
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LayerRanges() {}
|
||||||
|
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
|
||||||
|
void assign(const t_layer_config_ranges &in) {
|
||||||
|
m_ranges.clear();
|
||||||
|
m_ranges.reserve(in.size());
|
||||||
|
// Input ranges are sorted lexicographically. First range trims the other ranges.
|
||||||
|
coordf_t last_z = 0;
|
||||||
|
for (const std::pair<const t_layer_height_range, ModelConfig> &range : in)
|
||||||
|
if (range.first.second > last_z) {
|
||||||
|
coordf_t min_z = std::max(range.first.first, 0.);
|
||||||
|
if (min_z > last_z + EPSILON) {
|
||||||
|
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
|
||||||
|
last_z = min_z;
|
||||||
|
}
|
||||||
|
if (range.first.second > last_z + EPSILON) {
|
||||||
|
const DynamicPrintConfig *cfg = &range.second.get();
|
||||||
|
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
|
||||||
|
last_z = range.first.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_ranges.empty())
|
||||||
|
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
|
||||||
|
else if (m_ranges.back().second == nullptr)
|
||||||
|
m_ranges.back().first.second = DBL_MAX;
|
||||||
|
else
|
||||||
|
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DynamicPrintConfig* config(const t_layer_height_range &range) const {
|
||||||
|
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
|
||||||
|
// #ys_FIXME_COLOR
|
||||||
|
// assert(it != m_ranges.end());
|
||||||
|
// assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
|
||||||
|
// assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
|
||||||
|
if (it == m_ranges.end() ||
|
||||||
|
std::abs(it->first.first - range.first) > EPSILON ||
|
||||||
|
std::abs(it->first.second - range.second) > EPSILON )
|
||||||
|
return nullptr; // desired range doesn't found
|
||||||
|
return (it == m_ranges.end()) ? nullptr : it->second;
|
||||||
|
}
|
||||||
|
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
|
||||||
|
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
|
||||||
|
private:
|
||||||
|
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
|
||||||
|
};
|
||||||
|
struct ModelObjectStatus {
|
||||||
|
enum Status {
|
||||||
|
Unknown,
|
||||||
|
Old,
|
||||||
|
New,
|
||||||
|
Moved,
|
||||||
|
Deleted,
|
||||||
|
};
|
||||||
|
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
|
||||||
|
ObjectID id;
|
||||||
|
Status status;
|
||||||
|
LayerRanges layer_ranges;
|
||||||
|
// Search by id.
|
||||||
|
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
|
||||||
|
};
|
||||||
|
std::set<ModelObjectStatus> model_object_status;
|
||||||
|
|
||||||
|
// 1) Synchronize model objects.
|
||||||
|
if (model.id() != m_model.id()) {
|
||||||
|
// Kill everything, initialize from scratch.
|
||||||
|
// Stop background processing.
|
||||||
|
this->call_cancel_callback();
|
||||||
|
update_apply_status(this->invalidate_all_steps());
|
||||||
|
for (PrintObject *object : m_objects) {
|
||||||
|
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
|
||||||
|
update_apply_status(object->invalidate_all_steps());
|
||||||
|
delete object;
|
||||||
|
}
|
||||||
|
m_objects.clear();
|
||||||
|
m_model.assign_copy(model);
|
||||||
|
for (const ModelObject *model_object : m_model.objects)
|
||||||
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
|
||||||
|
} else {
|
||||||
|
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
|
||||||
|
update_apply_status(num_extruders_changed ||
|
||||||
|
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
|
||||||
|
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
|
||||||
|
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
|
||||||
|
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
|
||||||
|
// The Tool Ordering and the Wipe Tower are no more valid.
|
||||||
|
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
|
||||||
|
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
|
||||||
|
this->invalidate_step(psGCodeExport));
|
||||||
|
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
|
||||||
|
}
|
||||||
|
if (model_object_list_equal(m_model, model)) {
|
||||||
|
// The object list did not change.
|
||||||
|
for (const ModelObject *model_object : m_model.objects)
|
||||||
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
||||||
|
} else if (model_object_list_extended(m_model, model)) {
|
||||||
|
// Add new objects. Their volumes and configs will be synchronized later.
|
||||||
|
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||||
|
for (const ModelObject *model_object : m_model.objects)
|
||||||
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
||||||
|
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
|
||||||
|
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
|
||||||
|
m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
|
||||||
|
m_model.objects.back()->set_model(&m_model);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reorder the objects, add new objects.
|
||||||
|
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
|
||||||
|
this->call_cancel_callback();
|
||||||
|
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||||
|
// Second create a new list of objects.
|
||||||
|
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
|
||||||
|
m_model.objects.clear();
|
||||||
|
m_model.objects.reserve(model.objects.size());
|
||||||
|
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
|
||||||
|
std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
|
||||||
|
for (const ModelObject *mobj : model.objects) {
|
||||||
|
auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
|
||||||
|
if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
|
||||||
|
// New ModelObject added.
|
||||||
|
m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
|
||||||
|
m_model.objects.back()->set_model(&m_model);
|
||||||
|
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
|
||||||
|
} else {
|
||||||
|
// Existing ModelObject re-added (possibly moved in the list).
|
||||||
|
m_model.objects.emplace_back(*it);
|
||||||
|
model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool deleted_any = false;
|
||||||
|
for (ModelObject *&model_object : model_objects_old) {
|
||||||
|
if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
|
||||||
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
|
||||||
|
deleted_any = true;
|
||||||
|
} else
|
||||||
|
// Do not delete this ModelObject instance.
|
||||||
|
model_object = nullptr;
|
||||||
|
}
|
||||||
|
if (deleted_any) {
|
||||||
|
// Delete PrintObjects of the deleted ModelObjects.
|
||||||
|
PrintObjectPtrs print_objects_old = std::move(m_objects);
|
||||||
|
m_objects.clear();
|
||||||
|
m_objects.reserve(print_objects_old.size());
|
||||||
|
for (PrintObject *print_object : print_objects_old) {
|
||||||
|
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
|
||||||
|
assert(it_status != model_object_status.end());
|
||||||
|
if (it_status->status == ModelObjectStatus::Deleted) {
|
||||||
|
update_apply_status(print_object->invalidate_all_steps());
|
||||||
|
delete print_object;
|
||||||
|
} else
|
||||||
|
m_objects.emplace_back(print_object);
|
||||||
|
}
|
||||||
|
for (ModelObject *model_object : model_objects_old)
|
||||||
|
delete model_object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Map print objects including their transformation matrices.
|
||||||
|
struct PrintObjectStatus {
|
||||||
|
enum Status {
|
||||||
|
Unknown,
|
||||||
|
Deleted,
|
||||||
|
Reused,
|
||||||
|
New
|
||||||
|
};
|
||||||
|
PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
|
||||||
|
id(print_object->model_object()->id()),
|
||||||
|
print_object(print_object),
|
||||||
|
trafo(print_object->trafo()),
|
||||||
|
status(status) {}
|
||||||
|
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
|
||||||
|
// ID of the ModelObject & PrintObject
|
||||||
|
ObjectID id;
|
||||||
|
// Pointer to the old PrintObject
|
||||||
|
PrintObject *print_object;
|
||||||
|
// Trafo generated with model_object->world_matrix(true)
|
||||||
|
Transform3d trafo;
|
||||||
|
Status status;
|
||||||
|
// Search by id.
|
||||||
|
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
|
||||||
|
};
|
||||||
|
std::multiset<PrintObjectStatus> print_object_status;
|
||||||
|
for (PrintObject *print_object : m_objects)
|
||||||
|
print_object_status.emplace(PrintObjectStatus(print_object));
|
||||||
|
|
||||||
|
// 3) Synchronize ModelObjects & PrintObjects.
|
||||||
|
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
|
||||||
|
ModelObject &model_object = *m_model.objects[idx_model_object];
|
||||||
|
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
||||||
|
assert(it_status != model_object_status.end());
|
||||||
|
assert(it_status->status != ModelObjectStatus::Deleted);
|
||||||
|
const ModelObject& model_object_new = *model.objects[idx_model_object];
|
||||||
|
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
|
||||||
|
if (it_status->status == ModelObjectStatus::New)
|
||||||
|
// PrintObject instances will be added in the next loop.
|
||||||
|
continue;
|
||||||
|
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
||||||
|
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
||||||
|
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||||
|
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
|
||||||
|
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
||||||
|
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
|
||||||
|
bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
|
||||||
|
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
|
||||||
|
if (model_parts_differ || modifiers_differ ||
|
||||||
|
model_object.origin_translation != model_object_new.origin_translation ||
|
||||||
|
! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) ||
|
||||||
|
! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
|
||||||
|
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
||||||
|
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||||
|
for (auto it = range.first; it != range.second; ++ it) {
|
||||||
|
update_apply_status(it->print_object->invalidate_all_steps());
|
||||||
|
const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
|
||||||
|
}
|
||||||
|
// Copy content of the ModelObject including its ID, do not change the parent.
|
||||||
|
model_object.assign_copy(model_object_new);
|
||||||
|
} else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) {
|
||||||
|
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
|
||||||
|
if (supports_differ) {
|
||||||
|
this->call_cancel_callback();
|
||||||
|
update_apply_status(false);
|
||||||
|
}
|
||||||
|
// Invalidate just the supports step.
|
||||||
|
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||||
|
for (auto it = range.first; it != range.second; ++ it)
|
||||||
|
update_apply_status(it->print_object->invalidate_step(posSupportMaterial));
|
||||||
|
if (supports_differ) {
|
||||||
|
// Copy just the support volumes.
|
||||||
|
model_volume_list_update_supports(model_object, model_object_new);
|
||||||
|
}
|
||||||
|
} else if (model_custom_seam_data_changed(model_object, model_object_new)) {
|
||||||
|
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||||
|
}
|
||||||
|
if (! model_parts_differ && ! modifiers_differ) {
|
||||||
|
// Synchronize Object's config.
|
||||||
|
bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
|
||||||
|
if (object_config_changed)
|
||||||
|
model_object.config.assign_config(model_object_new.config);
|
||||||
|
if (! object_diff.empty() || object_config_changed || num_extruders_changed) {
|
||||||
|
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
|
||||||
|
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||||
|
for (auto it = range.first; it != range.second; ++ it) {
|
||||||
|
t_config_option_keys diff = it->print_object->config().diff(new_config);
|
||||||
|
if (! diff.empty()) {
|
||||||
|
update_apply_status(it->print_object->invalidate_state_by_config_options(it->print_object->config(), new_config, diff));
|
||||||
|
it->print_object->config_apply_only(new_config, diff, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data).
|
||||||
|
//FIXME What to do with m_material_id?
|
||||||
|
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
|
||||||
|
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
|
||||||
|
layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
|
||||||
|
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
|
||||||
|
model_object.name = model_object_new.name;
|
||||||
|
model_object.input_file = model_object_new.input_file;
|
||||||
|
// Only refresh ModelInstances if there is any change.
|
||||||
|
if (model_object.instances.size() != model_object_new.instances.size() ||
|
||||||
|
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
|
||||||
|
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
|
||||||
|
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||||
|
model_object.clear_instances();
|
||||||
|
model_object.instances.reserve(model_object_new.instances.size());
|
||||||
|
for (const ModelInstance *model_instance : model_object_new.instances) {
|
||||||
|
model_object.instances.emplace_back(new ModelInstance(*model_instance));
|
||||||
|
model_object.instances.back()->set_model_object(&model_object);
|
||||||
|
}
|
||||||
|
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
|
||||||
|
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
|
||||||
|
l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
|
||||||
|
// If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
|
||||||
|
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
|
||||||
|
model_object.invalidate_bounding_box();
|
||||||
|
// Synchronize the content of instances.
|
||||||
|
auto new_instance = model_object_new.instances.begin();
|
||||||
|
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
|
||||||
|
(*old_instance)->set_transformation((*new_instance)->get_transformation());
|
||||||
|
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
|
||||||
|
(*old_instance)->printable = (*new_instance)->printable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) Generate PrintObjects from ModelObjects and their instances.
|
||||||
|
bool print_regions_reshuffled = false;
|
||||||
|
{
|
||||||
|
PrintObjectPtrs print_objects_new;
|
||||||
|
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
|
||||||
|
bool new_objects = false;
|
||||||
|
// Walk over all new model objects and check, whether there are matching PrintObjects.
|
||||||
|
for (ModelObject *model_object : m_model.objects) {
|
||||||
|
auto range = print_object_status.equal_range(PrintObjectStatus(model_object->id()));
|
||||||
|
std::vector<const PrintObjectStatus*> old;
|
||||||
|
if (range.first != range.second) {
|
||||||
|
old.reserve(print_object_status.count(PrintObjectStatus(model_object->id())));
|
||||||
|
for (auto it = range.first; it != range.second; ++ it)
|
||||||
|
if (it->status != PrintObjectStatus::Deleted)
|
||||||
|
old.emplace_back(&(*it));
|
||||||
|
}
|
||||||
|
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
||||||
|
// Producing the config for PrintObject on demand, caching it at print_object_last.
|
||||||
|
const PrintObject *print_object_last = nullptr;
|
||||||
|
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) {
|
||||||
|
print_object->config_apply(print_object_last ?
|
||||||
|
print_object_last->config() :
|
||||||
|
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders));
|
||||||
|
print_object_last = print_object;
|
||||||
|
};
|
||||||
|
std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
|
||||||
|
if (old.empty()) {
|
||||||
|
// Simple case, just generate new instances.
|
||||||
|
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
|
||||||
|
PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
|
||||||
|
print_object_apply_config(print_object);
|
||||||
|
print_objects_new.emplace_back(print_object);
|
||||||
|
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
||||||
|
new_objects = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Complex case, try to merge the two lists.
|
||||||
|
// Sort the old lexicographically by their trafos.
|
||||||
|
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
|
||||||
|
// Merge the old / new lists.
|
||||||
|
auto it_old = old.begin();
|
||||||
|
for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) {
|
||||||
|
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
|
||||||
|
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
|
||||||
|
// This is a new instance (or a set of instances with the same trafo). Just add it.
|
||||||
|
PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
|
||||||
|
print_object_apply_config(print_object);
|
||||||
|
print_objects_new.emplace_back(print_object);
|
||||||
|
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
||||||
|
new_objects = true;
|
||||||
|
if (it_old != old.end())
|
||||||
|
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
|
||||||
|
} else {
|
||||||
|
// The PrintObject already exists and the copies differ.
|
||||||
|
PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
|
||||||
|
if (status != PrintBase::APPLY_STATUS_UNCHANGED)
|
||||||
|
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
|
||||||
|
print_objects_new.emplace_back((*it_old)->print_object);
|
||||||
|
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Reused;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_objects != print_objects_new) {
|
||||||
|
this->call_cancel_callback();
|
||||||
|
update_apply_status(this->invalidate_all_steps());
|
||||||
|
m_objects = print_objects_new;
|
||||||
|
// Delete the PrintObjects marked as Unknown or Deleted.
|
||||||
|
bool deleted_objects = false;
|
||||||
|
for (auto &pos : print_object_status)
|
||||||
|
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
|
||||||
|
update_apply_status(pos.print_object->invalidate_all_steps());
|
||||||
|
delete pos.print_object;
|
||||||
|
deleted_objects = true;
|
||||||
|
}
|
||||||
|
if (new_objects || deleted_objects)
|
||||||
|
update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport }));
|
||||||
|
if (new_objects)
|
||||||
|
update_apply_status(false);
|
||||||
|
print_regions_reshuffled = true;
|
||||||
|
}
|
||||||
|
print_object_status.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// All regions now have distinct settings.
|
||||||
|
// Check whether applying the new region config defaults we'd get different regions.
|
||||||
|
for (PrintObject *print_object : m_objects) {
|
||||||
|
const LayerRanges *layer_ranges;
|
||||||
|
{
|
||||||
|
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
|
||||||
|
assert(it_status != model_object_status.end());
|
||||||
|
assert(it_status->status != ModelObjectStatus::Deleted);
|
||||||
|
layer_ranges = &it_status->layer_ranges;
|
||||||
|
}
|
||||||
|
bool some_object_region_modified = false;
|
||||||
|
bool regions_merged = false;
|
||||||
|
for (size_t region_id = 0; region_id < print_object->m_region_volumes.size(); ++ region_id) {
|
||||||
|
PrintRegion ®ion = *print_object->m_all_regions[region_id];
|
||||||
|
PrintRegionConfig region_config;
|
||||||
|
bool region_config_set = false;
|
||||||
|
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : print_object->m_region_volumes[region_id].volumes) {
|
||||||
|
const ModelVolume &volume = *print_object->model_object()->volumes[volume_w_zrange.volume_idx];
|
||||||
|
const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_w_zrange.layer_height_range);
|
||||||
|
PrintRegionConfig this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
|
||||||
|
if (region_config_set) {
|
||||||
|
if (this_region_config != region_config) {
|
||||||
|
regions_merged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
region_config = std::move(this_region_config);
|
||||||
|
region_config_set = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (regions_merged)
|
||||||
|
break;
|
||||||
|
size_t region_config_hash = region_config.hash();
|
||||||
|
bool modified = region.config_hash() != region_config_hash || region.config() != region_config;
|
||||||
|
some_object_region_modified |= modified;
|
||||||
|
if (some_object_region_modified)
|
||||||
|
// Verify whether this region was not merged with some other region.
|
||||||
|
for (size_t i = 0; i < region_id; ++ i) {
|
||||||
|
const PrintRegion ®ion_other = *print_object->m_all_regions[i];
|
||||||
|
if (region_other.config_hash() == region_config_hash && region_other.config() == region_config) {
|
||||||
|
// Regions were merged. Reset this print_object.
|
||||||
|
regions_merged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (modified) {
|
||||||
|
// Stop the background process before assigning new configuration to the regions.
|
||||||
|
t_config_option_keys diff = region.config().diff(region_config);
|
||||||
|
update_apply_status(print_object->invalidate_state_by_config_options(region.config(), region_config, diff));
|
||||||
|
region.config_apply_only(region_config, diff, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (regions_merged) {
|
||||||
|
// Two regions of a single object were either split or merged. This invalidates the whole slicing.
|
||||||
|
update_apply_status(print_object->invalidate_all_steps());
|
||||||
|
print_object->m_region_volumes.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Possibly add new regions for the newly added or resetted PrintObjects.
|
||||||
|
for (size_t idx_print_object = 0; idx_print_object < m_objects.size();) {
|
||||||
|
PrintObject &print_object0 = *m_objects[idx_print_object];
|
||||||
|
const ModelObject &model_object = *print_object0.model_object();
|
||||||
|
const LayerRanges *layer_ranges;
|
||||||
|
{
|
||||||
|
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
||||||
|
assert(it_status != model_object_status.end());
|
||||||
|
assert(it_status->status != ModelObjectStatus::Deleted);
|
||||||
|
layer_ranges = &it_status->layer_ranges;
|
||||||
|
}
|
||||||
|
if (print_object0.m_region_volumes.empty()) {
|
||||||
|
// Fresh or completely invalidated print_object. Assign regions.
|
||||||
|
unsigned int volume_id = 0;
|
||||||
|
for (const ModelVolume *volume : model_object.volumes) {
|
||||||
|
if (! volume->is_model_part() && ! volume->is_modifier()) {
|
||||||
|
++ volume_id;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Filter the layer ranges, so they do not overlap and they contain at least a single layer.
|
||||||
|
// Now insert a volume with a layer range to its own region.
|
||||||
|
for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
|
||||||
|
int region_id = -1;
|
||||||
|
// Get the config applied to this volume.
|
||||||
|
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
|
||||||
|
size_t hash = config.hash();
|
||||||
|
for (size_t i = 0; i < print_object0.m_all_regions.size(); ++ i)
|
||||||
|
if (hash == print_object0.m_all_regions[i]->config_hash() && config == *print_object0.m_all_regions[i]) {
|
||||||
|
region_id = int(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If no region exists with the same config, create a new one.
|
||||||
|
if (region_id == -1) {
|
||||||
|
region_id = int(print_object0.m_all_regions.size());
|
||||||
|
print_object0.m_all_regions.emplace_back(std::make_unique<PrintRegion>(std::move(config), hash));
|
||||||
|
}
|
||||||
|
print_object0.add_region_volume(region_id, volume_id, it_range->first);
|
||||||
|
}
|
||||||
|
++ volume_id;
|
||||||
|
}
|
||||||
|
print_regions_reshuffled = true;
|
||||||
|
}
|
||||||
|
for (++ idx_print_object; idx_print_object < m_objects.size() && m_objects[idx_print_object]->model_object() == &model_object; ++ idx_print_object) {
|
||||||
|
PrintObject &print_object = *m_objects[idx_print_object];
|
||||||
|
if (print_object.m_region_volumes.empty()) {
|
||||||
|
// Copy region volumes and regions from print_object0.
|
||||||
|
print_object.m_region_volumes = print_object0.m_region_volumes;
|
||||||
|
print_object.m_all_regions.reserve(print_object0.m_all_regions.size());
|
||||||
|
for (const std::unique_ptr<Slic3r::PrintRegion> ®ion : print_object0.m_all_regions)
|
||||||
|
print_object.m_all_regions.emplace_back(std::make_unique<PrintRegion>(*region));
|
||||||
|
print_regions_reshuffled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (print_regions_reshuffled) {
|
||||||
|
// Update Print::m_print_regions from objects.
|
||||||
|
struct cmp { bool operator() (const PrintRegion *l, const PrintRegion *r) const { return l->config_hash() == r->config_hash() && l->config() == r->config(); } };
|
||||||
|
std::set<const PrintRegion*, cmp> region_set;
|
||||||
|
m_print_regions.clear();
|
||||||
|
for (PrintObject *print_object : m_objects)
|
||||||
|
for (std::unique_ptr<Slic3r::PrintRegion> &print_region : print_object->m_all_regions)
|
||||||
|
if (auto it = region_set.find(print_region.get()); it == region_set.end()) {
|
||||||
|
int print_region_id = int(m_print_regions.size());
|
||||||
|
m_print_regions.emplace_back(print_region.get());
|
||||||
|
print_region->m_print_region_id = print_region_id;
|
||||||
|
} else {
|
||||||
|
print_region->m_print_region_id = (*it)->print_region_id();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update SlicingParameters for each object where the SlicingParameters is not valid.
|
||||||
|
// If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use
|
||||||
|
// (posSlicing and posSupportMaterial was invalidated).
|
||||||
|
for (PrintObject *object : m_objects)
|
||||||
|
object->update_slicing_parameters();
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
check_model_ids_equal(m_model, model);
|
||||||
|
#endif /* _DEBUG */
|
||||||
|
|
||||||
|
return static_cast<ApplyStatus>(apply_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
@ -97,6 +97,15 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::reference_wrapper<const PrintRegion>> PrintObject::all_regions() const
|
||||||
|
{
|
||||||
|
std::vector<std::reference_wrapper<const PrintRegion>> out;
|
||||||
|
out.reserve(m_all_regions.size());
|
||||||
|
for (size_t i = 0; i < m_all_regions.size(); ++ i)
|
||||||
|
out.emplace_back(*m_all_regions[i]);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
// Called by make_perimeters()
|
// Called by make_perimeters()
|
||||||
// 1) Decides Z positions of the layers,
|
// 1) Decides Z positions of the layers,
|
||||||
// 2) Initializes layers and their regions
|
// 2) Initializes layers and their regions
|
||||||
|
|
@ -173,8 +182,8 @@ void PrintObject::make_perimeters()
|
||||||
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
|
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
|
||||||
// inside the object - infill_only_where_needed should be the method of choice for printing
|
// inside the object - infill_only_where_needed should be the method of choice for printing
|
||||||
// hollow objects
|
// hollow objects
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
const PrintRegion ®ion = *m_print->regions()[region_id];
|
const PrintRegion ®ion = this->printing_region(region_id);
|
||||||
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
|
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -294,7 +303,7 @@ void PrintObject::prepare_infill()
|
||||||
|
|
||||||
// Debugging output.
|
// Debugging output.
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
for (const Layer *layer : m_layers) {
|
for (const Layer *layer : m_layers) {
|
||||||
LayerRegion *layerm = layer->m_regions[region_id];
|
LayerRegion *layerm = layer->m_regions[region_id];
|
||||||
layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final");
|
layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final");
|
||||||
|
|
@ -313,7 +322,7 @@ void PrintObject::prepare_infill()
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
for (const Layer *layer : m_layers) {
|
for (const Layer *layer : m_layers) {
|
||||||
LayerRegion *layerm = layer->m_regions[region_id];
|
LayerRegion *layerm = layer->m_regions[region_id];
|
||||||
layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final");
|
layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final");
|
||||||
|
|
@ -332,7 +341,7 @@ void PrintObject::prepare_infill()
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
for (const Layer *layer : m_layers) {
|
for (const Layer *layer : m_layers) {
|
||||||
LayerRegion *layerm = layer->m_regions[region_id];
|
LayerRegion *layerm = layer->m_regions[region_id];
|
||||||
layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final");
|
layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final");
|
||||||
|
|
@ -351,7 +360,7 @@ void PrintObject::prepare_infill()
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
for (const Layer *layer : m_layers) {
|
for (const Layer *layer : m_layers) {
|
||||||
LayerRegion *layerm = layer->m_regions[region_id];
|
LayerRegion *layerm = layer->m_regions[region_id];
|
||||||
layerm->export_region_slices_to_svg_debug("9_prepare_infill-final");
|
layerm->export_region_slices_to_svg_debug("9_prepare_infill-final");
|
||||||
|
|
@ -716,10 +725,10 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
|
||||||
} else if (step == posSlice) {
|
} else if (step == posSlice) {
|
||||||
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial });
|
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial });
|
||||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||||
this->m_slicing_params.valid = false;
|
m_slicing_params.valid = false;
|
||||||
} else if (step == posSupportMaterial) {
|
} else if (step == posSupportMaterial) {
|
||||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||||
this->m_slicing_params.valid = false;
|
m_slicing_params.valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
|
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
|
||||||
|
|
@ -736,19 +745,11 @@ bool PrintObject::invalidate_all_steps()
|
||||||
// First call the "invalidate" functions, which may cancel background processing.
|
// First call the "invalidate" functions, which may cancel background processing.
|
||||||
bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
||||||
// Then reset some of the depending values.
|
// Then reset some of the depending values.
|
||||||
this->m_slicing_params.valid = false;
|
m_slicing_params.valid = false;
|
||||||
this->region_volumes.clear();
|
m_region_volumes.clear();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const PrintRegion* first_printing_region(const PrintObject &print_object)
|
|
||||||
{
|
|
||||||
for (size_t idx_region = 0; idx_region < print_object.region_volumes.size(); ++ idx_region)
|
|
||||||
if (!print_object.region_volumes.empty())
|
|
||||||
return print_object.print()->regions()[idx_region];
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function analyzes slices of a region (SurfaceCollection slices).
|
// This function analyzes slices of a region (SurfaceCollection slices).
|
||||||
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
|
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
|
||||||
// Initially all slices are of type stInternal.
|
// Initially all slices are of type stInternal.
|
||||||
|
|
@ -769,13 +770,13 @@ void PrintObject::detect_surfaces_type()
|
||||||
// should be visible.
|
// should be visible.
|
||||||
bool spiral_vase = this->print()->config().spiral_vase.value;
|
bool spiral_vase = this->print()->config().spiral_vase.value;
|
||||||
bool interface_shells = ! spiral_vase && m_config.interface_shells.value;
|
bool interface_shells = ! spiral_vase && m_config.interface_shells.value;
|
||||||
size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size();
|
size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size();
|
||||||
|
|
||||||
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start";
|
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " in parallel - start";
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
for (Layer *layer : m_layers)
|
for (Layer *layer : m_layers)
|
||||||
layer->m_regions[idx_region]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial");
|
layer->m_regions[region_id]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial");
|
||||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
|
|
||||||
// If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads.
|
// If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads.
|
||||||
|
|
@ -791,7 +792,7 @@ void PrintObject::detect_surfaces_type()
|
||||||
((num_layers > 1) ? num_layers - 1 : num_layers) :
|
((num_layers > 1) ? num_layers - 1 : num_layers) :
|
||||||
// In non-spiral vase mode, go over all layers.
|
// In non-spiral vase mode, go over all layers.
|
||||||
m_layers.size()),
|
m_layers.size()),
|
||||||
[this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
|
[this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
|
||||||
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
|
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
|
||||||
// the support from the print.
|
// the support from the print.
|
||||||
SurfaceType surface_type_bottom_other =
|
SurfaceType surface_type_bottom_other =
|
||||||
|
|
@ -799,9 +800,9 @@ void PrintObject::detect_surfaces_type()
|
||||||
stBottom : stBottomBridge;
|
stBottom : stBottomBridge;
|
||||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z;
|
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << region_id << " and layer " << layer->print_z;
|
||||||
Layer *layer = m_layers[idx_layer];
|
Layer *layer = m_layers[idx_layer];
|
||||||
LayerRegion *layerm = layer->m_regions[idx_region];
|
LayerRegion *layerm = layer->m_regions[region_id];
|
||||||
// comparison happens against the *full* slices (considering all regions)
|
// comparison happens against the *full* slices (considering all regions)
|
||||||
// unless internal shells are requested
|
// unless internal shells are requested
|
||||||
Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr;
|
Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr;
|
||||||
|
|
@ -814,7 +815,7 @@ void PrintObject::detect_surfaces_type()
|
||||||
Surfaces top;
|
Surfaces top;
|
||||||
if (upper_layer) {
|
if (upper_layer) {
|
||||||
ExPolygons upper_slices = interface_shells ?
|
ExPolygons upper_slices = interface_shells ?
|
||||||
diff_ex(layerm->slices.surfaces, upper_layer->m_regions[idx_region]->slices.surfaces, ApplySafetyOffset::Yes) :
|
diff_ex(layerm->slices.surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) :
|
||||||
diff_ex(layerm->slices.surfaces, upper_layer->lslices, ApplySafetyOffset::Yes);
|
diff_ex(layerm->slices.surfaces, upper_layer->lslices, ApplySafetyOffset::Yes);
|
||||||
surfaces_append(top, offset2_ex(upper_slices, -offset, offset), stTop);
|
surfaces_append(top, offset2_ex(upper_slices, -offset, offset), stTop);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -831,7 +832,7 @@ void PrintObject::detect_surfaces_type()
|
||||||
#if 0
|
#if 0
|
||||||
//FIXME Why is this branch failing t\multi.t ?
|
//FIXME Why is this branch failing t\multi.t ?
|
||||||
Polygons lower_slices = interface_shells ?
|
Polygons lower_slices = interface_shells ?
|
||||||
to_polygons(lower_layer->get_region(idx_region)->slices.surfaces) :
|
to_polygons(lower_layer->get_region(region_id)->slices.surfaces) :
|
||||||
to_polygons(lower_layer->slices);
|
to_polygons(lower_layer->slices);
|
||||||
surfaces_append(bottom,
|
surfaces_append(bottom,
|
||||||
offset2_ex(diff(layerm->slices.surfaces, lower_slices, true), -offset, offset),
|
offset2_ex(diff(layerm->slices.surfaces, lower_slices, true), -offset, offset),
|
||||||
|
|
@ -854,7 +855,7 @@ void PrintObject::detect_surfaces_type()
|
||||||
offset2_ex(
|
offset2_ex(
|
||||||
diff_ex(
|
diff_ex(
|
||||||
intersection(layerm->slices.surfaces, lower_layer->lslices), // supported
|
intersection(layerm->slices.surfaces, lower_layer->lslices), // supported
|
||||||
lower_layer->m_regions[idx_region]->slices.surfaces,
|
lower_layer->m_regions[region_id]->slices.surfaces,
|
||||||
ApplySafetyOffset::Yes),
|
ApplySafetyOffset::Yes),
|
||||||
-offset, offset),
|
-offset, offset),
|
||||||
stBottom);
|
stBottom);
|
||||||
|
|
@ -887,7 +888,7 @@ void PrintObject::detect_surfaces_type()
|
||||||
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green")));
|
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green")));
|
||||||
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown")));
|
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown")));
|
||||||
expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices.surfaces), SVG::ExPolygonAttributes("black")));
|
expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices.surfaces), SVG::ExPolygonAttributes("black")));
|
||||||
SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, idx_region, layer->print_z).c_str(), expolygons_with_attributes);
|
SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes);
|
||||||
}
|
}
|
||||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
|
|
||||||
|
|
@ -924,25 +925,25 @@ void PrintObject::detect_surfaces_type()
|
||||||
if (interface_shells) {
|
if (interface_shells) {
|
||||||
// Move surfaces_new to layerm->slices.surfaces
|
// Move surfaces_new to layerm->slices.surfaces
|
||||||
for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer)
|
for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer)
|
||||||
m_layers[idx_layer]->m_regions[idx_region]->slices.surfaces = std::move(surfaces_new[idx_layer]);
|
m_layers[idx_layer]->m_regions[region_id]->slices.surfaces = std::move(surfaces_new[idx_layer]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spiral_vase) {
|
if (spiral_vase) {
|
||||||
if (num_layers > 1)
|
if (num_layers > 1)
|
||||||
// Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern.
|
// Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern.
|
||||||
m_layers[num_layers - 1]->m_regions[idx_region]->slices.set_type(stTop);
|
m_layers[num_layers - 1]->m_regions[region_id]->slices.set_type(stTop);
|
||||||
for (size_t i = num_layers; i < m_layers.size(); ++ i)
|
for (size_t i = num_layers; i < m_layers.size(); ++ i)
|
||||||
m_layers[i]->m_regions[idx_region]->slices.set_type(stInternal);
|
m_layers[i]->m_regions[region_id]->slices.set_type(stInternal);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start";
|
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - start";
|
||||||
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
|
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||||
[this, idx_region](const tbb::blocked_range<size_t>& range) {
|
[this, region_id](const tbb::blocked_range<size_t>& range) {
|
||||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
LayerRegion *layerm = m_layers[idx_layer]->m_regions[idx_region];
|
LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
|
||||||
layerm->slices_to_fill_surfaces_clipped();
|
layerm->slices_to_fill_surfaces_clipped();
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final");
|
layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final");
|
||||||
|
|
@ -950,7 +951,7 @@ void PrintObject::detect_surfaces_type()
|
||||||
} // for each layer of a region
|
} // for each layer of a region
|
||||||
});
|
});
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - end";
|
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - end";
|
||||||
} // for each this->print->region_count
|
} // for each this->print->region_count
|
||||||
|
|
||||||
// Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
|
// Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
|
||||||
|
|
@ -966,8 +967,8 @@ void PrintObject::process_external_surfaces()
|
||||||
// Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only
|
// Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only
|
||||||
// over voids, which are supported by the layer below.
|
// over voids, which are supported by the layer below.
|
||||||
bool has_voids = false;
|
bool has_voids = false;
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id)
|
||||||
if (! this->region_volumes.empty() && this->print()->regions()[region_id]->config().fill_density == 0) {
|
if (this->printing_region(region_id).config().fill_density == 0) {
|
||||||
has_voids = true;
|
has_voids = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1003,12 +1004,12 @@ void PrintObject::process_external_surfaces()
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
Polygons voids;
|
Polygons voids;
|
||||||
for (const LayerRegion *layerm : m_layers[layer_idx]->regions()) {
|
for (const LayerRegion *layerm : m_layers[layer_idx]->regions()) {
|
||||||
if (layerm->region()->config().fill_density.value == 0.)
|
if (layerm->region().config().fill_density.value == 0.)
|
||||||
for (const Surface &surface : layerm->fill_surfaces.surfaces)
|
for (const Surface &surface : layerm->fill_surfaces.surfaces)
|
||||||
// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
|
// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
|
||||||
polygons_append(voids, offset(surface.expolygon, unsupported_width));
|
polygons_append(voids, offset(surface.expolygon, unsupported_width));
|
||||||
}
|
}
|
||||||
surfaces_covered[layer_idx] = diff(this->m_layers[layer_idx]->lslices, voids);
|
surfaces_covered[layer_idx] = diff(m_layers[layer_idx]->lslices, voids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -1016,7 +1017,7 @@ void PrintObject::process_external_surfaces()
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end";
|
BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start";
|
BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start";
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||||
|
|
@ -1024,7 +1025,7 @@ void PrintObject::process_external_surfaces()
|
||||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
// BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
|
// BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
|
||||||
m_layers[layer_idx]->get_region((int)region_id)->process_external_surfaces(
|
m_layers[layer_idx]->get_region(int(region_id))->process_external_surfaces(
|
||||||
(layer_idx == 0) ? nullptr : m_layers[layer_idx - 1],
|
(layer_idx == 0) ? nullptr : m_layers[layer_idx - 1],
|
||||||
(layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]);
|
(layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]);
|
||||||
}
|
}
|
||||||
|
|
@ -1049,7 +1050,7 @@ void PrintObject::discover_vertical_shells()
|
||||||
Polygons holes;
|
Polygons holes;
|
||||||
};
|
};
|
||||||
bool spiral_vase = this->print()->config().spiral_vase.value;
|
bool spiral_vase = this->print()->config().spiral_vase.value;
|
||||||
size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size();
|
size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size();
|
||||||
coordf_t min_layer_height = this->slicing_parameters().min_layer_height;
|
coordf_t min_layer_height = this->slicing_parameters().min_layer_height;
|
||||||
// Does this region possibly produce more than 1 top or bottom layer?
|
// Does this region possibly produce more than 1 top or bottom layer?
|
||||||
auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) {
|
auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) {
|
||||||
|
|
@ -1064,14 +1065,14 @@ void PrintObject::discover_vertical_shells()
|
||||||
num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0;
|
num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0;
|
||||||
};
|
};
|
||||||
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry());
|
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry());
|
||||||
bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value;
|
bool top_bottom_surfaces_all_regions = this->num_printing_regions() > 1 && ! m_config.interface_shells.value;
|
||||||
if (top_bottom_surfaces_all_regions) {
|
if (top_bottom_surfaces_all_regions) {
|
||||||
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
|
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
|
||||||
// is calculated over all materials.
|
// is calculated over all materials.
|
||||||
// Is the "ensure vertical wall thickness" applicable to any region?
|
// Is the "ensure vertical wall thickness" applicable to any region?
|
||||||
bool has_extra_layers = false;
|
bool has_extra_layers = false;
|
||||||
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++idx_region) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
|
||||||
const PrintRegionConfig &config = m_print->get_region(idx_region)->config();
|
const PrintRegionConfig &config = this->printing_region(region_id).config();
|
||||||
if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
|
if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
|
||||||
has_extra_layers = true;
|
has_extra_layers = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -1087,7 +1088,7 @@ void PrintObject::discover_vertical_shells()
|
||||||
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
||||||
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
|
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
|
||||||
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
|
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
|
||||||
const size_t num_regions = this->region_volumes.size();
|
const size_t num_regions = this->num_printing_regions();
|
||||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
const Layer &layer = *m_layers[idx_layer];
|
const Layer &layer = *m_layers[idx_layer];
|
||||||
|
|
@ -1099,8 +1100,8 @@ void PrintObject::discover_vertical_shells()
|
||||||
static size_t debug_idx = 0;
|
static size_t debug_idx = 0;
|
||||||
++ debug_idx;
|
++ debug_idx;
|
||||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
for (size_t idx_region = 0; idx_region < num_regions; ++ idx_region) {
|
for (size_t region_id = 0; region_id < num_regions; ++ region_id) {
|
||||||
LayerRegion &layerm = *layer.m_regions[idx_region];
|
LayerRegion &layerm = *layer.m_regions[region_id];
|
||||||
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
|
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
|
||||||
// Top surfaces.
|
// Top surfaces.
|
||||||
append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), min_perimeter_infill_spacing));
|
append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), min_perimeter_infill_spacing));
|
||||||
|
|
@ -1113,7 +1114,7 @@ void PrintObject::discover_vertical_shells()
|
||||||
unsigned int perimeters = 0;
|
unsigned int perimeters = 0;
|
||||||
for (Surface &s : layerm.slices.surfaces)
|
for (Surface &s : layerm.slices.surfaces)
|
||||||
perimeters = std::max<unsigned int>(perimeters, s.extra_perimeters);
|
perimeters = std::max<unsigned int>(perimeters, s.extra_perimeters);
|
||||||
perimeters += layerm.region()->config().perimeters.value;
|
perimeters += layerm.region().config().perimeters.value;
|
||||||
// Then calculate the infill offset.
|
// Then calculate the infill offset.
|
||||||
if (perimeters > 0) {
|
if (perimeters > 0) {
|
||||||
Flow extflow = layerm.flow(frExternalPerimeter);
|
Flow extflow = layerm.flow(frExternalPerimeter);
|
||||||
|
|
@ -1148,10 +1149,10 @@ void PrintObject::discover_vertical_shells()
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom";
|
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
PROFILE_BLOCK(discover_vertical_shells_region);
|
PROFILE_BLOCK(discover_vertical_shells_region);
|
||||||
|
|
||||||
const PrintRegion ®ion = *m_print->get_region(idx_region);
|
const PrintRegion ®ion = this->printing_region(region_id);
|
||||||
if (! region.config().ensure_vertical_shell_thickness.value)
|
if (! region.config().ensure_vertical_shell_thickness.value)
|
||||||
// This region will be handled by discover_horizontal_shells().
|
// This region will be handled by discover_horizontal_shells().
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1165,15 +1166,15 @@ void PrintObject::discover_vertical_shells()
|
||||||
if (! top_bottom_surfaces_all_regions) {
|
if (! top_bottom_surfaces_all_regions) {
|
||||||
// This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness
|
// This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness
|
||||||
// is calculated over a single material.
|
// is calculated over a single material.
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom";
|
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : cache top / bottom";
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
||||||
[this, idx_region, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
|
[this, region_id, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
|
||||||
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
|
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
|
||||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
Layer &layer = *m_layers[idx_layer];
|
Layer &layer = *m_layers[idx_layer];
|
||||||
LayerRegion &layerm = *layer.m_regions[idx_region];
|
LayerRegion &layerm = *layer.m_regions[region_id];
|
||||||
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
|
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
|
||||||
// Top surfaces.
|
// Top surfaces.
|
||||||
auto &cache = cache_top_botom_regions[idx_layer];
|
auto &cache = cache_top_botom_regions[idx_layer];
|
||||||
|
|
@ -1182,21 +1183,21 @@ void PrintObject::discover_vertical_shells()
|
||||||
// Bottom surfaces.
|
// Bottom surfaces.
|
||||||
cache.bottom_surfaces = offset(layerm.slices.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing);
|
cache.bottom_surfaces = offset(layerm.slices.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing);
|
||||||
append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing));
|
append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing));
|
||||||
// Holes over all regions. Only collect them once, they are valid for all idx_region iterations.
|
// Holes over all regions. Only collect them once, they are valid for all region_id iterations.
|
||||||
if (cache.holes.empty()) {
|
if (cache.holes.empty()) {
|
||||||
for (size_t idx_region = 0; idx_region < layer.regions().size(); ++ idx_region)
|
for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id)
|
||||||
polygons_append(cache.holes, to_polygons(layer.regions()[idx_region]->fill_expolygons));
|
polygons_append(cache.holes, to_polygons(layer.regions()[region_id]->fill_expolygons));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end : cache top / bottom";
|
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end : cache top / bottom";
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness";
|
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness";
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
||||||
[this, idx_region, &cache_top_botom_regions]
|
[this, region_id, &cache_top_botom_regions]
|
||||||
(const tbb::blocked_range<size_t>& range) {
|
(const tbb::blocked_range<size_t>& range) {
|
||||||
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
|
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
|
||||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||||
|
|
@ -1208,8 +1209,8 @@ void PrintObject::discover_vertical_shells()
|
||||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||||
|
|
||||||
Layer *layer = m_layers[idx_layer];
|
Layer *layer = m_layers[idx_layer];
|
||||||
LayerRegion *layerm = layer->m_regions[idx_region];
|
LayerRegion *layerm = layer->m_regions[region_id];
|
||||||
const PrintRegionConfig ®ion_config = layerm->region()->config();
|
const PrintRegionConfig ®ion_config = layerm->region().config();
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial");
|
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial");
|
||||||
|
|
@ -1423,11 +1424,11 @@ void PrintObject::discover_vertical_shells()
|
||||||
} // for each layer
|
} // for each layer
|
||||||
});
|
});
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - end";
|
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end";
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++idx_layer) {
|
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++idx_layer) {
|
||||||
LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region);
|
LayerRegion *layerm = m_layers[idx_layer]->get_region(region_id);
|
||||||
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final");
|
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final");
|
||||||
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final");
|
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final");
|
||||||
}
|
}
|
||||||
|
|
@ -1445,8 +1446,8 @@ void PrintObject::bridge_over_infill()
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info();
|
BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info();
|
||||||
|
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
const PrintRegion ®ion = *m_print->regions()[region_id];
|
const PrintRegion ®ion = this->printing_region(region_id);
|
||||||
|
|
||||||
// skip bridging in case there are no voids
|
// skip bridging in case there are no voids
|
||||||
if (region.config().fill_density.value == 100)
|
if (region.config().fill_density.value == 100)
|
||||||
|
|
@ -1627,9 +1628,15 @@ PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegion
|
||||||
|
|
||||||
void PrintObject::update_slicing_parameters()
|
void PrintObject::update_slicing_parameters()
|
||||||
{
|
{
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
if (!m_slicing_params.valid)
|
||||||
|
m_slicing_params = SlicingParameters::create_from_config(
|
||||||
|
this->print()->config(), m_config, this->model_object()->bounding_box().max.z(), this->object_extruders());
|
||||||
|
#else
|
||||||
if (! m_slicing_params.valid)
|
if (! m_slicing_params.valid)
|
||||||
m_slicing_params = SlicingParameters::create_from_config(
|
m_slicing_params = SlicingParameters::create_from_config(
|
||||||
this->print()->config(), m_config, unscale<double>(this->height()), this->object_extruders());
|
this->print()->config(), m_config, unscale<double>(this->height()), this->object_extruders());
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
}
|
}
|
||||||
|
|
||||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
|
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
|
||||||
|
|
@ -1662,6 +1669,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
|
||||||
object_extruders);
|
object_extruders);
|
||||||
}
|
}
|
||||||
sort_remove_duplicates(object_extruders);
|
sort_remove_duplicates(object_extruders);
|
||||||
|
//FIXME add painting extruders
|
||||||
|
|
||||||
if (object_max_z <= 0.f)
|
if (object_max_z <= 0.f)
|
||||||
object_max_z = (float)model_object.raw_bounding_box().size().z();
|
object_max_z = (float)model_object.raw_bounding_box().size().z();
|
||||||
|
|
@ -1672,10 +1680,9 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
|
||||||
std::vector<unsigned int> PrintObject::object_extruders() const
|
std::vector<unsigned int> PrintObject::object_extruders() const
|
||||||
{
|
{
|
||||||
std::vector<unsigned int> extruders;
|
std::vector<unsigned int> extruders;
|
||||||
extruders.reserve(this->region_volumes.size() * 3);
|
extruders.reserve(this->all_regions().size() * 3);
|
||||||
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region)
|
for (const PrintRegion ®ion : this->all_regions())
|
||||||
if (! this->region_volumes[idx_region].empty())
|
region.collect_object_printing_extruders(*this->print(), extruders);
|
||||||
m_print->get_region(idx_region)->collect_object_printing_extruders(extruders);
|
|
||||||
sort_remove_duplicates(extruders);
|
sort_remove_duplicates(extruders);
|
||||||
return extruders;
|
return extruders;
|
||||||
}
|
}
|
||||||
|
|
@ -1691,6 +1698,15 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
// Verify the layer_height_profile.
|
||||||
|
if (!layer_height_profile.empty() &&
|
||||||
|
// Must not be of even length.
|
||||||
|
((layer_height_profile.size() & 1) != 0 ||
|
||||||
|
// Last entry must be at the top of the object.
|
||||||
|
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max) > 1e-3))
|
||||||
|
layer_height_profile.clear();
|
||||||
|
#else
|
||||||
// Verify the layer_height_profile.
|
// Verify the layer_height_profile.
|
||||||
if (! layer_height_profile.empty() &&
|
if (! layer_height_profile.empty() &&
|
||||||
// Must not be of even length.
|
// Must not be of even length.
|
||||||
|
|
@ -1698,6 +1714,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
||||||
// Last entry must be at the top of the object.
|
// Last entry must be at the top of the object.
|
||||||
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_height()) > 1e-3))
|
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_height()) > 1e-3))
|
||||||
layer_height_profile.clear();
|
layer_height_profile.clear();
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
if (layer_height_profile.empty()) {
|
if (layer_height_profile.empty()) {
|
||||||
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
|
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
|
||||||
|
|
@ -1743,8 +1760,8 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
layer->lower_layer = prev;
|
layer->lower_layer = prev;
|
||||||
}
|
}
|
||||||
// Make sure all layers contain layer region objects for all regions.
|
// Make sure all layers contain layer region objects for all regions.
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
|
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id)
|
||||||
layer->add_region(this->print()->get_region(region_id));
|
layer->add_region(&this->printing_region(region_id));
|
||||||
prev = layer;
|
prev = layer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1754,16 +1771,15 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
bool has_z_ranges = false;
|
bool has_z_ranges = false;
|
||||||
size_t num_volumes = 0;
|
size_t num_volumes = 0;
|
||||||
size_t num_modifiers = 0;
|
size_t num_modifiers = 0;
|
||||||
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
|
for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) {
|
||||||
int last_volume_id = -1;
|
int last_volume_id = -1;
|
||||||
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
|
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
|
||||||
const int volume_id = volume_and_range.second;
|
const ModelVolume *model_volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
|
||||||
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
|
||||||
if (model_volume->is_model_part()) {
|
if (model_volume->is_model_part()) {
|
||||||
if (last_volume_id == volume_id) {
|
if (last_volume_id == volume_w_zrange.volume_idx) {
|
||||||
has_z_ranges = true;
|
has_z_ranges = true;
|
||||||
} else {
|
} else {
|
||||||
last_volume_id = volume_id;
|
last_volume_id = volume_w_zrange.volume_idx;
|
||||||
if (all_volumes_single_region == -2)
|
if (all_volumes_single_region == -2)
|
||||||
// first model volume met
|
// first model volume met
|
||||||
all_volumes_single_region = region_id;
|
all_volumes_single_region = region_id;
|
||||||
|
|
@ -1786,14 +1802,14 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
|
if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
|
||||||
// Cheap path: Slice regions without mutual clipping.
|
// Cheap path: Slice regions without mutual clipping.
|
||||||
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
|
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
|
||||||
// slicing in parallel
|
// slicing in parallel
|
||||||
size_t slicing_mode_normal_below_layer = 0;
|
size_t slicing_mode_normal_below_layer = 0;
|
||||||
if (spiral_vase) {
|
if (spiral_vase) {
|
||||||
// Slice the bottom layers with SlicingMode::Regular.
|
// Slice the bottom layers with SlicingMode::Regular.
|
||||||
// This needs to be in sync with LayerRegion::make_perimeters() spiral_vase!
|
// This needs to be in sync with LayerRegion::make_perimeters() spiral_vase!
|
||||||
const PrintRegionConfig &config = this->print()->regions()[region_id]->config();
|
const PrintRegionConfig &config = this->printing_region(region_id).config();
|
||||||
slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value);
|
slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value);
|
||||||
for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON;
|
for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON;
|
||||||
++ slicing_mode_normal_below_layer);
|
++ slicing_mode_normal_below_layer);
|
||||||
|
|
@ -1819,22 +1835,22 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
};
|
};
|
||||||
std::vector<SlicedVolume> sliced_volumes;
|
std::vector<SlicedVolume> sliced_volumes;
|
||||||
sliced_volumes.reserve(num_volumes);
|
sliced_volumes.reserve(num_volumes);
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
|
||||||
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
|
const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
|
||||||
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
|
for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
|
||||||
int volume_id = volumes_and_ranges[i].second;
|
int volume_id = volumes_and_ranges.volumes[i].volume_idx;
|
||||||
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
||||||
if (model_volume->is_model_part()) {
|
if (model_volume->is_model_part()) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
|
||||||
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
|
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
|
||||||
std::vector<t_layer_height_range> ranges;
|
std::vector<t_layer_height_range> ranges;
|
||||||
ranges.emplace_back(volumes_and_ranges[i].first);
|
ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
|
||||||
size_t j = i + 1;
|
size_t j = i + 1;
|
||||||
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
|
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j)
|
||||||
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
|
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON)
|
||||||
ranges.back().second = volumes_and_ranges[j].first.second;
|
ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second;
|
||||||
else
|
else
|
||||||
ranges.emplace_back(volumes_and_ranges[j].first);
|
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
|
||||||
// slicing in parallel
|
// slicing in parallel
|
||||||
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume));
|
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume));
|
||||||
i = j;
|
i = j;
|
||||||
|
|
@ -1871,7 +1887,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Collect and union volumes of a single region.
|
// Collect and union volumes of a single region.
|
||||||
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
|
for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) {
|
||||||
ExPolygons expolygons;
|
ExPolygons expolygons;
|
||||||
size_t num_volumes = 0;
|
size_t num_volumes = 0;
|
||||||
for (SlicedVolume &sliced_volume : sliced_volumes)
|
for (SlicedVolume &sliced_volume : sliced_volumes)
|
||||||
|
|
@ -1892,8 +1908,8 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slice all modifier volumes.
|
// Slice all modifier volumes.
|
||||||
if (this->region_volumes.size() > 1) {
|
if (m_region_volumes.size() > 1) {
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
|
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
|
||||||
// slicing in parallel
|
// slicing in parallel
|
||||||
std::vector<ExPolygons> expolygons_by_layer = this->slice_modifiers(region_id, slice_zs);
|
std::vector<ExPolygons> expolygons_by_layer = this->slice_modifiers(region_id, slice_zs);
|
||||||
|
|
@ -1906,7 +1922,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||||
[this, &expolygons_by_layer, region_id](const tbb::blocked_range<size_t>& range) {
|
[this, &expolygons_by_layer, region_id](const tbb::blocked_range<size_t>& range) {
|
||||||
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
||||||
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) {
|
for (size_t other_region_id = 0; other_region_id < m_region_volumes.size(); ++ other_region_id) {
|
||||||
if (region_id == other_region_id)
|
if (region_id == other_region_id)
|
||||||
continue;
|
continue;
|
||||||
Layer *layer = m_layers[layer_id];
|
Layer *layer = m_layers[layer_id];
|
||||||
|
|
@ -2046,9 +2062,9 @@ end:
|
||||||
std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const
|
std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const
|
||||||
{
|
{
|
||||||
std::vector<const ModelVolume*> volumes;
|
std::vector<const ModelVolume*> volumes;
|
||||||
if (region_id < this->region_volumes.size()) {
|
if (region_id < m_region_volumes.size()) {
|
||||||
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
|
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
|
||||||
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
|
const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
|
||||||
if (volume->is_model_part())
|
if (volume->is_model_part())
|
||||||
volumes.emplace_back(volume);
|
volumes.emplace_back(volume);
|
||||||
}
|
}
|
||||||
|
|
@ -2056,27 +2072,27 @@ std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::v
|
||||||
return this->slice_volumes(z, mode, slicing_mode_normal_below_layer, mode_below, volumes);
|
return this->slice_volumes(z, mode, slicing_mode_normal_below_layer, mode_below, volumes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_and_range at most once.
|
// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_w_zrange at most once.
|
||||||
std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const
|
std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const
|
||||||
{
|
{
|
||||||
std::vector<ExPolygons> out;
|
std::vector<ExPolygons> out;
|
||||||
if (region_id < this->region_volumes.size())
|
if (region_id < m_region_volumes.size())
|
||||||
{
|
{
|
||||||
std::vector<std::vector<t_layer_height_range>> volume_ranges;
|
std::vector<std::vector<t_layer_height_range>> volume_ranges;
|
||||||
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
|
const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
|
||||||
volume_ranges.reserve(volumes_and_ranges.size());
|
volume_ranges.reserve(volumes_and_ranges.volumes.size());
|
||||||
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
|
for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
|
||||||
int volume_id = volumes_and_ranges[i].second;
|
int volume_id = volumes_and_ranges.volumes[i].volume_idx;
|
||||||
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
||||||
if (model_volume->is_modifier()) {
|
if (model_volume->is_modifier()) {
|
||||||
std::vector<t_layer_height_range> ranges;
|
std::vector<t_layer_height_range> ranges;
|
||||||
ranges.emplace_back(volumes_and_ranges[i].first);
|
ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
|
||||||
size_t j = i + 1;
|
size_t j = i + 1;
|
||||||
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) {
|
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) {
|
||||||
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
|
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON)
|
||||||
ranges.back().second = volumes_and_ranges[j].first.second;
|
ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second;
|
||||||
else
|
else
|
||||||
ranges.emplace_back(volumes_and_ranges[j].first);
|
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
|
||||||
}
|
}
|
||||||
volume_ranges.emplace_back(std::move(ranges));
|
volume_ranges.emplace_back(std::move(ranges));
|
||||||
i = j;
|
i = j;
|
||||||
|
|
@ -2098,8 +2114,8 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
|
||||||
if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) {
|
if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) {
|
||||||
// No modifier in this region was split to layer spans.
|
// No modifier in this region was split to layer spans.
|
||||||
std::vector<const ModelVolume*> volumes;
|
std::vector<const ModelVolume*> volumes;
|
||||||
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
|
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
|
||||||
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
|
const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
|
||||||
if (volume->is_modifier())
|
if (volume->is_modifier())
|
||||||
volumes.emplace_back(volume);
|
volumes.emplace_back(volume);
|
||||||
}
|
}
|
||||||
|
|
@ -2107,19 +2123,19 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std
|
||||||
} else {
|
} else {
|
||||||
// Some modifier in this region was split to layer spans.
|
// Some modifier in this region was split to layer spans.
|
||||||
std::vector<char> merge;
|
std::vector<char> merge;
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
|
||||||
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
|
const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
|
||||||
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
|
for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
|
||||||
int volume_id = volumes_and_ranges[i].second;
|
int volume_id = volumes_and_ranges.volumes[i].volume_idx;
|
||||||
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
||||||
if (model_volume->is_modifier()) {
|
if (model_volume->is_modifier()) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id;
|
BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id;
|
||||||
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
|
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
|
||||||
std::vector<t_layer_height_range> ranges;
|
std::vector<t_layer_height_range> ranges;
|
||||||
ranges.emplace_back(volumes_and_ranges[i].first);
|
ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
|
||||||
size_t j = i + 1;
|
size_t j = i + 1;
|
||||||
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
|
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j)
|
||||||
ranges.emplace_back(volumes_and_ranges[j].first);
|
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
|
||||||
// slicing in parallel
|
// slicing in parallel
|
||||||
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume);
|
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume);
|
||||||
// Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume.
|
// Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume.
|
||||||
|
|
@ -2395,9 +2411,15 @@ void PrintObject::simplify_slices(double distance)
|
||||||
// fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
|
// fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
|
||||||
void PrintObject::clip_fill_surfaces()
|
void PrintObject::clip_fill_surfaces()
|
||||||
{
|
{
|
||||||
if (! m_config.infill_only_where_needed.value ||
|
if (! m_config.infill_only_where_needed.value)
|
||||||
! std::any_of(this->print()->regions().begin(), this->print()->regions().end(),
|
return;
|
||||||
[](const PrintRegion *region) { return region->config().fill_density > 0; }))
|
bool has_infill = false;
|
||||||
|
for (size_t i = 0; i < this->num_printing_regions(); ++ i)
|
||||||
|
if (this->printing_region(i).config().fill_density > 0) {
|
||||||
|
has_infill = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (! has_infill)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We only want infill under ceilings; this is almost like an
|
// We only want infill under ceilings; this is almost like an
|
||||||
|
|
@ -2450,7 +2472,7 @@ void PrintObject::clip_fill_surfaces()
|
||||||
upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
|
upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
|
||||||
// Apply new internal infill to regions.
|
// Apply new internal infill to regions.
|
||||||
for (LayerRegion *layerm : lower_layer->m_regions) {
|
for (LayerRegion *layerm : lower_layer->m_regions) {
|
||||||
if (layerm->region()->config().fill_density.value == 0)
|
if (layerm->region().config().fill_density.value == 0)
|
||||||
continue;
|
continue;
|
||||||
SurfaceType internal_surface_types[] = { stInternal, stInternalVoid };
|
SurfaceType internal_surface_types[] = { stInternal, stInternalVoid };
|
||||||
Polygons internal;
|
Polygons internal;
|
||||||
|
|
@ -2475,12 +2497,12 @@ void PrintObject::discover_horizontal_shells()
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
|
BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
|
||||||
|
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
for (size_t i = 0; i < m_layers.size(); ++ i) {
|
for (size_t i = 0; i < m_layers.size(); ++ i) {
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
Layer *layer = m_layers[i];
|
Layer *layer = m_layers[i];
|
||||||
LayerRegion *layerm = layer->regions()[region_id];
|
LayerRegion *layerm = layer->regions()[region_id];
|
||||||
const PrintRegionConfig ®ion_config = layerm->region()->config();
|
const PrintRegionConfig ®ion_config = layerm->region().config();
|
||||||
if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 &&
|
if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 &&
|
||||||
(i % region_config.solid_infill_every_layers) == 0) {
|
(i % region_config.solid_infill_every_layers) == 0) {
|
||||||
// Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge.
|
// Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge.
|
||||||
|
|
@ -2656,7 +2678,7 @@ void PrintObject::discover_horizontal_shells()
|
||||||
} // for each region
|
} // for each region
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
for (const Layer *layer : m_layers) {
|
for (const Layer *layer : m_layers) {
|
||||||
const LayerRegion *layerm = layer->m_regions[region_id];
|
const LayerRegion *layerm = layer->m_regions[region_id];
|
||||||
layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells");
|
layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells");
|
||||||
|
|
@ -2672,16 +2694,16 @@ void PrintObject::discover_horizontal_shells()
|
||||||
void PrintObject::combine_infill()
|
void PrintObject::combine_infill()
|
||||||
{
|
{
|
||||||
// Work on each region separately.
|
// Work on each region separately.
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||||
const PrintRegion *region = this->print()->regions()[region_id];
|
const PrintRegion ®ion = this->printing_region(region_id);
|
||||||
const size_t every = region->config().infill_every_layers.value;
|
const size_t every = region.config().infill_every_layers.value;
|
||||||
if (every < 2 || region->config().fill_density == 0.)
|
if (every < 2 || region.config().fill_density == 0.)
|
||||||
continue;
|
continue;
|
||||||
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
|
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
|
||||||
//FIXME limit the layer height to max_layer_height
|
//FIXME limit the layer height to max_layer_height
|
||||||
double nozzle_diameter = std::min(
|
double nozzle_diameter = std::min(
|
||||||
this->print()->config().nozzle_diameter.get_at(region->config().infill_extruder.value - 1),
|
this->print()->config().nozzle_diameter.get_at(region.config().infill_extruder.value - 1),
|
||||||
this->print()->config().nozzle_diameter.get_at(region->config().solid_infill_extruder.value - 1));
|
this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_extruder.value - 1));
|
||||||
// define the combinations
|
// define the combinations
|
||||||
std::vector<size_t> combine(m_layers.size(), 0);
|
std::vector<size_t> combine(m_layers.size(), 0);
|
||||||
{
|
{
|
||||||
|
|
@ -2745,11 +2767,11 @@ void PrintObject::combine_infill()
|
||||||
0.5f * layerms.back()->flow(frPerimeter).scaled_width() +
|
0.5f * layerms.back()->flow(frPerimeter).scaled_width() +
|
||||||
// Because fill areas for rectilinear and honeycomb are grown
|
// Because fill areas for rectilinear and honeycomb are grown
|
||||||
// later to overlap perimeters, we need to counteract that too.
|
// later to overlap perimeters, we need to counteract that too.
|
||||||
((region->config().fill_pattern == ipRectilinear ||
|
((region.config().fill_pattern == ipRectilinear ||
|
||||||
region->config().fill_pattern == ipMonotonic ||
|
region.config().fill_pattern == ipMonotonic ||
|
||||||
region->config().fill_pattern == ipGrid ||
|
region.config().fill_pattern == ipGrid ||
|
||||||
region->config().fill_pattern == ipLine ||
|
region.config().fill_pattern == ipLine ||
|
||||||
region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
|
region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
|
||||||
layerms.back()->flow(frSolidInfill).scaled_width();
|
layerms.back()->flow(frSolidInfill).scaled_width();
|
||||||
for (ExPolygon &expoly : intersection)
|
for (ExPolygon &expoly : intersection)
|
||||||
polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));
|
polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,12 @@ unsigned int PrintRegion::extruder(FlowRole role) const
|
||||||
|
|
||||||
Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const
|
Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const
|
||||||
{
|
{
|
||||||
ConfigOptionFloatOrPercent config_width;
|
const PrintConfig &print_config = object.print()->config();
|
||||||
|
ConfigOptionFloatOrPercent config_width;
|
||||||
// Get extrusion width from configuration.
|
// Get extrusion width from configuration.
|
||||||
// (might be an absolute value, or a percent value, or zero for auto)
|
// (might be an absolute value, or a percent value, or zero for auto)
|
||||||
if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) {
|
if (first_layer && print_config.first_layer_extrusion_width.value > 0) {
|
||||||
config_width = m_print->config().first_layer_extrusion_width;
|
config_width = print_config.first_layer_extrusion_width;
|
||||||
} else if (role == frExternalPerimeter) {
|
} else if (role == frExternalPerimeter) {
|
||||||
config_width = m_config.external_perimeter_extrusion_width;
|
config_width = m_config.external_perimeter_extrusion_width;
|
||||||
} else if (role == frPerimeter) {
|
} else if (role == frPerimeter) {
|
||||||
|
|
@ -44,7 +45,7 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he
|
||||||
|
|
||||||
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
|
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
|
||||||
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
|
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
|
||||||
auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1));
|
auto nozzle_diameter = float(print_config.nozzle_diameter.get_at(this->extruder(role) - 1));
|
||||||
return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height));
|
return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,17 +77,17 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con
|
||||||
emplace_extruder(region_config.solid_infill_extruder);
|
emplace_extruder(region_config.solid_infill_extruder);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const
|
void PrintRegion::collect_object_printing_extruders(const Print &print, std::vector<unsigned int> &object_extruders) const
|
||||||
{
|
{
|
||||||
// PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder.
|
// PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder.
|
||||||
// If not, then there must be something wrong with the Print::apply() function.
|
// If not, then there must be something wrong with the Print::apply() function.
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
auto num_extruders = (int)print()->config().nozzle_diameter.size();
|
auto num_extruders = int(print.config().nozzle_diameter.size());
|
||||||
assert(this->config().perimeter_extruder <= num_extruders);
|
assert(this->config().perimeter_extruder <= num_extruders);
|
||||||
assert(this->config().infill_extruder <= num_extruders);
|
assert(this->config().infill_extruder <= num_extruders);
|
||||||
assert(this->config().solid_infill_extruder <= num_extruders);
|
assert(this->config().solid_infill_extruder <= num_extruders);
|
||||||
#endif
|
#endif
|
||||||
collect_object_printing_extruders(print()->config(), this->config(), print()->has_brim(), object_extruders);
|
collect_object_printing_extruders(print.config(), this->config(), print.has_brim(), object_extruders);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ protected:
|
||||||
|
|
||||||
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); }
|
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); }
|
||||||
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
|
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
|
||||||
{ this->m_config.apply_only(other, keys, ignore_nonexistent); }
|
{ m_config.apply_only(other, keys, ignore_nonexistent); }
|
||||||
|
|
||||||
void set_trafo(const Transform3d& trafo, bool left_handed) {
|
void set_trafo(const Transform3d& trafo, bool left_handed) {
|
||||||
m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; });
|
m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; });
|
||||||
|
|
|
||||||
|
|
@ -345,17 +345,14 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
|
||||||
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
||||||
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
||||||
coordf_t external_perimeter_width = 0.;
|
coordf_t external_perimeter_width = 0.;
|
||||||
size_t num_nonempty_regions = 0;
|
|
||||||
coordf_t bridge_flow_ratio = 0;
|
coordf_t bridge_flow_ratio = 0;
|
||||||
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id)
|
for (size_t region_id = 0; region_id < object->num_printing_regions(); ++ region_id) {
|
||||||
if (! object->region_volumes[region_id].empty()) {
|
const PrintRegion ®ion = object->printing_region(region_id);
|
||||||
++ num_nonempty_regions;
|
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width()));
|
||||||
const PrintRegion ®ion = *object->print()->get_region(region_id);
|
bridge_flow_ratio += region.config().bridge_flow_ratio;
|
||||||
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width()));
|
}
|
||||||
bridge_flow_ratio += region.config().bridge_flow_ratio;
|
|
||||||
}
|
|
||||||
m_support_params.gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width);
|
m_support_params.gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width);
|
||||||
bridge_flow_ratio /= num_nonempty_regions;
|
bridge_flow_ratio /= object->num_printing_regions();
|
||||||
|
|
||||||
m_support_params.support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ?
|
m_support_params.support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ?
|
||||||
m_support_params.support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
|
m_support_params.support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
|
||||||
|
|
@ -364,7 +361,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
|
||||||
m_support_params.can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value;
|
m_support_params.can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value;
|
||||||
if (!m_support_params.can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) {
|
if (!m_support_params.can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) {
|
||||||
// One of the support extruders is of "don't care" type.
|
// One of the support extruders is of "don't care" type.
|
||||||
auto object_extruders = m_object->print()->object_extruders();
|
auto object_extruders = m_object->object_extruders();
|
||||||
if (object_extruders.size() == 1 &&
|
if (object_extruders.size() == 1 &&
|
||||||
*object_extruders.begin() == std::max<unsigned int>(m_object_config->support_material_extruder.value, m_object_config->support_material_interface_extruder.value))
|
*object_extruders.begin() == std::max<unsigned int>(m_object_config->support_material_extruder.value, m_object_config->support_material_interface_extruder.value))
|
||||||
// Object is printed with the same extruder as the support.
|
// Object is printed with the same extruder as the support.
|
||||||
|
|
@ -1254,7 +1251,7 @@ namespace SupportMaterialInternal {
|
||||||
// Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
|
// Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
|
||||||
Polygons lower_grown_slices = offset(lower_layer_polygons,
|
Polygons lower_grown_slices = offset(lower_layer_polygons,
|
||||||
//FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
|
//FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
|
||||||
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1))),
|
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region().config().perimeter_extruder-1))),
|
||||||
SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||||
// Collect perimeters of this layer.
|
// Collect perimeters of this layer.
|
||||||
//FIXME split_at_first_point() could split a bridge mid-way
|
//FIXME split_at_first_point() could split a bridge mid-way
|
||||||
|
|
@ -1640,7 +1637,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
||||||
if (object_config.thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) {
|
if (object_config.thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) {
|
||||||
coordf_t bridging_height = 0.;
|
coordf_t bridging_height = 0.;
|
||||||
for (const LayerRegion* region : layer.regions())
|
for (const LayerRegion* region : layer.regions())
|
||||||
bridging_height += region->region()->bridging_height_avg(print_config);
|
bridging_height += region->region().bridging_height_avg(print_config);
|
||||||
bridging_height /= coordf_t(layer.regions().size());
|
bridging_height /= coordf_t(layer.regions().size());
|
||||||
coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object;
|
coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object;
|
||||||
if (bridging_print_z >= slicing_params.first_print_layer_height - EPSILON) {
|
if (bridging_print_z >= slicing_params.first_print_layer_height - EPSILON) {
|
||||||
|
|
@ -2767,13 +2764,13 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
||||||
const Layer &object_layer = *object.layers()[i];
|
const Layer &object_layer = *object.layers()[i];
|
||||||
bool some_region_overlaps = false;
|
bool some_region_overlaps = false;
|
||||||
for (LayerRegion *region : object_layer.regions()) {
|
for (LayerRegion *region : object_layer.regions()) {
|
||||||
coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config);
|
coordf_t bridging_height = region->region().bridging_height_avg(*m_print_config);
|
||||||
if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
|
if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
|
||||||
break;
|
break;
|
||||||
some_region_overlaps = true;
|
some_region_overlaps = true;
|
||||||
polygons_append(polygons_trimming,
|
polygons_append(polygons_trimming,
|
||||||
offset(region->fill_surfaces.filter_by_type(stBottomBridge), gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
offset(region->fill_surfaces.filter_by_type(stBottomBridge), gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||||
if (region->region()->config().overhangs.value)
|
if (region->region().config().overhangs.value)
|
||||||
// Add bridging perimeters.
|
// Add bridging perimeters.
|
||||||
SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
|
SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
|
||||||
}
|
}
|
||||||
|
|
@ -3185,7 +3182,7 @@ struct MyLayerExtruded
|
||||||
MyLayerExtruded& operator=(MyLayerExtruded &&rhs) {
|
MyLayerExtruded& operator=(MyLayerExtruded &&rhs) {
|
||||||
this->layer = rhs.layer;
|
this->layer = rhs.layer;
|
||||||
this->extrusions = std::move(rhs.extrusions);
|
this->extrusions = std::move(rhs.extrusions);
|
||||||
this->m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude);
|
m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude);
|
||||||
rhs.layer = nullptr;
|
rhs.layer = nullptr;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,15 +41,9 @@
|
||||||
//====================
|
//====================
|
||||||
#define ENABLE_2_4_0_ALPHA0 1
|
#define ENABLE_2_4_0_ALPHA0 1
|
||||||
|
|
||||||
// Enable splitting of vertex buffers used to render toolpaths
|
|
||||||
#define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_4_0_ALPHA0)
|
|
||||||
// Enable rendering only starting and final caps for toolpaths
|
|
||||||
#define ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
|
|
||||||
// Enable reload from disk command for 3mf files
|
// Enable reload from disk command for 3mf files
|
||||||
#define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_4_0_ALPHA0)
|
#define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
// Removes obsolete warning texture code
|
// Enable showing gcode line numbers in preview horizontal slider
|
||||||
#define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_4_0_ALPHA0)
|
|
||||||
// Enable showing gcode line numbers in previeww horizontal slider
|
|
||||||
#define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_4_0_ALPHA0)
|
#define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
// Enable validation of custom gcode against gcode processor reserved keywords
|
// Enable validation of custom gcode against gcode processor reserved keywords
|
||||||
#define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_4_0_ALPHA0)
|
#define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
|
|
@ -59,10 +53,19 @@
|
||||||
#define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE)
|
#define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE)
|
||||||
// Enable a modified version of automatic downscale on load of objects too big
|
// Enable a modified version of automatic downscale on load of objects too big
|
||||||
#define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0)
|
#define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
|
// Enable scrollable legend in preview
|
||||||
|
#define ENABLE_SCROLLABLE_LEGEND (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
// Enable visualization of start gcode as regular toolpaths
|
// Enable visualization of start gcode as regular toolpaths
|
||||||
#define ENABLE_START_GCODE_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
|
#define ENABLE_START_GCODE_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
// Enable visualization of seams in preview
|
// Enable visualization of seams in preview
|
||||||
#define ENABLE_SEAMS_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
|
#define ENABLE_SEAMS_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
|
// Enable project dirty state manager
|
||||||
|
#define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
|
// Enable project dirty state manager debug window
|
||||||
|
#define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW (0 && ENABLE_PROJECT_DIRTY_STATE)
|
||||||
|
// Enable to push object instances under the bed
|
||||||
|
#define ENABLE_ALLOW_NEGATIVE_Z (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
|
#define DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA (1 && ENABLE_ALLOW_NEGATIVE_Z)
|
||||||
// Enable visualization of objects clearance for sequential prints
|
// Enable visualization of objects clearance for sequential prints
|
||||||
#define ENABLE_SEQUENTIAL_LIMITS (1 && ENABLE_2_4_0_ALPHA0)
|
#define ENABLE_SEQUENTIAL_LIMITS (1 && ENABLE_2_4_0_ALPHA0)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,10 @@ set(SLIC3R_GUI_SOURCES
|
||||||
GUI/UnsavedChangesDialog.hpp
|
GUI/UnsavedChangesDialog.hpp
|
||||||
GUI/ExtraRenderers.cpp
|
GUI/ExtraRenderers.cpp
|
||||||
GUI/ExtraRenderers.hpp
|
GUI/ExtraRenderers.hpp
|
||||||
|
GUI/ProjectDirtyStateManager.hpp
|
||||||
|
GUI/ProjectDirtyStateManager.cpp
|
||||||
|
GUI/DesktopIntegrationDialog.cpp
|
||||||
|
GUI/DesktopIntegrationDialog.hpp
|
||||||
Utils/Http.cpp
|
Utils/Http.cpp
|
||||||
Utils/Http.hpp
|
Utils/Http.hpp
|
||||||
Utils/FixModelByWin10.cpp
|
Utils/FixModelByWin10.cpp
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@
|
||||||
#include "libslic3r/Format/STL.hpp"
|
#include "libslic3r/Format/STL.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
#include "libslic3r/AppConfig.hpp"
|
#include "libslic3r/AppConfig.hpp"
|
||||||
|
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
|
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -345,9 +348,16 @@ void GLVolume::set_render_color(const float* rgba, unsigned int size)
|
||||||
|
|
||||||
void GLVolume::set_render_color()
|
void GLVolume::set_render_color()
|
||||||
{
|
{
|
||||||
if (force_native_color || force_neutral_color)
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
{
|
bool outside = is_outside || is_below_printbed();
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
|
if (force_native_color || force_neutral_color) {
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
if (outside && shader_outside_printer_detection_enabled)
|
||||||
|
#else
|
||||||
if (is_outside && shader_outside_printer_detection_enabled)
|
if (is_outside && shader_outside_printer_detection_enabled)
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
set_render_color(OUTSIDE_COLOR, 4);
|
set_render_color(OUTSIDE_COLOR, 4);
|
||||||
else {
|
else {
|
||||||
if (force_native_color)
|
if (force_native_color)
|
||||||
|
|
@ -362,17 +372,24 @@ void GLVolume::set_render_color()
|
||||||
else if (hover == HS_Deselect)
|
else if (hover == HS_Deselect)
|
||||||
set_render_color(HOVER_DESELECT_COLOR, 4);
|
set_render_color(HOVER_DESELECT_COLOR, 4);
|
||||||
else if (selected)
|
else if (selected)
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
set_render_color(outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
|
||||||
|
#else
|
||||||
set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
|
set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
else if (disabled)
|
else if (disabled)
|
||||||
set_render_color(DISABLED_COLOR, 4);
|
set_render_color(DISABLED_COLOR, 4);
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
else if (outside && shader_outside_printer_detection_enabled)
|
||||||
|
#else
|
||||||
else if (is_outside && shader_outside_printer_detection_enabled)
|
else if (is_outside && shader_outside_printer_detection_enabled)
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
set_render_color(OUTSIDE_COLOR, 4);
|
set_render_color(OUTSIDE_COLOR, 4);
|
||||||
else
|
else
|
||||||
set_render_color(color, 4);
|
set_render_color(color, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!printable)
|
if (!printable) {
|
||||||
{
|
|
||||||
render_color[0] /= 4;
|
render_color[0] /= 4;
|
||||||
render_color[1] /= 4;
|
render_color[1] /= 4;
|
||||||
render_color[2] /= 4;
|
render_color[2] /= 4;
|
||||||
|
|
@ -504,6 +521,25 @@ void GLVolume::render() const
|
||||||
bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
|
bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
|
||||||
bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); }
|
bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); }
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
bool GLVolume::is_sinking() const
|
||||||
|
{
|
||||||
|
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
if (is_modifier || GUI::wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA)
|
||||||
|
#else
|
||||||
|
if (is_modifier)
|
||||||
|
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
return false;
|
||||||
|
const BoundingBoxf3& box = transformed_convex_hull_bounding_box();
|
||||||
|
return box.min(2) < -EPSILON && box.max(2) >= -EPSILON;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLVolume::is_below_printbed() const
|
||||||
|
{
|
||||||
|
return transformed_convex_hull_bounding_box().max(2) < 0.0;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
std::vector<int> GLVolumeCollection::load_object(
|
std::vector<int> GLVolumeCollection::load_object(
|
||||||
const ModelObject *model_object,
|
const ModelObject *model_object,
|
||||||
int obj_idx,
|
int obj_idx,
|
||||||
|
|
@ -778,6 +814,9 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
||||||
shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
|
shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
|
||||||
shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
|
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.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
shader->set_uniform("sinking", volume.first->is_sinking());
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
volume.first->render();
|
volume.first->render();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -453,6 +453,11 @@ public:
|
||||||
bool is_sla_support() const;
|
bool is_sla_support() const;
|
||||||
bool is_sla_pad() const;
|
bool is_sla_pad() const;
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
bool is_sinking() const;
|
||||||
|
bool is_below_printbed() const;
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
// Return an estimate of the memory consumed by this class.
|
// Return an estimate of the memory consumed by this class.
|
||||||
size_t cpu_memory_used() const {
|
size_t cpu_memory_used() const {
|
||||||
//FIXME what to do wih m_convex_hull?
|
//FIXME what to do wih m_convex_hull?
|
||||||
|
|
|
||||||
|
|
@ -361,7 +361,7 @@ bool BackgroundSlicingProcess::stop()
|
||||||
// m_print->state_mutex() shall NOT be held. Unfortunately there is no interface to test for it.
|
// m_print->state_mutex() shall NOT be held. Unfortunately there is no interface to test for it.
|
||||||
std::unique_lock<std::mutex> lck(m_mutex);
|
std::unique_lock<std::mutex> lck(m_mutex);
|
||||||
if (m_state == STATE_INITIAL) {
|
if (m_state == STATE_INITIAL) {
|
||||||
// this->m_export_path.clear();
|
// m_export_path.clear();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// assert(this->running());
|
// assert(this->running());
|
||||||
|
|
@ -379,7 +379,7 @@ bool BackgroundSlicingProcess::stop()
|
||||||
m_state = STATE_IDLE;
|
m_state = STATE_IDLE;
|
||||||
m_print->set_cancel_callback([](){});
|
m_print->set_cancel_callback([](){});
|
||||||
}
|
}
|
||||||
// this->m_export_path.clear();
|
// m_export_path.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -496,7 +496,7 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn
|
||||||
assert(config.opt_enum<PrinterTechnology>("printer_technology") == m_print->technology());
|
assert(config.opt_enum<PrinterTechnology>("printer_technology") == m_print->technology());
|
||||||
Print::ApplyStatus invalidated = m_print->apply(model, config);
|
Print::ApplyStatus invalidated = m_print->apply(model, config);
|
||||||
if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
|
if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
|
||||||
!this->m_fff_print->is_step_done(psGCodeExport)) {
|
!m_fff_print->is_step_done(psGCodeExport)) {
|
||||||
// Some FFF status was invalidated, and the G-code was not exported yet.
|
// Some FFF status was invalidated, and the G-code was not exported yet.
|
||||||
// Let the G-code preview UI know that the final G-code preview is not valid.
|
// Let the G-code preview UI know that the final G-code preview is not valid.
|
||||||
// In addition, this early memory deallocation reduces memory footprint.
|
// In addition, this early memory deallocation reduces memory footprint.
|
||||||
|
|
@ -621,7 +621,7 @@ ThumbnailsList BackgroundSlicingProcess::render_thumbnails(const ThumbnailsParam
|
||||||
{
|
{
|
||||||
ThumbnailsList thumbnails;
|
ThumbnailsList thumbnails;
|
||||||
if (m_thumbnail_cb)
|
if (m_thumbnail_cb)
|
||||||
this->execute_ui_task([this, ¶ms, &thumbnails](){ thumbnails = this->m_thumbnail_cb(params); });
|
this->execute_ui_task([this, ¶ms, &thumbnails](){ thumbnails = m_thumbnail_cb(params); });
|
||||||
return thumbnails;
|
return thumbnails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||||
wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
|
wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
|
||||||
DynamicPrintConfig new_conf = *config;
|
DynamicPrintConfig new_conf = *config;
|
||||||
auto answer = dialog.ShowModal();
|
auto answer = dialog.ShowModal();
|
||||||
|
bool support = true;
|
||||||
if (!is_global_config || answer == wxID_YES) {
|
if (!is_global_config || answer == wxID_YES) {
|
||||||
new_conf.set_key_value("perimeters", new ConfigOptionInt(1));
|
new_conf.set_key_value("perimeters", new ConfigOptionInt(1));
|
||||||
new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0));
|
new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0));
|
||||||
|
|
@ -100,13 +101,17 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||||
new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true));
|
new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true));
|
||||||
new_conf.set_key_value("thin_walls", new ConfigOptionBool(false));
|
new_conf.set_key_value("thin_walls", new ConfigOptionBool(false));
|
||||||
fill_density = 0;
|
fill_density = 0;
|
||||||
|
support = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
new_conf.set_key_value("spiral_vase", new ConfigOptionBool(false));
|
new_conf.set_key_value("spiral_vase", new ConfigOptionBool(false));
|
||||||
}
|
}
|
||||||
apply(config, &new_conf);
|
apply(config, &new_conf);
|
||||||
if (cb_value_change)
|
if (cb_value_change) {
|
||||||
cb_value_change("fill_density", fill_density);
|
cb_value_change("fill_density", fill_density);
|
||||||
|
if (!support)
|
||||||
|
cb_value_change("support_material", false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->opt_bool("wipe_tower") && config->opt_bool("support_material") &&
|
if (config->opt_bool("wipe_tower") && config->opt_bool("support_material") &&
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,18 @@
|
||||||
#include <wx/wupdlock.h>
|
#include <wx/wupdlock.h>
|
||||||
#include <wx/debug.h>
|
#include <wx/debug.h>
|
||||||
|
|
||||||
|
#include "libslic3r/Platform.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
#include "libslic3r/Config.hpp"
|
#include "libslic3r/Config.hpp"
|
||||||
#include "GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "GUI_Utils.hpp"
|
#include "GUI_Utils.hpp"
|
||||||
#include "GUI_ObjectManipulation.hpp"
|
#include "GUI_ObjectManipulation.hpp"
|
||||||
|
#include "Field.hpp"
|
||||||
|
#include "DesktopIntegrationDialog.hpp"
|
||||||
#include "slic3r/Config/Snapshot.hpp"
|
#include "slic3r/Config/Snapshot.hpp"
|
||||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||||
|
#include "format.hpp"
|
||||||
|
|
||||||
#if defined(__linux__) && defined(__WXGTK3__)
|
#if defined(__linux__) && defined(__WXGTK3__)
|
||||||
#define wxLinux_gtk3 true
|
#define wxLinux_gtk3 true
|
||||||
|
|
@ -450,7 +454,6 @@ void ConfigWizardPage::append_spacer(int space)
|
||||||
content->AddSpacer(space);
|
content->AddSpacer(space);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Wizard pages
|
// Wizard pages
|
||||||
|
|
||||||
PageWelcome::PageWelcome(ConfigWizard *parent)
|
PageWelcome::PageWelcome(ConfigWizard *parent)
|
||||||
|
|
@ -469,9 +472,21 @@ PageWelcome::PageWelcome(ConfigWizard *parent)
|
||||||
, cbox_reset(append(
|
, cbox_reset(append(
|
||||||
new wxCheckBox(this, wxID_ANY, _L("Remove user profiles (a snapshot will be taken beforehand)"))
|
new wxCheckBox(this, wxID_ANY, _L("Remove user profiles (a snapshot will be taken beforehand)"))
|
||||||
))
|
))
|
||||||
|
, cbox_integrate(append(
|
||||||
|
new wxCheckBox(this, wxID_ANY, _L("Perform desktop integration (Sets this binary to be searchable by the system)."))
|
||||||
|
))
|
||||||
{
|
{
|
||||||
welcome_text->Hide();
|
welcome_text->Hide();
|
||||||
cbox_reset->Hide();
|
cbox_reset->Hide();
|
||||||
|
#ifdef __linux__
|
||||||
|
if (!DesktopIntegrationDialog::is_integrated())
|
||||||
|
cbox_integrate->Show(true);
|
||||||
|
else
|
||||||
|
cbox_integrate->Hide();
|
||||||
|
#else
|
||||||
|
cbox_integrate->Hide();
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason)
|
void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason)
|
||||||
|
|
@ -479,6 +494,14 @@ void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason)
|
||||||
const bool data_empty = run_reason == ConfigWizard::RR_DATA_EMPTY;
|
const bool data_empty = run_reason == ConfigWizard::RR_DATA_EMPTY;
|
||||||
welcome_text->Show(data_empty);
|
welcome_text->Show(data_empty);
|
||||||
cbox_reset->Show(!data_empty);
|
cbox_reset->Show(!data_empty);
|
||||||
|
#ifdef __linux__
|
||||||
|
if (!DesktopIntegrationDialog::is_integrated())
|
||||||
|
cbox_integrate->Show(true);
|
||||||
|
else
|
||||||
|
cbox_integrate->Hide();
|
||||||
|
#else
|
||||||
|
cbox_integrate->Hide();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1361,20 +1384,39 @@ void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
|
||||||
config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model));
|
config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void focus_event(wxFocusEvent& e, wxTextCtrl* ctrl, double def_value)
|
||||||
|
{
|
||||||
|
e.Skip();
|
||||||
|
wxString str = ctrl->GetValue();
|
||||||
|
// Replace the first occurence of comma in decimal number.
|
||||||
|
bool was_replace = str.Replace(",", ".", false) > 0;
|
||||||
|
double val = 0.0;
|
||||||
|
if (!str.ToCDouble(&val)) {
|
||||||
|
if (val == 0.0)
|
||||||
|
val = def_value;
|
||||||
|
ctrl->SetValue(double_to_string(val));
|
||||||
|
show_error(nullptr, _L("Invalid numeric input."));
|
||||||
|
ctrl->SetFocus();
|
||||||
|
}
|
||||||
|
else if (was_replace)
|
||||||
|
ctrl->SetValue(double_to_string(val));
|
||||||
|
}
|
||||||
|
|
||||||
PageDiameters::PageDiameters(ConfigWizard *parent)
|
PageDiameters::PageDiameters(ConfigWizard *parent)
|
||||||
: ConfigWizardPage(parent, _L("Filament and Nozzle Diameters"), _L("Print Diameters"), 1)
|
: ConfigWizardPage(parent, _L("Filament and Nozzle Diameters"), _L("Print Diameters"), 1)
|
||||||
, spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY))
|
, diam_nozzle(new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(Field::def_width_thinner() * wxGetApp().em_unit(), wxDefaultCoord)))
|
||||||
, spin_filam(new wxSpinCtrlDouble(this, wxID_ANY))
|
, diam_filam (new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(Field::def_width_thinner() * wxGetApp().em_unit(), wxDefaultCoord)))
|
||||||
{
|
{
|
||||||
spin_nozzle->SetDigits(2);
|
|
||||||
spin_nozzle->SetIncrement(0.1);
|
|
||||||
auto *default_nozzle = print_config_def.get("nozzle_diameter")->get_default_value<ConfigOptionFloats>();
|
auto *default_nozzle = print_config_def.get("nozzle_diameter")->get_default_value<ConfigOptionFloats>();
|
||||||
spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5);
|
wxString value = double_to_string(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5);
|
||||||
|
diam_nozzle->SetValue(value);
|
||||||
|
|
||||||
spin_filam->SetDigits(2);
|
|
||||||
spin_filam->SetIncrement(0.25);
|
|
||||||
auto *default_filam = print_config_def.get("filament_diameter")->get_default_value<ConfigOptionFloats>();
|
auto *default_filam = print_config_def.get("filament_diameter")->get_default_value<ConfigOptionFloats>();
|
||||||
spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0);
|
value = double_to_string(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0);
|
||||||
|
diam_filam->SetValue(value);
|
||||||
|
|
||||||
|
diam_nozzle->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) { focus_event(e, diam_nozzle, 0.5); }, diam_nozzle->GetId());
|
||||||
|
diam_filam ->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) { focus_event(e, diam_filam , 3.0); }, diam_filam->GetId());
|
||||||
|
|
||||||
append_text(_L("Enter the diameter of your printer's hot end nozzle."));
|
append_text(_L("Enter the diameter of your printer's hot end nozzle."));
|
||||||
|
|
||||||
|
|
@ -1383,7 +1425,7 @@ PageDiameters::PageDiameters(ConfigWizard *parent)
|
||||||
auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _L("mm"));
|
auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _L("mm"));
|
||||||
sizer_nozzle->AddGrowableCol(0, 1);
|
sizer_nozzle->AddGrowableCol(0, 1);
|
||||||
sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
|
sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
|
||||||
sizer_nozzle->Add(spin_nozzle);
|
sizer_nozzle->Add(diam_nozzle);
|
||||||
sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
|
sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
|
||||||
append(sizer_nozzle);
|
append(sizer_nozzle);
|
||||||
|
|
||||||
|
|
@ -1397,16 +1439,21 @@ PageDiameters::PageDiameters(ConfigWizard *parent)
|
||||||
auto *unit_filam = new wxStaticText(this, wxID_ANY, _L("mm"));
|
auto *unit_filam = new wxStaticText(this, wxID_ANY, _L("mm"));
|
||||||
sizer_filam->AddGrowableCol(0, 1);
|
sizer_filam->AddGrowableCol(0, 1);
|
||||||
sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL);
|
sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL);
|
||||||
sizer_filam->Add(spin_filam);
|
sizer_filam->Add(diam_filam);
|
||||||
sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL);
|
sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL);
|
||||||
append(sizer_filam);
|
append(sizer_filam);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageDiameters::apply_custom_config(DynamicPrintConfig &config)
|
void PageDiameters::apply_custom_config(DynamicPrintConfig &config)
|
||||||
{
|
{
|
||||||
auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue());
|
double val = 0.0;
|
||||||
|
diam_nozzle->GetValue().ToCDouble(&val);
|
||||||
|
auto *opt_nozzle = new ConfigOptionFloats(1, val);
|
||||||
config.set_key_value("nozzle_diameter", opt_nozzle);
|
config.set_key_value("nozzle_diameter", opt_nozzle);
|
||||||
auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue());
|
|
||||||
|
val = 0.0;
|
||||||
|
diam_filam->GetValue().ToCDouble(&val);
|
||||||
|
auto * opt_filam = new ConfigOptionFloats(1, val);
|
||||||
config.set_key_value("filament_diameter", opt_filam);
|
config.set_key_value("filament_diameter", opt_filam);
|
||||||
|
|
||||||
auto set_extrusion_width = [&config, opt_nozzle](const char *key, double dmr) {
|
auto set_extrusion_width = [&config, opt_nozzle](const char *key, double dmr) {
|
||||||
|
|
@ -2373,6 +2420,12 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
// Desktop integration on Linux
|
||||||
|
if (page_welcome->integrate_desktop())
|
||||||
|
DesktopIntegrationDialog::perform_desktop_integration();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
|
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
|
||||||
bool snapshot = true;
|
bool snapshot = true;
|
||||||
Snapshot::Reason snapshot_reason = Snapshot::SNAPSHOT_UPGRADE;
|
Snapshot::Reason snapshot_reason = Snapshot::SNAPSHOT_UPGRADE;
|
||||||
|
|
@ -2490,7 +2543,6 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||||
// Update the selections from the compatibilty.
|
// Update the selections from the compatibilty.
|
||||||
preset_bundle->export_selections(*app_config);
|
preset_bundle->export_selections(*app_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigWizard::priv::update_presets_in_config(const std::string& section, const std::string& alias_key, bool add)
|
void ConfigWizard::priv::update_presets_in_config(const std::string& section, const std::string& alias_key, bool add)
|
||||||
{
|
{
|
||||||
const PresetAliases& aliases = section == AppConfig::SECTION_FILAMENTS ? aliases_fff : aliases_sla;
|
const PresetAliases& aliases = section == AppConfig::SECTION_FILAMENTS ? aliases_fff : aliases_sla;
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ public:
|
||||||
bool run(RunReason reason, StartPage start_page = SP_WELCOME);
|
bool run(RunReason reason, StartPage start_page = SP_WELCOME);
|
||||||
|
|
||||||
static const wxString& name(const bool from_menu = false);
|
static const wxString& name(const bool from_menu = false);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void on_dpi_changed(const wxRect &suggested_rect) override ;
|
void on_dpi_changed(const wxRect &suggested_rect) override ;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,10 +227,12 @@ struct PageWelcome: ConfigWizardPage
|
||||||
{
|
{
|
||||||
wxStaticText *welcome_text;
|
wxStaticText *welcome_text;
|
||||||
wxCheckBox *cbox_reset;
|
wxCheckBox *cbox_reset;
|
||||||
|
wxCheckBox *cbox_integrate;
|
||||||
|
|
||||||
PageWelcome(ConfigWizard *parent);
|
PageWelcome(ConfigWizard *parent);
|
||||||
|
|
||||||
bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
|
bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
|
||||||
|
bool integrate_desktop() const { return cbox_integrate != nullptr ? cbox_integrate->GetValue() : false; }
|
||||||
|
|
||||||
virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
|
virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
|
||||||
};
|
};
|
||||||
|
|
@ -449,8 +451,8 @@ struct PageBedShape: ConfigWizardPage
|
||||||
|
|
||||||
struct PageDiameters: ConfigWizardPage
|
struct PageDiameters: ConfigWizardPage
|
||||||
{
|
{
|
||||||
wxSpinCtrlDouble *spin_nozzle;
|
wxTextCtrl *diam_nozzle;
|
||||||
wxSpinCtrlDouble *spin_filam;
|
wxTextCtrl *diam_filam;
|
||||||
|
|
||||||
PageDiameters(ConfigWizard *parent);
|
PageDiameters(ConfigWizard *parent);
|
||||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||||
|
|
@ -615,7 +617,9 @@ struct ConfigWizard::priv
|
||||||
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||||
// #ys_FIXME_alise
|
// #ys_FIXME_alise
|
||||||
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
|
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
|
||||||
|
#ifdef __linux__
|
||||||
|
void perform_desktop_integration() const;
|
||||||
|
#endif
|
||||||
bool check_fff_selected(); // Used to decide whether to display Filaments page
|
bool check_fff_selected(); // Used to decide whether to display Filaments page
|
||||||
bool check_sla_selected(); // Used to decide whether to display SLA Materials page
|
bool check_sla_selected(); // Used to decide whether to display SLA Materials page
|
||||||
|
|
||||||
|
|
|
||||||
408
src/slic3r/GUI/DesktopIntegrationDialog.cpp
Normal file
408
src/slic3r/GUI/DesktopIntegrationDialog.cpp
Normal file
|
|
@ -0,0 +1,408 @@
|
||||||
|
#ifdef __linux__
|
||||||
|
#include "DesktopIntegrationDialog.hpp"
|
||||||
|
#include "GUI_App.hpp"
|
||||||
|
#include "format.hpp"
|
||||||
|
#include "I18N.hpp"
|
||||||
|
#include "NotificationManager.hpp"
|
||||||
|
#include "libslic3r/AppConfig.hpp"
|
||||||
|
#include "libslic3r/Utils.hpp"
|
||||||
|
#include "libslic3r/Platform.hpp"
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
#include <wx/filename.h>
|
||||||
|
#include <wx/stattext.h>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
namespace integrate_desktop_internal{
|
||||||
|
// Disects path strings stored in system variable divided by ':' and adds into vector
|
||||||
|
static void resolve_path_from_var(const std::string& var, std::vector<std::string>& paths)
|
||||||
|
{
|
||||||
|
wxString wxdirs;
|
||||||
|
if (! wxGetEnv(boost::nowide::widen(var), &wxdirs) || wxdirs.empty() )
|
||||||
|
return;
|
||||||
|
std::string dirs = boost::nowide::narrow(wxdirs);
|
||||||
|
for (size_t i = dirs.find(':'); i != std::string::npos; i = dirs.find(':'))
|
||||||
|
{
|
||||||
|
paths.push_back(dirs.substr(0, i));
|
||||||
|
if (dirs.size() > i+1)
|
||||||
|
dirs = dirs.substr(i+1);
|
||||||
|
}
|
||||||
|
paths.push_back(dirs);
|
||||||
|
}
|
||||||
|
// Return true if directory in path p+dir_name exists
|
||||||
|
static bool contains_path_dir(const std::string& p, const std::string& dir_name)
|
||||||
|
{
|
||||||
|
if (p.empty() || dir_name.empty())
|
||||||
|
return false;
|
||||||
|
boost::filesystem::path path(p + (p[p.size()-1] == '/' ? "" : "/") + dir_name);
|
||||||
|
if (boost::filesystem::exists(path) && boost::filesystem::is_directory(path)) {
|
||||||
|
//BOOST_LOG_TRIVIAL(debug) << path.string() << " " << std::oct << boost::filesystem::status(path).permissions();
|
||||||
|
return true; //boost::filesystem::status(path).permissions() & boost::filesystem::owner_write;
|
||||||
|
} else
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << path.string() << " doesnt exists";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Creates directory in path if not exists yet
|
||||||
|
static void create_dir(const boost::filesystem::path& path)
|
||||||
|
{
|
||||||
|
if (boost::filesystem::exists(path))
|
||||||
|
return;
|
||||||
|
BOOST_LOG_TRIVIAL(debug)<< "creating " << path.string();
|
||||||
|
boost::system::error_code ec;
|
||||||
|
boost::filesystem::create_directory(path, ec);
|
||||||
|
if (ec)
|
||||||
|
BOOST_LOG_TRIVIAL(error)<< "create directory failed: " << ec.message();
|
||||||
|
}
|
||||||
|
// Starts at basic_path (excluded) and creates all directories in dir_path
|
||||||
|
static void create_path(const std::string& basic_path, const std::string& dir_path)
|
||||||
|
{
|
||||||
|
if (basic_path.empty() || dir_path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
boost::filesystem::path path(basic_path);
|
||||||
|
std::string dirs = dir_path;
|
||||||
|
for (size_t i = dirs.find('/'); i != std::string::npos; i = dirs.find('/'))
|
||||||
|
{
|
||||||
|
std::string dir = dirs.substr(0, i);
|
||||||
|
path = boost::filesystem::path(path.string() +"/"+ dir);
|
||||||
|
create_dir(path);
|
||||||
|
dirs = dirs.substr(i+1);
|
||||||
|
}
|
||||||
|
path = boost::filesystem::path(path.string() +"/"+ dirs);
|
||||||
|
create_dir(path);
|
||||||
|
}
|
||||||
|
// Calls our internal copy_file function to copy file at icon_path to dest_path
|
||||||
|
static bool copy_icon(const std::string& icon_path, const std::string& dest_path)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) <<"icon from "<< icon_path;
|
||||||
|
BOOST_LOG_TRIVIAL(debug) <<"icon to "<< dest_path;
|
||||||
|
std::string error_message;
|
||||||
|
auto cfr = copy_file(icon_path, dest_path, error_message, false);
|
||||||
|
if (cfr) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Copy icon fail(" << cfr << "): " << error_message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Copy icon success.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Creates new file filled with data.
|
||||||
|
static bool create_desktop_file(const std::string& path, const std::string& data)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) <<".desktop to "<< path;
|
||||||
|
std::ofstream output(path);
|
||||||
|
output << data;
|
||||||
|
struct stat buffer;
|
||||||
|
if (stat(path.c_str(), &buffer) == 0)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Desktop file created.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Desktop file NOT created.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace integratec_desktop_internal
|
||||||
|
|
||||||
|
// methods that actually do / undo desktop integration. Static to be accesible from anywhere.
|
||||||
|
bool DesktopIntegrationDialog::is_integrated()
|
||||||
|
{
|
||||||
|
const char *appimage_env = std::getenv("APPIMAGE");
|
||||||
|
if (!appimage_env)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const AppConfig *app_config = wxGetApp().app_config;
|
||||||
|
std::string path(app_config->get("desktop_integration_app_path"));
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Desktop integration desktop file path: " << path;
|
||||||
|
|
||||||
|
if (path.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// confirmation that PrusaSlicer.desktop exists
|
||||||
|
struct stat buffer;
|
||||||
|
return (stat (path.c_str(), &buffer) == 0);
|
||||||
|
}
|
||||||
|
bool DesktopIntegrationDialog::integration_possible()
|
||||||
|
{
|
||||||
|
|
||||||
|
const char *appimage_env = std::getenv("APPIMAGE");
|
||||||
|
if (!appimage_env)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void DesktopIntegrationDialog::perform_desktop_integration()
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "performing desktop integration";
|
||||||
|
|
||||||
|
// Path to appimage
|
||||||
|
const char *appimage_env = std::getenv("APPIMAGE");
|
||||||
|
std::string appimage_path;
|
||||||
|
if (appimage_env) {
|
||||||
|
try {
|
||||||
|
appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string();
|
||||||
|
} catch (std::exception &) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// not appimage - not performing
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - not Appimage executable.";
|
||||||
|
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find directories icons and applications
|
||||||
|
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
|
||||||
|
// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
|
||||||
|
// $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory.
|
||||||
|
// The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
|
||||||
|
// If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
|
||||||
|
std::vector<std::string>target_candidates;
|
||||||
|
integrate_desktop_internal::resolve_path_from_var("XDG_DATA_HOME", target_candidates);
|
||||||
|
integrate_desktop_internal::resolve_path_from_var("XDG_DATA_DIRS", target_candidates);
|
||||||
|
|
||||||
|
AppConfig *app_config = wxGetApp().app_config;
|
||||||
|
// suffix string to create different desktop file for alpha, beta.
|
||||||
|
|
||||||
|
std::string version_suffix;
|
||||||
|
std::string name_suffix;
|
||||||
|
std::string version(SLIC3R_VERSION);
|
||||||
|
if (version.find("alpha") != std::string::npos)
|
||||||
|
{
|
||||||
|
version_suffix = "-alpha";
|
||||||
|
name_suffix = " - alpha";
|
||||||
|
}else if (version.find("beta") != std::string::npos)
|
||||||
|
{
|
||||||
|
version_suffix = "-beta";
|
||||||
|
name_suffix = " - beta";
|
||||||
|
}
|
||||||
|
|
||||||
|
// theme path to icon destination
|
||||||
|
std::string icon_theme_path;
|
||||||
|
std::string icon_theme_dirs;
|
||||||
|
|
||||||
|
if (platform_flavor() == PlatformFlavor::LinuxOnChromium) {
|
||||||
|
icon_theme_path = "hicolor/96x96/apps/";
|
||||||
|
icon_theme_dirs = "/hicolor/96x96/apps";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string target_dir_icons;
|
||||||
|
std::string target_dir_desktop;
|
||||||
|
|
||||||
|
// slicer icon
|
||||||
|
// iterate thru target_candidates to find icons folder
|
||||||
|
for (size_t i = 0; i < target_candidates.size(); ++i) {
|
||||||
|
// Copy icon PrusaSlicer.png from resources_dir()/icons to target_dir_icons/icons/
|
||||||
|
if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "icons")) {
|
||||||
|
target_dir_icons = target_candidates[i];
|
||||||
|
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir());
|
||||||
|
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||||
|
if (integrate_desktop_internal::copy_icon(icon_path, dest_path))
|
||||||
|
break; // success
|
||||||
|
else
|
||||||
|
target_dir_icons.clear(); // copying failed
|
||||||
|
// if all failed - try creating default home folder
|
||||||
|
if (i == target_candidates.size() - 1) {
|
||||||
|
// create $HOME/.local/share
|
||||||
|
integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs);
|
||||||
|
// copy icon
|
||||||
|
target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
|
||||||
|
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir());
|
||||||
|
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||||
|
if (!integrate_desktop_internal::contains_path_dir(target_dir_icons, "icons")
|
||||||
|
|| !integrate_desktop_internal::copy_icon(icon_path, dest_path)) {
|
||||||
|
// every attempt failed - icon wont be present
|
||||||
|
target_dir_icons.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(target_dir_icons.empty()) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Copying PrusaSlicer icon to icons directory failed.";
|
||||||
|
} else
|
||||||
|
// save path to icon
|
||||||
|
app_config->set("desktop_integration_icon_slicer_path", GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix));
|
||||||
|
|
||||||
|
// desktop file
|
||||||
|
// iterate thru target_candidates to find applications folder
|
||||||
|
for (size_t i = 0; i < target_candidates.size(); ++i)
|
||||||
|
{
|
||||||
|
if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "applications")) {
|
||||||
|
target_dir_desktop = target_candidates[i];
|
||||||
|
// Write slicer desktop file
|
||||||
|
std::string desktop_file = GUI::format(
|
||||||
|
"[Desktop Entry]\n"
|
||||||
|
"Name=PrusaSlicer%1%\n"
|
||||||
|
"GenericName=3D Printing Software\n"
|
||||||
|
"Icon=PrusaSlicer%2%\n"
|
||||||
|
"Exec=%3% %%F\n"
|
||||||
|
"Terminal=false\n"
|
||||||
|
"Type=Application\n"
|
||||||
|
"MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n"
|
||||||
|
"Categories=Graphics;3DGraphics;Engineering;\n"
|
||||||
|
"Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA\n"
|
||||||
|
"StartupNotify=false\n"
|
||||||
|
"StartupWMClass=prusa-slicer", name_suffix, version_suffix, appimage_path);
|
||||||
|
|
||||||
|
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
|
||||||
|
if (integrate_desktop_internal::create_desktop_file(path, desktop_file)){
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "PrusaSlicer.desktop file installation success.";
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// write failed - try another path
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "PrusaSlicer.desktop file installation failed.";
|
||||||
|
target_dir_desktop.clear();
|
||||||
|
}
|
||||||
|
// if all failed - try creating default home folder
|
||||||
|
if (i == target_candidates.size() - 1) {
|
||||||
|
// create $HOME/.local/share
|
||||||
|
integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications");
|
||||||
|
// create desktop file
|
||||||
|
target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
|
||||||
|
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
|
||||||
|
if (integrate_desktop_internal::contains_path_dir(target_dir_desktop, "applications")) {
|
||||||
|
if (!integrate_desktop_internal::create_desktop_file(path, desktop_file)) {
|
||||||
|
// Desktop file not written - end desktop integration
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Desktop file not written - end desktop integration
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not find applications directory";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(target_dir_desktop.empty()) {
|
||||||
|
// Desktop file not written - end desktop integration
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not find applications directory";
|
||||||
|
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// save path to desktop file
|
||||||
|
app_config->set("desktop_integration_app_path", GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix));
|
||||||
|
|
||||||
|
// Repeat for Gcode viewer - use same paths as for slicer files
|
||||||
|
// Icon
|
||||||
|
if (!target_dir_icons.empty())
|
||||||
|
{
|
||||||
|
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir());
|
||||||
|
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||||
|
if (integrate_desktop_internal::copy_icon(icon_path, dest_path))
|
||||||
|
// save path to icon
|
||||||
|
app_config->set("desktop_integration_icon_viewer_path", dest_path);
|
||||||
|
else
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Copying Gcode Viewer icon to icons directory failed.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desktop file
|
||||||
|
std::string desktop_file = GUI::format(
|
||||||
|
"[Desktop Entry]\n"
|
||||||
|
"Name=Prusa Gcode Viewer%1%\n"
|
||||||
|
"GenericName=3D Printing Software\n"
|
||||||
|
"Icon=PrusaSlicer-gcodeviewer%2%\n"
|
||||||
|
"Exec=%3% --gcodeviwer %%F\n"
|
||||||
|
"Terminal=false\n"
|
||||||
|
"Type=Application\n"
|
||||||
|
"MimeType=text/x.gcode;\n"
|
||||||
|
"Categories=Graphics;3DGraphics;\n"
|
||||||
|
"Keywords=3D;Printing;Slicer;\n"
|
||||||
|
"StartupNotify=false", name_suffix, version_suffix, appimage_path);
|
||||||
|
|
||||||
|
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
|
||||||
|
if (integrate_desktop_internal::create_desktop_file(desktop_path, desktop_file))
|
||||||
|
// save path to desktop file
|
||||||
|
app_config->set("desktop_integration_app_viewer_path", desktop_path);
|
||||||
|
else {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could create gcode viewer desktop file";
|
||||||
|
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail);
|
||||||
|
}
|
||||||
|
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
||||||
|
}
|
||||||
|
void DesktopIntegrationDialog::undo_desktop_intgration()
|
||||||
|
{
|
||||||
|
const char *appimage_env = std::getenv("APPIMAGE");
|
||||||
|
if (!appimage_env) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Undo desktop integration failed - not Appimage executable.";
|
||||||
|
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationFail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const AppConfig *app_config = wxGetApp().app_config;
|
||||||
|
// slicer .desktop
|
||||||
|
std::string path = std::string(app_config->get("desktop_integration_app_path"));
|
||||||
|
if (!path.empty()) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||||
|
std::remove(path.c_str());
|
||||||
|
}
|
||||||
|
// slicer icon
|
||||||
|
path = std::string(app_config->get("desktop_integration_icon_slicer_path"));
|
||||||
|
if (!path.empty()) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||||
|
std::remove(path.c_str());
|
||||||
|
}
|
||||||
|
// gcode viwer .desktop
|
||||||
|
path = std::string(app_config->get("desktop_integration_app_viewer_path"));
|
||||||
|
if (!path.empty()) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||||
|
std::remove(path.c_str());
|
||||||
|
}
|
||||||
|
// gcode viewer icon
|
||||||
|
path = std::string(app_config->get("desktop_integration_icon_viewer_path"));
|
||||||
|
if (!path.empty()) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||||
|
std::remove(path.c_str());
|
||||||
|
}
|
||||||
|
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent)
|
||||||
|
: wxDialog(parent, wxID_ANY, _(L("Desktop Integration")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||||
|
{
|
||||||
|
bool can_undo = DesktopIntegrationDialog::is_integrated();
|
||||||
|
|
||||||
|
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
|
||||||
|
wxString text = _L("Desktop Integration sets this binary to be searchable by the system.\n\nPress \"Perform\" to proceed.");
|
||||||
|
if (can_undo)
|
||||||
|
text += "\nPress \"Undo\" to remove previous integration.";
|
||||||
|
|
||||||
|
vbox->Add(
|
||||||
|
new wxStaticText( this, wxID_ANY, text),
|
||||||
|
// , wxDefaultPosition, wxSize(100,50), wxTE_MULTILINE),
|
||||||
|
1, // make vertically stretchable
|
||||||
|
wxEXPAND | // make horizontally stretchable
|
||||||
|
wxALL, // and make border all around
|
||||||
|
10 ); // set border width to 10
|
||||||
|
|
||||||
|
|
||||||
|
wxBoxSizer *btn_szr = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
wxButton *btn_perform = new wxButton(this, wxID_ANY, _L("Perform"));
|
||||||
|
btn_szr->Add(btn_perform, 0, wxALL, 10);
|
||||||
|
|
||||||
|
btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(); EndModal(wxID_ANY); });
|
||||||
|
|
||||||
|
if (can_undo){
|
||||||
|
wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo"));
|
||||||
|
btn_szr->Add(btn_undo, 0, wxALL, 10);
|
||||||
|
btn_undo->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::undo_desktop_intgration(); EndModal(wxID_ANY); });
|
||||||
|
}
|
||||||
|
wxButton *btn_cancel = new wxButton(this, wxID_ANY, _L("Cancel"));
|
||||||
|
btn_szr->Add(btn_cancel, 0, wxALL, 10);
|
||||||
|
btn_cancel->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { EndModal(wxID_ANY); });
|
||||||
|
|
||||||
|
vbox->Add(btn_szr, 0, wxALIGN_CENTER);
|
||||||
|
|
||||||
|
SetSizerAndFit(vbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopIntegrationDialog::~DesktopIntegrationDialog()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GUI
|
||||||
|
} // namespace Slic3r
|
||||||
|
#endif // __linux__
|
||||||
39
src/slic3r/GUI/DesktopIntegrationDialog.hpp
Normal file
39
src/slic3r/GUI/DesktopIntegrationDialog.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifdef __linux__
|
||||||
|
#ifndef slic3r_DesktopIntegrationDialog_hpp_
|
||||||
|
#define slic3r_DesktopIntegrationDialog_hpp_
|
||||||
|
|
||||||
|
#include <wx/dialog.h>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
class DesktopIntegrationDialog : public wxDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DesktopIntegrationDialog(wxWindow *parent);
|
||||||
|
DesktopIntegrationDialog(DesktopIntegrationDialog &&) = delete;
|
||||||
|
DesktopIntegrationDialog(const DesktopIntegrationDialog &) = delete;
|
||||||
|
DesktopIntegrationDialog &operator=(DesktopIntegrationDialog &&) = delete;
|
||||||
|
DesktopIntegrationDialog &operator=(const DesktopIntegrationDialog &) = delete;
|
||||||
|
~DesktopIntegrationDialog();
|
||||||
|
|
||||||
|
// methods that actually do / undo desktop integration. Static to be accesible from anywhere.
|
||||||
|
|
||||||
|
// returns true if path to PrusaSlicer.desktop is stored in App Config and existence of desktop file.
|
||||||
|
// Does not check if desktop file leads to this binary or existence of icons and viewer desktop file.
|
||||||
|
static bool is_integrated();
|
||||||
|
// true if appimage
|
||||||
|
static bool integration_possible();
|
||||||
|
// Creates Desktop files and icons for both PrusaSlicer and GcodeViewer.
|
||||||
|
// Stores paths into App Config.
|
||||||
|
// Rewrites if files already existed.
|
||||||
|
static void perform_desktop_integration();
|
||||||
|
// Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config.
|
||||||
|
static void undo_desktop_intgration();
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
} // namespace GUI
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // slic3r_DesktopIntegrationDialog_hpp_
|
||||||
|
#endif // __linux__
|
||||||
|
|
@ -326,10 +326,10 @@ double Control::get_double_value(const SelectedSlider& selection)
|
||||||
return m_values[selection == ssLower ? m_lower_value : m_higher_value];
|
return m_values[selection == ssLower ? m_lower_value : m_higher_value];
|
||||||
}
|
}
|
||||||
|
|
||||||
int Control::get_tick_from_value(double value)
|
int Control::get_tick_from_value(double value, bool force_lower_bound/* = false*/)
|
||||||
{
|
{
|
||||||
std::vector<double>::iterator it;
|
std::vector<double>::iterator it;
|
||||||
if (m_is_wipe_tower)
|
if (m_is_wipe_tower && !force_lower_bound)
|
||||||
it = std::find_if(m_values.begin(), m_values.end(),
|
it = std::find_if(m_values.begin(), m_values.end(),
|
||||||
[value](const double & val) { return fabs(value - val) <= epsilon(); });
|
[value](const double & val) { return fabs(value - val) <= epsilon(); });
|
||||||
else
|
else
|
||||||
|
|
@ -575,7 +575,10 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_
|
||||||
else
|
else
|
||||||
is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3;
|
is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3;
|
||||||
|
|
||||||
dc.DrawBitmap(*icon, x_draw, y_draw);
|
if (m_draw_mode == dmSequentialFffPrint)
|
||||||
|
dc.DrawBitmap(create_scaled_bitmap("colorchange_add", nullptr, 16, true), x_draw, y_draw);
|
||||||
|
else
|
||||||
|
dc.DrawBitmap(*icon, x_draw, y_draw);
|
||||||
|
|
||||||
//update rect of the tick action icon
|
//update rect of the tick action icon
|
||||||
m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim);
|
m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim);
|
||||||
|
|
@ -591,7 +594,7 @@ void Control::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const Selec
|
||||||
dc.DrawLine(pt_beg, pt_end);
|
dc.DrawLine(pt_beg, pt_end);
|
||||||
|
|
||||||
//draw action icon
|
//draw action icon
|
||||||
if (m_draw_mode == dmRegular)
|
if (m_draw_mode == dmRegular || m_draw_mode == dmSequentialFffPrint)
|
||||||
draw_action_icon(dc, pt_beg, pt_end);
|
draw_action_icon(dc, pt_beg, pt_end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1377,6 +1380,10 @@ wxString Control::get_tooltip(int tick/*=-1*/)
|
||||||
|
|
||||||
if (tick_code_it == m_ticks.ticks.end() && m_focus == fiActionIcon) // tick doesn't exist
|
if (tick_code_it == m_ticks.ticks.end() && m_focus == fiActionIcon) // tick doesn't exist
|
||||||
{
|
{
|
||||||
|
if (m_draw_mode == dmSequentialFffPrint)
|
||||||
|
return _L("The sequential print is on.\n"
|
||||||
|
"It's impossible to apply any custom G-code for objects printing sequentually.\n");
|
||||||
|
|
||||||
// Show mode as a first string of tooltop
|
// Show mode as a first string of tooltop
|
||||||
tooltip = " " + _L("Print mode") + ": ";
|
tooltip = " " + _L("Print mode") + ": ";
|
||||||
tooltip += (m_mode == SingleExtruder ? SingleExtruderMode :
|
tooltip += (m_mode == SingleExtruder ? SingleExtruderMode :
|
||||||
|
|
@ -2388,7 +2395,7 @@ void Control::edit_extruder_sequence()
|
||||||
extruder = 0;
|
extruder = 0;
|
||||||
if (m_extruders_sequence.is_mm_intervals) {
|
if (m_extruders_sequence.is_mm_intervals) {
|
||||||
value += m_extruders_sequence.interval_by_mm;
|
value += m_extruders_sequence.interval_by_mm;
|
||||||
tick = get_tick_from_value(value);
|
tick = get_tick_from_value(value, true);
|
||||||
if (tick < 0)
|
if (tick < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -322,7 +322,7 @@ private:
|
||||||
wxSize get_size() const;
|
wxSize get_size() const;
|
||||||
void get_size(int* w, int* h) const;
|
void get_size(int* w, int* h) const;
|
||||||
double get_double_value(const SelectedSlider& selection);
|
double get_double_value(const SelectedSlider& selection);
|
||||||
int get_tick_from_value(double value);
|
int get_tick_from_value(double value, bool force_lower_bound = false);
|
||||||
wxString get_tooltip(int tick = -1);
|
wxString get_tooltip(int tick = -1);
|
||||||
int get_edited_tick_for_position(wxPoint pos, Type type = ColorChange);
|
int get_edited_tick_for_position(wxPoint pos, Type type = ColorChange);
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -23,17 +23,11 @@ namespace GUI {
|
||||||
|
|
||||||
class GCodeViewer
|
class GCodeViewer
|
||||||
{
|
{
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
using IBufferType = unsigned short;
|
using IBufferType = unsigned short;
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
using Color = std::array<float, 3>;
|
using Color = std::array<float, 3>;
|
||||||
using VertexBuffer = std::vector<float>;
|
using VertexBuffer = std::vector<float>;
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
using MultiVertexBuffer = std::vector<VertexBuffer>;
|
using MultiVertexBuffer = std::vector<VertexBuffer>;
|
||||||
using IndexBuffer = std::vector<IBufferType>;
|
using IndexBuffer = std::vector<IBufferType>;
|
||||||
#else
|
|
||||||
using IndexBuffer = std::vector<unsigned int>;
|
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
using MultiIndexBuffer = std::vector<IndexBuffer>;
|
using MultiIndexBuffer = std::vector<IndexBuffer>;
|
||||||
|
|
||||||
static const std::vector<Color> Extrusion_Role_Colors;
|
static const std::vector<Color> Extrusion_Role_Colors;
|
||||||
|
|
@ -69,24 +63,17 @@ class GCodeViewer
|
||||||
};
|
};
|
||||||
|
|
||||||
EFormat format{ EFormat::Position };
|
EFormat format{ EFormat::Position };
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
// vbos id
|
// vbos id
|
||||||
std::vector<unsigned int> vbos;
|
std::vector<unsigned int> vbos;
|
||||||
// sizes of the buffers, in bytes, used in export to obj
|
// sizes of the buffers, in bytes, used in export to obj
|
||||||
std::vector<size_t> sizes;
|
std::vector<size_t> sizes;
|
||||||
#else
|
|
||||||
// vbo id
|
|
||||||
unsigned int id{ 0 };
|
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
// count of vertices, updated after data are sent to gpu
|
// count of vertices, updated after data are sent to gpu
|
||||||
size_t count{ 0 };
|
size_t count{ 0 };
|
||||||
|
|
||||||
size_t data_size_bytes() const { return count * vertex_size_bytes(); }
|
size_t data_size_bytes() const { return count * vertex_size_bytes(); }
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
// We set 65536 as max count of vertices inside a vertex buffer to allow
|
// We set 65536 as max count of vertices inside a vertex buffer to allow
|
||||||
// to use unsigned short in place of unsigned int for indices in the index buffer, to save memory
|
// to use unsigned short in place of unsigned int for indices in the index buffer, to save memory
|
||||||
size_t max_size_bytes() const { return 65536 * vertex_size_bytes(); }
|
size_t max_size_bytes() const { return 65536 * vertex_size_bytes(); }
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
|
|
||||||
size_t vertex_size_floats() const { return position_size_floats() + normal_size_floats(); }
|
size_t vertex_size_floats() const { return position_size_floats() + normal_size_floats(); }
|
||||||
size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); }
|
size_t vertex_size_bytes() const { return vertex_size_floats() * sizeof(float); }
|
||||||
|
|
@ -116,15 +103,10 @@ class GCodeViewer
|
||||||
// ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type
|
// ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type
|
||||||
struct IBuffer
|
struct IBuffer
|
||||||
{
|
{
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
// id of the associated vertex buffer
|
// id of the associated vertex buffer
|
||||||
unsigned int vbo{ 0 };
|
unsigned int vbo{ 0 };
|
||||||
// ibo id
|
// ibo id
|
||||||
unsigned int ibo{ 0 };
|
unsigned int ibo{ 0 };
|
||||||
#else
|
|
||||||
// ibo id
|
|
||||||
unsigned int id{ 0 };
|
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
// count of indices, updated after data are sent to gpu
|
// count of indices, updated after data are sent to gpu
|
||||||
size_t count{ 0 };
|
size_t count{ 0 };
|
||||||
|
|
||||||
|
|
@ -148,7 +130,6 @@ class GCodeViewer
|
||||||
Vec3f position{ Vec3f::Zero() };
|
Vec3f position{ Vec3f::Zero() };
|
||||||
};
|
};
|
||||||
|
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
struct Sub_Path
|
struct Sub_Path
|
||||||
{
|
{
|
||||||
Endpoint first;
|
Endpoint first;
|
||||||
|
|
@ -158,14 +139,9 @@ class GCodeViewer
|
||||||
return first.s_id <= s_id && s_id <= last.s_id;
|
return first.s_id <= s_id && s_id <= last.s_id;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
|
|
||||||
EMoveType type{ EMoveType::Noop };
|
EMoveType type{ EMoveType::Noop };
|
||||||
ExtrusionRole role{ erNone };
|
ExtrusionRole role{ erNone };
|
||||||
#if !ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
Endpoint first;
|
|
||||||
Endpoint last;
|
|
||||||
#endif // !ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
float delta_extruder{ 0.0f };
|
float delta_extruder{ 0.0f };
|
||||||
float height{ 0.0f };
|
float height{ 0.0f };
|
||||||
float width{ 0.0f };
|
float width{ 0.0f };
|
||||||
|
|
@ -175,12 +151,9 @@ class GCodeViewer
|
||||||
float volumetric_rate{ 0.0f };
|
float volumetric_rate{ 0.0f };
|
||||||
unsigned char extruder_id{ 0 };
|
unsigned char extruder_id{ 0 };
|
||||||
unsigned char cp_color_id{ 0 };
|
unsigned char cp_color_id{ 0 };
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
std::vector<Sub_Path> sub_paths;
|
std::vector<Sub_Path> sub_paths;
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
|
|
||||||
bool matches(const GCodeProcessor::MoveVertex& move) const;
|
bool matches(const GCodeProcessor::MoveVertex& move) const;
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
size_t vertices_count() const {
|
size_t vertices_count() const {
|
||||||
return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1;
|
return sub_paths.empty() ? 0 : sub_paths.back().last.s_id - sub_paths.front().first.s_id + 1;
|
||||||
}
|
}
|
||||||
|
|
@ -202,19 +175,13 @@ class GCodeViewer
|
||||||
Endpoint endpoint = { b_id, i_id, s_id, move.position };
|
Endpoint endpoint = { b_id, i_id, s_id, move.position };
|
||||||
sub_paths.push_back({ endpoint , endpoint });
|
sub_paths.push_back({ endpoint , endpoint });
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
size_t vertices_count() const { return last.s_id - first.s_id + 1; }
|
|
||||||
bool contains(size_t id) const { return first.s_id <= id && id <= last.s_id; }
|
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used to batch the indices needed to render the paths
|
// Used to batch the indices needed to render the paths
|
||||||
struct RenderPath
|
struct RenderPath
|
||||||
{
|
{
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
// Index of the parent tbuffer
|
// Index of the parent tbuffer
|
||||||
unsigned char tbuffer_id;
|
unsigned char tbuffer_id;
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
// Render path property
|
// Render path property
|
||||||
Color color;
|
Color color;
|
||||||
// Index of the buffer in TBuffer::indices
|
// Index of the buffer in TBuffer::indices
|
||||||
|
|
@ -224,7 +191,6 @@ class GCodeViewer
|
||||||
unsigned int path_id;
|
unsigned int path_id;
|
||||||
std::vector<unsigned int> sizes;
|
std::vector<unsigned int> sizes;
|
||||||
std::vector<size_t> offsets; // use size_t because we need an unsigned integer whose size matches pointer's size (used in the call glMultiDrawElements())
|
std::vector<size_t> offsets; // use size_t because we need an unsigned integer whose size matches pointer's size (used in the call glMultiDrawElements())
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
bool contains(size_t offset) const {
|
bool contains(size_t offset) const {
|
||||||
for (size_t i = 0; i < offsets.size(); ++i) {
|
for (size_t i = 0; i < offsets.size(); ++i) {
|
||||||
if (offsets[i] <= offset && offset <= offsets[i] + static_cast<size_t>(sizes[i] * sizeof(IBufferType)))
|
if (offsets[i] <= offset && offset <= offsets[i] + static_cast<size_t>(sizes[i] * sizeof(IBufferType)))
|
||||||
|
|
@ -232,7 +198,6 @@ class GCodeViewer
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
};
|
};
|
||||||
// // for unordered_set implementation of render_paths
|
// // for unordered_set implementation of render_paths
|
||||||
// struct RenderPathPropertyHash {
|
// struct RenderPathPropertyHash {
|
||||||
|
|
@ -244,10 +209,8 @@ class GCodeViewer
|
||||||
// };
|
// };
|
||||||
struct RenderPathPropertyLower {
|
struct RenderPathPropertyLower {
|
||||||
bool operator() (const RenderPath &l, const RenderPath &r) const {
|
bool operator() (const RenderPath &l, const RenderPath &r) const {
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
if (l.tbuffer_id < r.tbuffer_id)
|
if (l.tbuffer_id < r.tbuffer_id)
|
||||||
return true;
|
return true;
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
if (l.color[i] < r.color[i])
|
if (l.color[i] < r.color[i])
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -259,11 +222,7 @@ class GCodeViewer
|
||||||
};
|
};
|
||||||
struct RenderPathPropertyEqual {
|
struct RenderPathPropertyEqual {
|
||||||
bool operator() (const RenderPath &l, const RenderPath &r) const {
|
bool operator() (const RenderPath &l, const RenderPath &r) const {
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
return l.tbuffer_id == r.tbuffer_id && l.ibuffer_id == r.ibuffer_id && l.color == r.color;
|
return l.tbuffer_id == r.tbuffer_id && l.ibuffer_id == r.ibuffer_id && l.color == r.color;
|
||||||
#else
|
|
||||||
return l.color == r.color && l.ibuffer_id == r.ibuffer_id;
|
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -295,7 +254,6 @@ class GCodeViewer
|
||||||
// s_id index of first vertex contained in this->vertices
|
// 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 GCodeProcessor::MoveVertex& move, unsigned int b_id, size_t i_id, size_t s_id);
|
||||||
|
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
unsigned int max_vertices_per_segment() const {
|
unsigned int max_vertices_per_segment() const {
|
||||||
switch (render_primitive_type)
|
switch (render_primitive_type)
|
||||||
{
|
{
|
||||||
|
|
@ -308,24 +266,16 @@ class GCodeViewer
|
||||||
|
|
||||||
size_t max_vertices_per_segment_size_floats() const { return vertices.vertex_size_floats() * static_cast<size_t>(max_vertices_per_segment()); }
|
size_t max_vertices_per_segment_size_floats() const { return vertices.vertex_size_floats() * static_cast<size_t>(max_vertices_per_segment()); }
|
||||||
size_t max_vertices_per_segment_size_bytes() const { return max_vertices_per_segment_size_floats() * sizeof(float); }
|
size_t max_vertices_per_segment_size_bytes() const { return max_vertices_per_segment_size_floats() * sizeof(float); }
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
unsigned int indices_per_segment() const {
|
unsigned int indices_per_segment() const {
|
||||||
switch (render_primitive_type)
|
switch (render_primitive_type)
|
||||||
{
|
{
|
||||||
case ERenderPrimitiveType::Point: { return 1; }
|
case ERenderPrimitiveType::Point: { return 1; }
|
||||||
case ERenderPrimitiveType::Line: { return 2; }
|
case ERenderPrimitiveType::Line: { return 2; }
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
case ERenderPrimitiveType::Triangle: { return 30; } // 3 indices x 10 triangles
|
case ERenderPrimitiveType::Triangle: { return 30; } // 3 indices x 10 triangles
|
||||||
#else
|
|
||||||
case ERenderPrimitiveType::Triangle: { return 42; } // 3 indices x 14 triangles
|
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
default: { return 0; }
|
default: { return 0; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(IBufferType)); }
|
size_t indices_per_segment_size_bytes() const { return static_cast<size_t>(indices_per_segment() * sizeof(IBufferType)); }
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
unsigned int max_indices_per_segment() const {
|
unsigned int max_indices_per_segment() const {
|
||||||
switch (render_primitive_type)
|
switch (render_primitive_type)
|
||||||
{
|
{
|
||||||
|
|
@ -336,26 +286,10 @@ class GCodeViewer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); }
|
size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); }
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
|
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
bool has_data() const {
|
bool has_data() const {
|
||||||
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
unsigned int start_segment_vertex_offset() const { return 0; }
|
|
||||||
unsigned int end_segment_vertex_offset() const {
|
|
||||||
switch (render_primitive_type)
|
|
||||||
{
|
|
||||||
case ERenderPrimitiveType::Point: { return 0; }
|
|
||||||
case ERenderPrimitiveType::Line: { return 1; }
|
|
||||||
case ERenderPrimitiveType::Triangle: { return 36; } // 1st vertex of 13th triangle
|
|
||||||
default: { return 0; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_data() const { return vertices.id != 0 && !indices.empty() && indices.front().id != 0; }
|
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// helper to render shells
|
// helper to render shells
|
||||||
|
|
@ -434,11 +368,9 @@ class GCodeViewer
|
||||||
size_t first{ 0 };
|
size_t first{ 0 };
|
||||||
size_t last{ 0 };
|
size_t last{ 0 };
|
||||||
|
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
bool operator == (const Endpoints& other) const {
|
bool operator == (const Endpoints& other) const {
|
||||||
return first == other.first && last == other.last;
|
return first == other.first && last == other.last;
|
||||||
}
|
}
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -464,7 +396,6 @@ class GCodeViewer
|
||||||
double get_z_at(unsigned int id) const { return (id < m_zs.size()) ? m_zs[id] : 0.0; }
|
double get_z_at(unsigned int id) const { return (id < m_zs.size()) ? m_zs[id] : 0.0; }
|
||||||
Endpoints get_endpoints_at(unsigned int id) const { return (id < m_endpoints.size()) ? m_endpoints[id] : Endpoints(); }
|
Endpoints get_endpoints_at(unsigned int id) const { return (id < m_endpoints.size()) ? m_endpoints[id] : Endpoints(); }
|
||||||
|
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
bool operator != (const Layers& other) const {
|
bool operator != (const Layers& other) const {
|
||||||
if (m_zs != other.m_zs)
|
if (m_zs != other.m_zs)
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -473,10 +404,8 @@ class GCodeViewer
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
// used to render the toolpath caps of the current sequential range
|
// used to render the toolpath caps of the current sequential range
|
||||||
// (i.e. when sliding on the horizontal slider)
|
// (i.e. when sliding on the horizontal slider)
|
||||||
struct SequentialRangeCap
|
struct SequentialRangeCap
|
||||||
|
|
@ -491,7 +420,6 @@ class GCodeViewer
|
||||||
void reset();
|
void reset();
|
||||||
size_t indices_count() const { return 6; }
|
size_t indices_count() const { return 6; }
|
||||||
};
|
};
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
|
|
||||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||||
struct Statistics
|
struct Statistics
|
||||||
|
|
@ -508,9 +436,7 @@ class GCodeViewer
|
||||||
int64_t gl_multi_points_calls_count{ 0 };
|
int64_t gl_multi_points_calls_count{ 0 };
|
||||||
int64_t gl_multi_lines_calls_count{ 0 };
|
int64_t gl_multi_lines_calls_count{ 0 };
|
||||||
int64_t gl_multi_triangles_calls_count{ 0 };
|
int64_t gl_multi_triangles_calls_count{ 0 };
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
int64_t gl_triangles_calls_count{ 0 };
|
int64_t gl_triangles_calls_count{ 0 };
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
// memory
|
// memory
|
||||||
int64_t results_size{ 0 };
|
int64_t results_size{ 0 };
|
||||||
int64_t total_vertices_gpu_size{ 0 };
|
int64_t total_vertices_gpu_size{ 0 };
|
||||||
|
|
@ -547,9 +473,7 @@ class GCodeViewer
|
||||||
gl_multi_points_calls_count = 0;
|
gl_multi_points_calls_count = 0;
|
||||||
gl_multi_lines_calls_count = 0;
|
gl_multi_lines_calls_count = 0;
|
||||||
gl_multi_triangles_calls_count = 0;
|
gl_multi_triangles_calls_count = 0;
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
gl_triangles_calls_count = 0;
|
gl_triangles_calls_count = 0;
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_sizes() {
|
void reset_sizes() {
|
||||||
|
|
@ -700,16 +624,14 @@ private:
|
||||||
Shells m_shells;
|
Shells m_shells;
|
||||||
EViewType m_view_type{ EViewType::FeatureType };
|
EViewType m_view_type{ EViewType::FeatureType };
|
||||||
bool m_legend_enabled{ true };
|
bool m_legend_enabled{ true };
|
||||||
PrintEstimatedTimeStatistics m_time_statistics;
|
PrintEstimatedStatistics m_print_statistics;
|
||||||
PrintEstimatedTimeStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedTimeStatistics::ETimeMode::Normal };
|
PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal };
|
||||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||||
Statistics m_statistics;
|
Statistics m_statistics;
|
||||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||||
std::array<float, 2> m_detected_point_sizes = { 0.0f, 0.0f };
|
std::array<float, 2> m_detected_point_sizes = { 0.0f, 0.0f };
|
||||||
GCodeProcessor::Result::SettingsIds m_settings_ids;
|
GCodeProcessor::Result::SettingsIds m_settings_ids;
|
||||||
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
std::array<SequentialRangeCap, 2> m_sequential_range_caps;
|
std::array<SequentialRangeCap, 2> m_sequential_range_caps;
|
||||||
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GCodeViewer();
|
GCodeViewer();
|
||||||
|
|
@ -726,9 +648,7 @@ public:
|
||||||
void render() const;
|
void render() const;
|
||||||
|
|
||||||
bool has_data() const { return !m_roles.empty(); }
|
bool has_data() const { return !m_roles.empty(); }
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
bool can_export_toolpaths() const;
|
bool can_export_toolpaths() const;
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
|
|
||||||
const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; }
|
const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; }
|
||||||
const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; }
|
const BoundingBoxf3& get_max_bounding_box() const { return m_max_bounding_box; }
|
||||||
|
|
|
||||||
|
|
@ -134,27 +134,9 @@ void Size::set_scale_factor(int scale_factor)
|
||||||
m_scale_factor = scale_factor;
|
m_scale_factor = scale_factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLCanvas3D::LayersEditing::LayersEditing()
|
|
||||||
: m_enabled(false)
|
|
||||||
, m_z_texture_id(0)
|
|
||||||
, m_model_object(nullptr)
|
|
||||||
, m_object_max_z(0.f)
|
|
||||||
, m_slicing_parameters(nullptr)
|
|
||||||
, m_layer_height_profile_modified(false)
|
|
||||||
, m_adaptive_quality(0.5f)
|
|
||||||
, state(Unknown)
|
|
||||||
, band_width(2.0f)
|
|
||||||
, strength(0.005f)
|
|
||||||
, last_object_id(-1)
|
|
||||||
, last_z(0.0f)
|
|
||||||
, last_action(LAYER_HEIGHT_EDIT_ACTION_INCREASE)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GLCanvas3D::LayersEditing::~LayersEditing()
|
GLCanvas3D::LayersEditing::~LayersEditing()
|
||||||
{
|
{
|
||||||
if (m_z_texture_id != 0)
|
if (m_z_texture_id != 0) {
|
||||||
{
|
|
||||||
glsafe(::glDeleteTextures(1, &m_z_texture_id));
|
glsafe(::glDeleteTextures(1, &m_z_texture_id));
|
||||||
m_z_texture_id = 0;
|
m_z_texture_id = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -186,11 +168,18 @@ void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config)
|
||||||
void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
|
void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
|
||||||
{
|
{
|
||||||
const ModelObject *model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr;
|
const ModelObject *model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr;
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
// Maximum height of an object changes when the object gets rotated or scaled.
|
||||||
|
// Changing maximum height of an object will invalidate the layer heigth editing profile.
|
||||||
|
// m_model_object->bounding_box() is cached, therefore it is cheap even if this method is called frequently.
|
||||||
|
const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast<float>(model_object_new->bounding_box().max.z());
|
||||||
|
#else
|
||||||
// Maximum height of an object changes when the object gets rotated or scaled.
|
// Maximum height of an object changes when the object gets rotated or scaled.
|
||||||
// Changing maximum height of an object will invalidate the layer heigth editing profile.
|
// Changing maximum height of an object will invalidate the layer heigth editing profile.
|
||||||
// m_model_object->raw_bounding_box() is cached, therefore it is cheap even if this method is called frequently.
|
// m_model_object->raw_bounding_box() is cached, therefore it is cheap even if this method is called frequently.
|
||||||
float new_max_z = (model_object_new == nullptr) ? 0.f : model_object_new->raw_bounding_box().size().z();
|
float new_max_z = (model_object_new == nullptr) ? 0.f : model_object_new->raw_bounding_box().size().z();
|
||||||
if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z ||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z ||
|
||||||
(model_object_new != nullptr && m_model_object->id() != model_object_new->id())) {
|
(model_object_new != nullptr && m_model_object->id() != model_object_new->id())) {
|
||||||
m_layer_height_profile.clear();
|
m_layer_height_profile.clear();
|
||||||
m_layer_height_profile_modified = false;
|
m_layer_height_profile_modified = false;
|
||||||
|
|
@ -218,7 +207,7 @@ void GLCanvas3D::LayersEditing::set_enabled(bool enabled)
|
||||||
m_enabled = is_allowed() && enabled;
|
m_enabled = is_allowed() && enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
float GLCanvas3D::LayersEditing::s_overelay_window_width;
|
float GLCanvas3D::LayersEditing::s_overlay_window_width;
|
||||||
|
|
||||||
void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
|
void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
|
||||||
{
|
{
|
||||||
|
|
@ -302,7 +291,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
|
||||||
if (imgui.button(_L("Reset")))
|
if (imgui.button(_L("Reset")))
|
||||||
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE));
|
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE));
|
||||||
|
|
||||||
GLCanvas3D::LayersEditing::s_overelay_window_width = ImGui::GetWindowSize().x /*+ (float)m_layers_texture.width/4*/;
|
GLCanvas3D::LayersEditing::s_overlay_window_width = ImGui::GetWindowSize().x /*+ (float)m_layers_texture.width/4*/;
|
||||||
imgui.end();
|
imgui.end();
|
||||||
|
|
||||||
const Rect& bar_rect = get_bar_rect_viewport(canvas);
|
const Rect& bar_rect = get_bar_rect_viewport(canvas);
|
||||||
|
|
@ -319,7 +308,7 @@ float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas)
|
||||||
float t = rect.get_top();
|
float t = rect.get_top();
|
||||||
float b = rect.get_bottom();
|
float b = rect.get_bottom();
|
||||||
|
|
||||||
return ((rect.get_left() <= x) && (x <= rect.get_right()) && (t <= y) && (y <= b)) ?
|
return (rect.get_left() <= x && x <= rect.get_right() && t <= y && y <= b) ?
|
||||||
// Inside the bar.
|
// Inside the bar.
|
||||||
(b - y - 1.0f) / (b - t - 1.0f) :
|
(b - y - 1.0f) / (b - t - 1.0f) :
|
||||||
// Outside the bar.
|
// Outside the bar.
|
||||||
|
|
@ -329,7 +318,7 @@ float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas)
|
||||||
bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y)
|
bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y)
|
||||||
{
|
{
|
||||||
const Rect& rect = get_bar_rect_screen(canvas);
|
const Rect& rect = get_bar_rect_screen(canvas);
|
||||||
return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
|
return rect.get_left() <= x && x <= rect.get_right() && rect.get_top() <= y && y <= rect.get_bottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
||||||
|
|
@ -338,7 +327,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
||||||
float w = (float)cnv_size.get_width();
|
float w = (float)cnv_size.get_width();
|
||||||
float h = (float)cnv_size.get_height();
|
float h = (float)cnv_size.get_height();
|
||||||
|
|
||||||
return Rect(w - thickness_bar_width(canvas), 0.0f, w, h);
|
return { w - thickness_bar_width(canvas), 0.0f, w, h };
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
||||||
|
|
@ -347,7 +336,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
||||||
float half_w = 0.5f * (float)cnv_size.get_width();
|
float half_w = 0.5f * (float)cnv_size.get_width();
|
||||||
float half_h = 0.5f * (float)cnv_size.get_height();
|
float half_h = 0.5f * (float)cnv_size.get_height();
|
||||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
|
return { (half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::LayersEditing::is_initialized() const
|
bool GLCanvas3D::LayersEditing::is_initialized() const
|
||||||
|
|
@ -365,11 +354,12 @@ std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) con
|
||||||
|
|
||||||
float h = 0.0f;
|
float h = 0.0f;
|
||||||
for (size_t i = m_layer_height_profile.size() - 2; i >= 2; i -= 2) {
|
for (size_t i = m_layer_height_profile.size() - 2; i >= 2; i -= 2) {
|
||||||
float zi = m_layer_height_profile[i];
|
const float zi = static_cast<float>(m_layer_height_profile[i]);
|
||||||
float zi_1 = m_layer_height_profile[i - 2];
|
const float zi_1 = static_cast<float>(m_layer_height_profile[i - 2]);
|
||||||
if (zi_1 <= z && z <= zi) {
|
if (zi_1 <= z && z <= zi) {
|
||||||
float dz = zi - zi_1;
|
float dz = zi - zi_1;
|
||||||
h = (dz != 0.0f) ? lerp(m_layer_height_profile[i - 1], m_layer_height_profile[i + 1], (z - zi_1) / dz) : m_layer_height_profile[i + 1];
|
h = (dz != 0.0f) ? static_cast<float>(lerp(m_layer_height_profile[i - 1], m_layer_height_profile[i + 1], (z - zi_1) / dz)) :
|
||||||
|
static_cast<float>(m_layer_height_profile[i + 1]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -398,10 +388,10 @@ void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3
|
||||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
|
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
|
||||||
|
|
||||||
// Render the color bar
|
// Render the color bar
|
||||||
float l = bar_rect.get_left();
|
const float l = bar_rect.get_left();
|
||||||
float r = bar_rect.get_right();
|
const float r = bar_rect.get_right();
|
||||||
float t = bar_rect.get_top();
|
const float t = bar_rect.get_top();
|
||||||
float b = bar_rect.get_bottom();
|
const float b = bar_rect.get_bottom();
|
||||||
|
|
||||||
::glBegin(GL_QUADS);
|
::glBegin(GL_QUADS);
|
||||||
::glNormal3f(0.0f, 0.0f, 1.0f);
|
::glNormal3f(0.0f, 0.0f, 1.0f);
|
||||||
|
|
@ -564,7 +554,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
|
||||||
{
|
{
|
||||||
if (last_object_id >= 0) {
|
if (last_object_id >= 0) {
|
||||||
if (m_layer_height_profile_modified) {
|
if (m_layer_height_profile_modified) {
|
||||||
wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Manual edit")));
|
wxGetApp().plater()->take_snapshot(_L("Variable layer height - Manual edit"));
|
||||||
const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
|
const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
|
||||||
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||||
wxGetApp().obj_list()->update_info_items(last_object_id);
|
wxGetApp().obj_list()->update_info_items(last_object_id);
|
||||||
|
|
@ -577,7 +567,7 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters()
|
||||||
{
|
{
|
||||||
if (m_slicing_parameters == nullptr) {
|
if (m_slicing_parameters == nullptr) {
|
||||||
m_slicing_parameters = new SlicingParameters();
|
m_slicing_parameters = new SlicingParameters();
|
||||||
*m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z);
|
*m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,278 +604,6 @@ GLCanvas3D::Mouse::Mouse()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 120, 120, 120 };//{ 9, 91, 134 };
|
|
||||||
const unsigned char GLCanvas3D::WarningTexture::Opacity = 255;
|
|
||||||
|
|
||||||
GLCanvas3D::WarningTexture::WarningTexture()
|
|
||||||
: GUI::GLTexture()
|
|
||||||
, m_original_width(0)
|
|
||||||
, m_original_height(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool state, const GLCanvas3D& canvas)
|
|
||||||
{
|
|
||||||
// Since we have NotificationsManager.hpp the warning textures are no loger needed.
|
|
||||||
// However i have left the infrastructure here and only commented the rendering.
|
|
||||||
// The plater warning / error notifications are added and closed from here.
|
|
||||||
|
|
||||||
std::string text;
|
|
||||||
bool error = false;
|
|
||||||
switch (warning) {
|
|
||||||
case ObjectOutside: text = _u8L("An object outside the print area was detected."); break;
|
|
||||||
case ToolpathOutside: text = _u8L("A toolpath outside the print area was detected."); error = true; break;
|
|
||||||
case SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = true; break;
|
|
||||||
case SomethingNotShown: text = _u8L("Some objects are not visible."); break;
|
|
||||||
case ObjectClashed:
|
|
||||||
text = _u8L( "An object outside the print area was detected.\n"
|
|
||||||
"Resolve the current problem to continue slicing.");
|
|
||||||
error = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
BOOST_LOG_TRIVIAL(error) << state << " : " << text ;
|
|
||||||
auto ¬ification_manager = *wxGetApp().plater()->get_notification_manager();
|
|
||||||
if (state) {
|
|
||||||
if(error)
|
|
||||||
notification_manager.push_plater_error_notification(text);
|
|
||||||
else
|
|
||||||
notification_manager.push_plater_warning_notification(text);
|
|
||||||
} else {
|
|
||||||
if (error)
|
|
||||||
notification_manager.close_plater_error_notification(text);
|
|
||||||
else
|
|
||||||
notification_manager.close_plater_warning_notification(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
auto it = std::find(m_warnings.begin(), m_warnings.end(), warning);
|
|
||||||
|
|
||||||
if (state) {
|
|
||||||
if (it != m_warnings.end()) // this warning is already set to be shown
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_warnings.push_back(warning);
|
|
||||||
std::sort(m_warnings.begin(), m_warnings.end());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (it == m_warnings.end()) // deactivating something that is not active is an easy task
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_warnings.erase(it);
|
|
||||||
if (m_warnings.empty()) { // nothing remains to be shown
|
|
||||||
reset();
|
|
||||||
m_msg_text = "";// save information for rescaling
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look at the end of our vector and generate proper texture.
|
|
||||||
std::string text;
|
|
||||||
bool red_colored = false;
|
|
||||||
switch (m_warnings.back()) {
|
|
||||||
case ObjectOutside : text = L("An object outside the print area was detected"); break;
|
|
||||||
case ToolpathOutside : text = L("A toolpath outside the print area was detected"); break;
|
|
||||||
case SlaSupportsOutside : text = L("SLA supports outside the print area were detected"); break;
|
|
||||||
case SomethingNotShown : text = L("Some objects are not visible when editing supports"); break;
|
|
||||||
case ObjectClashed: {
|
|
||||||
text = L("An object outside the print area was detected\n"
|
|
||||||
"Resolve the current problem to continue slicing");
|
|
||||||
red_colored = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generate(text, canvas, true, red_colored); // GUI::GLTexture::reset() is called at the beginning of generate(...)
|
|
||||||
|
|
||||||
// save information for rescaling
|
|
||||||
m_msg_text = text;
|
|
||||||
m_is_colored_red = red_colored;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
|
||||||
static bool is_font_cleartype(const wxFont &font)
|
|
||||||
{
|
|
||||||
// Native font description: on MSW, it is a version number plus the content of LOGFONT, separated by semicolon.
|
|
||||||
wxString font_desc = font.GetNativeFontInfoDesc();
|
|
||||||
// Find the quality field.
|
|
||||||
wxString sep(";");
|
|
||||||
size_t startpos = 0;
|
|
||||||
for (size_t i = 0; i < 12; ++ i)
|
|
||||||
startpos = font_desc.find(sep, startpos + 1);
|
|
||||||
++ startpos;
|
|
||||||
size_t endpos = font_desc.find(sep, startpos);
|
|
||||||
int quality = wxAtoi(font_desc(startpos, endpos - startpos));
|
|
||||||
return quality == CLEARTYPE_QUALITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearType produces renders, which are difficult to convert into an alpha blended OpenGL texture.
|
|
||||||
// Therefore it is better to disable it, though Vojtech found out, that the font returned with ClearType
|
|
||||||
// disabled is signifcantly thicker than the default ClearType font.
|
|
||||||
// This function modifies the font provided.
|
|
||||||
static void msw_disable_cleartype(wxFont &font)
|
|
||||||
{
|
|
||||||
// Native font description: on MSW, it is a version number plus the content of LOGFONT, separated by semicolon.
|
|
||||||
wxString font_desc = font.GetNativeFontInfoDesc();
|
|
||||||
// Find the quality field.
|
|
||||||
wxString sep(";");
|
|
||||||
size_t startpos_weight = 0;
|
|
||||||
for (size_t i = 0; i < 5; ++ i)
|
|
||||||
startpos_weight = font_desc.find(sep, startpos_weight + 1);
|
|
||||||
++ startpos_weight;
|
|
||||||
size_t endpos_weight = font_desc.find(sep, startpos_weight);
|
|
||||||
// Parse the weight field.
|
|
||||||
unsigned int weight = wxAtoi(font_desc(startpos_weight, endpos_weight - startpos_weight));
|
|
||||||
size_t startpos = endpos_weight;
|
|
||||||
for (size_t i = 0; i < 6; ++ i)
|
|
||||||
startpos = font_desc.find(sep, startpos + 1);
|
|
||||||
++ startpos;
|
|
||||||
size_t endpos = font_desc.find(sep, startpos);
|
|
||||||
int quality = wxAtoi(font_desc(startpos, endpos - startpos));
|
|
||||||
if (quality == CLEARTYPE_QUALITY) {
|
|
||||||
// Replace the weight with a smaller value to compensate the weight of non ClearType font.
|
|
||||||
wxString sweight = std::to_string(weight * 2 / 4);
|
|
||||||
size_t len_weight = endpos_weight - startpos_weight;
|
|
||||||
wxString squality = std::to_string(ANTIALIASED_QUALITY);
|
|
||||||
font_desc.replace(startpos_weight, len_weight, sweight);
|
|
||||||
font_desc.replace(startpos + sweight.size() - len_weight, endpos - startpos, squality);
|
|
||||||
font.SetNativeFontInfo(font_desc);
|
|
||||||
wxString font_desc2 = font.GetNativeFontInfoDesc();
|
|
||||||
}
|
|
||||||
wxString font_desc2 = font.GetNativeFontInfoDesc();
|
|
||||||
}
|
|
||||||
#endif /* __WXMSW__ */
|
|
||||||
|
|
||||||
bool GLCanvas3D::WarningTexture::generate(const std::string& msg_utf8, const GLCanvas3D& canvas, bool compress, bool red_colored/* = false*/)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
|
|
||||||
if (msg_utf8.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
wxString msg = _(msg_utf8);
|
|
||||||
|
|
||||||
wxMemoryDC memDC;
|
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
|
||||||
// set scaled application normal font as default font
|
|
||||||
wxFont font = wxGetApp().normal_font();
|
|
||||||
#else
|
|
||||||
// select default font
|
|
||||||
const float scale = canvas.get_canvas_size().get_scale_factor();
|
|
||||||
#if ENABLE_RETINA_GL
|
|
||||||
// For non-visible or non-created window getBackingScaleFactor function return 0.0 value.
|
|
||||||
// And using of the zero scale causes a crash, when we trying to draw text to the (0,0) rectangle
|
|
||||||
// https://github.com/prusa3d/PrusaSlicer/issues/3916
|
|
||||||
if (scale <= 0.0f)
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
font.MakeLarger();
|
|
||||||
font.MakeBold();
|
|
||||||
memDC.SetFont(font);
|
|
||||||
|
|
||||||
// calculates texture size
|
|
||||||
wxCoord w, h;
|
|
||||||
memDC.GetMultiLineTextExtent(msg, &w, &h);
|
|
||||||
|
|
||||||
m_original_width = (int)w;
|
|
||||||
m_original_height = (int)h;
|
|
||||||
m_width = (int)next_highest_power_of_2((uint32_t)w);
|
|
||||||
m_height = (int)next_highest_power_of_2((uint32_t)h);
|
|
||||||
|
|
||||||
// generates bitmap
|
|
||||||
wxBitmap bitmap(m_width, m_height);
|
|
||||||
|
|
||||||
memDC.SelectObject(bitmap);
|
|
||||||
memDC.SetBackground(wxBrush(*wxBLACK));
|
|
||||||
memDC.Clear();
|
|
||||||
|
|
||||||
// draw message
|
|
||||||
memDC.SetTextForeground(*wxRED);
|
|
||||||
memDC.DrawLabel(msg, wxRect(0,0, m_original_width, m_original_height), wxALIGN_CENTER);
|
|
||||||
|
|
||||||
memDC.SelectObject(wxNullBitmap);
|
|
||||||
|
|
||||||
// Convert the bitmap into a linear data ready to be loaded into the GPU.
|
|
||||||
wxImage image = bitmap.ConvertToImage();
|
|
||||||
|
|
||||||
// prepare buffer
|
|
||||||
std::vector<unsigned char> data(4 * m_width * m_height, 0);
|
|
||||||
const unsigned char *src = image.GetData();
|
|
||||||
for (int h = 0; h < m_height; ++h) {
|
|
||||||
unsigned char* dst = data.data() + 4 * h * m_width;
|
|
||||||
for (int w = 0; w < m_width; ++w) {
|
|
||||||
*dst++ = 255;
|
|
||||||
if (red_colored) {
|
|
||||||
*dst++ = 72; // 204
|
|
||||||
*dst++ = 65; // 204
|
|
||||||
} else {
|
|
||||||
*dst++ = 255;
|
|
||||||
*dst++ = 255;
|
|
||||||
}
|
|
||||||
*dst++ = (unsigned char)std::min<int>(255, *src);
|
|
||||||
src += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sends buffer to gpu
|
|
||||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
|
||||||
glsafe(::glGenTextures(1, &m_id));
|
|
||||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
|
|
||||||
if (compress && GLEW_EXT_texture_compression_s3tc)
|
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
|
||||||
else
|
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
|
||||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
|
||||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
|
||||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
|
||||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const
|
|
||||||
{
|
|
||||||
if (m_warnings.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_id > 0 && m_original_width > 0 && m_original_height > 0 && m_width > 0 && m_height > 0) {
|
|
||||||
const Size& cnv_size = canvas.get_canvas_size();
|
|
||||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
|
||||||
float left = (-0.5f * (float)m_original_width) * inv_zoom;
|
|
||||||
float top = (-0.5f * (float)cnv_size.get_height() + (float)m_original_height + 2.0f) * inv_zoom;
|
|
||||||
float right = left + (float)m_original_width * inv_zoom;
|
|
||||||
float bottom = top - (float)m_original_height * inv_zoom;
|
|
||||||
|
|
||||||
float uv_left = 0.0f;
|
|
||||||
float uv_top = 0.0f;
|
|
||||||
float uv_right = (float)m_original_width / (float)m_width;
|
|
||||||
float uv_bottom = (float)m_original_height / (float)m_height;
|
|
||||||
|
|
||||||
GLTexture::Quad_UVs uvs;
|
|
||||||
uvs.left_top = { uv_left, uv_top };
|
|
||||||
uvs.left_bottom = { uv_left, uv_bottom };
|
|
||||||
uvs.right_bottom = { uv_right, uv_bottom };
|
|
||||||
uvs.right_top = { uv_right, uv_top };
|
|
||||||
|
|
||||||
GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::WarningTexture::msw_rescale(const GLCanvas3D& canvas)
|
|
||||||
{
|
|
||||||
if (m_msg_text.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
generate(m_msg_text, canvas, true, m_is_colored_red);
|
|
||||||
}
|
|
||||||
#endif // !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
|
|
||||||
void GLCanvas3D::Labels::render(const std::vector<const ModelInstance*>& sorted_instances) const
|
void GLCanvas3D::Labels::render(const std::vector<const ModelInstance*>& sorted_instances) const
|
||||||
{
|
{
|
||||||
if (!m_enabled || !is_shown())
|
if (!m_enabled || !is_shown())
|
||||||
|
|
@ -1153,8 +871,6 @@ void GLCanvas3D::SequentialPrintClearance::render() const
|
||||||
}
|
}
|
||||||
#endif // ENABLE_SEQUENTIAL_LIMITS
|
#endif // ENABLE_SEQUENTIAL_LIMITS
|
||||||
|
|
||||||
float GLCanvas3D::Slope::s_window_width;
|
|
||||||
|
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
|
||||||
|
|
@ -1388,11 +1104,7 @@ void GLCanvas3D::reset_volumes()
|
||||||
m_volumes.clear();
|
m_volumes.clear();
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
_set_warning_notification(EWarning::ObjectOutside, false);
|
_set_warning_notification(EWarning::ObjectOutside, false);
|
||||||
#else
|
|
||||||
_set_warning_texture(WarningTexture::ObjectOutside, false);
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int GLCanvas3D::check_volumes_outside_state() const
|
int GLCanvas3D::check_volumes_outside_state() const
|
||||||
|
|
@ -1446,19 +1158,11 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject
|
||||||
if (visible && !mo)
|
if (visible && !mo)
|
||||||
toggle_sla_auxiliaries_visibility(true, mo, instance_idx);
|
toggle_sla_auxiliaries_visibility(true, mo, instance_idx);
|
||||||
|
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
if (!mo && !visible && !m_model->objects.empty() && (m_model->objects.size() > 1 || m_model->objects.front()->instances.size() > 1))
|
if (!mo && !visible && !m_model->objects.empty() && (m_model->objects.size() > 1 || m_model->objects.front()->instances.size() > 1))
|
||||||
_set_warning_notification(EWarning::SomethingNotShown, true);
|
_set_warning_notification(EWarning::SomethingNotShown, true);
|
||||||
|
|
||||||
if (!mo && visible)
|
if (!mo && visible)
|
||||||
_set_warning_notification(EWarning::SomethingNotShown, false);
|
_set_warning_notification(EWarning::SomethingNotShown, false);
|
||||||
#else
|
|
||||||
if (!mo && !visible && !m_model->objects.empty() && (m_model->objects.size() > 1 || m_model->objects.front()->instances.size() > 1))
|
|
||||||
_set_warning_texture(WarningTexture::SomethingNotShown, true);
|
|
||||||
|
|
||||||
if (!mo && visible)
|
|
||||||
_set_warning_texture(WarningTexture::SomethingNotShown, false);
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::update_instance_printable_state_for_object(const size_t obj_idx)
|
void GLCanvas3D::update_instance_printable_state_for_object(const size_t obj_idx)
|
||||||
|
|
@ -1812,6 +1516,11 @@ void GLCanvas3D::render()
|
||||||
}
|
}
|
||||||
#endif // ENABLE_RENDER_STATISTICS
|
#endif // ENABLE_RENDER_STATISTICS
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
if (wxGetApp().is_editor() && wxGetApp().plater()->is_view3D_shown())
|
||||||
|
wxGetApp().plater()->render_project_state_debug_window();
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
|
||||||
#if ENABLE_CAMERA_STATISTICS
|
#if ENABLE_CAMERA_STATISTICS
|
||||||
camera.debug_render();
|
camera.debug_render();
|
||||||
#endif // ENABLE_CAMERA_STATISTICS
|
#endif // ENABLE_CAMERA_STATISTICS
|
||||||
|
|
@ -2045,7 +1754,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||||
|
|
||||||
m_reload_delayed = !m_canvas->IsShown() && !refresh_immediately && !force_full_scene_refresh;
|
m_reload_delayed = !m_canvas->IsShown() && !refresh_immediately && !force_full_scene_refresh;
|
||||||
|
|
||||||
PrinterTechnology printer_technology = m_process->current_printer_technology();
|
PrinterTechnology printer_technology = current_printer_technology();
|
||||||
int volume_idx_wipe_tower_old = -1;
|
int volume_idx_wipe_tower_old = -1;
|
||||||
|
|
||||||
// Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed).
|
// Release invalidated volumes to conserve GPU memory in case of delayed refresh (see m_reload_delayed).
|
||||||
|
|
@ -2335,31 +2044,18 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||||
bool fullyOut = false;
|
bool fullyOut = false;
|
||||||
const bool contained_min_one = m_volumes.check_outside_state(m_config, partlyOut, fullyOut);
|
const bool contained_min_one = m_volumes.check_outside_state(m_config, partlyOut, fullyOut);
|
||||||
|
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
_set_warning_notification(EWarning::ObjectClashed, partlyOut);
|
_set_warning_notification(EWarning::ObjectClashed, partlyOut);
|
||||||
_set_warning_notification(EWarning::ObjectOutside, fullyOut);
|
_set_warning_notification(EWarning::ObjectOutside, fullyOut);
|
||||||
if (printer_technology != ptSLA || !contained_min_one)
|
if (printer_technology != ptSLA || !contained_min_one)
|
||||||
_set_warning_notification(EWarning::SlaSupportsOutside, false);
|
_set_warning_notification(EWarning::SlaSupportsOutside, false);
|
||||||
#else
|
|
||||||
_set_warning_texture(WarningTexture::ObjectClashed, partlyOut);
|
|
||||||
_set_warning_texture(WarningTexture::ObjectOutside, fullyOut);
|
|
||||||
if(printer_technology != ptSLA || !contained_min_one)
|
|
||||||
_set_warning_texture(WarningTexture::SlaSupportsOutside, false);
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
|
|
||||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS,
|
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS,
|
||||||
contained_min_one && !m_model->objects.empty() && !partlyOut));
|
contained_min_one && !m_model->objects.empty() && !partlyOut));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
_set_warning_notification(EWarning::ObjectOutside, false);
|
_set_warning_notification(EWarning::ObjectOutside, false);
|
||||||
_set_warning_notification(EWarning::ObjectClashed, false);
|
_set_warning_notification(EWarning::ObjectClashed, false);
|
||||||
_set_warning_notification(EWarning::SlaSupportsOutside, false);
|
_set_warning_notification(EWarning::SlaSupportsOutside, false);
|
||||||
#else
|
|
||||||
_set_warning_texture(WarningTexture::ObjectOutside, false);
|
|
||||||
_set_warning_texture(WarningTexture::ObjectClashed, false);
|
|
||||||
_set_warning_texture(WarningTexture::SlaSupportsOutside, false);
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
|
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2402,11 +2098,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessor::Result& gcode_result)
|
||||||
|
|
||||||
if (wxGetApp().is_editor()) {
|
if (wxGetApp().is_editor()) {
|
||||||
m_gcode_viewer.update_shells_color_by_extruder(m_config);
|
m_gcode_viewer.update_shells_color_by_extruder(m_config);
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
|
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
|
||||||
#else
|
|
||||||
_show_warning_texture_if_needed(WarningTexture::ToolpathOutside);
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2433,11 +2125,7 @@ void GLCanvas3D::load_sla_preview()
|
||||||
this->reset_volumes();
|
this->reset_volumes();
|
||||||
_load_sla_shells();
|
_load_sla_shells();
|
||||||
_update_sla_shells_outside_state();
|
_update_sla_shells_outside_state();
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
_set_warning_notification_if_needed(EWarning::SlaSupportsOutside);
|
_set_warning_notification_if_needed(EWarning::SlaSupportsOutside);
|
||||||
#else
|
|
||||||
_show_warning_texture_if_needed(WarningTexture::SlaSupportsOutside);
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2458,11 +2146,7 @@ void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, c
|
||||||
_load_print_object_toolpaths(*object, str_tool_colors, color_print_values);
|
_load_print_object_toolpaths(*object, str_tool_colors, color_print_values);
|
||||||
|
|
||||||
_update_toolpath_volumes_outside_state();
|
_update_toolpath_volumes_outside_state();
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
|
_set_warning_notification_if_needed(EWarning::ToolpathOutside);
|
||||||
#else
|
|
||||||
_show_warning_texture_if_needed(WarningTexture::ToolpathOutside);
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::bind_event_handlers()
|
void GLCanvas3D::bind_event_handlers()
|
||||||
|
|
@ -2608,9 +2292,19 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (wxGetApp().app_config->get("use_legacy_3DConnexion") == "1") {
|
if (wxGetApp().app_config->get("use_legacy_3DConnexion") == "1") {
|
||||||
#endif //_WIN32
|
#endif //_WIN32
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// On OSX use Cmd+Shift+M to "Show/Hide 3Dconnexion devices settings dialog"
|
||||||
|
if ((evt.GetModifiers() & shiftMask) != 0) {
|
||||||
|
#endif // __APPLE__
|
||||||
Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller();
|
Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller();
|
||||||
controller.show_settings_dialog(!controller.is_settings_dialog_shown());
|
controller.show_settings_dialog(!controller.is_settings_dialog_shown());
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// and Cmd+M to minimize application
|
||||||
|
wxGetApp().mainframe->Iconize();
|
||||||
|
#endif // __APPLE__
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
}
|
}
|
||||||
#endif //_WIN32
|
#endif //_WIN32
|
||||||
|
|
@ -3246,7 +2940,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_gizmos.on_mouse(evt)) {
|
if (m_gizmos.on_mouse(evt)) {
|
||||||
if (wxWindow::FindFocus() != this->m_canvas)
|
if (wxWindow::FindFocus() != m_canvas)
|
||||||
// Grab keyboard focus for input in gizmo dialogs.
|
// Grab keyboard focus for input in gizmo dialogs.
|
||||||
m_canvas->SetFocus();
|
m_canvas->SetFocus();
|
||||||
|
|
||||||
|
|
@ -3269,7 +2963,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
m_mouse.set_move_start_threshold_position_2D_as_invalid();
|
m_mouse.set_move_start_threshold_position_2D_as_invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt.ButtonDown() && wxWindow::FindFocus() != this->m_canvas)
|
if (evt.ButtonDown() && wxWindow::FindFocus() != m_canvas)
|
||||||
// Grab keyboard focus on any mouse click event.
|
// Grab keyboard focus on any mouse click event.
|
||||||
m_canvas->SetFocus();
|
m_canvas->SetFocus();
|
||||||
|
|
||||||
|
|
@ -3662,14 +3356,38 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
||||||
wipe_tower_origin = v->get_volume_offset();
|
wipe_tower_origin = v->get_volume_offset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
// Fixes flying instances
|
||||||
|
#else
|
||||||
// Fixes sinking/flying instances
|
// Fixes sinking/flying instances
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
for (const std::pair<int, int>& i : done) {
|
for (const std::pair<int, int>& i : done) {
|
||||||
ModelObject* m = m_model->objects[i.first];
|
ModelObject* m = m_model->objects[i.first];
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
double shift_z = m->get_instance_min_z(i.second);
|
||||||
|
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
if (current_printer_technology() == ptSLA || shift_z > 0.0) {
|
||||||
|
#else
|
||||||
|
if (shift_z > 0.0) {
|
||||||
|
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
Vec3d shift(0.0, 0.0, -shift_z);
|
||||||
|
#else
|
||||||
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
|
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
m_selection.translate(i.first, i.second, shift);
|
m_selection.translate(i.first, i.second, shift);
|
||||||
m->translate_instance(i.second, shift);
|
m->translate_instance(i.second, shift);
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
// if the selection is not valid to allow for layer editing after the move, we need to turn off the tool if it is running
|
||||||
|
// similar to void Plater::priv::selection_changed()
|
||||||
|
if (!wxGetApp().plater()->can_layers_editing() && is_layers_editing_enabled())
|
||||||
|
post_event(SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING));
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
if (object_moved)
|
if (object_moved)
|
||||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
|
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
|
||||||
|
|
||||||
|
|
@ -3687,18 +3405,30 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
||||||
if (!snapshot_type.empty())
|
if (!snapshot_type.empty())
|
||||||
wxGetApp().plater()->take_snapshot(_(snapshot_type));
|
wxGetApp().plater()->take_snapshot(_(snapshot_type));
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
// stores current min_z of instances
|
||||||
|
std::map<std::pair<int, int>, double> min_zs;
|
||||||
|
if (!snapshot_type.empty()) {
|
||||||
|
for (int i = 0; i < static_cast<int>(m_model->objects.size()); ++i) {
|
||||||
|
const ModelObject* obj = m_model->objects[i];
|
||||||
|
for (int j = 0; j < static_cast<int>(obj->instances.size()); ++j) {
|
||||||
|
min_zs[{ i, j }] = obj->instance_bounding_box(j).min(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
std::set<std::pair<int, int>> done; // keeps track of modified instances
|
std::set<std::pair<int, int>> done; // keeps track of modified instances
|
||||||
|
|
||||||
Selection::EMode selection_mode = m_selection.get_mode();
|
Selection::EMode selection_mode = m_selection.get_mode();
|
||||||
|
|
||||||
for (const GLVolume* v : m_volumes.volumes)
|
for (const GLVolume* v : m_volumes.volumes) {
|
||||||
{
|
|
||||||
int object_idx = v->object_idx();
|
int object_idx = v->object_idx();
|
||||||
if (object_idx == 1000) { // the wipe tower
|
if (object_idx == 1000) { // the wipe tower
|
||||||
Vec3d offset = v->get_volume_offset();
|
Vec3d offset = v->get_volume_offset();
|
||||||
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3d(offset(0), offset(1), v->get_volume_rotation()(2))));
|
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3d(offset(0), offset(1), v->get_volume_rotation()(2))));
|
||||||
}
|
}
|
||||||
if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx))
|
if (object_idx < 0 || (int)m_model->objects.size() <= object_idx)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int instance_idx = v->instance_idx();
|
int instance_idx = v->instance_idx();
|
||||||
|
|
@ -3708,15 +3438,12 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
||||||
|
|
||||||
// Rotate instances/volumes.
|
// Rotate instances/volumes.
|
||||||
ModelObject* model_object = m_model->objects[object_idx];
|
ModelObject* model_object = m_model->objects[object_idx];
|
||||||
if (model_object != nullptr)
|
if (model_object != nullptr) {
|
||||||
{
|
if (selection_mode == Selection::Instance) {
|
||||||
if (selection_mode == Selection::Instance)
|
|
||||||
{
|
|
||||||
model_object->instances[instance_idx]->set_rotation(v->get_instance_rotation());
|
model_object->instances[instance_idx]->set_rotation(v->get_instance_rotation());
|
||||||
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
||||||
}
|
}
|
||||||
else if (selection_mode == Selection::Volume)
|
else if (selection_mode == Selection::Volume) {
|
||||||
{
|
|
||||||
model_object->volumes[volume_idx]->set_rotation(v->get_volume_rotation());
|
model_object->volumes[volume_idx]->set_rotation(v->get_volume_rotation());
|
||||||
model_object->volumes[volume_idx]->set_offset(v->get_volume_offset());
|
model_object->volumes[volume_idx]->set_offset(v->get_volume_offset());
|
||||||
}
|
}
|
||||||
|
|
@ -3725,12 +3452,21 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fixes sinking/flying instances
|
// Fixes sinking/flying instances
|
||||||
for (const std::pair<int, int>& i : done)
|
for (const std::pair<int, int>& i : done) {
|
||||||
{
|
|
||||||
ModelObject* m = m_model->objects[i.first];
|
ModelObject* m = m_model->objects[i.first];
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
double shift_z = m->get_instance_min_z(i.second);
|
||||||
|
// leave sinking instances as sinking
|
||||||
|
if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= 0.0 || shift_z > 0.0) {
|
||||||
|
Vec3d shift(0.0, 0.0, -shift_z);
|
||||||
|
#else
|
||||||
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
|
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
|
||||||
m_selection.translate(i.first, i.second, shift);
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
m->translate_instance(i.second, shift);
|
m_selection.translate(i.first, i.second, shift);
|
||||||
|
m->translate_instance(i.second, shift);
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!done.empty())
|
if (!done.empty())
|
||||||
|
|
@ -3747,14 +3483,26 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
||||||
if (!snapshot_type.empty())
|
if (!snapshot_type.empty())
|
||||||
wxGetApp().plater()->take_snapshot(_(snapshot_type));
|
wxGetApp().plater()->take_snapshot(_(snapshot_type));
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
// stores current min_z of instances
|
||||||
|
std::map<std::pair<int, int>, double> min_zs;
|
||||||
|
if (!snapshot_type.empty()) {
|
||||||
|
for (int i = 0; i < static_cast<int>(m_model->objects.size()); ++i) {
|
||||||
|
const ModelObject* obj = m_model->objects[i];
|
||||||
|
for (int j = 0; j < static_cast<int>(obj->instances.size()); ++j) {
|
||||||
|
min_zs[{ i, j }] = obj->instance_bounding_box(j).min(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
std::set<std::pair<int, int>> done; // keeps track of modified instances
|
std::set<std::pair<int, int>> done; // keeps track of modified instances
|
||||||
|
|
||||||
Selection::EMode selection_mode = m_selection.get_mode();
|
Selection::EMode selection_mode = m_selection.get_mode();
|
||||||
|
|
||||||
for (const GLVolume* v : m_volumes.volumes)
|
for (const GLVolume* v : m_volumes.volumes) {
|
||||||
{
|
|
||||||
int object_idx = v->object_idx();
|
int object_idx = v->object_idx();
|
||||||
if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx))
|
if (object_idx < 0 || (int)m_model->objects.size() <= object_idx)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int instance_idx = v->instance_idx();
|
int instance_idx = v->instance_idx();
|
||||||
|
|
@ -3764,15 +3512,12 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
||||||
|
|
||||||
// Rotate instances/volumes
|
// Rotate instances/volumes
|
||||||
ModelObject* model_object = m_model->objects[object_idx];
|
ModelObject* model_object = m_model->objects[object_idx];
|
||||||
if (model_object != nullptr)
|
if (model_object != nullptr) {
|
||||||
{
|
if (selection_mode == Selection::Instance) {
|
||||||
if (selection_mode == Selection::Instance)
|
|
||||||
{
|
|
||||||
model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor());
|
model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor());
|
||||||
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
||||||
}
|
}
|
||||||
else if (selection_mode == Selection::Volume)
|
else if (selection_mode == Selection::Volume) {
|
||||||
{
|
|
||||||
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
model_object->instances[instance_idx]->set_offset(v->get_instance_offset());
|
||||||
model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor());
|
model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor());
|
||||||
model_object->volumes[volume_idx]->set_offset(v->get_volume_offset());
|
model_object->volumes[volume_idx]->set_offset(v->get_volume_offset());
|
||||||
|
|
@ -3782,16 +3527,25 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fixes sinking/flying instances
|
// Fixes sinking/flying instances
|
||||||
for (const std::pair<int, int>& i : done)
|
for (const std::pair<int, int>& i : done) {
|
||||||
{
|
|
||||||
ModelObject* m = m_model->objects[i.first];
|
ModelObject* m = m_model->objects[i.first];
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
double shift_z = m->get_instance_min_z(i.second);
|
||||||
|
// leave sinking instances as sinking
|
||||||
|
if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= 0.0 || shift_z > 0.0) {
|
||||||
|
Vec3d shift(0.0, 0.0, -shift_z);
|
||||||
|
#else
|
||||||
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
|
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
m_selection.translate(i.first, i.second, shift);
|
m_selection.translate(i.first, i.second, shift);
|
||||||
m->translate_instance(i.second, shift);
|
m->translate_instance(i.second, shift);
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!done.empty())
|
if (!done.empty())
|
||||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
@ -3950,16 +3704,13 @@ void GLCanvas3D::set_cursor(ECursorType type)
|
||||||
|
|
||||||
void GLCanvas3D::msw_rescale()
|
void GLCanvas3D::msw_rescale()
|
||||||
{
|
{
|
||||||
#if !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
m_warning_texture.msw_rescale(*this);
|
|
||||||
#endif // !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar()
|
void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar()
|
||||||
{
|
{
|
||||||
std::string new_tooltip = _u8L("Switch to Settings") +
|
std::string new_tooltip = _u8L("Switch to Settings") +
|
||||||
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") +
|
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") +
|
||||||
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) +
|
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) +
|
||||||
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ;
|
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ;
|
||||||
|
|
||||||
m_main_toolbar.set_tooltip(get_main_toolbar_item_id("settings"), new_tooltip);
|
m_main_toolbar.set_tooltip(get_main_toolbar_item_id("settings"), new_tooltip);
|
||||||
|
|
@ -3967,11 +3718,7 @@ void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar()
|
||||||
|
|
||||||
bool GLCanvas3D::has_toolpaths_to_export() const
|
bool GLCanvas3D::has_toolpaths_to_export() const
|
||||||
{
|
{
|
||||||
#if ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
return m_gcode_viewer.can_export_toolpaths();
|
return m_gcode_viewer.can_export_toolpaths();
|
||||||
#else
|
|
||||||
return m_gcode_viewer.has_data();
|
|
||||||
#endif // ENABLE_SPLITTED_VERTEX_BUFFER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const
|
void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const
|
||||||
|
|
@ -4113,7 +3860,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x)
|
||||||
ArrangeSettings &settings_out = get_arrange_settings();
|
ArrangeSettings &settings_out = get_arrange_settings();
|
||||||
|
|
||||||
auto &appcfg = wxGetApp().app_config;
|
auto &appcfg = wxGetApp().app_config;
|
||||||
PrinterTechnology ptech = m_process->current_printer_technology();
|
PrinterTechnology ptech = current_printer_technology();
|
||||||
|
|
||||||
bool settings_changed = false;
|
bool settings_changed = false;
|
||||||
float dist_min = 0.f;
|
float dist_min = 0.f;
|
||||||
|
|
@ -4680,7 +4427,7 @@ bool GLCanvas3D::_init_main_toolbar()
|
||||||
item.name = "settings";
|
item.name = "settings";
|
||||||
item.icon_filename = "settings.svg";
|
item.icon_filename = "settings.svg";
|
||||||
item.tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") +
|
item.tooltip = _u8L("Switch to Settings") + "\n" + "[" + GUI::shortkey_ctrl_prefix() + "2] - " + _u8L("Print Settings Tab") +
|
||||||
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (m_process->current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) +
|
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "3] - " + (current_printer_technology() == ptFFF ? _u8L("Filament Settings Tab") : _u8L("Material Settings Tab")) +
|
||||||
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ;
|
"\n" + "[" + GUI::shortkey_ctrl_prefix() + "4] - " + _u8L("Printer Settings Tab") ;
|
||||||
item.sprite_id = 10;
|
item.sprite_id = 10;
|
||||||
item.enabling_callback = GLToolbarItem::Default_Enabling_Callback;
|
item.enabling_callback = GLToolbarItem::Default_Enabling_Callback;
|
||||||
|
|
@ -4722,7 +4469,7 @@ bool GLCanvas3D::_init_main_toolbar()
|
||||||
item.sprite_id = 12;
|
item.sprite_id = 12;
|
||||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
|
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
|
||||||
item.visibility_callback = [this]()->bool {
|
item.visibility_callback = [this]()->bool {
|
||||||
bool res = m_process->current_printer_technology() == ptFFF;
|
bool res = current_printer_technology() == ptFFF;
|
||||||
// turns off if changing printer technology
|
// turns off if changing printer technology
|
||||||
if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting"))
|
if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting"))
|
||||||
force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting"));
|
force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting"));
|
||||||
|
|
@ -4870,7 +4617,7 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto *imgui = wxGetApp().imgui();
|
auto *imgui = wxGetApp().imgui();
|
||||||
imgui->set_display_size((float)w, (float)h);
|
imgui->set_display_size(static_cast<float>(w), static_cast<float>(h));
|
||||||
const float font_size = 1.5f * wxGetApp().em_unit();
|
const float font_size = 1.5f * wxGetApp().em_unit();
|
||||||
#if ENABLE_RETINA_GL
|
#if ENABLE_RETINA_GL
|
||||||
imgui->set_scaling(font_size, 1.0f, m_retina_helper->get_scale_factor());
|
imgui->set_scaling(font_size, 1.0f, m_retina_helper->get_scale_factor());
|
||||||
|
|
@ -4878,6 +4625,10 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
|
||||||
imgui->set_scaling(font_size, m_canvas->GetContentScaleFactor(), 1.0f);
|
imgui->set_scaling(font_size, m_canvas->GetContentScaleFactor(), 1.0f);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_SCROLLABLE_LEGEND
|
||||||
|
this->request_extra_frame();
|
||||||
|
#endif // ENABLE_SCROLLABLE_LEGEND
|
||||||
|
|
||||||
// ensures that this canvas is current
|
// ensures that this canvas is current
|
||||||
_set_current();
|
_set_current();
|
||||||
}
|
}
|
||||||
|
|
@ -4918,8 +4669,7 @@ void GLCanvas3D::_update_camera_zoom(double zoom)
|
||||||
|
|
||||||
void GLCanvas3D::_refresh_if_shown_on_screen()
|
void GLCanvas3D::_refresh_if_shown_on_screen()
|
||||||
{
|
{
|
||||||
if (_is_shown_on_screen())
|
if (_is_shown_on_screen()) {
|
||||||
{
|
|
||||||
const Size& cnv_size = get_canvas_size();
|
const Size& cnv_size = get_canvas_size();
|
||||||
_resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
|
_resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
|
||||||
|
|
||||||
|
|
@ -5304,9 +5054,6 @@ void GLCanvas3D::_render_overlays() const
|
||||||
_check_and_update_toolbar_icon_scale();
|
_check_and_update_toolbar_icon_scale();
|
||||||
|
|
||||||
_render_gizmos_overlay();
|
_render_gizmos_overlay();
|
||||||
#if !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
_render_warning_texture();
|
|
||||||
#endif // !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
|
|
||||||
// main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed
|
// main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed
|
||||||
// to correctly place them
|
// to correctly place them
|
||||||
|
|
@ -5344,13 +5091,6 @@ void GLCanvas3D::_render_overlays() const
|
||||||
glsafe(::glPopMatrix());
|
glsafe(::glPopMatrix());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
void GLCanvas3D::_render_warning_texture() const
|
|
||||||
{
|
|
||||||
m_warning_texture.render(*this);
|
|
||||||
}
|
|
||||||
#endif // !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
|
|
||||||
void GLCanvas3D::_render_volumes_for_picking() const
|
void GLCanvas3D::_render_volumes_for_picking() const
|
||||||
{
|
{
|
||||||
static const GLfloat INV_255 = 1.0f / 255.0f;
|
static const GLfloat INV_255 = 1.0f / 255.0f;
|
||||||
|
|
@ -6031,7 +5771,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
||||||
{
|
{
|
||||||
if (layerm->slices.surfaces.empty())
|
if (layerm->slices.surfaces.empty())
|
||||||
continue;
|
continue;
|
||||||
const PrintRegionConfig& cfg = layerm->region()->config();
|
const PrintRegionConfig& cfg = layerm->region().config();
|
||||||
if (cfg.perimeter_extruder.value == m_selected_extruder ||
|
if (cfg.perimeter_extruder.value == m_selected_extruder ||
|
||||||
cfg.infill_extruder.value == m_selected_extruder ||
|
cfg.infill_extruder.value == m_selected_extruder ||
|
||||||
cfg.solid_infill_extruder.value == m_selected_extruder ) {
|
cfg.solid_infill_extruder.value == m_selected_extruder ) {
|
||||||
|
|
@ -6054,7 +5794,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
||||||
for (const LayerRegion *layerm : layer->regions()) {
|
for (const LayerRegion *layerm : layer->regions()) {
|
||||||
if (is_selected_separate_extruder)
|
if (is_selected_separate_extruder)
|
||||||
{
|
{
|
||||||
const PrintRegionConfig& cfg = layerm->region()->config();
|
const PrintRegionConfig& cfg = layerm->region().config();
|
||||||
if (cfg.perimeter_extruder.value != m_selected_extruder ||
|
if (cfg.perimeter_extruder.value != m_selected_extruder ||
|
||||||
cfg.infill_extruder.value != m_selected_extruder ||
|
cfg.infill_extruder.value != m_selected_extruder ||
|
||||||
cfg.solid_infill_extruder.value != m_selected_extruder)
|
cfg.solid_infill_extruder.value != m_selected_extruder)
|
||||||
|
|
@ -6062,7 +5802,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
||||||
}
|
}
|
||||||
if (ctxt.has_perimeters)
|
if (ctxt.has_perimeters)
|
||||||
_3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
|
_3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
|
||||||
volume(idx_layer, layerm->region()->config().perimeter_extruder.value, 0));
|
volume(idx_layer, layerm->region().config().perimeter_extruder.value, 0));
|
||||||
if (ctxt.has_infill) {
|
if (ctxt.has_infill) {
|
||||||
for (const ExtrusionEntity *ee : layerm->fills.entities) {
|
for (const ExtrusionEntity *ee : layerm->fills.entities) {
|
||||||
// fill represents infill extrusions of a single island.
|
// fill represents infill extrusions of a single island.
|
||||||
|
|
@ -6071,8 +5811,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
|
||||||
_3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
|
_3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
|
||||||
volume(idx_layer,
|
volume(idx_layer,
|
||||||
is_solid_infill(fill->entities.front()->role()) ?
|
is_solid_infill(fill->entities.front()->role()) ?
|
||||||
layerm->region()->config().solid_infill_extruder :
|
layerm->region().config().solid_infill_extruder :
|
||||||
layerm->region()->config().infill_extruder,
|
layerm->region().config().infill_extruder,
|
||||||
1));
|
1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6297,7 +6037,7 @@ void GLCanvas3D::_load_sla_shells()
|
||||||
#else
|
#else
|
||||||
v.indexed_vertex_array.load_mesh(mesh);
|
v.indexed_vertex_array.load_mesh(mesh);
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
#endif // ENABLE_SMOOTH_NORMALS
|
||||||
v.indexed_vertex_array.finalize_geometry(this->m_initialized);
|
v.indexed_vertex_array.finalize_geometry(m_initialized);
|
||||||
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
|
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
|
||||||
v.composite_id.volume_id = volume_id;
|
v.composite_id.volume_id = volume_id;
|
||||||
v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0));
|
v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0));
|
||||||
|
|
@ -6347,7 +6087,6 @@ void GLCanvas3D::_update_sla_shells_outside_state()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
||||||
{
|
{
|
||||||
_set_current();
|
_set_current();
|
||||||
|
|
@ -6364,24 +6103,6 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
||||||
}
|
}
|
||||||
_set_warning_notification(warning, show);
|
_set_warning_notification(warning, show);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
void GLCanvas3D::_show_warning_texture_if_needed(WarningTexture::Warning warning)
|
|
||||||
{
|
|
||||||
_set_current();
|
|
||||||
bool show = false;
|
|
||||||
if (!m_volumes.empty())
|
|
||||||
show = _is_any_volume_outside();
|
|
||||||
else {
|
|
||||||
if (wxGetApp().is_editor()) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_set_warning_texture(warning, show);
|
|
||||||
}
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
|
|
||||||
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
|
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
|
||||||
{
|
{
|
||||||
|
|
@ -6405,7 +6126,6 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
||||||
{
|
{
|
||||||
enum ErrorType{
|
enum ErrorType{
|
||||||
|
|
@ -6451,12 +6171,6 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
void GLCanvas3D::_set_warning_texture(WarningTexture::Warning warning, bool state)
|
|
||||||
{
|
|
||||||
m_warning_texture.activate(warning, state, *this);
|
|
||||||
}
|
|
||||||
#endif // !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
|
|
||||||
bool GLCanvas3D::_is_any_volume_outside() const
|
bool GLCanvas3D::_is_any_volume_outside() const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -154,53 +154,50 @@ class GLCanvas3D
|
||||||
static const float THICKNESS_BAR_WIDTH;
|
static const float THICKNESS_BAR_WIDTH;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool m_enabled;
|
bool m_enabled{ false };
|
||||||
unsigned int m_z_texture_id;
|
unsigned int m_z_texture_id{ 0 };
|
||||||
// Not owned by LayersEditing.
|
// Not owned by LayersEditing.
|
||||||
const DynamicPrintConfig *m_config;
|
const DynamicPrintConfig *m_config{ nullptr };
|
||||||
// ModelObject for the currently selected object (Model::objects[last_object_id]).
|
// ModelObject for the currently selected object (Model::objects[last_object_id]).
|
||||||
const ModelObject *m_model_object;
|
const ModelObject *m_model_object{ nullptr };
|
||||||
// Maximum z of the currently selected object (Model::objects[last_object_id]).
|
// Maximum z of the currently selected object (Model::objects[last_object_id]).
|
||||||
float m_object_max_z;
|
float m_object_max_z{ 0.0f };
|
||||||
// Owned by LayersEditing.
|
// Owned by LayersEditing.
|
||||||
SlicingParameters *m_slicing_parameters;
|
SlicingParameters *m_slicing_parameters{ nullptr };
|
||||||
std::vector<double> m_layer_height_profile;
|
std::vector<double> m_layer_height_profile;
|
||||||
bool m_layer_height_profile_modified;
|
bool m_layer_height_profile_modified{ false };
|
||||||
|
|
||||||
mutable float m_adaptive_quality;
|
mutable float m_adaptive_quality{ 0.5f };
|
||||||
mutable HeightProfileSmoothingParams m_smooth_params;
|
mutable HeightProfileSmoothingParams m_smooth_params;
|
||||||
|
|
||||||
static float s_overelay_window_width;
|
static float s_overlay_window_width;
|
||||||
|
|
||||||
class LayersTexture
|
struct LayersTexture
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
LayersTexture() : width(0), height(0), levels(0), cells(0), valid(false) {}
|
|
||||||
|
|
||||||
// Texture data
|
// Texture data
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
// Width of the texture, top level.
|
// Width of the texture, top level.
|
||||||
size_t width;
|
size_t width{ 0 };
|
||||||
// Height of the texture, top level.
|
// Height of the texture, top level.
|
||||||
size_t height;
|
size_t height{ 0 };
|
||||||
// For how many levels of detail is the data allocated?
|
// For how many levels of detail is the data allocated?
|
||||||
size_t levels;
|
size_t levels{ 0 };
|
||||||
// Number of texture cells allocated for the height texture.
|
// Number of texture cells allocated for the height texture.
|
||||||
size_t cells;
|
size_t cells{ 0 };
|
||||||
// Does it need to be refreshed?
|
// Does it need to be refreshed?
|
||||||
bool valid;
|
bool valid{ false };
|
||||||
};
|
};
|
||||||
LayersTexture m_layers_texture;
|
LayersTexture m_layers_texture;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EState state;
|
EState state{ Unknown };
|
||||||
float band_width;
|
float band_width{ 2.0f };
|
||||||
float strength;
|
float strength{ 0.005f };
|
||||||
int last_object_id;
|
int last_object_id{ -1 };
|
||||||
float last_z;
|
float last_z{ 0.0f };
|
||||||
LayerHeightEditActionType last_action;
|
LayerHeightEditActionType last_action{ LAYER_HEIGHT_EDIT_ACTION_INCREASE };
|
||||||
|
|
||||||
LayersEditing();
|
LayersEditing() = default;
|
||||||
~LayersEditing();
|
~LayersEditing();
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
@ -226,7 +223,7 @@ class GLCanvas3D
|
||||||
static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
||||||
static Rect get_bar_rect_screen(const GLCanvas3D& canvas);
|
static Rect get_bar_rect_screen(const GLCanvas3D& canvas);
|
||||||
static Rect get_bar_rect_viewport(const GLCanvas3D& canvas);
|
static Rect get_bar_rect_viewport(const GLCanvas3D& canvas);
|
||||||
static float get_overlay_window_width() { return LayersEditing::s_overelay_window_width; }
|
static float get_overlay_window_width() { return LayersEditing::s_overlay_window_width; }
|
||||||
|
|
||||||
float object_max_z() const { return m_object_max_z; }
|
float object_max_z() const { return m_object_max_z; }
|
||||||
|
|
||||||
|
|
@ -298,7 +295,6 @@ class GLCanvas3D
|
||||||
bool matches(double z) const { return this->z == z; }
|
bool matches(double z) const { return this->z == z; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
enum class EWarning {
|
enum class EWarning {
|
||||||
ObjectOutside,
|
ObjectOutside,
|
||||||
ToolpathOutside,
|
ToolpathOutside,
|
||||||
|
|
@ -306,46 +302,6 @@ class GLCanvas3D
|
||||||
SomethingNotShown,
|
SomethingNotShown,
|
||||||
ObjectClashed
|
ObjectClashed
|
||||||
};
|
};
|
||||||
#else
|
|
||||||
class WarningTexture : public GUI::GLTexture
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WarningTexture();
|
|
||||||
|
|
||||||
enum Warning {
|
|
||||||
ObjectOutside,
|
|
||||||
ToolpathOutside,
|
|
||||||
SlaSupportsOutside,
|
|
||||||
SomethingNotShown,
|
|
||||||
ObjectClashed
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sets a warning of the given type to be active/inactive. If several warnings are active simultaneously,
|
|
||||||
// only the last one is shown (decided by the order in the enum above).
|
|
||||||
void activate(WarningTexture::Warning warning, bool state, const GLCanvas3D& canvas);
|
|
||||||
void render(const GLCanvas3D& canvas) const;
|
|
||||||
|
|
||||||
// function used to get an information for rescaling of the warning
|
|
||||||
void msw_rescale(const GLCanvas3D& canvas);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static const unsigned char Background_Color[3];
|
|
||||||
static const unsigned char Opacity;
|
|
||||||
|
|
||||||
int m_original_width;
|
|
||||||
int m_original_height;
|
|
||||||
|
|
||||||
// information for rescaling of the warning legend
|
|
||||||
std::string m_msg_text = "";
|
|
||||||
bool m_is_colored_red{false};
|
|
||||||
|
|
||||||
// Information about which warnings are currently active.
|
|
||||||
std::vector<Warning> m_warnings;
|
|
||||||
|
|
||||||
// Generates the texture with given text.
|
|
||||||
bool generate(const std::string& msg, const GLCanvas3D& canvas, bool compress, bool red_colored = false);
|
|
||||||
};
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
|
|
||||||
#if ENABLE_RENDER_STATISTICS
|
#if ENABLE_RENDER_STATISTICS
|
||||||
class RenderStats
|
class RenderStats
|
||||||
|
|
@ -401,7 +357,6 @@ class GLCanvas3D
|
||||||
{
|
{
|
||||||
bool m_enabled{ false };
|
bool m_enabled{ false };
|
||||||
GLVolumeCollection& m_volumes;
|
GLVolumeCollection& m_volumes;
|
||||||
static float s_window_width;
|
|
||||||
public:
|
public:
|
||||||
Slope(GLVolumeCollection& volumes) : m_volumes(volumes) {}
|
Slope(GLVolumeCollection& volumes) : m_volumes(volumes) {}
|
||||||
|
|
||||||
|
|
@ -412,7 +367,6 @@ class GLCanvas3D
|
||||||
void set_normal_angle(float angle_in_deg) const {
|
void set_normal_angle(float angle_in_deg) const {
|
||||||
m_volumes.set_slope_normal_z(-::cos(Geometry::deg2rad(90.0f - angle_in_deg)));
|
m_volumes.set_slope_normal_z(-::cos(Geometry::deg2rad(90.0f - angle_in_deg)));
|
||||||
}
|
}
|
||||||
static float get_window_width() { return s_window_width; };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RenderTimer : public wxTimer {
|
class RenderTimer : public wxTimer {
|
||||||
|
|
@ -443,9 +397,6 @@ private:
|
||||||
std::unique_ptr<RetinaHelper> m_retina_helper;
|
std::unique_ptr<RetinaHelper> m_retina_helper;
|
||||||
#endif
|
#endif
|
||||||
bool m_in_render;
|
bool m_in_render;
|
||||||
#if !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
WarningTexture m_warning_texture;
|
|
||||||
#endif // !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
wxTimer m_timer;
|
wxTimer m_timer;
|
||||||
LayersEditing m_layers_editing;
|
LayersEditing m_layers_editing;
|
||||||
Mouse m_mouse;
|
Mouse m_mouse;
|
||||||
|
|
@ -836,9 +787,6 @@ private:
|
||||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
void _check_and_update_toolbar_icon_scale() const;
|
void _check_and_update_toolbar_icon_scale() const;
|
||||||
void _render_overlays() const;
|
void _render_overlays() const;
|
||||||
#if !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
void _render_warning_texture() const;
|
|
||||||
#endif // !ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
void _render_volumes_for_picking() const;
|
void _render_volumes_for_picking() const;
|
||||||
void _render_current_gizmo() const;
|
void _render_current_gizmo() const;
|
||||||
void _render_gizmos_overlay() const;
|
void _render_gizmos_overlay() const;
|
||||||
|
|
@ -891,17 +839,10 @@ private:
|
||||||
void _load_sla_shells();
|
void _load_sla_shells();
|
||||||
void _update_toolpath_volumes_outside_state();
|
void _update_toolpath_volumes_outside_state();
|
||||||
void _update_sla_shells_outside_state();
|
void _update_sla_shells_outside_state();
|
||||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
void _set_warning_notification_if_needed(EWarning warning);
|
void _set_warning_notification_if_needed(EWarning warning);
|
||||||
|
|
||||||
// generates a warning notification containing the given message
|
// generates a warning notification containing the given message
|
||||||
void _set_warning_notification(EWarning warning, bool state);
|
void _set_warning_notification(EWarning warning, bool state);
|
||||||
#else
|
|
||||||
void _show_warning_texture_if_needed(WarningTexture::Warning warning);
|
|
||||||
|
|
||||||
// generates a warning texture containing the given message
|
|
||||||
void _set_warning_texture(WarningTexture::Warning warning, bool state);
|
|
||||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
|
||||||
|
|
||||||
bool _is_any_volume_outside() const;
|
bool _is_any_volume_outside() const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
@ -68,6 +69,7 @@
|
||||||
#include "UnsavedChangesDialog.hpp"
|
#include "UnsavedChangesDialog.hpp"
|
||||||
#include "SavePresetDialog.hpp"
|
#include "SavePresetDialog.hpp"
|
||||||
#include "PrintHostDialogs.hpp"
|
#include "PrintHostDialogs.hpp"
|
||||||
|
#include "DesktopIntegrationDialog.hpp"
|
||||||
|
|
||||||
#include "BitmapCache.hpp"
|
#include "BitmapCache.hpp"
|
||||||
|
|
||||||
|
|
@ -633,8 +635,17 @@ void GUI_App::post_init()
|
||||||
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
|
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
|
||||||
this->mainframe->load_config_file(this->init_params->load_configs.back());
|
this->mainframe->load_config_file(this->init_params->load_configs.back());
|
||||||
// If loading a 3MF file, the config is loaded from the last one.
|
// If loading a 3MF file, the config is loaded from the last one.
|
||||||
if (! this->init_params->input_files.empty())
|
if (!this->init_params->input_files.empty()) {
|
||||||
this->plater()->load_files(this->init_params->input_files, true, true);
|
const std::vector<size_t> res = this->plater()->load_files(this->init_params->input_files, true, true);
|
||||||
|
if (!res.empty() && this->init_params->input_files.size() == 1) {
|
||||||
|
// Update application titlebar when opening a project file
|
||||||
|
const std::string& filename = this->init_params->input_files.front();
|
||||||
|
if (boost::algorithm::iends_with(filename, ".amf") ||
|
||||||
|
boost::algorithm::iends_with(filename, ".amf.xml") ||
|
||||||
|
boost::algorithm::iends_with(filename, ".3mf"))
|
||||||
|
this->plater()->set_project_filename(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (! this->init_params->extra_config.empty())
|
if (! this->init_params->extra_config.empty())
|
||||||
this->mainframe->load_config(this->init_params->extra_config);
|
this->mainframe->load_config(this->init_params->extra_config);
|
||||||
}
|
}
|
||||||
|
|
@ -905,6 +916,14 @@ bool GUI_App::on_init_inner()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
load_current_presets();
|
load_current_presets();
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (plater_ != nullptr) {
|
||||||
|
plater_->reset_project_dirty_initial_presets();
|
||||||
|
plater_->update_project_dirty_from_presets();
|
||||||
|
}
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
mainframe->Show(true);
|
mainframe->Show(true);
|
||||||
|
|
||||||
obj_list()->set_min_height();
|
obj_list()->set_min_height();
|
||||||
|
|
@ -1624,6 +1643,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||||
local_menu->Append(config_id_base + ConfigMenuSnapshots, _L("&Configuration Snapshots") + dots, _L("Inspect / activate configuration snapshots"));
|
local_menu->Append(config_id_base + ConfigMenuSnapshots, _L("&Configuration Snapshots") + dots, _L("Inspect / activate configuration snapshots"));
|
||||||
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot"));
|
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot"));
|
||||||
local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for updates"), _L("Check for configuration updates"));
|
local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for updates"), _L("Check for configuration updates"));
|
||||||
|
#ifdef __linux__
|
||||||
|
if (DesktopIntegrationDialog::integration_possible())
|
||||||
|
local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration"));
|
||||||
|
#endif
|
||||||
local_menu->AppendSeparator();
|
local_menu->AppendSeparator();
|
||||||
}
|
}
|
||||||
local_menu->Append(config_id_base + ConfigMenuPreferences, _L("&Preferences") + dots +
|
local_menu->Append(config_id_base + ConfigMenuPreferences, _L("&Preferences") + dots +
|
||||||
|
|
@ -1664,9 +1687,18 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||||
case ConfigMenuUpdate:
|
case ConfigMenuUpdate:
|
||||||
check_updates(true);
|
check_updates(true);
|
||||||
break;
|
break;
|
||||||
|
#ifdef __linux__
|
||||||
|
case ConfigMenuDesktopIntegration:
|
||||||
|
show_desktop_integration_dialog();
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case ConfigMenuTakeSnapshot:
|
case ConfigMenuTakeSnapshot:
|
||||||
// Take a configuration snapshot.
|
// Take a configuration snapshot.
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (check_and_save_current_preset_changes()) {
|
||||||
|
#else
|
||||||
if (check_unsaved_changes()) {
|
if (check_unsaved_changes()) {
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
wxTextEntryDialog dlg(nullptr, _L("Taking configuration snapshot"), _L("Snapshot name"));
|
wxTextEntryDialog dlg(nullptr, _L("Taking configuration snapshot"), _L("Snapshot name"));
|
||||||
|
|
||||||
// set current normal font for dialog children,
|
// set current normal font for dialog children,
|
||||||
|
|
@ -1681,7 +1713,11 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ConfigMenuSnapshots:
|
case ConfigMenuSnapshots:
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (check_and_save_current_preset_changes()) {
|
||||||
|
#else
|
||||||
if (check_unsaved_changes()) {
|
if (check_unsaved_changes()) {
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
std::string on_snapshot;
|
std::string on_snapshot;
|
||||||
if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config))
|
if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config))
|
||||||
on_snapshot = app_config->get("on_snapshot");
|
on_snapshot = app_config->get("on_snapshot");
|
||||||
|
|
@ -1782,8 +1818,57 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||||
menu->Append(local_menu, _L("&Configuration"));
|
menu->Append(local_menu, _L("&Configuration"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool GUI_App::has_unsaved_preset_changes() const
|
||||||
|
{
|
||||||
|
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
|
||||||
|
for (const Tab* const tab : tabs_list) {
|
||||||
|
if (tab->supports_printer_technology(printer_technology) && tab->saved_preset_is_dirty())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GUI_App::has_current_preset_changes() const
|
||||||
|
{
|
||||||
|
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
|
||||||
|
for (const Tab* const tab : tabs_list) {
|
||||||
|
if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI_App::update_saved_preset_from_current_preset()
|
||||||
|
{
|
||||||
|
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
|
||||||
|
for (Tab* tab : tabs_list) {
|
||||||
|
if (tab->supports_printer_technology(printer_technology))
|
||||||
|
tab->update_saved_preset_from_current_preset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<unsigned int, std::string>> GUI_App::get_selected_presets() const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<unsigned int, std::string>> ret;
|
||||||
|
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
|
||||||
|
for (Tab* tab : tabs_list) {
|
||||||
|
if (tab->supports_printer_technology(printer_technology)) {
|
||||||
|
const PresetCollection* presets = tab->get_presets();
|
||||||
|
ret.push_back({ static_cast<unsigned int>(presets->type()), presets->get_selected_preset_name() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
// This is called when closing the application, when loading a config file or when starting the config wizard
|
// This is called when closing the application, when loading a config file or when starting the config wizard
|
||||||
// to notify the user whether he is aware that some preset changes will be lost.
|
// to notify the user whether he is aware that some preset changes will be lost.
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool GUI_App::check_and_save_current_preset_changes(const wxString& header)
|
||||||
|
{
|
||||||
|
if (this->plater()->model().objects.empty() && has_current_preset_changes()) {
|
||||||
|
#else
|
||||||
bool GUI_App::check_unsaved_changes(const wxString &header)
|
bool GUI_App::check_unsaved_changes(const wxString &header)
|
||||||
{
|
{
|
||||||
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
|
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
|
||||||
|
|
@ -1795,8 +1880,8 @@ bool GUI_App::check_unsaved_changes(const wxString &header)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_unsaved_changes)
|
if (has_unsaved_changes) {
|
||||||
{
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
UnsavedChangesDialog dlg(header);
|
UnsavedChangesDialog dlg(header);
|
||||||
if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL)
|
if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2056,6 +2141,15 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GUI_App::show_desktop_integration_dialog()
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
//wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
|
||||||
|
DesktopIntegrationDialog dialog(mainframe);
|
||||||
|
dialog.ShowModal();
|
||||||
|
#endif //__linux__
|
||||||
|
}
|
||||||
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
||||||
void GUI_App::gcode_thumbnails_debug()
|
void GUI_App::gcode_thumbnails_debug()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ enum ConfigMenuIDs {
|
||||||
ConfigMenuSnapshots,
|
ConfigMenuSnapshots,
|
||||||
ConfigMenuTakeSnapshot,
|
ConfigMenuTakeSnapshot,
|
||||||
ConfigMenuUpdate,
|
ConfigMenuUpdate,
|
||||||
|
ConfigMenuDesktopIntegration,
|
||||||
ConfigMenuPreferences,
|
ConfigMenuPreferences,
|
||||||
ConfigMenuModeSimple,
|
ConfigMenuModeSimple,
|
||||||
ConfigMenuModeAdvanced,
|
ConfigMenuModeAdvanced,
|
||||||
|
|
@ -209,7 +210,15 @@ public:
|
||||||
void update_mode();
|
void update_mode();
|
||||||
|
|
||||||
void add_config_menu(wxMenuBar *menu);
|
void add_config_menu(wxMenuBar *menu);
|
||||||
bool check_unsaved_changes(const wxString &header = wxString());
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool has_unsaved_preset_changes() const;
|
||||||
|
bool has_current_preset_changes() const;
|
||||||
|
void update_saved_preset_from_current_preset();
|
||||||
|
std::vector<std::pair<unsigned int, std::string>> get_selected_presets() const;
|
||||||
|
bool check_and_save_current_preset_changes(const wxString& header = wxString());
|
||||||
|
#else
|
||||||
|
bool check_unsaved_changes(const wxString& header = wxString());
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
bool check_print_host_queue();
|
bool check_print_host_queue();
|
||||||
bool checked_tab(Tab* tab);
|
bool checked_tab(Tab* tab);
|
||||||
void load_current_presets(bool check_printer_presets = true);
|
void load_current_presets(bool check_printer_presets = true);
|
||||||
|
|
@ -268,6 +277,7 @@ public:
|
||||||
|
|
||||||
void open_web_page_localized(const std::string &http_address);
|
void open_web_page_localized(const std::string &http_address);
|
||||||
bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME);
|
bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME);
|
||||||
|
void show_desktop_integration_dialog();
|
||||||
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
|
||||||
// temporary and debug only -> extract thumbnails from selected gcode and save them as png files
|
// temporary and debug only -> extract thumbnails from selected gcode and save them as png files
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
sizer->Add(editor);
|
sizer->Add(editor);
|
||||||
|
|
||||||
auto temp = new wxStaticText(m_parent, wxID_ANY, _(L("mm")));
|
auto temp = new wxStaticText(m_parent, wxID_ANY, _L("mm"));
|
||||||
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||||
temp->SetFont(wxGetApp().normal_font());
|
temp->SetFont(wxGetApp().normal_font());
|
||||||
sizer->Add(temp, 0, wxLEFT, wxGetApp().em_unit());
|
sizer->Add(temp, 0, wxLEFT, wxGetApp().em_unit());
|
||||||
|
|
@ -154,15 +154,14 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
|
||||||
|
|
||||||
void ObjectLayers::create_layers_list()
|
void ObjectLayers::create_layers_list()
|
||||||
{
|
{
|
||||||
for (const auto &layer : m_object->layer_config_ranges)
|
for (const auto &layer : m_object->layer_config_ranges) {
|
||||||
{
|
|
||||||
const t_layer_height_range& range = layer.first;
|
const t_layer_height_range& range = layer.first;
|
||||||
auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range);
|
auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range);
|
||||||
del_btn->SetToolTip(_(L("Remove layer range")));
|
del_btn->SetToolTip(_L("Remove layer range"));
|
||||||
|
|
||||||
auto add_btn = new PlusMinusButton(m_parent, m_bmp_add, range);
|
auto add_btn = new PlusMinusButton(m_parent, m_bmp_add, range);
|
||||||
wxString tooltip = wxGetApp().obj_list()->can_add_new_range_after_current(range);
|
wxString tooltip = wxGetApp().obj_list()->can_add_new_range_after_current(range);
|
||||||
add_btn->SetToolTip(tooltip.IsEmpty() ? _(L("Add layer range")) : tooltip);
|
add_btn->SetToolTip(tooltip.IsEmpty() ? _L("Add layer range") : tooltip);
|
||||||
add_btn->Enable(tooltip.IsEmpty());
|
add_btn->Enable(tooltip.IsEmpty());
|
||||||
|
|
||||||
auto sizer = create_layer(range, del_btn, add_btn);
|
auto sizer = create_layer(range, del_btn, add_btn);
|
||||||
|
|
@ -242,11 +241,9 @@ void ObjectLayers::msw_rescale()
|
||||||
|
|
||||||
// rescale edit-boxes
|
// rescale edit-boxes
|
||||||
const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount();
|
const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount();
|
||||||
for (int i = 0; i < cells_cnt; i++)
|
for (int i = 0; i < cells_cnt; ++i) {
|
||||||
{
|
|
||||||
const wxSizerItem* item = m_grid_sizer->GetItem(i);
|
const wxSizerItem* item = m_grid_sizer->GetItem(i);
|
||||||
if (item->IsWindow())
|
if (item->IsWindow()) {
|
||||||
{
|
|
||||||
LayerRangeEditor* editor = dynamic_cast<LayerRangeEditor*>(item->GetWindow());
|
LayerRangeEditor* editor = dynamic_cast<LayerRangeEditor*>(item->GetWindow());
|
||||||
if (editor != nullptr)
|
if (editor != nullptr)
|
||||||
editor->msw_rescale();
|
editor->msw_rescale();
|
||||||
|
|
@ -283,8 +280,7 @@ void ObjectLayers::sys_color_changed()
|
||||||
|
|
||||||
// rescale edit-boxes
|
// rescale edit-boxes
|
||||||
const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount();
|
const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount();
|
||||||
for (int i = 0; i < cells_cnt; i++)
|
for (int i = 0; i < cells_cnt; ++i) {
|
||||||
{
|
|
||||||
const wxSizerItem* item = m_grid_sizer->GetItem(i);
|
const wxSizerItem* item = m_grid_sizer->GetItem(i);
|
||||||
if (item->IsSizer()) {// case when we have editor with buttons
|
if (item->IsSizer()) {// case when we have editor with buttons
|
||||||
const std::vector<size_t> btns = {2, 3}; // del_btn, add_btn
|
const std::vector<size_t> btns = {2, 3}; // del_btn, add_btn
|
||||||
|
|
@ -405,11 +401,9 @@ coordf_t LayerRangeEditor::get_value()
|
||||||
str.Replace(",", ".", false);
|
str.Replace(",", ".", false);
|
||||||
if (str == ".")
|
if (str == ".")
|
||||||
layer_height = 0.0;
|
layer_height = 0.0;
|
||||||
else
|
else {
|
||||||
{
|
if (!str.ToCDouble(&layer_height) || layer_height < 0.0f) {
|
||||||
if (!str.ToCDouble(&layer_height) || layer_height < 0.0f)
|
show_error(m_parent, _L("Invalid numeric input."));
|
||||||
{
|
|
||||||
show_error(m_parent, _(L("Invalid numeric input.")));
|
|
||||||
SetValue(double_to_string(layer_height));
|
SetValue(double_to_string(layer_height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
#include "MainFrame.hpp"
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
#include "OptionsGroup.hpp"
|
#include "OptionsGroup.hpp"
|
||||||
#include "Tab.hpp"
|
#include "Tab.hpp"
|
||||||
|
|
@ -18,6 +21,7 @@
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <wx/progdlg.h>
|
#include <wx/progdlg.h>
|
||||||
|
#include <wx/numformatter.h>
|
||||||
|
|
||||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
#include "slic3r/Utils/FixModelByWin10.hpp"
|
||||||
|
|
||||||
|
|
@ -143,18 +147,28 @@ ObjectList::ObjectList(wxWindow* parent) :
|
||||||
// Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this);
|
// Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this);
|
||||||
{
|
{
|
||||||
// Accelerators
|
// Accelerators
|
||||||
wxAcceleratorEntry entries[10];
|
wxAcceleratorEntry entries[33];
|
||||||
entries[0].Set(wxACCEL_CTRL, (int) 'C', wxID_COPY);
|
entries[0].Set(wxACCEL_CTRL, (int)'C', wxID_COPY);
|
||||||
entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_CUT);
|
entries[1].Set(wxACCEL_CTRL, (int)'X', wxID_CUT);
|
||||||
entries[2].Set(wxACCEL_CTRL, (int) 'V', wxID_PASTE);
|
entries[2].Set(wxACCEL_CTRL, (int)'V', wxID_PASTE);
|
||||||
entries[3].Set(wxACCEL_CTRL, (int) 'A', wxID_SELECTALL);
|
entries[3].Set(wxACCEL_CTRL, (int)'A', wxID_SELECTALL);
|
||||||
entries[4].Set(wxACCEL_CTRL, (int) 'Z', wxID_UNDO);
|
entries[4].Set(wxACCEL_CTRL, (int)'Z', wxID_UNDO);
|
||||||
entries[5].Set(wxACCEL_CTRL, (int) 'Y', wxID_REDO);
|
entries[5].Set(wxACCEL_CTRL, (int)'Y', wxID_REDO);
|
||||||
entries[6].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
|
entries[6].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
|
||||||
entries[7].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE);
|
entries[7].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE);
|
||||||
entries[8].Set(wxACCEL_NORMAL, int('+'), wxID_ADD);
|
entries[8].Set(wxACCEL_NORMAL, int('+'), wxID_ADD);
|
||||||
entries[9].Set(wxACCEL_NORMAL, int('-'), wxID_REMOVE);
|
entries[9].Set(wxACCEL_NORMAL, WXK_NUMPAD_ADD, wxID_ADD);
|
||||||
wxAcceleratorTable accel(10, entries);
|
entries[10].Set(wxACCEL_NORMAL, int('-'), wxID_REMOVE);
|
||||||
|
entries[11].Set(wxACCEL_NORMAL, WXK_NUMPAD_SUBTRACT, wxID_REMOVE);
|
||||||
|
entries[12].Set(wxACCEL_NORMAL, int('p'), wxID_PRINT);
|
||||||
|
|
||||||
|
int numbers_cnt = 1;
|
||||||
|
for (auto char_number : { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }) {
|
||||||
|
entries[12 + numbers_cnt].Set(wxACCEL_NORMAL, int(char_number), wxID_LAST + numbers_cnt);
|
||||||
|
entries[22 + numbers_cnt].Set(wxACCEL_NORMAL, WXK_NUMPAD0 + numbers_cnt - 1, wxID_LAST + numbers_cnt);
|
||||||
|
numbers_cnt++;
|
||||||
|
}
|
||||||
|
wxAcceleratorTable accel(33, entries);
|
||||||
SetAcceleratorTable(accel);
|
SetAcceleratorTable(accel);
|
||||||
|
|
||||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY);
|
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY);
|
||||||
|
|
@ -165,6 +179,13 @@ ObjectList::ObjectList(wxWindow* parent) :
|
||||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->redo(); }, wxID_REDO);
|
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->redo(); }, wxID_REDO);
|
||||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->increase_instances(); }, wxID_ADD);
|
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->increase_instances(); }, wxID_ADD);
|
||||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->decrease_instances(); }, wxID_REMOVE);
|
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->decrease_instances(); }, wxID_REMOVE);
|
||||||
|
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->toggle_printable_state(); }, wxID_PRINT);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
this->Bind(wxEVT_MENU, [this, i](wxCommandEvent &evt) {
|
||||||
|
if (extruders_count() > 1 && i <= extruders_count())
|
||||||
|
this->set_extruder_for_selected_items(i);
|
||||||
|
}, wxID_LAST+i+1);
|
||||||
}
|
}
|
||||||
#else //__WXOSX__
|
#else //__WXOSX__
|
||||||
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
|
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
|
||||||
|
|
@ -1031,6 +1052,20 @@ void ObjectList::key_event(wxKeyEvent& event)
|
||||||
increase_instances();
|
increase_instances();
|
||||||
else if (event.GetUnicodeKey() == '-')
|
else if (event.GetUnicodeKey() == '-')
|
||||||
decrease_instances();
|
decrease_instances();
|
||||||
|
else if (event.GetUnicodeKey() == 'p')
|
||||||
|
toggle_printable_state();
|
||||||
|
else if (extruders_count() > 1) {
|
||||||
|
std::vector<wxChar> numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||||
|
wxChar key_char = event.GetUnicodeKey();
|
||||||
|
if (std::find(numbers.begin(), numbers.end(), key_char) != numbers.end()) {
|
||||||
|
long extruder_number;
|
||||||
|
if (wxNumberFormatter::FromString(wxString(key_char), &extruder_number) &&
|
||||||
|
extruders_count() >= extruder_number)
|
||||||
|
set_extruder_for_selected_items(int(extruder_number));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
|
@ -1457,12 +1492,15 @@ void ObjectList::load_shape_object(const std::string& type_name)
|
||||||
if (obj_idx < 0)
|
if (obj_idx < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
take_snapshot(_(L("Add Shape")));
|
take_snapshot(_L("Add Shape"));
|
||||||
|
|
||||||
// Create mesh
|
// Create mesh
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
TriangleMesh mesh = create_mesh(type_name, bb);
|
TriangleMesh mesh = create_mesh(type_name, bb);
|
||||||
load_mesh_object(mesh, _(L("Shape")) + "-" + _(type_name));
|
load_mesh_object(mesh, _L("Shape") + "-" + _(type_name));
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
wxGetApp().mainframe->update_title();
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center)
|
void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center)
|
||||||
|
|
@ -2113,18 +2151,15 @@ void ObjectList::part_selection_changed()
|
||||||
|
|
||||||
const auto item = GetSelection();
|
const auto item = GetSelection();
|
||||||
|
|
||||||
if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot ))
|
if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) {
|
||||||
{
|
og_name = _L("Group manipulation");
|
||||||
og_name = _(L("Group manipulation"));
|
|
||||||
|
|
||||||
const Selection& selection = scene_selection();
|
const Selection& selection = scene_selection();
|
||||||
// don't show manipulation panel for case of all Object's parts selection
|
// don't show manipulation panel for case of all Object's parts selection
|
||||||
update_and_show_manipulations = !selection.is_single_full_instance();
|
update_and_show_manipulations = !selection.is_single_full_instance();
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
if (item) {
|
||||||
if (item)
|
|
||||||
{
|
|
||||||
const ItemType type = m_objects_model->GetItemType(item);
|
const ItemType type = m_objects_model->GetItemType(item);
|
||||||
const wxDataViewItem parent = m_objects_model->GetParent(item);
|
const wxDataViewItem parent = m_objects_model->GetParent(item);
|
||||||
const ItemType parent_type = m_objects_model->GetItemType(parent);
|
const ItemType parent_type = m_objects_model->GetItemType(parent);
|
||||||
|
|
@ -2132,7 +2167,7 @@ void ObjectList::part_selection_changed()
|
||||||
|
|
||||||
if (parent == wxDataViewItem(nullptr)
|
if (parent == wxDataViewItem(nullptr)
|
||||||
|| type == itInfo) {
|
|| type == itInfo) {
|
||||||
og_name = _(L("Object manipulation"));
|
og_name = _L("Object manipulation");
|
||||||
m_config = &(*m_objects)[obj_idx]->config;
|
m_config = &(*m_objects)[obj_idx]->config;
|
||||||
update_and_show_manipulations = true;
|
update_and_show_manipulations = true;
|
||||||
|
|
||||||
|
|
@ -2152,35 +2187,35 @@ void ObjectList::part_selection_changed()
|
||||||
else {
|
else {
|
||||||
if (type & itSettings) {
|
if (type & itSettings) {
|
||||||
if (parent_type & itObject) {
|
if (parent_type & itObject) {
|
||||||
og_name = _(L("Object Settings to modify"));
|
og_name = _L("Object Settings to modify");
|
||||||
m_config = &(*m_objects)[obj_idx]->config;
|
m_config = &(*m_objects)[obj_idx]->config;
|
||||||
}
|
}
|
||||||
else if (parent_type & itVolume) {
|
else if (parent_type & itVolume) {
|
||||||
og_name = _(L("Part Settings to modify"));
|
og_name = _L("Part Settings to modify");
|
||||||
volume_id = m_objects_model->GetVolumeIdByItem(parent);
|
volume_id = m_objects_model->GetVolumeIdByItem(parent);
|
||||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
||||||
}
|
}
|
||||||
else if (parent_type & itLayer) {
|
else if (parent_type & itLayer) {
|
||||||
og_name = _(L("Layer range Settings to modify"));
|
og_name = _L("Layer range Settings to modify");
|
||||||
m_config = &get_item_config(parent);
|
m_config = &get_item_config(parent);
|
||||||
}
|
}
|
||||||
update_and_show_settings = true;
|
update_and_show_settings = true;
|
||||||
}
|
}
|
||||||
else if (type & itVolume) {
|
else if (type & itVolume) {
|
||||||
og_name = _(L("Part manipulation"));
|
og_name = _L("Part manipulation");
|
||||||
volume_id = m_objects_model->GetVolumeIdByItem(item);
|
volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
||||||
update_and_show_manipulations = true;
|
update_and_show_manipulations = true;
|
||||||
}
|
}
|
||||||
else if (type & itInstance) {
|
else if (type & itInstance) {
|
||||||
og_name = _(L("Instance manipulation"));
|
og_name = _L("Instance manipulation");
|
||||||
update_and_show_manipulations = true;
|
update_and_show_manipulations = true;
|
||||||
|
|
||||||
// fill m_config by object's values
|
// fill m_config by object's values
|
||||||
m_config = &(*m_objects)[obj_idx]->config;
|
m_config = &(*m_objects)[obj_idx]->config;
|
||||||
}
|
}
|
||||||
else if (type & (itLayerRoot|itLayer)) {
|
else if (type & (itLayerRoot|itLayer)) {
|
||||||
og_name = type & itLayerRoot ? _(L("Height ranges")) : _(L("Settings for height range"));
|
og_name = type & itLayerRoot ? _L("Height ranges") : _L("Settings for height range");
|
||||||
update_and_show_layers = true;
|
update_and_show_layers = true;
|
||||||
|
|
||||||
if (type & itLayer)
|
if (type & itLayer)
|
||||||
|
|
@ -2809,7 +2844,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay
|
||||||
const int obj_idx = m_selected_object_id;
|
const int obj_idx = m_selected_object_id;
|
||||||
if (obj_idx < 0) return false;
|
if (obj_idx < 0) return false;
|
||||||
|
|
||||||
take_snapshot(_(L("Edit Height Range")));
|
take_snapshot(_L("Edit Height Range"));
|
||||||
|
|
||||||
const ItemType sel_type = m_objects_model->GetItemType(GetSelection());
|
const ItemType sel_type = m_objects_model->GetItemType(GetSelection());
|
||||||
|
|
||||||
|
|
@ -3787,33 +3822,6 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event)
|
||||||
plater->set_current_canvas_as_dirty();
|
plater->set_current_canvas_as_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::extruder_selection()
|
|
||||||
{
|
|
||||||
wxArrayString choices;
|
|
||||||
choices.Add(_(L("default")));
|
|
||||||
for (int i = 1; i <= extruders_count(); ++i)
|
|
||||||
choices.Add(wxString::Format("%d", i));
|
|
||||||
|
|
||||||
const wxString& selected_extruder = wxGetSingleChoice(_(L("Select extruder number:")),
|
|
||||||
_(L("This extruder will be set for selected items")),
|
|
||||||
choices, 0, this);
|
|
||||||
if (selected_extruder.IsEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const int extruder_num = selected_extruder == _(L("default")) ? 0 : atoi(selected_extruder.c_str());
|
|
||||||
|
|
||||||
// /* Another variant for an extruder selection */
|
|
||||||
// extruder_num = wxGetNumberFromUser(_(L("Attention!!! \n"
|
|
||||||
// "It's a possibile to set an extruder number \n"
|
|
||||||
// "for whole Object(s) and/or object Part(s), \n"
|
|
||||||
// "not for an Instance. ")),
|
|
||||||
// _(L("Enter extruder number:")),
|
|
||||||
// _(L("This extruder will be set for selected items")),
|
|
||||||
// 1, 1, 5, this);
|
|
||||||
|
|
||||||
set_extruder_for_selected_items(extruder_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectList::set_extruder_for_selected_items(const int extruder) const
|
void ObjectList::set_extruder_for_selected_items(const int extruder) const
|
||||||
{
|
{
|
||||||
wxDataViewItemArray sels;
|
wxDataViewItemArray sels;
|
||||||
|
|
@ -3906,6 +3914,8 @@ void ObjectList::toggle_printable_state()
|
||||||
{
|
{
|
||||||
wxDataViewItemArray sels;
|
wxDataViewItemArray sels;
|
||||||
GetSelections(sels);
|
GetSelections(sels);
|
||||||
|
if (sels.IsEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
wxDataViewItem frst_item = sels[0];
|
wxDataViewItem frst_item = sels[0];
|
||||||
|
|
||||||
|
|
@ -3918,10 +3928,10 @@ void ObjectList::toggle_printable_state()
|
||||||
int inst_idx = type == itObject ? 0 : m_objects_model->GetInstanceIdByItem(frst_item);
|
int inst_idx = type == itObject ? 0 : m_objects_model->GetInstanceIdByItem(frst_item);
|
||||||
bool printable = !object(obj_idx)->instances[inst_idx]->printable;
|
bool printable = !object(obj_idx)->instances[inst_idx]->printable;
|
||||||
|
|
||||||
const wxString snapshot_text = sels.Count() > 1 ? (printable ? _L("Set Printable group") : _L("Set Unprintable group")) :
|
const wxString snapshot_text = sels.Count() > 1 ?
|
||||||
object(obj_idx)->instances.size() == 1 ? from_u8((boost::format("%1% %2%")
|
(printable ? _L("Set Printable group") : _L("Set Unprintable group")) :
|
||||||
% (printable ? _L("Set Printable") : _L("Set Unprintable"))
|
object(obj_idx)->instances.size() == 1 ?
|
||||||
% object(obj_idx)->name).str()) :
|
format_wxstr("%1% %2%", (printable ? _L("Set Printable") : _L("Set Unprintable")), from_u8(object(obj_idx)->name)) :
|
||||||
(printable ? _L("Set Printable Instance") : _L("Set Unprintable Instance"));
|
(printable ? _L("Set Printable Instance") : _L("Set Unprintable Instance"));
|
||||||
take_snapshot(snapshot_text);
|
take_snapshot(snapshot_text);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -390,7 +390,6 @@ private:
|
||||||
void OnEditingStarted(wxDataViewEvent &event);
|
void OnEditingStarted(wxDataViewEvent &event);
|
||||||
#endif /* __WXMSW__ */
|
#endif /* __WXMSW__ */
|
||||||
void OnEditingDone(wxDataViewEvent &event);
|
void OnEditingDone(wxDataViewEvent &event);
|
||||||
void extruder_selection();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,26 @@ const double ObjectManipulation::mm_to_in = 0.0393700787;
|
||||||
|
|
||||||
// Helper function to be used by drop to bed button. Returns lowest point of this
|
// Helper function to be used by drop to bed button. Returns lowest point of this
|
||||||
// volume in world coordinate system.
|
// volume in world coordinate system.
|
||||||
static double get_volume_min_z(const GLVolume* volume)
|
static double get_volume_min_z(const GLVolume& volume)
|
||||||
{
|
{
|
||||||
const Transform3f& world_matrix = volume->world_matrix().cast<float>();
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
return volume.transformed_convex_hull_bounding_box().min.z();
|
||||||
|
#else
|
||||||
|
const Transform3f& world_matrix = volume.world_matrix().cast<float>();
|
||||||
|
|
||||||
// need to get the ModelVolume pointer
|
// need to get the ModelVolume pointer
|
||||||
const ModelObject* mo = wxGetApp().model().objects[volume->composite_id.object_id];
|
const ModelObject* mo = wxGetApp().model().objects[volume.composite_id.object_id];
|
||||||
const ModelVolume* mv = mo->volumes[volume->composite_id.volume_id];
|
const ModelVolume* mv = mo->volumes[volume.composite_id.volume_id];
|
||||||
const TriangleMesh& hull = mv->get_convex_hull();
|
const TriangleMesh& hull = mv->get_convex_hull();
|
||||||
|
|
||||||
float min_z = std::numeric_limits<float>::max();
|
float min_z = std::numeric_limits<float>::max();
|
||||||
for (const stl_facet& facet : hull.stl.facet_start) {
|
for (const stl_facet& facet : hull.stl.facet_start) {
|
||||||
for (int i = 0; i < 3; ++ i)
|
for (int i = 0; i < 3; ++i)
|
||||||
min_z = std::min(min_z, Vec3f::UnitZ().dot(world_matrix * facet.vertex[i]));
|
min_z = std::min(min_z, Vec3f::UnitZ().dot(world_matrix * facet.vertex[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return min_z;
|
return min_z;
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -341,13 +346,27 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||||
|
|
||||||
const Geometry::Transformation& instance_trafo = volume->get_instance_transformation();
|
const Geometry::Transformation& instance_trafo = volume->get_instance_transformation();
|
||||||
Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume));
|
const Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(*volume));
|
||||||
|
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed"));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed"));
|
||||||
change_position_value(0, diff.x());
|
change_position_value(0, diff.x());
|
||||||
change_position_value(1, diff.y());
|
change_position_value(1, diff.y());
|
||||||
change_position_value(2, diff.z());
|
change_position_value(2, diff.z());
|
||||||
}
|
}
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
else if (selection.is_single_full_instance()) {
|
||||||
|
const ModelObjectPtrs& objects = wxGetApp().model().objects;
|
||||||
|
const int idx = selection.get_object_idx();
|
||||||
|
if (0 <= idx && idx < static_cast<int>(objects.size())) {
|
||||||
|
const ModelObject* mo = wxGetApp().model().objects[idx];
|
||||||
|
const double min_z = mo->bounding_box().min.z();
|
||||||
|
if (std::abs(min_z) > EPSILON) {
|
||||||
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed"));
|
||||||
|
change_position_value(2, m_cache.position.z() - min_z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
});
|
});
|
||||||
editors_grid_sizer->Add(m_drop_to_bed_button);
|
editors_grid_sizer->Add(m_drop_to_bed_button);
|
||||||
|
|
||||||
|
|
@ -671,11 +690,15 @@ void ObjectManipulation::update_reset_buttons_visibility()
|
||||||
if (selection.is_single_full_instance()) {
|
if (selection.is_single_full_instance()) {
|
||||||
rotation = volume->get_instance_rotation();
|
rotation = volume->get_instance_rotation();
|
||||||
scale = volume->get_instance_scaling_factor();
|
scale = volume->get_instance_scaling_factor();
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
min_z = wxGetApp().model().objects[volume->composite_id.object_id]->bounding_box().min.z();
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rotation = volume->get_volume_rotation();
|
rotation = volume->get_volume_rotation();
|
||||||
scale = volume->get_volume_scaling_factor();
|
scale = volume->get_volume_scaling_factor();
|
||||||
min_z = get_volume_min_z(volume);
|
min_z = get_volume_min_z(*volume);
|
||||||
}
|
}
|
||||||
show_rotation = !rotation.isApprox(Vec3d::Zero());
|
show_rotation = !rotation.isApprox(Vec3d::Zero());
|
||||||
show_scale = !scale.isApprox(Vec3d::Ones());
|
show_scale = !scale.isApprox(Vec3d::Ones());
|
||||||
|
|
|
||||||
|
|
@ -643,7 +643,7 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
||||||
if (sla_print_technology)
|
if (sla_print_technology)
|
||||||
m_layers_slider->SetLayersTimes(plater->sla_print().print_statistics().layers_times);
|
m_layers_slider->SetLayersTimes(plater->sla_print().print_statistics().layers_times);
|
||||||
else {
|
else {
|
||||||
auto print_mode_stat = m_gcode_result->time_statistics.modes.front();
|
auto print_mode_stat = m_gcode_result->print_statistics.modes.front();
|
||||||
m_layers_slider->SetLayersTimes(print_mode_stat.layers_times, print_mode_stat.time);
|
m_layers_slider->SetLayersTimes(print_mode_stat.layers_times, print_mode_stat.time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,42 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
// port of 948bc382655993721d93d3b9fce9b0186fcfb211
|
||||||
|
void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
|
||||||
|
{
|
||||||
|
Plater* plater = wxGetApp().plater();
|
||||||
|
|
||||||
|
// Following is needed to prevent taking an extra snapshot when the activation of
|
||||||
|
// the internal stack happens when the gizmo is already active (such as open gizmo,
|
||||||
|
// close gizmo, undo, start painting). The internal stack does not activate on the
|
||||||
|
// undo, because that would obliterate all future of the main stack (user would
|
||||||
|
// have to close the gizmo himself, he has no access to main undo/redo after the
|
||||||
|
// internal stack opens). We don't want the "entering" snapshot taken in this case,
|
||||||
|
// because there already is one.
|
||||||
|
std::string last_snapshot_name;
|
||||||
|
plater->undo_redo_topmost_string_getter(plater->can_undo(), last_snapshot_name);
|
||||||
|
|
||||||
|
if (activate && !m_internal_stack_active) {
|
||||||
|
std::string str = get_painter_type() == PainterGizmoType::FDM_SUPPORTS
|
||||||
|
? _u8L("Entering Paint-on supports")
|
||||||
|
: _u8L("Entering Seam painting");
|
||||||
|
if (last_snapshot_name != str)
|
||||||
|
Plater::TakeSnapshot(plater, str);
|
||||||
|
plater->enter_gizmos_stack();
|
||||||
|
m_internal_stack_active = true;
|
||||||
|
}
|
||||||
|
if (!activate && m_internal_stack_active) {
|
||||||
|
plater->leave_gizmos_stack();
|
||||||
|
std::string str = get_painter_type() == PainterGizmoType::SEAM
|
||||||
|
? _u8L("Leaving Seam painting")
|
||||||
|
: _u8L("Leaving Paint-on supports");
|
||||||
|
if (last_snapshot_name != str)
|
||||||
|
Plater::TakeSnapshot(plater, str);
|
||||||
|
m_internal_stack_active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
|
void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
|
||||||
{
|
{
|
||||||
if (activate && ! m_internal_stack_active) {
|
if (activate && ! m_internal_stack_active) {
|
||||||
|
|
@ -51,6 +87,7 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
|
||||||
m_internal_stack_active = false;
|
m_internal_stack_active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,18 +45,18 @@ bool GLGizmoSlaSupports::on_init()
|
||||||
{
|
{
|
||||||
m_shortcut_key = WXK_CONTROL_L;
|
m_shortcut_key = WXK_CONTROL_L;
|
||||||
|
|
||||||
m_desc["head_diameter"] = _(L("Head diameter")) + ": ";
|
m_desc["head_diameter"] = _L("Head diameter") + ": ";
|
||||||
m_desc["lock_supports"] = _(L("Lock supports under new islands"));
|
m_desc["lock_supports"] = _L("Lock supports under new islands");
|
||||||
m_desc["remove_selected"] = _(L("Remove selected points"));
|
m_desc["remove_selected"] = _L("Remove selected points");
|
||||||
m_desc["remove_all"] = _(L("Remove all points"));
|
m_desc["remove_all"] = _L("Remove all points");
|
||||||
m_desc["apply_changes"] = _(L("Apply changes"));
|
m_desc["apply_changes"] = _L("Apply changes");
|
||||||
m_desc["discard_changes"] = _(L("Discard changes"));
|
m_desc["discard_changes"] = _L("Discard changes");
|
||||||
m_desc["minimal_distance"] = _(L("Minimal points distance")) + ": ";
|
m_desc["minimal_distance"] = _L("Minimal points distance") + ": ";
|
||||||
m_desc["points_density"] = _(L("Support points density")) + ": ";
|
m_desc["points_density"] = _L("Support points density") + ": ";
|
||||||
m_desc["auto_generate"] = _(L("Auto-generate points"));
|
m_desc["auto_generate"] = _L("Auto-generate points");
|
||||||
m_desc["manual_editing"] = _(L("Manual editing"));
|
m_desc["manual_editing"] = _L("Manual editing");
|
||||||
m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": ";
|
m_desc["clipping_of_view"] = _L("Clipping of view")+ ": ";
|
||||||
m_desc["reset_direction"] = _(L("Reset direction"));
|
m_desc["reset_direction"] = _L("Reset direction");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -372,7 +372,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||||
if (m_selection_empty) {
|
if (m_selection_empty) {
|
||||||
std::pair<Vec3f, Vec3f> pos_and_normal;
|
std::pair<Vec3f, Vec3f> pos_and_normal;
|
||||||
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
|
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add support point")));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add support point"));
|
||||||
m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second);
|
m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second);
|
||||||
m_parent.set_as_dirty();
|
m_parent.set_as_dirty();
|
||||||
m_wait_for_up_event = true;
|
m_wait_for_up_event = true;
|
||||||
|
|
@ -512,7 +512,7 @@ void GLGizmoSlaSupports::delete_selected_points(bool force)
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete support point")));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete support point"));
|
||||||
|
|
||||||
for (unsigned int idx=0; idx<m_editing_cache.size(); ++idx) {
|
for (unsigned int idx=0; idx<m_editing_cache.size(); ++idx) {
|
||||||
if (m_editing_cache[idx].selected && (!m_editing_cache[idx].support_point.is_new_island || !m_lock_unique_islands || force)) {
|
if (m_editing_cache[idx].selected && (!m_editing_cache[idx].support_point.is_new_island || !m_lock_unique_islands || force)) {
|
||||||
|
|
@ -692,7 +692,7 @@ RENDER_AGAIN:
|
||||||
cache_entry.support_point.head_front_radius = m_old_point_head_diameter / 2.f;
|
cache_entry.support_point.head_front_radius = m_old_point_head_diameter / 2.f;
|
||||||
float backup = m_new_point_head_diameter;
|
float backup = m_new_point_head_diameter;
|
||||||
m_new_point_head_diameter = m_old_point_head_diameter;
|
m_new_point_head_diameter = m_old_point_head_diameter;
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change point head diameter")));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Change point head diameter"));
|
||||||
m_new_point_head_diameter = backup;
|
m_new_point_head_diameter = backup;
|
||||||
for (auto& cache_entry : m_editing_cache)
|
for (auto& cache_entry : m_editing_cache)
|
||||||
if (cache_entry.selected)
|
if (cache_entry.selected)
|
||||||
|
|
@ -760,7 +760,7 @@ RENDER_AGAIN:
|
||||||
if (slider_released) {
|
if (slider_released) {
|
||||||
mo->config.set("support_points_minimal_distance", m_minimal_point_distance_stash);
|
mo->config.set("support_points_minimal_distance", m_minimal_point_distance_stash);
|
||||||
mo->config.set("support_points_density_relative", (int)m_density_stash);
|
mo->config.set("support_points_density_relative", (int)m_density_stash);
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change")));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Support parameter change"));
|
||||||
mo->config.set("support_points_minimal_distance", minimal_point_distance);
|
mo->config.set("support_points_minimal_distance", minimal_point_distance);
|
||||||
mo->config.set("support_points_density_relative", (int)density);
|
mo->config.set("support_points_density_relative", (int)density);
|
||||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||||
|
|
@ -867,10 +867,9 @@ bool GLGizmoSlaSupports::on_is_selectable() const
|
||||||
|
|
||||||
std::string GLGizmoSlaSupports::on_get_name() const
|
std::string GLGizmoSlaSupports::on_get_name() const
|
||||||
{
|
{
|
||||||
return (_(L("SLA Support Points")) + " [L]").ToUTF8().data();
|
return (_L("SLA Support Points") + " [L]").ToUTF8().data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
|
CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
|
||||||
{
|
{
|
||||||
return CommonGizmosDataID(
|
return CommonGizmosDataID(
|
||||||
|
|
@ -895,7 +894,11 @@ void GLGizmoSlaSupports::on_set_state()
|
||||||
// data are not yet available, the CallAfter will postpone taking the
|
// data are not yet available, the CallAfter will postpone taking the
|
||||||
// snapshot until they are. No, it does not feel right.
|
// snapshot until they are. No, it does not feel right.
|
||||||
wxGetApp().CallAfter([]() {
|
wxGetApp().CallAfter([]() {
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on")));
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Entering SLA gizmo"));
|
||||||
|
#else
|
||||||
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("SLA gizmo turned on"));
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -909,8 +912,8 @@ void GLGizmoSlaSupports::on_set_state()
|
||||||
wxGetApp().CallAfter([this]() {
|
wxGetApp().CallAfter([this]() {
|
||||||
// Following is called through CallAfter, because otherwise there was a problem
|
// Following is called through CallAfter, because otherwise there was a problem
|
||||||
// on OSX with the wxMessageDialog being shown several times when clicked into.
|
// on OSX with the wxMessageDialog being shown several times when clicked into.
|
||||||
wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually "
|
wxMessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually "
|
||||||
"edited support points?")) + "\n",_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
|
"edited support points?") + "\n",_L("Save changes?"), wxICON_QUESTION | wxYES | wxNO);
|
||||||
if (dlg.ShowModal() == wxID_YES)
|
if (dlg.ShowModal() == wxID_YES)
|
||||||
editing_mode_apply_changes();
|
editing_mode_apply_changes();
|
||||||
else
|
else
|
||||||
|
|
@ -922,7 +925,11 @@ void GLGizmoSlaSupports::on_set_state()
|
||||||
else {
|
else {
|
||||||
// we are actually shutting down
|
// we are actually shutting down
|
||||||
disable_editing_mode(); // so it is not active next time the gizmo opens
|
disable_editing_mode(); // so it is not active next time the gizmo opens
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off")));
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Leaving SLA gizmo"));
|
||||||
|
#else
|
||||||
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("SLA gizmo turned off"));
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
m_normal_cache.clear();
|
m_normal_cache.clear();
|
||||||
m_old_mo_id = -1;
|
m_old_mo_id = -1;
|
||||||
}
|
}
|
||||||
|
|
@ -953,7 +960,7 @@ void GLGizmoSlaSupports::on_stop_dragging()
|
||||||
&& backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected
|
&& backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected
|
||||||
{
|
{
|
||||||
m_editing_cache[m_hover_id] = m_point_before_drag;
|
m_editing_cache[m_hover_id] = m_point_before_drag;
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move support point")));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move support point"));
|
||||||
m_editing_cache[m_hover_id] = backup;
|
m_editing_cache[m_hover_id] = backup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1046,7 +1053,7 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
|
||||||
disable_editing_mode(); // this leaves the editing mode undo/redo stack and must be done before the snapshot is taken
|
disable_editing_mode(); // this leaves the editing mode undo/redo stack and must be done before the snapshot is taken
|
||||||
|
|
||||||
if (unsaved_changes()) {
|
if (unsaved_changes()) {
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support points edit")));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Support points edit"));
|
||||||
|
|
||||||
m_normal_cache.clear();
|
m_normal_cache.clear();
|
||||||
for (const CacheEntry& ce : m_editing_cache)
|
for (const CacheEntry& ce : m_editing_cache)
|
||||||
|
|
@ -1125,14 +1132,14 @@ void GLGizmoSlaSupports::get_data_from_backend()
|
||||||
void GLGizmoSlaSupports::auto_generate()
|
void GLGizmoSlaSupports::auto_generate()
|
||||||
{
|
{
|
||||||
wxMessageDialog dlg(GUI::wxGetApp().plater(),
|
wxMessageDialog dlg(GUI::wxGetApp().plater(),
|
||||||
_(L("Autogeneration will erase all manually edited points.")) + "\n\n" +
|
_L("Autogeneration will erase all manually edited points.") + "\n\n" +
|
||||||
_(L("Are you sure you want to do it?")) + "\n",
|
_L("Are you sure you want to do it?") + "\n",
|
||||||
_(L("Warning")), wxICON_WARNING | wxYES | wxNO);
|
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
|
||||||
|
|
||||||
ModelObject* mo = m_c->selection_info()->model_object();
|
ModelObject* mo = m_c->selection_info()->model_object();
|
||||||
|
|
||||||
if (mo->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) {
|
if (mo->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) {
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points")));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points"));
|
||||||
wxGetApp().CallAfter([this]() { reslice_SLA_supports(); });
|
wxGetApp().CallAfter([this]() { reslice_SLA_supports(); });
|
||||||
mo->sla_points_status = sla::PointsStatus::Generating;
|
mo->sla_points_status = sla::PointsStatus::Generating;
|
||||||
}
|
}
|
||||||
|
|
@ -1180,7 +1187,7 @@ bool GLGizmoSlaSupports::unsaved_changes() const
|
||||||
}
|
}
|
||||||
|
|
||||||
SlaGizmoHelpDialog::SlaGizmoHelpDialog()
|
SlaGizmoHelpDialog::SlaGizmoHelpDialog()
|
||||||
: wxDialog(nullptr, wxID_ANY, _(L("SLA gizmo keyboard shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
: wxDialog(nullptr, wxID_ANY, _L("SLA gizmo keyboard shortcuts"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
|
||||||
{
|
{
|
||||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||||
const wxString ctrl = GUI::shortkey_ctrl_prefix();
|
const wxString ctrl = GUI::shortkey_ctrl_prefix();
|
||||||
|
|
@ -1191,7 +1198,7 @@ SlaGizmoHelpDialog::SlaGizmoHelpDialog()
|
||||||
const wxFont& font = wxGetApp().small_font();
|
const wxFont& font = wxGetApp().small_font();
|
||||||
const wxFont& bold_font = wxGetApp().bold_font();
|
const wxFont& bold_font = wxGetApp().bold_font();
|
||||||
|
|
||||||
auto note_text = new wxStaticText(this, wxID_ANY, _(L("Note: some shortcuts work in (non)editing mode only.")));
|
auto note_text = new wxStaticText(this, wxID_ANY, _L("Note: some shortcuts work in (non)editing mode only."));
|
||||||
note_text->SetFont(font);
|
note_text->SetFont(font);
|
||||||
|
|
||||||
auto vsizer = new wxBoxSizer(wxVERTICAL);
|
auto vsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
@ -1209,21 +1216,21 @@ SlaGizmoHelpDialog::SlaGizmoHelpDialog()
|
||||||
vsizer->AddSpacer(20);
|
vsizer->AddSpacer(20);
|
||||||
|
|
||||||
std::vector<std::pair<wxString, wxString>> shortcuts;
|
std::vector<std::pair<wxString, wxString>> shortcuts;
|
||||||
shortcuts.push_back(std::make_pair(_(L("Left click")), _(L("Add point"))));
|
shortcuts.push_back(std::make_pair(_L("Left click"), _L("Add point")));
|
||||||
shortcuts.push_back(std::make_pair(_(L("Right click")), _(L("Remove point"))));
|
shortcuts.push_back(std::make_pair(_L("Right click"), _L("Remove point")));
|
||||||
shortcuts.push_back(std::make_pair(_(L("Drag")), _(L("Move point"))));
|
shortcuts.push_back(std::make_pair(_L("Drag"), _L("Move point")));
|
||||||
shortcuts.push_back(std::make_pair(ctrl+_(L("Left click")), _(L("Add point to selection"))));
|
shortcuts.push_back(std::make_pair(ctrl+_L("Left click"), _L("Add point to selection")));
|
||||||
shortcuts.push_back(std::make_pair(alt+_(L("Left click")), _(L("Remove point from selection"))));
|
shortcuts.push_back(std::make_pair(alt+_L("Left click"), _L("Remove point from selection")));
|
||||||
shortcuts.push_back(std::make_pair(wxString("Shift+")+_(L("Drag")), _(L("Select by rectangle"))));
|
shortcuts.push_back(std::make_pair(wxString("Shift+")+_L("Drag"), _L("Select by rectangle")));
|
||||||
shortcuts.push_back(std::make_pair(alt+_(L("Drag")), _(L("Deselect by rectangle"))));
|
shortcuts.push_back(std::make_pair(alt+_(L("Drag")), _L("Deselect by rectangle")));
|
||||||
shortcuts.push_back(std::make_pair(ctrl+"A", _(L("Select all points"))));
|
shortcuts.push_back(std::make_pair(ctrl+"A", _L("Select all points")));
|
||||||
shortcuts.push_back(std::make_pair("Delete", _(L("Remove selected points"))));
|
shortcuts.push_back(std::make_pair("Delete", _L("Remove selected points")));
|
||||||
shortcuts.push_back(std::make_pair(ctrl+_(L("Mouse wheel")), _(L("Move clipping plane"))));
|
shortcuts.push_back(std::make_pair(ctrl+_L("Mouse wheel"), _L("Move clipping plane")));
|
||||||
shortcuts.push_back(std::make_pair("R", _(L("Reset clipping plane"))));
|
shortcuts.push_back(std::make_pair("R", _L("Reset clipping plane")));
|
||||||
shortcuts.push_back(std::make_pair("Enter", _(L("Apply changes"))));
|
shortcuts.push_back(std::make_pair("Enter", _L("Apply changes")));
|
||||||
shortcuts.push_back(std::make_pair("Esc", _(L("Discard changes"))));
|
shortcuts.push_back(std::make_pair("Esc", _L("Discard changes")));
|
||||||
shortcuts.push_back(std::make_pair("M", _(L("Switch to editing mode"))));
|
shortcuts.push_back(std::make_pair("M", _L("Switch to editing mode")));
|
||||||
shortcuts.push_back(std::make_pair("A", _(L("Auto-generate points"))));
|
shortcuts.push_back(std::make_pair("A", _L("Auto-generate points")));
|
||||||
|
|
||||||
for (const auto& pair : shortcuts) {
|
for (const auto& pair : shortcuts) {
|
||||||
auto shortcut = new wxStaticText(this, wxID_ANY, pair.first);
|
auto shortcut = new wxStaticText(this, wxID_ANY, pair.first);
|
||||||
|
|
|
||||||
|
|
@ -488,8 +488,7 @@ bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (
|
||||||
|
|
||||||
int i=0;
|
int i=0;
|
||||||
const char* item_text;
|
const char* item_text;
|
||||||
while (items_getter(is_undo, i, &item_text))
|
while (items_getter(is_undo, i, &item_text)) {
|
||||||
{
|
|
||||||
ImGui::Selectable(item_text, i < hovered);
|
ImGui::Selectable(item_text, i < hovered);
|
||||||
|
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,11 @@ void KBShortcutsDialog::fill_shortcuts()
|
||||||
{ "0-6", L("Camera view") },
|
{ "0-6", L("Camera view") },
|
||||||
{ "E", L("Show/Hide object/instance labels") },
|
{ "E", L("Show/Hide object/instance labels") },
|
||||||
// Configuration
|
// Configuration
|
||||||
|
#ifdef __APPLE__
|
||||||
|
{ ctrl + ",", L("Preferences") },
|
||||||
|
#else
|
||||||
{ ctrl + "P", L("Preferences") },
|
{ ctrl + "P", L("Preferences") },
|
||||||
|
#endif
|
||||||
// Help
|
// Help
|
||||||
{ "?", L("Show keyboard shortcuts list") }
|
{ "?", L("Show keyboard shortcuts list") }
|
||||||
};
|
};
|
||||||
|
|
@ -149,8 +153,13 @@ void KBShortcutsDialog::fill_shortcuts()
|
||||||
{ "Shift+Tab", L("Collapse/Expand the sidebar") },
|
{ "Shift+Tab", L("Collapse/Expand the sidebar") },
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
{ ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog, if enabled") },
|
{ ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog, if enabled") },
|
||||||
|
#else
|
||||||
|
#ifdef __APPLE__
|
||||||
|
{ ctrl + "Shift+M", L("Show/Hide 3Dconnexion devices settings dialog") },
|
||||||
|
{ ctrl + "M", L("Minimize application") },
|
||||||
#else
|
#else
|
||||||
{ ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") },
|
{ ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") },
|
||||||
|
#endif // __APPLE__
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
#if ENABLE_RENDER_PICKING_PASS
|
#if ENABLE_RENDER_PICKING_PASS
|
||||||
// Don't localize debugging texts.
|
// Don't localize debugging texts.
|
||||||
|
|
@ -171,6 +180,14 @@ void KBShortcutsDialog::fill_shortcuts()
|
||||||
};
|
};
|
||||||
|
|
||||||
m_full_shortcuts.push_back({ { _L("Gizmos"), _L("The following shortcuts are applicable when the specified gizmo is active") }, gizmos_shortcuts });
|
m_full_shortcuts.push_back({ { _L("Gizmos"), _L("The following shortcuts are applicable when the specified gizmo is active") }, gizmos_shortcuts });
|
||||||
|
|
||||||
|
Shortcuts object_list_shortcuts = {
|
||||||
|
{ "P", L("Set selected items as Ptrintable/Unprintable") },
|
||||||
|
{ "0", L("Set default extruder for the selected items") },
|
||||||
|
{ "1-9", L("Set extruder number for the selected items") },
|
||||||
|
};
|
||||||
|
|
||||||
|
m_full_shortcuts.push_back({ { _L("Objects List"), "" }, object_list_shortcuts });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Shortcuts commands_shortcuts = {
|
Shortcuts commands_shortcuts = {
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,14 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
||||||
|
|
||||||
// declare events
|
// declare events
|
||||||
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
|
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (m_plater != nullptr)
|
||||||
|
m_plater->save_project_if_dirty();
|
||||||
|
|
||||||
|
if (event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes()) {
|
||||||
|
#else
|
||||||
if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
|
if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
event.Veto();
|
event.Veto();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -487,8 +494,14 @@ void MainFrame::update_title()
|
||||||
// m_plater->get_project_filename() produces file name including path, but excluding extension.
|
// m_plater->get_project_filename() produces file name including path, but excluding extension.
|
||||||
// Don't try to remove the extension, it would remove part of the file name after the last dot!
|
// Don't try to remove the extension, it would remove part of the file name after the last dot!
|
||||||
wxString project = from_path(into_path(m_plater->get_project_filename()).filename());
|
wxString project = from_path(into_path(m_plater->get_project_filename()).filename());
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
wxString dirty_marker = (!m_plater->model().objects.empty() && m_plater->is_project_dirty()) ? "*" : "";
|
||||||
|
if (!dirty_marker.empty() || !project.empty())
|
||||||
|
title = dirty_marker + project + " - ";
|
||||||
|
#else
|
||||||
if (!project.empty())
|
if (!project.empty())
|
||||||
title += (project + " - ");
|
title += (project + " - ");
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string build_id = wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID;
|
std::string build_id = wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID;
|
||||||
|
|
@ -531,9 +544,12 @@ void MainFrame::init_tabpanel()
|
||||||
|
|
||||||
m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) {
|
m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) {
|
||||||
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
#if ENABLE_VALIDATE_CUSTOM_GCODE
|
||||||
Tab* old_tab = dynamic_cast<Tab*>(m_tabpanel->GetPage(e.GetOldSelection()));
|
if (int old_selection = e.GetOldSelection();
|
||||||
if (old_tab)
|
old_selection != wxNOT_FOUND && old_selection < m_tabpanel->GetPageCount()) {
|
||||||
old_tab->validate_custom_gcodes();
|
Tab* old_tab = dynamic_cast<Tab*>(m_tabpanel->GetPage(old_selection));
|
||||||
|
if (old_tab)
|
||||||
|
old_tab->validate_custom_gcodes();
|
||||||
|
}
|
||||||
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
|
||||||
|
|
||||||
wxWindow* panel = m_tabpanel->GetCurrentPage();
|
wxWindow* panel = m_tabpanel->GetCurrentPage();
|
||||||
|
|
@ -672,10 +688,36 @@ bool MainFrame::can_start_new_project() const
|
||||||
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty());
|
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool MainFrame::can_save() const
|
||||||
|
{
|
||||||
|
return (m_plater != nullptr) && !m_plater->model().objects.empty() && !m_plater->get_project_filename().empty() && m_plater->is_project_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainFrame::can_save_as() const
|
||||||
|
{
|
||||||
|
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainFrame::save_project()
|
||||||
|
{
|
||||||
|
save_project_as(m_plater->get_project_filename(".3mf"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainFrame::save_project_as(const wxString& filename)
|
||||||
|
{
|
||||||
|
bool ret = (m_plater != nullptr) ? m_plater->export_3mf(into_path(filename)) : false;
|
||||||
|
if (ret) {
|
||||||
|
// wxGetApp().update_saved_preset_from_current_preset();
|
||||||
|
m_plater->reset_project_dirty_after_save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
bool MainFrame::can_save() const
|
bool MainFrame::can_save() const
|
||||||
{
|
{
|
||||||
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
return (m_plater != nullptr) && !m_plater->model().objects.empty();
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
bool MainFrame::can_export_model() const
|
bool MainFrame::can_export_model() const
|
||||||
{
|
{
|
||||||
|
|
@ -984,16 +1026,27 @@ void MainFrame::init_menubar_as_editor()
|
||||||
|
|
||||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId());
|
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId());
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"),
|
||||||
|
[this](wxCommandEvent&) { save_project(); }, "save", nullptr,
|
||||||
|
[this](){return m_plater != nullptr && can_save(); }, this);
|
||||||
|
#else
|
||||||
append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"),
|
append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"),
|
||||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, "save", nullptr,
|
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, "save", nullptr,
|
||||||
[this](){return m_plater != nullptr && can_save(); }, this);
|
[this](){return m_plater != nullptr && can_save(); }, this);
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"),
|
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"),
|
||||||
#else
|
#else
|
||||||
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"),
|
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"),
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
[this](wxCommandEvent&) { save_project_as(); }, "save", nullptr,
|
||||||
|
[this](){return m_plater != nullptr && can_save_as(); }, this);
|
||||||
|
#else
|
||||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "save", nullptr,
|
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "save", nullptr,
|
||||||
[this](){return m_plater != nullptr && can_save(); }, this);
|
[this](){return m_plater != nullptr && can_save(); }, this);
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
fileMenu->AppendSeparator();
|
fileMenu->AppendSeparator();
|
||||||
|
|
||||||
|
|
@ -1518,7 +1571,11 @@ void MainFrame::export_config()
|
||||||
// Load a config file containing a Print, Filament & Printer preset.
|
// Load a config file containing a Print, Filament & Printer preset.
|
||||||
void MainFrame::load_config_file()
|
void MainFrame::load_config_file()
|
||||||
{
|
{
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (!wxGetApp().check_and_save_current_preset_changes())
|
||||||
|
#else
|
||||||
if (!wxGetApp().check_unsaved_changes())
|
if (!wxGetApp().check_unsaved_changes())
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
return;
|
return;
|
||||||
wxFileDialog dlg(this, _L("Select configuration to load:"),
|
wxFileDialog dlg(this, _L("Select configuration to load:"),
|
||||||
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
|
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
|
||||||
|
|
@ -1547,7 +1604,11 @@ bool MainFrame::load_config_file(const std::string &path)
|
||||||
|
|
||||||
void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
|
void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (!wxGetApp().check_and_save_current_preset_changes())
|
||||||
|
#else
|
||||||
if (!wxGetApp().check_unsaved_changes())
|
if (!wxGetApp().check_unsaved_changes())
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
return;
|
return;
|
||||||
// validate current configuration in case it's dirty
|
// validate current configuration in case it's dirty
|
||||||
auto err = wxGetApp().preset_bundle->full_config().validate();
|
auto err = wxGetApp().preset_bundle->full_config().validate();
|
||||||
|
|
@ -1579,7 +1640,11 @@ void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
|
||||||
// but that behavior was not documented and likely buggy.
|
// but that behavior was not documented and likely buggy.
|
||||||
void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/)
|
void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (!wxGetApp().check_and_save_current_preset_changes())
|
||||||
|
#else
|
||||||
if (!wxGetApp().check_unsaved_changes())
|
if (!wxGetApp().check_unsaved_changes())
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
return;
|
return;
|
||||||
if (file.IsEmpty()) {
|
if (file.IsEmpty()) {
|
||||||
wxFileDialog dlg(this, _L("Select configuration to load:"),
|
wxFileDialog dlg(this, _L("Select configuration to load:"),
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,9 @@ class MainFrame : public DPIFrame
|
||||||
void on_value_changed(wxCommandEvent&);
|
void on_value_changed(wxCommandEvent&);
|
||||||
|
|
||||||
bool can_start_new_project() const;
|
bool can_start_new_project() const;
|
||||||
|
#if !ENABLE_PROJECT_DIRTY_STATE
|
||||||
bool can_save() const;
|
bool can_save() const;
|
||||||
|
#endif // !ENABLE_PROJECT_DIRTY_STATE
|
||||||
bool can_export_model() const;
|
bool can_export_model() const;
|
||||||
bool can_export_toolpaths() const;
|
bool can_export_toolpaths() const;
|
||||||
bool can_export_supports() const;
|
bool can_export_supports() const;
|
||||||
|
|
@ -184,6 +186,13 @@ public:
|
||||||
// Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig
|
// Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig
|
||||||
void on_config_changed(DynamicPrintConfig* cfg) const ;
|
void on_config_changed(DynamicPrintConfig* cfg) const ;
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool can_save() const;
|
||||||
|
bool can_save_as() const;
|
||||||
|
void save_project();
|
||||||
|
void save_project_as(const wxString& filename = wxString());
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
void add_to_recent_projects(const wxString& filename);
|
void add_to_recent_projects(const wxString& filename);
|
||||||
|
|
||||||
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
|
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ void NotificationManager::PopNotification::count_spaces()
|
||||||
|
|
||||||
void NotificationManager::PopNotification::count_lines()
|
void NotificationManager::PopNotification::count_lines()
|
||||||
{
|
{
|
||||||
std::string text = m_text1 + " " + m_hypertext;
|
std::string text = m_text1;
|
||||||
size_t last_end = 0;
|
size_t last_end = 0;
|
||||||
m_lines_count = 0;
|
m_lines_count = 0;
|
||||||
|
|
||||||
|
|
@ -302,6 +302,14 @@ void NotificationManager::PopNotification::count_lines()
|
||||||
}
|
}
|
||||||
m_lines_count++;
|
m_lines_count++;
|
||||||
}
|
}
|
||||||
|
// hypertext calculation
|
||||||
|
if (!m_hypertext.empty()) {
|
||||||
|
int prev_end = m_endlines.size() > 1 ? m_endlines[m_endlines.size() - 2] : 0;
|
||||||
|
if (ImGui::CalcTextSize((text.substr(prev_end, last_end - prev_end) + m_hypertext).c_str()).x > m_window_width - m_window_width_offset) {
|
||||||
|
m_endlines.push_back(last_end);
|
||||||
|
m_lines_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationManager::PopNotification::init()
|
void NotificationManager::PopNotification::init()
|
||||||
|
|
@ -312,7 +320,7 @@ void NotificationManager::PopNotification::init()
|
||||||
|
|
||||||
count_spaces();
|
count_spaces();
|
||||||
count_lines();
|
count_lines();
|
||||||
|
|
||||||
if (m_lines_count == 3)
|
if (m_lines_count == 3)
|
||||||
m_multiline = true;
|
m_multiline = true;
|
||||||
m_notification_start = GLCanvas3D::timestamp_now();
|
m_notification_start = GLCanvas3D::timestamp_now();
|
||||||
|
|
@ -342,39 +350,48 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
|
||||||
int last_end = 0;
|
int last_end = 0;
|
||||||
float starting_y = m_line_height/2;
|
float starting_y = m_line_height/2;
|
||||||
float shift_y = m_line_height;
|
float shift_y = m_line_height;
|
||||||
|
std::string line;
|
||||||
|
|
||||||
for (size_t i = 0; i < m_lines_count; i++) {
|
for (size_t i = 0; i < m_lines_count; i++) {
|
||||||
std::string line = m_text1.substr(last_end , m_endlines[i] - last_end);
|
line.clear();
|
||||||
if(i < m_lines_count - 1)
|
|
||||||
last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
|
|
||||||
ImGui::SetCursorPosX(x_offset);
|
ImGui::SetCursorPosX(x_offset);
|
||||||
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
||||||
imgui.text(line.c_str());
|
if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) {
|
||||||
|
line = m_text1.substr(last_end, m_endlines[i] - last_end);
|
||||||
|
last_end = m_endlines[i];
|
||||||
|
if (m_text1.size() > m_endlines[i])
|
||||||
|
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
|
||||||
|
imgui.text(line.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//hyperlink text
|
//hyperlink text
|
||||||
if (!m_hypertext.empty())
|
if (!m_hypertext.empty()) {
|
||||||
{
|
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext);
|
||||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(m_text1.substr(m_endlines[m_lines_count - 2] + 1, m_endlines[m_lines_count - 1] - m_endlines[m_lines_count - 2] - 1).c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// line1
|
// line1
|
||||||
ImGui::SetCursorPosX(x_offset);
|
if (m_text1.size() >= m_endlines[0]) {
|
||||||
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
|
ImGui::SetCursorPosX(x_offset);
|
||||||
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
|
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
|
||||||
|
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
|
||||||
|
}
|
||||||
// line2
|
// line2
|
||||||
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
std::string line;
|
||||||
if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x)
|
|
||||||
{
|
|
||||||
line = line.substr(0, line.length() - 6);
|
|
||||||
line += "..";
|
|
||||||
}else
|
|
||||||
line += " ";
|
|
||||||
ImGui::SetCursorPosX(x_offset);
|
ImGui::SetCursorPosX(x_offset);
|
||||||
ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2);
|
ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2);
|
||||||
imgui.text(line.c_str());
|
if (m_text1.size() >= m_endlines[1]) {
|
||||||
|
line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
||||||
|
if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) {
|
||||||
|
line = line.substr(0, line.length() - 6);
|
||||||
|
line += "..";
|
||||||
|
} else
|
||||||
|
line += " ";
|
||||||
|
imgui.text(line.c_str());
|
||||||
|
}
|
||||||
// "More" hypertext
|
// "More" hypertext
|
||||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true);
|
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//text 1
|
//text 1
|
||||||
|
|
@ -382,15 +399,17 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
|
||||||
float cursor_x = x_offset;
|
float cursor_x = x_offset;
|
||||||
if(m_lines_count > 1) {
|
if(m_lines_count > 1) {
|
||||||
// line1
|
// line1
|
||||||
ImGui::SetCursorPosX(x_offset);
|
if (m_text1.length() >= m_endlines[0]) { // could be equal than substr takes whole string
|
||||||
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
|
|
||||||
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
|
|
||||||
// line2
|
|
||||||
if (m_text1.length() > m_endlines[0]) {
|
|
||||||
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
|
||||||
cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2;
|
|
||||||
ImGui::SetCursorPosX(x_offset);
|
ImGui::SetCursorPosX(x_offset);
|
||||||
ImGui::SetCursorPosY(cursor_y);
|
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
|
||||||
|
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
|
||||||
|
}
|
||||||
|
// line2
|
||||||
|
ImGui::SetCursorPosX(x_offset);
|
||||||
|
cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2;
|
||||||
|
ImGui::SetCursorPosY(cursor_y);
|
||||||
|
if (m_text1.length() > m_endlines[0]) { // must be greater otherwise theres nothing to show and m_text1[m_endlines[0]] is beyond last letter
|
||||||
|
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
||||||
imgui.text(line.c_str());
|
imgui.text(line.c_str());
|
||||||
cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x;
|
cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x;
|
||||||
}
|
}
|
||||||
|
|
@ -401,8 +420,7 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
|
||||||
cursor_x = x_offset + ImGui::CalcTextSize(m_text1.c_str()).x;
|
cursor_x = x_offset + ImGui::CalcTextSize(m_text1.c_str()).x;
|
||||||
}
|
}
|
||||||
//hyperlink text
|
//hyperlink text
|
||||||
if (!m_hypertext.empty())
|
if (!m_hypertext.empty()) {
|
||||||
{
|
|
||||||
render_hypertext(imgui, cursor_x + 4, cursor_y, m_hypertext);
|
render_hypertext(imgui, cursor_x + 4, cursor_y, m_hypertext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -712,15 +730,18 @@ void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper&
|
||||||
float starting_y = m_line_height / 2;//10;
|
float starting_y = m_line_height / 2;//10;
|
||||||
float shift_y = m_line_height;// -m_line_height / 20;
|
float shift_y = m_line_height;// -m_line_height / 20;
|
||||||
for (size_t i = 0; i < m_lines_count; i++) {
|
for (size_t i = 0; i < m_lines_count; i++) {
|
||||||
std::string line = m_text1.substr(last_end, m_endlines[i] - last_end);
|
if (m_text1.size() >= m_endlines[i]) {
|
||||||
if (i < m_lines_count - 1)
|
std::string line = m_text1.substr(last_end, m_endlines[i] - last_end);
|
||||||
last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
|
last_end = m_endlines[i];
|
||||||
ImGui::SetCursorPosX(x_offset);
|
if (m_text1.size() > m_endlines[i])
|
||||||
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
|
||||||
imgui.text(line.c_str());
|
ImGui::SetCursorPosX(x_offset);
|
||||||
//hyperlink text
|
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
||||||
if ( i == 0 ) {
|
imgui.text(line.c_str());
|
||||||
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(m_text1.substr(0, last_end).c_str()).x + ImGui::CalcTextSize(" ").x, starting_y, _u8L("Open Folder."));
|
//hyperlink text
|
||||||
|
if ( i == 0 ) {
|
||||||
|
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x + ImGui::CalcTextSize(" ").x, starting_y, _u8L("Open Folder."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,13 @@ enum class NotificationType
|
||||||
// Notification emitted by Print::validate
|
// Notification emitted by Print::validate
|
||||||
PrintValidateWarning,
|
PrintValidateWarning,
|
||||||
// Notification telling user to quit SLA supports manual editing
|
// Notification telling user to quit SLA supports manual editing
|
||||||
QuitSLAManualMode
|
QuitSLAManualMode,
|
||||||
|
// Desktop integration basic info
|
||||||
|
DesktopIntegrationSuccess,
|
||||||
|
DesktopIntegrationFail,
|
||||||
|
UndoDesktopIntegrationSuccess,
|
||||||
|
UndoDesktopIntegrationFail
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class NotificationManager
|
class NotificationManager
|
||||||
|
|
@ -514,6 +520,14 @@ private:
|
||||||
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
|
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
|
||||||
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10,
|
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10,
|
||||||
_u8L("This model doesn't allow to automatically add the color changes") },
|
_u8L("This model doesn't allow to automatically add the color changes") },
|
||||||
|
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
|
||||||
|
_u8L("Desktop integration was successful.") },
|
||||||
|
{NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
|
||||||
|
_u8L("Desktop integration failed.") },
|
||||||
|
{NotificationType::UndoDesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
|
||||||
|
_u8L("Undo desktop integration was successful.") },
|
||||||
|
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
|
||||||
|
_u8L("Undo desktop integration failed.") },
|
||||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
||||||
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") },
|
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") },
|
||||||
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
|
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
|
||||||
|
|
|
||||||
|
|
@ -1180,6 +1180,15 @@ int ObjectDataViewModel::GetExtruderNumber(const wxDataViewItem& item) const
|
||||||
return atoi(node->m_extruder.c_str());
|
return atoi(node->m_extruder.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxString ObjectDataViewModel::GetColumnType(unsigned int col) const
|
||||||
|
{
|
||||||
|
if (col == colName || col == colExtruder)
|
||||||
|
return wxT("DataViewBitmapText");
|
||||||
|
if (col == colPrint || col == colEditing)
|
||||||
|
return wxT("DataViewBitmap");
|
||||||
|
return wxT("string");
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const
|
void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const
|
||||||
{
|
{
|
||||||
wxASSERT(item.IsOk());
|
wxASSERT(item.IsOk());
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,7 @@ public:
|
||||||
// helper methods to change the model
|
// helper methods to change the model
|
||||||
|
|
||||||
unsigned int GetColumnCount() const override { return 3;}
|
unsigned int GetColumnCount() const override { return 3;}
|
||||||
wxString GetColumnType(unsigned int col) const override{ return wxT("string"); }
|
wxString GetColumnType(unsigned int col) const override;
|
||||||
|
|
||||||
void GetValue( wxVariant &variant,
|
void GetValue( wxVariant &variant,
|
||||||
const wxDataViewItem &item,
|
const wxDataViewItem &item,
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@ public:
|
||||||
int config_type() const throw() { return m_config_type; }
|
int config_type() const throw() { return m_config_type; }
|
||||||
const t_opt_map& opt_map() const throw() { return m_opt_map; }
|
const t_opt_map& opt_map() const throw() { return m_opt_map; }
|
||||||
|
|
||||||
void set_config_category_and_type(const wxString &category, int type) { this->m_config_category = category; this->m_config_type = type; }
|
void set_config_category_and_type(const wxString &category, int type) { m_config_category = category; m_config_type = type; }
|
||||||
void set_config(DynamicPrintConfig* config) { m_config = config; m_modelconfig = nullptr; }
|
void set_config(DynamicPrintConfig* config) { m_config = config; m_modelconfig = nullptr; }
|
||||||
Option get_option(const std::string& opt_key, int opt_index = -1);
|
Option get_option(const std::string& opt_key, int opt_index = -1);
|
||||||
Line create_single_option_line(const std::string& title, const wxString& path = wxEmptyString, int idx = -1) /*const*/{
|
Line create_single_option_line(const std::string& title, const wxString& path = wxEmptyString, int idx = -1) /*const*/{
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,9 @@
|
||||||
#include "InstanceCheck.hpp"
|
#include "InstanceCheck.hpp"
|
||||||
#include "NotificationManager.hpp"
|
#include "NotificationManager.hpp"
|
||||||
#include "PresetComboBoxes.hpp"
|
#include "PresetComboBoxes.hpp"
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
#include "ProjectDirtyStateManager.hpp"
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include "Gizmos/GLGizmosManager.hpp"
|
#include "Gizmos/GLGizmosManager.hpp"
|
||||||
|
|
@ -1172,10 +1175,10 @@ void Sidebar::update_sliced_info_sizer()
|
||||||
new_label += format_wxstr(":\n - %1%\n - %2%", _L("objects"), _L("wipe tower"));
|
new_label += format_wxstr(":\n - %1%\n - %2%", _L("objects"), _L("wipe tower"));
|
||||||
|
|
||||||
wxString info_text = is_wipe_tower ?
|
wxString info_text = is_wipe_tower ?
|
||||||
wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / /*1000*/koef,
|
wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / koef,
|
||||||
(ps.total_used_filament - ps.total_wipe_tower_filament) / /*1000*/koef,
|
(ps.total_used_filament - ps.total_wipe_tower_filament) / koef,
|
||||||
ps.total_wipe_tower_filament / /*1000*/koef) :
|
ps.total_wipe_tower_filament / koef) :
|
||||||
wxString::Format("%.2f", ps.total_used_filament / /*1000*/koef);
|
wxString::Format("%.2f", ps.total_used_filament / koef);
|
||||||
p->sliced_info->SetTextAndShow(siFilament_m, info_text, new_label);
|
p->sliced_info->SetTextAndShow(siFilament_m, info_text, new_label);
|
||||||
|
|
||||||
koef = imperial_units ? pow(ObjectManipulation::mm_to_in, 3) : 1.0f;
|
koef = imperial_units ? pow(ObjectManipulation::mm_to_in, 3) : 1.0f;
|
||||||
|
|
@ -1203,7 +1206,7 @@ void Sidebar::update_sliced_info_sizer()
|
||||||
filament_weight = ps.total_weight;
|
filament_weight = ps.total_weight;
|
||||||
else {
|
else {
|
||||||
double filament_density = filament_preset->config.opt_float("filament_density", 0);
|
double filament_density = filament_preset->config.opt_float("filament_density", 0);
|
||||||
filament_weight = filament.second * filament_density * 2.4052f * 0.001; // assumes 1.75mm filament diameter;
|
filament_weight = filament.second * filament_density/* *2.4052f*/ * 0.001; // assumes 1.75mm filament diameter;
|
||||||
|
|
||||||
new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament.first + 1);
|
new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament.first + 1);
|
||||||
info_text += wxString::Format("\n%.2f", filament_weight);
|
info_text += wxString::Format("\n%.2f", filament_weight);
|
||||||
|
|
@ -1357,7 +1360,8 @@ void Sidebar::update_ui_from_settings()
|
||||||
update_sliced_info_sizer();
|
update_sliced_info_sizer();
|
||||||
// update Cut gizmo, if it's open
|
// update Cut gizmo, if it's open
|
||||||
p->plater->canvas3D()->update_gizmos_on_off_state();
|
p->plater->canvas3D()->update_gizmos_on_off_state();
|
||||||
p->plater->canvas3D()->request_extra_frame();
|
p->plater->set_current_canvas_as_dirty();
|
||||||
|
p->plater->get_current_canvas3D()->request_extra_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PlaterPresetComboBox*>& Sidebar::combos_filament()
|
std::vector<PlaterPresetComboBox*>& Sidebar::combos_filament()
|
||||||
|
|
@ -1395,7 +1399,13 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi
|
||||||
this->MSWUpdateDragImageOnLeave();
|
this->MSWUpdateDragImageOnLeave();
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool res = (m_plater != nullptr) ? m_plater->load_files(filenames) : false;
|
||||||
|
wxGetApp().mainframe->update_title();
|
||||||
|
return res;
|
||||||
|
#else
|
||||||
return (m_plater != nullptr) ? m_plater->load_files(filenames) : false;
|
return (m_plater != nullptr) ? m_plater->load_files(filenames) : false;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
// State to manage showing after export notifications and device ejecting
|
// State to manage showing after export notifications and device ejecting
|
||||||
|
|
@ -1439,6 +1449,10 @@ struct Plater::priv
|
||||||
Preview *preview;
|
Preview *preview;
|
||||||
NotificationManager* notification_manager { nullptr };
|
NotificationManager* notification_manager { nullptr };
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
ProjectDirtyStateManager dirty_state;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
BackgroundSlicingProcess background_process;
|
BackgroundSlicingProcess background_process;
|
||||||
bool suppressed_backround_processing_update { false };
|
bool suppressed_backround_processing_update { false };
|
||||||
|
|
||||||
|
|
@ -1509,6 +1523,31 @@ struct Plater::priv
|
||||||
priv(Plater *q, MainFrame *main_frame);
|
priv(Plater *q, MainFrame *main_frame);
|
||||||
~priv();
|
~priv();
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool is_project_dirty() const { return dirty_state.is_dirty(); }
|
||||||
|
void update_project_dirty_from_presets() { dirty_state.update_from_presets(); }
|
||||||
|
bool save_project_if_dirty() {
|
||||||
|
if (dirty_state.is_dirty()) {
|
||||||
|
MainFrame* mainframe = wxGetApp().mainframe;
|
||||||
|
if (mainframe->can_save_as()) {
|
||||||
|
wxMessageDialog dlg(mainframe, _L("Do you want to save the changes to the current project ?"), wxString(SLIC3R_APP_NAME), wxYES_NO | wxCANCEL);
|
||||||
|
int res = dlg.ShowModal();
|
||||||
|
if (res == wxID_YES)
|
||||||
|
mainframe->save_project_as(wxGetApp().plater()->get_project_filename());
|
||||||
|
else if (res == wxID_CANCEL)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void reset_project_dirty_after_save() { dirty_state.reset_after_save(); }
|
||||||
|
void reset_project_dirty_initial_presets() { dirty_state.reset_initial_presets(); }
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
void render_project_state_debug_window() const { dirty_state.render_debug_window(); }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
enum class UpdateParams {
|
enum class UpdateParams {
|
||||||
FORCE_FULL_SCREEN_REFRESH = 1,
|
FORCE_FULL_SCREEN_REFRESH = 1,
|
||||||
FORCE_BACKGROUND_PROCESSING_UPDATE = 2,
|
FORCE_BACKGROUND_PROCESSING_UPDATE = 2,
|
||||||
|
|
@ -1554,7 +1593,11 @@ struct Plater::priv
|
||||||
BoundingBox scaled_bed_shape_bb() const;
|
BoundingBox scaled_bed_shape_bb() const;
|
||||||
|
|
||||||
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
|
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false);
|
||||||
|
#else
|
||||||
std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects);
|
std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects);
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
wxString get_export_file(GUI::FileType file_type);
|
wxString get_export_file(GUI::FileType file_type);
|
||||||
|
|
||||||
const Selection& get_selection() const;
|
const Selection& get_selection() const;
|
||||||
|
|
@ -1588,8 +1631,8 @@ struct Plater::priv
|
||||||
void redo();
|
void redo();
|
||||||
void undo_redo_to(size_t time_to_load);
|
void undo_redo_to(size_t time_to_load);
|
||||||
|
|
||||||
void suppress_snapshots() { this->m_prevent_snapshots++; }
|
void suppress_snapshots() { m_prevent_snapshots++; }
|
||||||
void allow_snapshots() { this->m_prevent_snapshots--; }
|
void allow_snapshots() { m_prevent_snapshots--; }
|
||||||
|
|
||||||
void process_validation_warning(const std::string& warning) const;
|
void process_validation_warning(const std::string& warning) const;
|
||||||
|
|
||||||
|
|
@ -2294,11 +2337,19 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||||
return obj_idxs;
|
return obj_idxs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
for (ModelObject* model_object : model.objects) {
|
||||||
|
if (!type_3mf && !type_zip_amf)
|
||||||
|
model_object->center_around_origin(false);
|
||||||
|
model_object->ensure_on_bed(is_project_file);
|
||||||
|
}
|
||||||
|
#else
|
||||||
for (ModelObject* model_object : model.objects) {
|
for (ModelObject* model_object : model.objects) {
|
||||||
if (!type_3mf && !type_zip_amf)
|
if (!type_3mf && !type_zip_amf)
|
||||||
model_object->center_around_origin(false);
|
model_object->center_around_origin(false);
|
||||||
model_object->ensure_on_bed();
|
model_object->ensure_on_bed();
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
// check multi-part object adding for the SLA-printing
|
// check multi-part object adding for the SLA-printing
|
||||||
if (printer_technology == ptSLA) {
|
if (printer_technology == ptSLA) {
|
||||||
|
|
@ -2312,7 +2363,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||||
}
|
}
|
||||||
|
|
||||||
if (one_by_one) {
|
if (one_by_one) {
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
|
||||||
|
#else
|
||||||
auto loaded_idxs = load_model_objects(model.objects);
|
auto loaded_idxs = load_model_objects(model.objects);
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
||||||
} else {
|
} else {
|
||||||
// This must be an .stl or .obj file, which may contain a maximum of one volume.
|
// This must be an .stl or .obj file, which may contain a maximum of one volume.
|
||||||
|
|
@ -2364,7 +2419,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||||
|
|
||||||
// #define AUTOPLACEMENT_ON_LOAD
|
// #define AUTOPLACEMENT_ON_LOAD
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z)
|
||||||
|
#else
|
||||||
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects)
|
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects)
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
{
|
{
|
||||||
const BoundingBoxf bed_shape = bed_shape_bb();
|
const BoundingBoxf bed_shape = bed_shape_bb();
|
||||||
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
|
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
|
||||||
|
|
@ -2441,7 +2500,11 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
||||||
}
|
}
|
||||||
#endif // ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG
|
#endif // ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
object->ensure_on_bed(allow_negative_z);
|
||||||
|
#else
|
||||||
object->ensure_on_bed();
|
object->ensure_on_bed();
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AUTOPLACEMENT_ON_LOAD
|
#ifdef AUTOPLACEMENT_ON_LOAD
|
||||||
|
|
@ -2597,8 +2660,7 @@ int Plater::priv::get_selected_volume_idx() const
|
||||||
void Plater::priv::selection_changed()
|
void Plater::priv::selection_changed()
|
||||||
{
|
{
|
||||||
// if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running
|
// if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running
|
||||||
bool enable_layer_editing = layers_height_allowed();
|
if (!layers_height_allowed() && view3D->is_layers_editing_enabled()) {
|
||||||
if (!enable_layer_editing && view3D->is_layers_editing_enabled()) {
|
|
||||||
SimpleEvent evt(EVT_GLTOOLBAR_LAYERSEDITING);
|
SimpleEvent evt(EVT_GLTOOLBAR_LAYERSEDITING);
|
||||||
on_action_layersediting(evt);
|
on_action_layersediting(evt);
|
||||||
}
|
}
|
||||||
|
|
@ -2735,10 +2797,11 @@ void Plater::priv::split_object()
|
||||||
Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split because it contains only one solid part."));
|
Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split because it contains only one solid part."));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (current_model_object->volumes.size() != new_objects.size())
|
// If we splited object which is contain some parts/modifiers then all non-solid parts (modifiers) were deleted
|
||||||
|
if (current_model_object->volumes.size() > 1 && current_model_object->volumes.size() != new_objects.size())
|
||||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||||
NotificationManager::NotificationLevel::RegularNotification,
|
NotificationManager::NotificationLevel::RegularNotification,
|
||||||
_u8L("All non-solid parts (modifiers) was deleted"));
|
_u8L("All non-solid parts (modifiers) were deleted"));
|
||||||
|
|
||||||
Plater::TakeSnapshot snapshot(q, _L("Split to Objects"));
|
Plater::TakeSnapshot snapshot(q, _L("Split to Objects"));
|
||||||
|
|
||||||
|
|
@ -3054,21 +3117,19 @@ void Plater::priv::reload_from_disk()
|
||||||
int volume_idx;
|
int volume_idx;
|
||||||
|
|
||||||
// operators needed by std::algorithms
|
// operators needed by std::algorithms
|
||||||
bool operator < (const SelectedVolume& other) const { return (object_idx < other.object_idx) || ((object_idx == other.object_idx) && (volume_idx < other.volume_idx)); }
|
bool operator < (const SelectedVolume& other) const { return object_idx < other.object_idx || (object_idx == other.object_idx && volume_idx < other.volume_idx); }
|
||||||
bool operator == (const SelectedVolume& other) const { return (object_idx == other.object_idx) && (volume_idx == other.volume_idx); }
|
bool operator == (const SelectedVolume& other) const { return object_idx == other.object_idx && volume_idx == other.volume_idx; }
|
||||||
};
|
};
|
||||||
std::vector<SelectedVolume> selected_volumes;
|
std::vector<SelectedVolume> selected_volumes;
|
||||||
|
|
||||||
// collects selected ModelVolumes
|
// collects selected ModelVolumes
|
||||||
const std::set<unsigned int>& selected_volumes_idxs = selection.get_volume_idxs();
|
const std::set<unsigned int>& selected_volumes_idxs = selection.get_volume_idxs();
|
||||||
for (unsigned int idx : selected_volumes_idxs)
|
for (unsigned int idx : selected_volumes_idxs) {
|
||||||
{
|
|
||||||
const GLVolume* v = selection.get_volume(idx);
|
const GLVolume* v = selection.get_volume(idx);
|
||||||
int v_idx = v->volume_idx();
|
int v_idx = v->volume_idx();
|
||||||
if (v_idx >= 0)
|
if (v_idx >= 0) {
|
||||||
{
|
|
||||||
int o_idx = v->object_idx();
|
int o_idx = v->object_idx();
|
||||||
if ((0 <= o_idx) && (o_idx < (int)model.objects.size()))
|
if (0 <= o_idx && o_idx < (int)model.objects.size())
|
||||||
selected_volumes.push_back({ o_idx, v_idx });
|
selected_volumes.push_back({ o_idx, v_idx });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3078,13 +3139,11 @@ void Plater::priv::reload_from_disk()
|
||||||
// collects paths of files to load
|
// collects paths of files to load
|
||||||
std::vector<fs::path> input_paths;
|
std::vector<fs::path> input_paths;
|
||||||
std::vector<fs::path> missing_input_paths;
|
std::vector<fs::path> missing_input_paths;
|
||||||
for (const SelectedVolume& v : selected_volumes)
|
for (const SelectedVolume& v : selected_volumes) {
|
||||||
{
|
|
||||||
const ModelObject* object = model.objects[v.object_idx];
|
const ModelObject* object = model.objects[v.object_idx];
|
||||||
const ModelVolume* volume = object->volumes[v.volume_idx];
|
const ModelVolume* volume = object->volumes[v.volume_idx];
|
||||||
|
|
||||||
if (!volume->source.input_file.empty())
|
if (!volume->source.input_file.empty()) {
|
||||||
{
|
|
||||||
if (fs::exists(volume->source.input_file))
|
if (fs::exists(volume->source.input_file))
|
||||||
input_paths.push_back(volume->source.input_file);
|
input_paths.push_back(volume->source.input_file);
|
||||||
else
|
else
|
||||||
|
|
@ -3097,8 +3156,7 @@ void Plater::priv::reload_from_disk()
|
||||||
std::sort(missing_input_paths.begin(), missing_input_paths.end());
|
std::sort(missing_input_paths.begin(), missing_input_paths.end());
|
||||||
missing_input_paths.erase(std::unique(missing_input_paths.begin(), missing_input_paths.end()), missing_input_paths.end());
|
missing_input_paths.erase(std::unique(missing_input_paths.begin(), missing_input_paths.end()), missing_input_paths.end());
|
||||||
|
|
||||||
while (!missing_input_paths.empty())
|
while (!missing_input_paths.empty()) {
|
||||||
{
|
|
||||||
// ask user to select the missing file
|
// ask user to select the missing file
|
||||||
fs::path search = missing_input_paths.back();
|
fs::path search = missing_input_paths.back();
|
||||||
wxString title = _L("Please select the file to reload");
|
wxString title = _L("Please select the file to reload");
|
||||||
|
|
@ -3112,21 +3170,18 @@ void Plater::priv::reload_from_disk()
|
||||||
|
|
||||||
std::string sel_filename_path = dialog.GetPath().ToUTF8().data();
|
std::string sel_filename_path = dialog.GetPath().ToUTF8().data();
|
||||||
std::string sel_filename = fs::path(sel_filename_path).filename().string();
|
std::string sel_filename = fs::path(sel_filename_path).filename().string();
|
||||||
if (boost::algorithm::iequals(search.filename().string(), sel_filename))
|
if (boost::algorithm::iequals(search.filename().string(), sel_filename)) {
|
||||||
{
|
|
||||||
input_paths.push_back(sel_filename_path);
|
input_paths.push_back(sel_filename_path);
|
||||||
missing_input_paths.pop_back();
|
missing_input_paths.pop_back();
|
||||||
|
|
||||||
fs::path sel_path = fs::path(sel_filename_path).remove_filename().string();
|
fs::path sel_path = fs::path(sel_filename_path).remove_filename().string();
|
||||||
|
|
||||||
std::vector<fs::path>::iterator it = missing_input_paths.begin();
|
std::vector<fs::path>::iterator it = missing_input_paths.begin();
|
||||||
while (it != missing_input_paths.end())
|
while (it != missing_input_paths.end()) {
|
||||||
{
|
|
||||||
// try to use the path of the selected file with all remaining missing files
|
// try to use the path of the selected file with all remaining missing files
|
||||||
fs::path repathed_filename = sel_path;
|
fs::path repathed_filename = sel_path;
|
||||||
repathed_filename /= it->filename();
|
repathed_filename /= it->filename();
|
||||||
if (fs::exists(repathed_filename))
|
if (fs::exists(repathed_filename)) {
|
||||||
{
|
|
||||||
input_paths.push_back(repathed_filename.string());
|
input_paths.push_back(repathed_filename.string());
|
||||||
it = missing_input_paths.erase(it);
|
it = missing_input_paths.erase(it);
|
||||||
}
|
}
|
||||||
|
|
@ -3134,8 +3189,7 @@ void Plater::priv::reload_from_disk()
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
wxString message = _L("It is not allowed to change the file to reload") + " (" + from_u8(search.filename().string()) + ").\n" + _L("Do you want to retry") + " ?";
|
wxString message = _L("It is not allowed to change the file to reload") + " (" + from_u8(search.filename().string()) + ").\n" + _L("Do you want to retry") + " ?";
|
||||||
wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
|
wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
|
||||||
if (dlg.ShowModal() != wxID_YES)
|
if (dlg.ShowModal() != wxID_YES)
|
||||||
|
|
@ -3149,8 +3203,7 @@ void Plater::priv::reload_from_disk()
|
||||||
std::vector<wxString> fail_list;
|
std::vector<wxString> fail_list;
|
||||||
|
|
||||||
// load one file at a time
|
// load one file at a time
|
||||||
for (size_t i = 0; i < input_paths.size(); ++i)
|
for (size_t i = 0; i < input_paths.size(); ++i) {
|
||||||
{
|
|
||||||
const auto& path = input_paths[i].string();
|
const auto& path = input_paths[i].string();
|
||||||
|
|
||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
|
|
@ -3160,8 +3213,7 @@ void Plater::priv::reload_from_disk()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
new_model = Model::read_from_file(path, nullptr, true, false);
|
new_model = Model::read_from_file(path, nullptr, true, false);
|
||||||
for (ModelObject* model_object : new_model.objects)
|
for (ModelObject* model_object : new_model.objects) {
|
||||||
{
|
|
||||||
model_object->center_around_origin();
|
model_object->center_around_origin();
|
||||||
model_object->ensure_on_bed();
|
model_object->ensure_on_bed();
|
||||||
}
|
}
|
||||||
|
|
@ -3173,34 +3225,31 @@ void Plater::priv::reload_from_disk()
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the selected volumes whose source is the current file
|
// update the selected volumes whose source is the current file
|
||||||
for (const SelectedVolume& sel_v : selected_volumes)
|
for (const SelectedVolume& sel_v : selected_volumes) {
|
||||||
{
|
|
||||||
ModelObject* old_model_object = model.objects[sel_v.object_idx];
|
ModelObject* old_model_object = model.objects[sel_v.object_idx];
|
||||||
ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx];
|
ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx];
|
||||||
|
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
bool sinking = old_model_object->bounding_box().min.z() < 0.0;
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
|
||||||
bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string());
|
bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string());
|
||||||
bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string());
|
bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string());
|
||||||
if (has_source || has_name)
|
if (has_source || has_name) {
|
||||||
{
|
|
||||||
int new_volume_idx = -1;
|
int new_volume_idx = -1;
|
||||||
int new_object_idx = -1;
|
int new_object_idx = -1;
|
||||||
if (has_source)
|
if (has_source) {
|
||||||
{
|
|
||||||
// take idxs from source
|
// take idxs from source
|
||||||
new_volume_idx = old_volume->source.volume_idx;
|
new_volume_idx = old_volume->source.volume_idx;
|
||||||
new_object_idx = old_volume->source.object_idx;
|
new_object_idx = old_volume->source.object_idx;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
// take idxs from the 1st matching volume
|
// take idxs from the 1st matching volume
|
||||||
for (size_t o = 0; o < new_model.objects.size(); ++o)
|
for (size_t o = 0; o < new_model.objects.size(); ++o) {
|
||||||
{
|
|
||||||
ModelObject* obj = new_model.objects[o];
|
ModelObject* obj = new_model.objects[o];
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (size_t v = 0; v < obj->volumes.size(); ++v)
|
for (size_t v = 0; v < obj->volumes.size(); ++v) {
|
||||||
{
|
if (obj->volumes[v]->name == old_volume->name) {
|
||||||
if (obj->volumes[v]->name == old_volume->name)
|
|
||||||
{
|
|
||||||
new_volume_idx = (int)v;
|
new_volume_idx = (int)v;
|
||||||
new_object_idx = (int)o;
|
new_object_idx = (int)o;
|
||||||
found = true;
|
found = true;
|
||||||
|
|
@ -3212,19 +3261,16 @@ void Plater::priv::reload_from_disk()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((new_object_idx < 0) && ((int)new_model.objects.size() <= new_object_idx))
|
if (new_object_idx < 0 && (int)new_model.objects.size() <= new_object_idx) {
|
||||||
{
|
|
||||||
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
|
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ModelObject* new_model_object = new_model.objects[new_object_idx];
|
ModelObject* new_model_object = new_model.objects[new_object_idx];
|
||||||
if ((new_volume_idx < 0) && ((int)new_model.objects.size() <= new_volume_idx))
|
if (new_volume_idx < 0 && (int)new_model.objects.size() <= new_volume_idx) {
|
||||||
{
|
|
||||||
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
|
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (new_volume_idx < (int)new_model_object->volumes.size())
|
if (new_volume_idx < (int)new_model_object->volumes.size()) {
|
||||||
{
|
|
||||||
old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
|
old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
|
||||||
ModelVolume* new_volume = old_model_object->volumes.back();
|
ModelVolume* new_volume = old_model_object->volumes.back();
|
||||||
new_volume->set_new_unique_id();
|
new_volume->set_new_unique_id();
|
||||||
|
|
@ -3241,7 +3287,10 @@ void Plater::priv::reload_from_disk()
|
||||||
new_volume->seam_facets.assign(old_volume->seam_facets);
|
new_volume->seam_facets.assign(old_volume->seam_facets);
|
||||||
std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back());
|
std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back());
|
||||||
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
|
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
|
||||||
old_model_object->ensure_on_bed();
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
if (!sinking)
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
old_model_object->ensure_on_bed();
|
||||||
|
|
||||||
sla::reproject_points_and_holes(old_model_object);
|
sla::reproject_points_and_holes(old_model_object);
|
||||||
}
|
}
|
||||||
|
|
@ -3249,11 +3298,9 @@ void Plater::priv::reload_from_disk()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fail_list.empty())
|
if (!fail_list.empty()) {
|
||||||
{
|
|
||||||
wxString message = _L("Unable to reload:") + "\n";
|
wxString message = _L("Unable to reload:") + "\n";
|
||||||
for (const wxString& s : fail_list)
|
for (const wxString& s : fail_list) {
|
||||||
{
|
|
||||||
message += s + "\n";
|
message += s + "\n";
|
||||||
}
|
}
|
||||||
wxMessageDialog dlg(q, message, _L("Error during reload"), wxOK | wxOK_DEFAULT | wxICON_WARNING);
|
wxMessageDialog dlg(q, message, _L("Error during reload"), wxOK | wxOK_DEFAULT | wxICON_WARNING);
|
||||||
|
|
@ -3264,8 +3311,7 @@ void Plater::priv::reload_from_disk()
|
||||||
update();
|
update();
|
||||||
|
|
||||||
// new GLVolumes have been created at this point, so update their printable state
|
// new GLVolumes have been created at this point, so update their printable state
|
||||||
for (size_t i = 0; i < model.objects.size(); ++i)
|
for (size_t i = 0; i < model.objects.size(); ++i) {
|
||||||
{
|
|
||||||
view3D->get_canvas3d()->update_instance_printable_state_for_object(i);
|
view3D->get_canvas3d()->update_instance_printable_state_for_object(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3285,8 +3331,7 @@ void Plater::priv::reload_all_from_disk()
|
||||||
reload_from_disk();
|
reload_from_disk();
|
||||||
// restore previous selection
|
// restore previous selection
|
||||||
selection.clear();
|
selection.clear();
|
||||||
for (unsigned int idx : curr_idxs)
|
for (unsigned int idx : curr_idxs) {
|
||||||
{
|
|
||||||
selection.add(idx, false);
|
selection.add(idx, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4006,7 +4051,7 @@ void Plater::priv::reset_gcode_toolpaths()
|
||||||
bool Plater::priv::can_set_instance_to_object() const
|
bool Plater::priv::can_set_instance_to_object() const
|
||||||
{
|
{
|
||||||
const int obj_idx = get_selected_object_idx();
|
const int obj_idx = get_selected_object_idx();
|
||||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1);
|
return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->instances.size() > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Plater::priv::can_split(bool to_objects) const
|
bool Plater::priv::can_split(bool to_objects) const
|
||||||
|
|
@ -4020,7 +4065,12 @@ bool Plater::priv::layers_height_allowed() const
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int obj_idx = get_selected_object_idx();
|
int obj_idx = get_selected_object_idx();
|
||||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed();
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->bounding_box().max.z() > 0.0 &&
|
||||||
|
config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed();
|
||||||
|
#else
|
||||||
|
return 0 <= obj_idx && obj_idx < (int)model.objects.size() && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed();
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Plater::priv::can_mirror() const
|
bool Plater::priv::can_mirror() const
|
||||||
|
|
@ -4213,9 +4263,9 @@ int Plater::priv::get_active_snapshot_index()
|
||||||
|
|
||||||
void Plater::priv::take_snapshot(const std::string& snapshot_name)
|
void Plater::priv::take_snapshot(const std::string& snapshot_name)
|
||||||
{
|
{
|
||||||
if (this->m_prevent_snapshots > 0)
|
if (m_prevent_snapshots > 0)
|
||||||
return;
|
return;
|
||||||
assert(this->m_prevent_snapshots >= 0);
|
assert(m_prevent_snapshots >= 0);
|
||||||
UndoRedo::SnapshotData snapshot_data;
|
UndoRedo::SnapshotData snapshot_data;
|
||||||
snapshot_data.printer_technology = this->printer_technology;
|
snapshot_data.printer_technology = this->printer_technology;
|
||||||
if (this->view3D->is_layers_editing_enabled())
|
if (this->view3D->is_layers_editing_enabled())
|
||||||
|
|
@ -4245,6 +4295,11 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name)
|
||||||
}
|
}
|
||||||
this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), snapshot_data);
|
this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), snapshot_data);
|
||||||
this->undo_redo_stack().release_least_recently_used();
|
this->undo_redo_stack().release_least_recently_used();
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::TakeSnapshot);
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
// Save the last active preset name of a particular printer technology.
|
// Save the last active preset name of a particular printer technology.
|
||||||
((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name();
|
((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name();
|
||||||
BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack().memsize()) << log_memory_info();
|
BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot taken: " << snapshot_name << ", Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack().memsize()) << log_memory_info();
|
||||||
|
|
@ -4285,8 +4340,13 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
||||||
if (printer_technology_changed) {
|
if (printer_technology_changed) {
|
||||||
// Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type.
|
// Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type.
|
||||||
std::string s_pt = (it_snapshot->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA";
|
std::string s_pt = (it_snapshot->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA";
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (!wxGetApp().check_and_save_current_preset_changes(format_wxstr(_L(
|
||||||
|
"%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
|
||||||
|
#else
|
||||||
if (! wxGetApp().check_unsaved_changes(format_wxstr(_L(
|
if (! wxGetApp().check_unsaved_changes(format_wxstr(_L(
|
||||||
"%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
|
"%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
// Don't switch the profiles.
|
// Don't switch the profiles.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -4375,6 +4435,10 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
||||||
if (! view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active)
|
if (! view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active)
|
||||||
view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting"));
|
view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::UndoRedoTo);
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */)
|
void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */)
|
||||||
|
|
@ -4458,9 +4522,16 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame)
|
||||||
// Initialization performed in the private c-tor
|
// Initialization performed in the private c-tor
|
||||||
}
|
}
|
||||||
|
|
||||||
Plater::~Plater()
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
{
|
bool Plater::is_project_dirty() const { return p->is_project_dirty(); }
|
||||||
}
|
void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); }
|
||||||
|
bool Plater::save_project_if_dirty() { return p->save_project_if_dirty(); }
|
||||||
|
void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); }
|
||||||
|
void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_initial_presets(); }
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
Sidebar& Plater::sidebar() { return *p->sidebar; }
|
Sidebar& Plater::sidebar() { return *p->sidebar; }
|
||||||
Model& Plater::model() { return p->model; }
|
Model& Plater::model() { return p->model; }
|
||||||
|
|
@ -4471,12 +4542,30 @@ SLAPrint& Plater::sla_print() { return p->sla_print; }
|
||||||
|
|
||||||
void Plater::new_project()
|
void Plater::new_project()
|
||||||
{
|
{
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (!p->save_project_if_dirty())
|
||||||
|
return;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
p->select_view_3D("3D");
|
p->select_view_3D("3D");
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
take_snapshot(_L("New Project"));
|
||||||
|
Plater::SuppressSnapshots suppress(this);
|
||||||
|
reset();
|
||||||
|
reset_project_dirty_initial_presets();
|
||||||
|
update_project_dirty_from_presets();
|
||||||
|
#else
|
||||||
wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL));
|
wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL));
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::load_project()
|
void Plater::load_project()
|
||||||
{
|
{
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (!p->save_project_if_dirty())
|
||||||
|
return;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
// Ask user for a project file name.
|
// Ask user for a project file name.
|
||||||
wxString input_file;
|
wxString input_file;
|
||||||
wxGetApp().load_project(this, input_file);
|
wxGetApp().load_project(this, input_file);
|
||||||
|
|
@ -4500,8 +4589,16 @@ void Plater::load_project(const wxString& filename)
|
||||||
std::vector<size_t> res = load_files(input_paths);
|
std::vector<size_t> res = load_files(input_paths);
|
||||||
|
|
||||||
// if res is empty no data has been loaded
|
// if res is empty no data has been loaded
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (!res.empty()) {
|
||||||
|
p->set_project_filename(filename);
|
||||||
|
reset_project_dirty_initial_presets();
|
||||||
|
update_project_dirty_from_presets();
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (!res.empty())
|
if (!res.empty())
|
||||||
p->set_project_filename(filename);
|
p->set_project_filename(filename);
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::add_model(bool imperial_units/* = false*/)
|
void Plater::add_model(bool imperial_units/* = false*/)
|
||||||
|
|
@ -4532,7 +4629,13 @@ void Plater::add_model(bool imperial_units/* = false*/)
|
||||||
}
|
}
|
||||||
|
|
||||||
Plater::TakeSnapshot snapshot(this, snapshot_label);
|
Plater::TakeSnapshot snapshot(this, snapshot_label);
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
std::vector<size_t> res = load_files(paths, true, false, imperial_units);
|
||||||
|
if (!res.empty())
|
||||||
|
wxGetApp().mainframe->update_title();
|
||||||
|
#else
|
||||||
load_files(paths, true, false, imperial_units);
|
load_files(paths, true, false, imperial_units);
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::import_sl1_archive()
|
void Plater::import_sl1_archive()
|
||||||
|
|
@ -5247,24 +5350,39 @@ void Plater::export_amf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||||
|
#else
|
||||||
void Plater::export_3mf(const boost::filesystem::path& output_path)
|
void Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
{
|
{
|
||||||
if (p->model.objects.empty()
|
if (p->model.objects.empty()
|
||||||
|| canvas3D()->get_gizmos_manager().is_in_editing_mode(true))
|
|| canvas3D()->get_gizmos_manager().is_in_editing_mode(true))
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
return;
|
return;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
wxString path;
|
wxString path;
|
||||||
bool export_config = true;
|
bool export_config = true;
|
||||||
if (output_path.empty())
|
if (output_path.empty()) {
|
||||||
{
|
|
||||||
path = p->get_export_file(FT_3MF);
|
path = p->get_export_file(FT_3MF);
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
if (path.empty()) { return false; }
|
||||||
|
#else
|
||||||
if (path.empty()) { return; }
|
if (path.empty()) { return; }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
path = from_path(output_path);
|
path = from_path(output_path);
|
||||||
|
|
||||||
if (!path.Lower().EndsWith(".3mf"))
|
if (!path.Lower().EndsWith(".3mf"))
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
return;
|
return;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||||
const std::string path_u8 = into_u8(path);
|
const std::string path_u8 = into_u8(path);
|
||||||
|
|
@ -5272,6 +5390,19 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||||
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
|
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
|
||||||
ThumbnailData thumbnail_data;
|
ThumbnailData thumbnail_data;
|
||||||
p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true);
|
p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true);
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool ret = Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data);
|
||||||
|
if (ret) {
|
||||||
|
// Success
|
||||||
|
p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path));
|
||||||
|
p->set_project_filename(path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Failure
|
||||||
|
p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
#else
|
||||||
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) {
|
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) {
|
||||||
// Success
|
// Success
|
||||||
p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path));
|
p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path));
|
||||||
|
|
@ -5281,6 +5412,7 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||||
// Failure
|
// Failure
|
||||||
p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
|
p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::reload_from_disk()
|
void Plater::reload_from_disk()
|
||||||
|
|
@ -5737,7 +5869,7 @@ wxString Plater::get_project_filename(const wxString& extension) const
|
||||||
|
|
||||||
void Plater::set_project_filename(const wxString& filename)
|
void Plater::set_project_filename(const wxString& filename)
|
||||||
{
|
{
|
||||||
return p->set_project_filename(filename);
|
p->set_project_filename(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Plater::is_export_gcode_scheduled() const
|
bool Plater::is_export_gcode_scheduled() const
|
||||||
|
|
@ -5817,6 +5949,14 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology)
|
||||||
//FIXME for SLA synchronize
|
//FIXME for SLA synchronize
|
||||||
//p->background_process.apply(Model)!
|
//p->background_process.apply(Model)!
|
||||||
|
|
||||||
|
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
if (printer_technology == ptSLA) {
|
||||||
|
for (ModelObject* model_object : p->model.objects) {
|
||||||
|
model_object->ensure_on_bed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
|
||||||
p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export");
|
p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export");
|
||||||
p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer");
|
p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer");
|
||||||
|
|
||||||
|
|
@ -5836,7 +5976,15 @@ void Plater::changed_object(int obj_idx)
|
||||||
return;
|
return;
|
||||||
// recenter and re - align to Z = 0
|
// recenter and re - align to Z = 0
|
||||||
auto model_object = p->model.objects[obj_idx];
|
auto model_object = p->model.objects[obj_idx];
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
model_object->ensure_on_bed(this->p->printer_technology != ptSLA);
|
||||||
|
#else
|
||||||
|
model_object->ensure_on_bed(true);
|
||||||
|
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
#else
|
||||||
model_object->ensure_on_bed();
|
model_object->ensure_on_bed();
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
if (this->p->printer_technology == ptSLA) {
|
if (this->p->printer_technology == ptSLA) {
|
||||||
// Update the SLAPrint from the current Model, so that the reload_scene()
|
// Update the SLAPrint from the current Model, so that the reload_scene()
|
||||||
// pulls the correct data, update the 3D scene.
|
// pulls the correct data, update the 3D scene.
|
||||||
|
|
@ -5854,11 +6002,18 @@ void Plater::changed_objects(const std::vector<size_t>& object_idxs)
|
||||||
if (object_idxs.empty())
|
if (object_idxs.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (size_t obj_idx : object_idxs)
|
for (size_t obj_idx : object_idxs) {
|
||||||
{
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
if (obj_idx < p->model.objects.size()) {
|
||||||
|
if (p->model.objects[obj_idx]->bounding_box().min.z() >= 0.0)
|
||||||
|
// re - align to Z = 0
|
||||||
|
p->model.objects[obj_idx]->ensure_on_bed();
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (obj_idx < p->model.objects.size())
|
if (obj_idx < p->model.objects.size())
|
||||||
// recenter and re - align to Z = 0
|
// recenter and re - align to Z = 0
|
||||||
p->model.objects[obj_idx]->ensure_on_bed();
|
p->model.objects[obj_idx]->ensure_on_bed();
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
}
|
}
|
||||||
if (this->p->printer_technology == ptSLA) {
|
if (this->p->printer_technology == ptSLA) {
|
||||||
// Update the SLAPrint from the current Model, so that the reload_scene()
|
// Update the SLAPrint from the current Model, so that the reload_scene()
|
||||||
|
|
@ -6141,6 +6296,9 @@ bool Plater::can_mirror() const { return p->can_mirror(); }
|
||||||
bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); }
|
bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); }
|
||||||
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
|
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
|
||||||
void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); }
|
void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); }
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
const UndoRedo::Stack& Plater::undo_redo_stack_active() const { return p->undo_redo_stack(); }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
|
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
|
||||||
void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); }
|
void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); }
|
||||||
bool Plater::inside_snapshot_capture() { return p->inside_snapshot_capture(); }
|
bool Plater::inside_snapshot_capture() { return p->inside_snapshot_capture(); }
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,18 @@ public:
|
||||||
Plater(const Plater &) = delete;
|
Plater(const Plater &) = delete;
|
||||||
Plater &operator=(Plater &&) = delete;
|
Plater &operator=(Plater &&) = delete;
|
||||||
Plater &operator=(const Plater &) = delete;
|
Plater &operator=(const Plater &) = delete;
|
||||||
~Plater();
|
~Plater() = default;
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool is_project_dirty() const;
|
||||||
|
void update_project_dirty_from_presets();
|
||||||
|
bool save_project_if_dirty();
|
||||||
|
void reset_project_dirty_after_save();
|
||||||
|
void reset_project_dirty_initial_presets();
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
void render_project_state_debug_window() const;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
Sidebar& sidebar();
|
Sidebar& sidebar();
|
||||||
Model& model();
|
Model& model();
|
||||||
|
|
@ -198,7 +209,11 @@ public:
|
||||||
void export_gcode(bool prefer_removable);
|
void export_gcode(bool prefer_removable);
|
||||||
void export_stl(bool extended = false, bool selection_only = false);
|
void export_stl(bool extended = false, bool selection_only = false);
|
||||||
void export_amf();
|
void export_amf();
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
|
||||||
|
#else
|
||||||
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
|
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
void reload_from_disk();
|
void reload_from_disk();
|
||||||
void reload_all_from_disk();
|
void reload_all_from_disk();
|
||||||
bool has_toolpaths_to_export() const;
|
bool has_toolpaths_to_export() const;
|
||||||
|
|
@ -228,6 +243,9 @@ public:
|
||||||
// For the memory statistics.
|
// For the memory statistics.
|
||||||
const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const;
|
const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const;
|
||||||
void clear_undo_redo_stack_main();
|
void clear_undo_redo_stack_main();
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
const Slic3r::UndoRedo::Stack& undo_redo_stack_active() const;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
// Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo.
|
// Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo.
|
||||||
void enter_gizmos_stack();
|
void enter_gizmos_stack();
|
||||||
void leave_gizmos_stack();
|
void leave_gizmos_stack();
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ void PresetComboBox::OnSelect(wxCommandEvent& evt)
|
||||||
|
|
||||||
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
|
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
|
||||||
if (marker >= LABEL_ITEM_DISABLED && marker < LABEL_ITEM_MAX)
|
if (marker >= LABEL_ITEM_DISABLED && marker < LABEL_ITEM_MAX)
|
||||||
this->SetSelection(this->m_last_selected);
|
this->SetSelection(m_last_selected);
|
||||||
else if (on_selection_changed && (m_last_selected != selected_item || m_collection->current_is_dirty())) {
|
else if (on_selection_changed && (m_last_selected != selected_item || m_collection->current_is_dirty())) {
|
||||||
m_last_selected = selected_item;
|
m_last_selected = selected_item;
|
||||||
on_selection_changed(selected_item);
|
on_selection_changed(selected_item);
|
||||||
|
|
@ -698,7 +698,7 @@ void PlaterPresetComboBox::OnSelect(wxCommandEvent &evt)
|
||||||
|
|
||||||
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
|
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
|
||||||
if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) {
|
if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) {
|
||||||
this->SetSelection(this->m_last_selected);
|
this->SetSelection(m_last_selected);
|
||||||
evt.StopPropagation();
|
evt.StopPropagation();
|
||||||
if (marker == LABEL_ITEM_MARKER)
|
if (marker == LABEL_ITEM_MARKER)
|
||||||
return;
|
return;
|
||||||
|
|
@ -715,8 +715,8 @@ void PlaterPresetComboBox::OnSelect(wxCommandEvent &evt)
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (marker == LABEL_ITEM_PHYSICAL_PRINTER || this->m_last_selected != selected_item || m_collection->current_is_dirty())
|
else if (marker == LABEL_ITEM_PHYSICAL_PRINTER || m_last_selected != selected_item || m_collection->current_is_dirty())
|
||||||
this->m_last_selected = selected_item;
|
m_last_selected = selected_item;
|
||||||
|
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
}
|
}
|
||||||
|
|
@ -973,7 +973,7 @@ void TabPresetComboBox::OnSelect(wxCommandEvent &evt)
|
||||||
|
|
||||||
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
|
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
|
||||||
if (marker >= LABEL_ITEM_DISABLED && marker < LABEL_ITEM_MAX) {
|
if (marker >= LABEL_ITEM_DISABLED && marker < LABEL_ITEM_MAX) {
|
||||||
this->SetSelection(this->m_last_selected);
|
this->SetSelection(m_last_selected);
|
||||||
if (marker == LABEL_ITEM_WIZARD_PRINTERS)
|
if (marker == LABEL_ITEM_WIZARD_PRINTERS)
|
||||||
wxTheApp->CallAfter([this]() {
|
wxTheApp->CallAfter([this]() {
|
||||||
wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS);
|
wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS);
|
||||||
|
|
|
||||||
415
src/slic3r/GUI/ProjectDirtyStateManager.cpp
Normal file
415
src/slic3r/GUI/ProjectDirtyStateManager.cpp
Normal file
|
|
@ -0,0 +1,415 @@
|
||||||
|
#include "libslic3r/libslic3r.h"
|
||||||
|
|
||||||
|
#include "ProjectDirtyStateManager.hpp"
|
||||||
|
#include "ImGuiWrapper.hpp"
|
||||||
|
#include "GUI_App.hpp"
|
||||||
|
#include "MainFrame.hpp"
|
||||||
|
#include "I18N.hpp"
|
||||||
|
#include "Plater.hpp"
|
||||||
|
#include "../Utils/UndoRedo.hpp"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
enum class EStackType
|
||||||
|
{
|
||||||
|
Main,
|
||||||
|
Gizmo
|
||||||
|
};
|
||||||
|
|
||||||
|
// returns the current active snapshot (the topmost snapshot in the undo part of the stack) in the given stack
|
||||||
|
static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stack) {
|
||||||
|
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
||||||
|
const size_t active_snapshot_time = stack.active_snapshot_time();
|
||||||
|
const auto it = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot_time));
|
||||||
|
const int idx = it - snapshots.begin() - 1;
|
||||||
|
const Slic3r::UndoRedo::Snapshot* ret = (0 <= idx && (size_t)idx < snapshots.size() - 1) ?
|
||||||
|
&snapshots[idx] : nullptr;
|
||||||
|
|
||||||
|
assert(ret != nullptr);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the last saveable snapshot (the topmost snapshot in the undo part of the stack that can be saved) in the given stack
|
||||||
|
static const UndoRedo::Snapshot* get_last_saveable_snapshot(EStackType type, const UndoRedo::Stack& stack,
|
||||||
|
const ProjectDirtyStateManager::DirtyState::Gizmos& gizmos, size_t last_save_main) {
|
||||||
|
|
||||||
|
// returns true if the given snapshot is not saveable
|
||||||
|
auto skip_main = [&gizmos, last_save_main, &stack](const UndoRedo::Snapshot& snapshot) {
|
||||||
|
auto is_gizmo_with_modifications = [&gizmos, &stack](const UndoRedo::Snapshot& snapshot) {
|
||||||
|
if (boost::starts_with(snapshot.name, _utf8("Entering"))) {
|
||||||
|
if (gizmos.current)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
std::string topmost_redo;
|
||||||
|
wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo);
|
||||||
|
if (boost::starts_with(topmost_redo, _utf8("Leaving"))) {
|
||||||
|
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
||||||
|
const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(snapshot.timestamp + 1)));
|
||||||
|
if (gizmos.is_used_and_modified(*leaving_snapshot))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (snapshot.name == _utf8("New Project"))
|
||||||
|
return true;
|
||||||
|
else if (snapshot.name == _utf8("Reset Project"))
|
||||||
|
return true;
|
||||||
|
else if (boost::starts_with(snapshot.name, _utf8("Load Project")))
|
||||||
|
return true;
|
||||||
|
else if (boost::starts_with(snapshot.name, _utf8("Selection")))
|
||||||
|
return true;
|
||||||
|
else if (boost::starts_with(snapshot.name, _utf8("Entering"))) {
|
||||||
|
if (last_save_main != snapshot.timestamp + 1 && !is_gizmo_with_modifications(snapshot))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (boost::starts_with(snapshot.name, _utf8("Leaving"))) {
|
||||||
|
if (last_save_main != snapshot.timestamp && !gizmos.is_used_and_modified(snapshot))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// returns true if the given snapshot is not saveable
|
||||||
|
auto skip_gizmo = [](const UndoRedo::Snapshot& snapshot) {
|
||||||
|
// put here any needed condition to skip the snapshot
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const UndoRedo::Snapshot* curr = get_active_snapshot(stack);
|
||||||
|
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
||||||
|
size_t shift = 1;
|
||||||
|
while (curr->timestamp > 0 && ((type == EStackType::Main && skip_main(*curr)) || (type == EStackType::Gizmo && skip_gizmo(*curr)))) {
|
||||||
|
const UndoRedo::Snapshot* temp = curr;
|
||||||
|
curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp - shift)));
|
||||||
|
shift = (curr == temp) ? shift + 1 : 1;
|
||||||
|
}
|
||||||
|
if (type == EStackType::Main) {
|
||||||
|
if (boost::starts_with(curr->name, _utf8("Entering")) && last_save_main == curr->timestamp + 1) {
|
||||||
|
std::string topmost_redo;
|
||||||
|
wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo);
|
||||||
|
if (boost::starts_with(topmost_redo, _utf8("Leaving")))
|
||||||
|
curr = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(curr->timestamp + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return curr->timestamp > 0 ? curr : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the name of the gizmo contained in the given string
|
||||||
|
static std::string extract_gizmo_name(const std::string& s) {
|
||||||
|
static const std::array<std::string, 2> prefixes = { _utf8("Entering"), _utf8("Leaving") };
|
||||||
|
|
||||||
|
std::string ret;
|
||||||
|
for (const std::string& prefix : prefixes) {
|
||||||
|
if (boost::starts_with(s, prefix))
|
||||||
|
ret = s.substr(prefix.length() + 1);
|
||||||
|
|
||||||
|
if (!ret.empty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::DirtyState::Gizmos::add_used(const UndoRedo::Snapshot& snapshot)
|
||||||
|
{
|
||||||
|
const std::string name = extract_gizmo_name(snapshot.name);
|
||||||
|
auto it = used.find(name);
|
||||||
|
if (it == used.end())
|
||||||
|
it = used.insert({ name, { {} } }).first;
|
||||||
|
|
||||||
|
it->second.modified_timestamps.push_back(snapshot.timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::DirtyState::Gizmos::remove_obsolete_used(const Slic3r::UndoRedo::Stack& main_stack)
|
||||||
|
{
|
||||||
|
const std::vector<UndoRedo::Snapshot>& snapshots = main_stack.snapshots();
|
||||||
|
for (auto& item : used) {
|
||||||
|
auto it = item.second.modified_timestamps.begin();
|
||||||
|
while (it != item.second.modified_timestamps.end()) {
|
||||||
|
size_t timestamp = *it;
|
||||||
|
auto snapshot_it = std::find_if(snapshots.begin(), snapshots.end(), [timestamp](const Slic3r::UndoRedo::Snapshot& snapshot) { return snapshot.timestamp == timestamp; });
|
||||||
|
if (snapshot_it == snapshots.end())
|
||||||
|
it = item.second.modified_timestamps.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
bool ProjectDirtyStateManager::DirtyState::Gizmos::any_used_modified() const
|
||||||
|
{
|
||||||
|
for (auto& [name, gizmo] : used) {
|
||||||
|
if (!gizmo.modified_timestamps.empty())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
|
||||||
|
// returns true if the given snapshot is contained in any of the gizmos caches
|
||||||
|
bool ProjectDirtyStateManager::DirtyState::Gizmos::is_used_and_modified(const UndoRedo::Snapshot& snapshot) const
|
||||||
|
{
|
||||||
|
for (const auto& item : used) {
|
||||||
|
for (size_t i : item.second.modified_timestamps) {
|
||||||
|
if (i == snapshot.timestamp)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::DirtyState::Gizmos::reset()
|
||||||
|
{
|
||||||
|
used.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type)
|
||||||
|
{
|
||||||
|
if (!wxGetApp().initialized())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Plater* plater = wxGetApp().plater();
|
||||||
|
if (plater == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main();
|
||||||
|
const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active();
|
||||||
|
|
||||||
|
if (&main_stack == &active_stack)
|
||||||
|
update_from_undo_redo_main_stack(type, main_stack);
|
||||||
|
else
|
||||||
|
update_from_undo_redo_gizmo_stack(type, active_stack);
|
||||||
|
|
||||||
|
wxGetApp().mainframe->update_title();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::update_from_presets()
|
||||||
|
{
|
||||||
|
m_state.presets = false;
|
||||||
|
std::vector<std::pair<unsigned int, std::string>> selected_presets = wxGetApp().get_selected_presets();
|
||||||
|
for (const auto& [type, name] : selected_presets) {
|
||||||
|
m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
|
||||||
|
}
|
||||||
|
m_state.presets |= wxGetApp().has_unsaved_preset_changes();
|
||||||
|
wxGetApp().mainframe->update_title();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::reset_after_save()
|
||||||
|
{
|
||||||
|
const Plater* plater = wxGetApp().plater();
|
||||||
|
const UndoRedo::Stack& main_stack = plater->undo_redo_stack_main();
|
||||||
|
const UndoRedo::Stack& active_stack = plater->undo_redo_stack_active();
|
||||||
|
|
||||||
|
if (&main_stack == &active_stack) {
|
||||||
|
const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main);
|
||||||
|
m_last_save.main = (saveable_snapshot != nullptr) ? saveable_snapshot->timestamp : 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack);
|
||||||
|
if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) {
|
||||||
|
if (m_state.gizmos.current)
|
||||||
|
m_last_save.main = main_active_snapshot->timestamp + 1;
|
||||||
|
}
|
||||||
|
const UndoRedo::Snapshot* saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, active_stack, m_state.gizmos, m_last_save.main);
|
||||||
|
m_last_save.gizmo = saveable_snapshot->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_initial_presets();
|
||||||
|
m_state.reset();
|
||||||
|
wxGetApp().mainframe->update_title();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::reset_initial_presets()
|
||||||
|
{
|
||||||
|
m_initial_presets = std::array<std::string, Preset::TYPE_COUNT>();
|
||||||
|
std::vector<std::pair<unsigned int, std::string>> selected_presets = wxGetApp().get_selected_presets();
|
||||||
|
for (const auto& [type, name] : selected_presets) {
|
||||||
|
m_initial_presets[type] = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
void ProjectDirtyStateManager::render_debug_window() const
|
||||||
|
{
|
||||||
|
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||||
|
|
||||||
|
auto color = [](bool value) {
|
||||||
|
return value ? ImVec4(1.0f, 0.49f, 0.216f, 1.0f) /* orange */: ImVec4(1.0f, 1.0f, 1.0f, 1.0f) /* white */;
|
||||||
|
};
|
||||||
|
auto bool_to_text = [](bool value) {
|
||||||
|
return value ? "true" : "false";
|
||||||
|
};
|
||||||
|
auto append_bool_item = [color, bool_to_text, &imgui](const std::string& name, bool value) {
|
||||||
|
imgui.text_colored(color(value), name);
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text_colored(color(value), bool_to_text(value));
|
||||||
|
};
|
||||||
|
auto append_int_item = [&imgui](const std::string& name, int value) {
|
||||||
|
imgui.text(name);
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text(std::to_string(value));
|
||||||
|
};
|
||||||
|
auto append_snapshot_item = [&imgui](const std::string& label, const UndoRedo::Snapshot* snapshot) {
|
||||||
|
imgui.text(label);
|
||||||
|
ImGui::SameLine(100);
|
||||||
|
if (snapshot != nullptr)
|
||||||
|
imgui.text(snapshot->name + " (" + std::to_string(snapshot->timestamp) + ")");
|
||||||
|
else
|
||||||
|
imgui.text("-");
|
||||||
|
};
|
||||||
|
|
||||||
|
imgui.begin(std::string("Project dirty state statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Dirty state", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
append_bool_item("Overall:", is_dirty());
|
||||||
|
ImGui::Separator();
|
||||||
|
append_bool_item("Plater:", m_state.plater);
|
||||||
|
append_bool_item("Presets:", m_state.presets);
|
||||||
|
append_bool_item("Current gizmo:", m_state.gizmos.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Last save timestamps", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
append_int_item("Main:", m_last_save.main);
|
||||||
|
append_int_item("Current gizmo:", m_last_save.gizmo);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UndoRedo::Stack& main_stack = wxGetApp().plater()->undo_redo_stack_main();
|
||||||
|
const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack);
|
||||||
|
const UndoRedo::Snapshot* main_last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, main_stack, m_state.gizmos, m_last_save.main);
|
||||||
|
const std::vector<UndoRedo::Snapshot>& main_snapshots = main_stack.snapshots();
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Main snapshots", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
append_snapshot_item("Active:", main_active_snapshot);
|
||||||
|
append_snapshot_item("Last saveable:", main_last_saveable_snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::CollapsingHeader("Main undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
for (const UndoRedo::Snapshot& snapshot : main_snapshots) {
|
||||||
|
bool active = main_active_snapshot->timestamp == snapshot.timestamp;
|
||||||
|
imgui.text_colored(color(active), snapshot.name);
|
||||||
|
ImGui::SameLine(150);
|
||||||
|
imgui.text_colored(color(active), " (" + std::to_string(snapshot.timestamp) + ")");
|
||||||
|
if (&snapshot == main_last_saveable_snapshot) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text_colored(color(active), " (S)");
|
||||||
|
}
|
||||||
|
if (m_last_save.main > 0 && m_last_save.main == snapshot.timestamp) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text_colored(color(active), " (LS)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const UndoRedo::Stack& active_stack = wxGetApp().plater()->undo_redo_stack_active();
|
||||||
|
if (&active_stack != &main_stack) {
|
||||||
|
if (ImGui::CollapsingHeader("Gizmo undo/redo stack", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
const UndoRedo::Snapshot* active_active_snapshot = get_active_snapshot(active_stack);
|
||||||
|
const std::vector<UndoRedo::Snapshot>& active_snapshots = active_stack.snapshots();
|
||||||
|
for (const UndoRedo::Snapshot& snapshot : active_snapshots) {
|
||||||
|
bool active = active_active_snapshot->timestamp == snapshot.timestamp;
|
||||||
|
imgui.text_colored(color(active), snapshot.name);
|
||||||
|
ImGui::SameLine(150);
|
||||||
|
imgui.text_colored(color(active), " (" + std::to_string(snapshot.timestamp) + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state.gizmos.any_used_modified()) {
|
||||||
|
if (ImGui::CollapsingHeader("Gizmos", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
ImGui::Indent(10.0f);
|
||||||
|
for (const auto& [name, gizmo] : m_state.gizmos.used) {
|
||||||
|
if (!gizmo.modified_timestamps.empty()) {
|
||||||
|
if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
std::string modified_timestamps;
|
||||||
|
for (size_t i = 0; i < gizmo.modified_timestamps.size(); ++i) {
|
||||||
|
if (i > 0)
|
||||||
|
modified_timestamps += " | ";
|
||||||
|
modified_timestamps += std::to_string(gizmo.modified_timestamps[i]);
|
||||||
|
}
|
||||||
|
imgui.text(modified_timestamps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Unindent(10.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imgui.end();
|
||||||
|
}
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::update_from_undo_redo_main_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack)
|
||||||
|
{
|
||||||
|
m_state.plater = false;
|
||||||
|
|
||||||
|
if (type == UpdateType::TakeSnapshot) {
|
||||||
|
if (m_last_save.main != 0) {
|
||||||
|
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
||||||
|
auto snapshot_it = std::find_if(snapshots.begin(), snapshots.end(), [this](const Slic3r::UndoRedo::Snapshot& snapshot) { return snapshot.timestamp == m_last_save.main; });
|
||||||
|
if (snapshot_it == snapshots.end())
|
||||||
|
m_last_save.main = 0;
|
||||||
|
}
|
||||||
|
m_state.gizmos.remove_obsolete_used(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack);
|
||||||
|
if (active_snapshot->name == _utf8("New Project") ||
|
||||||
|
active_snapshot->name == _utf8("Reset Project") ||
|
||||||
|
boost::starts_with(active_snapshot->name, _utf8("Load Project")))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (boost::starts_with(active_snapshot->name, _utf8("Entering"))) {
|
||||||
|
if (type == UpdateType::UndoRedoTo) {
|
||||||
|
std::string topmost_redo;
|
||||||
|
wxGetApp().plater()->undo_redo_topmost_string_getter(false, topmost_redo);
|
||||||
|
if (boost::starts_with(topmost_redo, _utf8("Leaving"))) {
|
||||||
|
const std::vector<UndoRedo::Snapshot>& snapshots = stack.snapshots();
|
||||||
|
const UndoRedo::Snapshot* leaving_snapshot = &(*std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot->timestamp + 1)));
|
||||||
|
if (m_state.gizmos.is_used_and_modified(*leaving_snapshot)) {
|
||||||
|
m_state.plater = (leaving_snapshot != nullptr && leaving_snapshot->timestamp != m_last_save.main);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_state.gizmos.current = false;
|
||||||
|
m_last_save.gizmo = 0;
|
||||||
|
}
|
||||||
|
else if (boost::starts_with(active_snapshot->name, _utf8("Leaving"))) {
|
||||||
|
if (m_state.gizmos.current)
|
||||||
|
m_state.gizmos.add_used(*active_snapshot);
|
||||||
|
m_state.gizmos.current = false;
|
||||||
|
m_last_save.gizmo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Main, stack, m_state.gizmos, m_last_save.main);
|
||||||
|
m_state.plater = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.main);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack)
|
||||||
|
{
|
||||||
|
m_state.gizmos.current = false;
|
||||||
|
|
||||||
|
const UndoRedo::Snapshot* active_snapshot = get_active_snapshot(stack);
|
||||||
|
if (active_snapshot->name == "Gizmos-Initial")
|
||||||
|
return;
|
||||||
|
|
||||||
|
const UndoRedo::Snapshot* last_saveable_snapshot = get_last_saveable_snapshot(EStackType::Gizmo, stack, m_state.gizmos, m_last_save.main);
|
||||||
|
m_state.gizmos.current = (last_saveable_snapshot != nullptr && last_saveable_snapshot->timestamp != m_last_save.gizmo);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GUI
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
96
src/slic3r/GUI/ProjectDirtyStateManager.hpp
Normal file
96
src/slic3r/GUI/ProjectDirtyStateManager.hpp
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
#ifndef slic3r_ProjectDirtyStateManager_hpp_
|
||||||
|
#define slic3r_ProjectDirtyStateManager_hpp_
|
||||||
|
|
||||||
|
#include "libslic3r/Preset.hpp"
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace UndoRedo {
|
||||||
|
class Stack;
|
||||||
|
struct Snapshot;
|
||||||
|
} // namespace UndoRedo
|
||||||
|
|
||||||
|
namespace GUI {
|
||||||
|
class ProjectDirtyStateManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class UpdateType : unsigned char
|
||||||
|
{
|
||||||
|
TakeSnapshot,
|
||||||
|
UndoRedoTo
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DirtyState
|
||||||
|
{
|
||||||
|
struct Gizmos
|
||||||
|
{
|
||||||
|
struct Gizmo
|
||||||
|
{
|
||||||
|
std::vector<size_t> modified_timestamps;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool current{ false };
|
||||||
|
std::map<std::string, Gizmo> used;
|
||||||
|
|
||||||
|
void add_used(const UndoRedo::Snapshot& snapshot);
|
||||||
|
void remove_obsolete_used(const Slic3r::UndoRedo::Stack& main_stack);
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
bool any_used_modified() const;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
bool is_used_and_modified(const UndoRedo::Snapshot& snapshot) const;
|
||||||
|
void reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
bool plater{ false };
|
||||||
|
bool presets{ false };
|
||||||
|
Gizmos gizmos;
|
||||||
|
|
||||||
|
bool is_dirty() const { return plater || presets || gizmos.current; }
|
||||||
|
void reset() {
|
||||||
|
plater = false;
|
||||||
|
presets = false;
|
||||||
|
gizmos.current = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct LastSaveTimestamps
|
||||||
|
{
|
||||||
|
size_t main{ 0 };
|
||||||
|
size_t gizmo{ 0 };
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
main = 0;
|
||||||
|
gizmo = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DirtyState m_state;
|
||||||
|
LastSaveTimestamps m_last_save;
|
||||||
|
|
||||||
|
// keeps track of initial selected presets
|
||||||
|
std::array<std::string, Preset::TYPE_COUNT> m_initial_presets;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool is_dirty() const { return m_state.is_dirty(); }
|
||||||
|
void update_from_undo_redo_stack(UpdateType type);
|
||||||
|
void update_from_presets();
|
||||||
|
void reset_after_save();
|
||||||
|
void reset_initial_presets();
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
void render_debug_window() const;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
|
||||||
|
private:
|
||||||
|
void update_from_undo_redo_main_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack);
|
||||||
|
void update_from_undo_redo_gizmo_stack(UpdateType type, const Slic3r::UndoRedo::Stack& stack);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace GUI
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
|
#endif // slic3r_ProjectDirtyStateManager_hpp_
|
||||||
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include <boost/nowide/convert.hpp>
|
#include <boost/nowide/convert.hpp>
|
||||||
|
|
||||||
#include "wx/dataview.h"
|
#include "wx/dataview.h"
|
||||||
|
#include "wx/numformatter.h"
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
|
|
@ -45,6 +46,11 @@ static char marker_by_type(Preset::Type type, PrinterTechnology pt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Option::opt_key() const
|
||||||
|
{
|
||||||
|
return boost::nowide::narrow(key).substr(2);
|
||||||
|
}
|
||||||
|
|
||||||
void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const
|
void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const
|
||||||
{
|
{
|
||||||
*label_ = marked_label.c_str();
|
*label_ = marked_label.c_str();
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ struct Option {
|
||||||
std::wstring category;
|
std::wstring category;
|
||||||
std::wstring category_local;
|
std::wstring category_local;
|
||||||
|
|
||||||
std::string opt_key() const { return boost::nowide::narrow(key).substr(2); }
|
std::string opt_key() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FoundOption {
|
struct FoundOption {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
|
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
|
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
|
|
||||||
|
|
@ -62,13 +65,11 @@ bool Selection::Clipboard::is_sla_compliant() const
|
||||||
if (m_mode == Selection::Volume)
|
if (m_mode == Selection::Volume)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (const ModelObject* o : m_model->objects)
|
for (const ModelObject* o : m_model->objects) {
|
||||||
{
|
|
||||||
if (o->is_multiparts())
|
if (o->is_multiparts())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (const ModelVolume* v : o->volumes)
|
for (const ModelVolume* v : o->volumes) {
|
||||||
{
|
|
||||||
if (v->is_modifier())
|
if (v->is_modifier())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +83,8 @@ Selection::Clipboard::Clipboard()
|
||||||
m_model.reset(new Model);
|
m_model.reset(new Model);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Selection::Clipboard::reset() {
|
void Selection::Clipboard::reset()
|
||||||
|
{
|
||||||
m_model->clear_objects();
|
m_model->clear_objects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,7 +155,7 @@ void Selection::set_model(Model* model)
|
||||||
|
|
||||||
void Selection::add(unsigned int volume_idx, bool as_single_selection, bool check_for_already_contained)
|
void Selection::add(unsigned int volume_idx, bool as_single_selection, bool check_for_already_contained)
|
||||||
{
|
{
|
||||||
if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx))
|
if (!m_valid || (unsigned int)m_volumes->size() <= volume_idx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const GLVolume* volume = (*m_volumes)[volume_idx];
|
const GLVolume* volume = (*m_volumes)[volume_idx];
|
||||||
|
|
@ -171,9 +173,8 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec
|
||||||
needs_reset |= as_single_selection && !is_any_modifier() && volume->is_modifier;
|
needs_reset |= as_single_selection && !is_any_modifier() && volume->is_modifier;
|
||||||
needs_reset |= is_any_modifier() && !volume->is_modifier;
|
needs_reset |= is_any_modifier() && !volume->is_modifier;
|
||||||
|
|
||||||
if (!already_contained || needs_reset)
|
if (!already_contained || needs_reset) {
|
||||||
{
|
wxGetApp().plater()->take_snapshot(_L("Selection-Add"));
|
||||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Add")));
|
|
||||||
|
|
||||||
if (needs_reset)
|
if (needs_reset)
|
||||||
clear();
|
clear();
|
||||||
|
|
@ -189,7 +190,7 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec
|
||||||
{
|
{
|
||||||
case Volume:
|
case Volume:
|
||||||
{
|
{
|
||||||
if ((volume->volume_idx() >= 0) && (is_empty() || (volume->instance_idx() == get_instance_idx())))
|
if (volume->volume_idx() >= 0 && (is_empty() || volume->instance_idx() == get_instance_idx()))
|
||||||
do_add_volume(volume_idx);
|
do_add_volume(volume_idx);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -208,13 +209,13 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec
|
||||||
|
|
||||||
void Selection::remove(unsigned int volume_idx)
|
void Selection::remove(unsigned int volume_idx)
|
||||||
{
|
{
|
||||||
if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx))
|
if (!m_valid || (unsigned int)m_volumes->size() <= volume_idx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!contains_volume(volume_idx))
|
if (!contains_volume(volume_idx))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove")));
|
wxGetApp().plater()->take_snapshot(_L("Selection-Remove"));
|
||||||
|
|
||||||
GLVolume* volume = (*m_volumes)[volume_idx];
|
GLVolume* volume = (*m_volumes)[volume_idx];
|
||||||
|
|
||||||
|
|
@ -246,7 +247,7 @@ void Selection::add_object(unsigned int object_idx, bool as_single_selection)
|
||||||
(as_single_selection && matches(volume_idxs)))
|
(as_single_selection && matches(volume_idxs)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Add Object")));
|
wxGetApp().plater()->take_snapshot(_L("Selection-Add Object"));
|
||||||
|
|
||||||
// resets the current list if needed
|
// resets the current list if needed
|
||||||
if (as_single_selection)
|
if (as_single_selection)
|
||||||
|
|
@ -265,7 +266,7 @@ void Selection::remove_object(unsigned int object_idx)
|
||||||
if (!m_valid)
|
if (!m_valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Object")));
|
wxGetApp().plater()->take_snapshot(_L("Selection-Remove Object"));
|
||||||
|
|
||||||
do_remove_object(object_idx);
|
do_remove_object(object_idx);
|
||||||
|
|
||||||
|
|
@ -278,12 +279,12 @@ void Selection::add_instance(unsigned int object_idx, unsigned int instance_idx,
|
||||||
if (!m_valid)
|
if (!m_valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<unsigned int> volume_idxs = get_volume_idxs_from_instance(object_idx, instance_idx);
|
const std::vector<unsigned int> volume_idxs = get_volume_idxs_from_instance(object_idx, instance_idx);
|
||||||
if ((!as_single_selection && contains_all_volumes(volume_idxs)) ||
|
if ((!as_single_selection && contains_all_volumes(volume_idxs)) ||
|
||||||
(as_single_selection && matches(volume_idxs)))
|
(as_single_selection && matches(volume_idxs)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Add Instance")));
|
wxGetApp().plater()->take_snapshot(_L("Selection-Add Instance"));
|
||||||
|
|
||||||
// resets the current list if needed
|
// resets the current list if needed
|
||||||
if (as_single_selection)
|
if (as_single_selection)
|
||||||
|
|
@ -302,7 +303,7 @@ void Selection::remove_instance(unsigned int object_idx, unsigned int instance_i
|
||||||
if (!m_valid)
|
if (!m_valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Instance")));
|
wxGetApp().plater()->take_snapshot(_L("Selection-Remove Instance"));
|
||||||
|
|
||||||
do_remove_instance(object_idx, instance_idx);
|
do_remove_instance(object_idx, instance_idx);
|
||||||
|
|
||||||
|
|
@ -337,10 +338,9 @@ void Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx)
|
||||||
if (!m_valid)
|
if (!m_valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
|
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) {
|
||||||
{
|
|
||||||
GLVolume* v = (*m_volumes)[i];
|
GLVolume* v = (*m_volumes)[i];
|
||||||
if ((v->object_idx() == (int)object_idx) && (v->volume_idx() == (int)volume_idx))
|
if (v->object_idx() == (int)object_idx && v->volume_idx() == (int)volume_idx)
|
||||||
do_remove_volume(i);
|
do_remove_volume(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,8 +362,7 @@ void Selection::add_volumes(EMode mode, const std::vector<unsigned int>& volume_
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
m_mode = mode;
|
m_mode = mode;
|
||||||
for (unsigned int i : volume_idxs)
|
for (unsigned int i : volume_idxs) {
|
||||||
{
|
|
||||||
if (i < (unsigned int)m_volumes->size())
|
if (i < (unsigned int)m_volumes->size())
|
||||||
do_add_volume(i);
|
do_add_volume(i);
|
||||||
}
|
}
|
||||||
|
|
@ -378,8 +377,7 @@ void Selection::remove_volumes(EMode mode, const std::vector<unsigned int>& volu
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_mode = mode;
|
m_mode = mode;
|
||||||
for (unsigned int i : volume_idxs)
|
for (unsigned int i : volume_idxs) {
|
||||||
{
|
|
||||||
if (i < (unsigned int)m_volumes->size())
|
if (i < (unsigned int)m_volumes->size())
|
||||||
do_remove_volume(i);
|
do_remove_volume(i);
|
||||||
}
|
}
|
||||||
|
|
@ -394,8 +392,7 @@ void Selection::add_all()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unsigned int count = 0;
|
unsigned int count = 0;
|
||||||
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
|
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) {
|
||||||
{
|
|
||||||
if (!(*m_volumes)[i]->is_wipe_tower)
|
if (!(*m_volumes)[i]->is_wipe_tower)
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
|
|
@ -408,8 +405,7 @@ void Selection::add_all()
|
||||||
m_mode = Instance;
|
m_mode = Instance;
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
|
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) {
|
||||||
{
|
|
||||||
if (!(*m_volumes)[i]->is_wipe_tower)
|
if (!(*m_volumes)[i]->is_wipe_tower)
|
||||||
do_add_volume(i);
|
do_add_volume(i);
|
||||||
}
|
}
|
||||||
|
|
@ -459,8 +455,7 @@ void Selection::clear()
|
||||||
if (m_list.empty())
|
if (m_list.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
(*m_volumes)[i]->selected = false;
|
(*m_volumes)[i]->selected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -526,16 +521,15 @@ bool Selection::is_single_full_instance() const
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int object_idx = m_valid ? get_object_idx() : -1;
|
int object_idx = m_valid ? get_object_idx() : -1;
|
||||||
if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx))
|
if (object_idx < 0 || (int)m_model->objects.size() <= object_idx)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int instance_idx = (*m_volumes)[*m_list.begin()]->instance_idx();
|
int instance_idx = (*m_volumes)[*m_list.begin()]->instance_idx();
|
||||||
|
|
||||||
std::set<int> volumes_idxs;
|
std::set<int> volumes_idxs;
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
const GLVolume* v = (*m_volumes)[i];
|
const GLVolume* v = (*m_volumes)[i];
|
||||||
if ((object_idx != v->object_idx()) || (instance_idx != v->instance_idx()))
|
if (object_idx != v->object_idx() || instance_idx != v->instance_idx())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int volume_idx = v->volume_idx();
|
int volume_idx = v->volume_idx();
|
||||||
|
|
@ -548,8 +542,8 @@ bool Selection::is_single_full_instance() const
|
||||||
|
|
||||||
bool Selection::is_from_single_object() const
|
bool Selection::is_from_single_object() const
|
||||||
{
|
{
|
||||||
int idx = get_object_idx();
|
const int idx = get_object_idx();
|
||||||
return (0 <= idx) && (idx < 1000);
|
return 0 <= idx && idx < 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Selection::is_sla_compliant() const
|
bool Selection::is_sla_compliant() const
|
||||||
|
|
@ -557,8 +551,7 @@ bool Selection::is_sla_compliant() const
|
||||||
if (m_mode == Volume)
|
if (m_mode == Volume)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
if ((*m_volumes)[i]->is_modifier)
|
if ((*m_volumes)[i]->is_modifier)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -568,8 +561,7 @@ bool Selection::is_sla_compliant() const
|
||||||
|
|
||||||
bool Selection::contains_all_volumes(const std::vector<unsigned int>& volume_idxs) const
|
bool Selection::contains_all_volumes(const std::vector<unsigned int>& volume_idxs) const
|
||||||
{
|
{
|
||||||
for (unsigned int i : volume_idxs)
|
for (unsigned int i : volume_idxs) {
|
||||||
{
|
|
||||||
if (m_list.find(i) == m_list.end())
|
if (m_list.find(i) == m_list.end())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -579,8 +571,7 @@ bool Selection::contains_all_volumes(const std::vector<unsigned int>& volume_idx
|
||||||
|
|
||||||
bool Selection::contains_any_volume(const std::vector<unsigned int>& volume_idxs) const
|
bool Selection::contains_any_volume(const std::vector<unsigned int>& volume_idxs) const
|
||||||
{
|
{
|
||||||
for (unsigned int i : volume_idxs)
|
for (unsigned int i : volume_idxs) {
|
||||||
{
|
|
||||||
if (m_list.find(i) != m_list.end())
|
if (m_list.find(i) != m_list.end())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -592,8 +583,7 @@ bool Selection::matches(const std::vector<unsigned int>& volume_idxs) const
|
||||||
{
|
{
|
||||||
unsigned int count = 0;
|
unsigned int count = 0;
|
||||||
|
|
||||||
for (unsigned int i : volume_idxs)
|
for (unsigned int i : volume_idxs) {
|
||||||
{
|
|
||||||
if (m_list.find(i) != m_list.end())
|
if (m_list.find(i) != m_list.end())
|
||||||
++count;
|
++count;
|
||||||
else
|
else
|
||||||
|
|
@ -618,8 +608,7 @@ int Selection::get_object_idx() const
|
||||||
|
|
||||||
int Selection::get_instance_idx() const
|
int Selection::get_instance_idx() const
|
||||||
{
|
{
|
||||||
if (m_cache.content.size() == 1)
|
if (m_cache.content.size() == 1) {
|
||||||
{
|
|
||||||
const InstanceIdxsList& idxs = m_cache.content.begin()->second;
|
const InstanceIdxsList& idxs = m_cache.content.begin()->second;
|
||||||
if (idxs.size() == 1)
|
if (idxs.size() == 1)
|
||||||
return *idxs.begin();
|
return *idxs.begin();
|
||||||
|
|
@ -676,25 +665,20 @@ void Selection::translate(const Vec3d& displacement, bool local)
|
||||||
|
|
||||||
EMode translation_type = m_mode;
|
EMode translation_type = m_mode;
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
if (m_mode == Volume || (*m_volumes)[i]->is_wipe_tower) {
|
||||||
if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower)
|
|
||||||
{
|
|
||||||
if (local)
|
if (local)
|
||||||
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement);
|
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement);
|
||||||
else
|
else {
|
||||||
{
|
const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement;
|
||||||
Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement;
|
|
||||||
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
|
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_mode == Instance)
|
else if (m_mode == Instance) {
|
||||||
{
|
|
||||||
if (is_from_fully_selected_instance(i))
|
if (is_from_fully_selected_instance(i))
|
||||||
(*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
|
(*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
|
||||||
else
|
else {
|
||||||
{
|
const Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement;
|
||||||
Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement;
|
|
||||||
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
|
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
|
||||||
translation_type = Volume;
|
translation_type = Volume;
|
||||||
}
|
}
|
||||||
|
|
@ -722,18 +706,14 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||||
|
|
||||||
if (!is_wipe_tower()) {
|
if (!is_wipe_tower()) {
|
||||||
int rot_axis_max = 0;
|
int rot_axis_max = 0;
|
||||||
if (rotation.isApprox(Vec3d::Zero()))
|
if (rotation.isApprox(Vec3d::Zero())) {
|
||||||
{
|
for (unsigned int i : m_list) {
|
||||||
for (unsigned int i : m_list)
|
|
||||||
{
|
|
||||||
GLVolume &volume = *(*m_volumes)[i];
|
GLVolume &volume = *(*m_volumes)[i];
|
||||||
if (m_mode == Instance)
|
if (m_mode == Instance) {
|
||||||
{
|
|
||||||
volume.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation());
|
volume.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation());
|
||||||
volume.set_instance_offset(m_cache.volumes_data[i].get_instance_position());
|
volume.set_instance_offset(m_cache.volumes_data[i].get_instance_position());
|
||||||
}
|
}
|
||||||
else if (m_mode == Volume)
|
else if (m_mode == Volume) {
|
||||||
{
|
|
||||||
volume.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation());
|
volume.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation());
|
||||||
volume.set_volume_offset(m_cache.volumes_data[i].get_volume_position());
|
volume.set_volume_offset(m_cache.volumes_data[i].get_volume_position());
|
||||||
}
|
}
|
||||||
|
|
@ -750,14 +730,14 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||||
// For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it.
|
// For generic rotation, we want to rotate the first volume in selection, and then to synchronize the other volumes with it.
|
||||||
std::vector<int> object_instance_first(m_model->objects.size(), -1);
|
std::vector<int> object_instance_first(m_model->objects.size(), -1);
|
||||||
auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) {
|
auto rotate_instance = [this, &rotation, &object_instance_first, rot_axis_max, transformation_type](GLVolume &volume, int i) {
|
||||||
int first_volume_idx = object_instance_first[volume.object_idx()];
|
const int first_volume_idx = object_instance_first[volume.object_idx()];
|
||||||
if (rot_axis_max != 2 && first_volume_idx != -1) {
|
if (rot_axis_max != 2 && first_volume_idx != -1) {
|
||||||
// Generic rotation, but no rotation around the Z axis.
|
// Generic rotation, but no rotation around the Z axis.
|
||||||
// Always do a local rotation (do not consider the selection to be a rigid body).
|
// Always do a local rotation (do not consider the selection to be a rigid body).
|
||||||
assert(is_approx(rotation.z(), 0.0));
|
assert(is_approx(rotation.z(), 0.0));
|
||||||
const GLVolume &first_volume = *(*m_volumes)[first_volume_idx];
|
const GLVolume &first_volume = *(*m_volumes)[first_volume_idx];
|
||||||
const Vec3d &rotation = first_volume.get_instance_rotation();
|
const Vec3d &rotation = first_volume.get_instance_rotation();
|
||||||
double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation());
|
const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation());
|
||||||
volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff));
|
volume.set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -767,7 +747,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||||
transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation();
|
transformation_type.absolute() ? rotation : rotation + m_cache.volumes_data[i].get_instance_rotation();
|
||||||
if (rot_axis_max == 2 && transformation_type.joint()) {
|
if (rot_axis_max == 2 && transformation_type.joint()) {
|
||||||
// Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis.
|
// Only allow rotation of multiple instances as a single rigid body when rotating around the Z axis.
|
||||||
double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation);
|
const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), new_rotation);
|
||||||
volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
|
volume.set_instance_offset(m_cache.dragging_center + Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()) * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
|
||||||
}
|
}
|
||||||
volume.set_instance_rotation(new_rotation);
|
volume.set_instance_rotation(new_rotation);
|
||||||
|
|
@ -775,19 +755,16 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
GLVolume &volume = *(*m_volumes)[i];
|
GLVolume &volume = *(*m_volumes)[i];
|
||||||
if (is_single_full_instance())
|
if (is_single_full_instance())
|
||||||
rotate_instance(volume, i);
|
rotate_instance(volume, i);
|
||||||
else if (is_single_volume() || is_single_modifier())
|
else if (is_single_volume() || is_single_modifier()) {
|
||||||
{
|
|
||||||
if (transformation_type.independent())
|
if (transformation_type.independent())
|
||||||
volume.set_volume_rotation(volume.get_volume_rotation() + rotation);
|
volume.set_volume_rotation(volume.get_volume_rotation() + rotation);
|
||||||
else
|
else {
|
||||||
{
|
const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
const Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
||||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
|
||||||
volume.set_volume_rotation(new_rotation);
|
volume.set_volume_rotation(new_rotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -795,15 +772,13 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||||
{
|
{
|
||||||
if (m_mode == Instance)
|
if (m_mode == Instance)
|
||||||
rotate_instance(volume, i);
|
rotate_instance(volume, i);
|
||||||
else if (m_mode == Volume)
|
else if (m_mode == Volume) {
|
||||||
{
|
|
||||||
// extracts rotations from the composed transformation
|
// extracts rotations from the composed transformation
|
||||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
||||||
if (transformation_type.joint())
|
if (transformation_type.joint()) {
|
||||||
{
|
const Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center;
|
||||||
Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center;
|
const Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot);
|
||||||
Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot);
|
|
||||||
volume.set_volume_offset(local_pivot + offset);
|
volume.set_volume_offset(local_pivot + offset);
|
||||||
}
|
}
|
||||||
volume.set_volume_rotation(new_rotation);
|
volume.set_volume_rotation(new_rotation);
|
||||||
|
|
@ -824,8 +799,8 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
|
||||||
|
|
||||||
// make sure the wipe tower rotates around its center, not origin
|
// make sure the wipe tower rotates around its center, not origin
|
||||||
// we can assume that only Z rotation changes
|
// we can assume that only Z rotation changes
|
||||||
Vec3d center_local = volume.transformed_bounding_box().center() - volume.get_volume_offset();
|
const Vec3d center_local = volume.transformed_bounding_box().center() - volume.get_volume_offset();
|
||||||
Vec3d center_local_new = Eigen::AngleAxisd(rotation(2)-volume.get_volume_rotation()(2), Vec3d(0, 0, 1)) * center_local;
|
const Vec3d center_local_new = Eigen::AngleAxisd(rotation(2)-volume.get_volume_rotation()(2), Vec3d(0.0, 0.0, 1.0)) * center_local;
|
||||||
volume.set_volume_rotation(rotation);
|
volume.set_volume_rotation(rotation);
|
||||||
volume.set_volume_offset(volume.get_volume_offset() + center_local - center_local_new);
|
volume.set_volume_offset(volume.get_volume_offset() + center_local - center_local_new);
|
||||||
}
|
}
|
||||||
|
|
@ -843,8 +818,7 @@ void Selection::flattening_rotate(const Vec3d& normal)
|
||||||
if (!m_valid)
|
if (!m_valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
// Normal transformed from the object coordinate space to the world coordinate space.
|
// Normal transformed from the object coordinate space to the world coordinate space.
|
||||||
const auto &voldata = m_cache.volumes_data[i];
|
const auto &voldata = m_cache.volumes_data[i];
|
||||||
Vec3d tnormal = (Geometry::assemble_transform(
|
Vec3d tnormal = (Geometry::assemble_transform(
|
||||||
|
|
@ -870,12 +844,10 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
|
||||||
if (!m_valid)
|
if (!m_valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
GLVolume &volume = *(*m_volumes)[i];
|
GLVolume &volume = *(*m_volumes)[i];
|
||||||
if (is_single_full_instance()) {
|
if (is_single_full_instance()) {
|
||||||
if (transformation_type.relative())
|
if (transformation_type.relative()) {
|
||||||
{
|
|
||||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
|
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
|
||||||
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3);
|
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3);
|
||||||
// extracts scaling factors from the composed transformation
|
// extracts scaling factors from the composed transformation
|
||||||
|
|
@ -885,8 +857,7 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
|
||||||
|
|
||||||
volume.set_instance_scaling_factor(new_scale);
|
volume.set_instance_scaling_factor(new_scale);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) {
|
if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) {
|
||||||
// Non-uniform scaling. Transform the scaling factors into the local coordinate system.
|
// Non-uniform scaling. Transform the scaling factors into the local coordinate system.
|
||||||
// This is only possible, if the instance rotation is mulitples of ninety degrees.
|
// This is only possible, if the instance rotation is mulitples of ninety degrees.
|
||||||
|
|
@ -899,11 +870,9 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
|
||||||
}
|
}
|
||||||
else if (is_single_volume() || is_single_modifier())
|
else if (is_single_volume() || is_single_modifier())
|
||||||
volume.set_volume_scaling_factor(scale);
|
volume.set_volume_scaling_factor(scale);
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
|
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
|
||||||
if (m_mode == Instance)
|
if (m_mode == Instance) {
|
||||||
{
|
|
||||||
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3);
|
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3);
|
||||||
// extracts scaling factors from the composed transformation
|
// extracts scaling factors from the composed transformation
|
||||||
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
|
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
|
||||||
|
|
@ -912,13 +881,11 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
|
||||||
|
|
||||||
volume.set_instance_scaling_factor(new_scale);
|
volume.set_instance_scaling_factor(new_scale);
|
||||||
}
|
}
|
||||||
else if (m_mode == Volume)
|
else if (m_mode == Volume) {
|
||||||
{
|
|
||||||
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3);
|
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3);
|
||||||
// extracts scaling factors from the composed transformation
|
// extracts scaling factors from the composed transformation
|
||||||
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
|
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
|
||||||
if (transformation_type.joint())
|
if (transformation_type.joint()) {
|
||||||
{
|
|
||||||
Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center);
|
Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center);
|
||||||
volume.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset);
|
volume.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset);
|
||||||
}
|
}
|
||||||
|
|
@ -933,35 +900,36 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
|
||||||
else if (m_mode == Volume)
|
else if (m_mode == Volume)
|
||||||
synchronize_unselected_volumes();
|
synchronize_unselected_volumes();
|
||||||
#endif // !DISABLE_INSTANCES_SYNCH
|
#endif // !DISABLE_INSTANCES_SYNCH
|
||||||
|
|
||||||
ensure_on_bed();
|
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA)
|
||||||
|
ensure_on_bed();
|
||||||
|
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
|
||||||
this->set_bounding_boxes_dirty();
|
this->set_bounding_boxes_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
|
void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
|
||||||
{
|
{
|
||||||
if (is_empty() || (m_mode == Volume))
|
if (is_empty() || m_mode == Volume)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
|
// adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
|
||||||
Vec3d box_size = get_bounding_box().size() + 0.01 * Vec3d::Ones();
|
Vec3d box_size = get_bounding_box().size() + 0.01 * Vec3d::Ones();
|
||||||
|
|
||||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config.option("bed_shape"));
|
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config.option("bed_shape"));
|
||||||
if (opt != nullptr)
|
if (opt != nullptr) {
|
||||||
{
|
|
||||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
|
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
|
||||||
BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config.opt_float("max_print_height")));
|
BoundingBoxf3 print_volume({ unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0 }, { unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config.opt_float("max_print_height") });
|
||||||
Vec3d print_volume_size = print_volume.size();
|
Vec3d print_volume_size = print_volume.size();
|
||||||
double sx = (box_size(0) != 0.0) ? print_volume_size(0) / box_size(0) : 0.0;
|
double sx = (box_size(0) != 0.0) ? print_volume_size(0) / box_size(0) : 0.0;
|
||||||
double sy = (box_size(1) != 0.0) ? print_volume_size(1) / box_size(1) : 0.0;
|
double sy = (box_size(1) != 0.0) ? print_volume_size(1) / box_size(1) : 0.0;
|
||||||
double sz = (box_size(2) != 0.0) ? print_volume_size(2) / box_size(2) : 0.0;
|
double sz = (box_size(2) != 0.0) ? print_volume_size(2) / box_size(2) : 0.0;
|
||||||
if ((sx != 0.0) && (sy != 0.0) && (sz != 0.0))
|
if (sx != 0.0 && sy != 0.0 && sz != 0.0)
|
||||||
{
|
{
|
||||||
double s = std::min(sx, std::min(sy, sz));
|
double s = std::min(sx, std::min(sy, sz));
|
||||||
if (s != 1.0)
|
if (s != 1.0) {
|
||||||
{
|
wxGetApp().plater()->take_snapshot(_L("Scale To Fit"));
|
||||||
wxGetApp().plater()->take_snapshot(_(L("Scale To Fit")));
|
|
||||||
|
|
||||||
TransformationType type;
|
TransformationType type;
|
||||||
type.set_world();
|
type.set_world();
|
||||||
|
|
@ -991,8 +959,7 @@ void Selection::mirror(Axis axis)
|
||||||
|
|
||||||
bool single_full_instance = is_single_full_instance();
|
bool single_full_instance = is_single_full_instance();
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
if (single_full_instance)
|
if (single_full_instance)
|
||||||
(*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis));
|
(*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis));
|
||||||
else if (m_mode == Volume)
|
else if (m_mode == Volume)
|
||||||
|
|
@ -1014,8 +981,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement)
|
||||||
if (!m_valid)
|
if (!m_valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
GLVolume* v = (*m_volumes)[i];
|
GLVolume* v = (*m_volumes)[i];
|
||||||
if (v->object_idx() == (int)object_idx)
|
if (v->object_idx() == (int)object_idx)
|
||||||
v->set_instance_offset(v->get_instance_offset() + displacement);
|
v->set_instance_offset(v->get_instance_offset() + displacement);
|
||||||
|
|
@ -1024,8 +990,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement)
|
||||||
std::set<unsigned int> done; // prevent processing volumes twice
|
std::set<unsigned int> done; // prevent processing volumes twice
|
||||||
done.insert(m_list.begin(), m_list.end());
|
done.insert(m_list.begin(), m_list.end());
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
if (done.size() == m_volumes->size())
|
if (done.size() == m_volumes->size())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -1034,8 +999,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Process unselected volumes of the object.
|
// Process unselected volumes of the object.
|
||||||
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j)
|
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) {
|
||||||
{
|
|
||||||
if (done.size() == m_volumes->size())
|
if (done.size() == m_volumes->size())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -1059,18 +1023,16 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co
|
||||||
if (!m_valid)
|
if (!m_valid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
GLVolume* v = (*m_volumes)[i];
|
GLVolume* v = (*m_volumes)[i];
|
||||||
if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx))
|
if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx)
|
||||||
v->set_instance_offset(v->get_instance_offset() + displacement);
|
v->set_instance_offset(v->get_instance_offset() + displacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<unsigned int> done; // prevent processing volumes twice
|
std::set<unsigned int> done; // prevent processing volumes twice
|
||||||
done.insert(m_list.begin(), m_list.end());
|
done.insert(m_list.begin(), m_list.end());
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
if (done.size() == m_volumes->size())
|
if (done.size() == m_volumes->size())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -1079,8 +1041,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Process unselected volumes of the object.
|
// Process unselected volumes of the object.
|
||||||
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j)
|
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) {
|
||||||
{
|
|
||||||
if (done.size() == m_volumes->size())
|
if (done.size() == m_volumes->size())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -1088,7 +1049,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GLVolume* v = (*m_volumes)[j];
|
GLVolume* v = (*m_volumes)[j];
|
||||||
if ((v->object_idx() != object_idx) || (v->instance_idx() != (int)instance_idx))
|
if (v->object_idx() != object_idx || v->instance_idx() != (int)instance_idx)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
v->set_instance_offset(v->get_instance_offset() + displacement);
|
v->set_instance_offset(v->get_instance_offset() + displacement);
|
||||||
|
|
@ -1803,18 +1764,16 @@ void Selection::render_synchronized_volumes() const
|
||||||
|
|
||||||
float color[3] = { 1.0f, 1.0f, 0.0f };
|
float color[3] = { 1.0f, 1.0f, 0.0f };
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
const GLVolume* volume = (*m_volumes)[i];
|
const GLVolume* volume = (*m_volumes)[i];
|
||||||
int object_idx = volume->object_idx();
|
int object_idx = volume->object_idx();
|
||||||
int volume_idx = volume->volume_idx();
|
int volume_idx = volume->volume_idx();
|
||||||
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j)
|
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) {
|
||||||
{
|
|
||||||
if (i == j)
|
if (i == j)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const GLVolume* v = (*m_volumes)[j];
|
const GLVolume* v = (*m_volumes)[j];
|
||||||
if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx))
|
if (v->object_idx() != object_idx || v->volume_idx() != volume_idx)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
render_bounding_box(v->transformed_convex_hull_bounding_box(), color);
|
render_bounding_box(v->transformed_convex_hull_bounding_box(), color);
|
||||||
|
|
@ -2087,9 +2046,9 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
|
static bool is_rotation_xy_synchronized(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
|
||||||
{
|
{
|
||||||
Eigen::AngleAxisd angle_axis(Geometry::rotation_xyz_diff(rot_xyz_from, rot_xyz_to));
|
const Eigen::AngleAxisd angle_axis(Geometry::rotation_xyz_diff(rot_xyz_from, rot_xyz_to));
|
||||||
Vec3d axis = angle_axis.axis();
|
const Vec3d axis = angle_axis.axis();
|
||||||
double angle = angle_axis.angle();
|
const double angle = angle_axis.angle();
|
||||||
if (std::abs(angle) < 1e-8)
|
if (std::abs(angle) < 1e-8)
|
||||||
return true;
|
return true;
|
||||||
assert(std::abs(axis.x()) < 1e-8);
|
assert(std::abs(axis.x()) < 1e-8);
|
||||||
|
|
@ -2126,24 +2085,22 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
|
||||||
std::set<unsigned int> done; // prevent processing volumes twice
|
std::set<unsigned int> done; // prevent processing volumes twice
|
||||||
done.insert(m_list.begin(), m_list.end());
|
done.insert(m_list.begin(), m_list.end());
|
||||||
|
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
if (done.size() == m_volumes->size())
|
if (done.size() == m_volumes->size())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
const GLVolume* volume = (*m_volumes)[i];
|
const GLVolume* volume = (*m_volumes)[i];
|
||||||
int object_idx = volume->object_idx();
|
const int object_idx = volume->object_idx();
|
||||||
if (object_idx >= 1000)
|
if (object_idx >= 1000)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int instance_idx = volume->instance_idx();
|
const int instance_idx = volume->instance_idx();
|
||||||
const Vec3d& rotation = volume->get_instance_rotation();
|
const Vec3d& rotation = volume->get_instance_rotation();
|
||||||
const Vec3d& scaling_factor = volume->get_instance_scaling_factor();
|
const Vec3d& scaling_factor = volume->get_instance_scaling_factor();
|
||||||
const Vec3d& mirror = volume->get_instance_mirror();
|
const Vec3d& mirror = volume->get_instance_mirror();
|
||||||
|
|
||||||
// Process unselected instances.
|
// Process unselected instances.
|
||||||
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j)
|
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) {
|
||||||
{
|
|
||||||
if (done.size() == m_volumes->size())
|
if (done.size() == m_volumes->size())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -2151,24 +2108,36 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GLVolume* v = (*m_volumes)[j];
|
GLVolume* v = (*m_volumes)[j];
|
||||||
if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx))
|
if (v->object_idx() != object_idx || v->instance_idx() == instance_idx)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
assert(is_rotation_xy_synchronized(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()));
|
assert(is_rotation_xy_synchronized(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()));
|
||||||
switch (sync_rotation_type) {
|
switch (sync_rotation_type) {
|
||||||
case SYNC_ROTATION_NONE:
|
case SYNC_ROTATION_NONE: {
|
||||||
|
#if ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
// z only rotation -> synch instance z
|
||||||
|
// The X,Y rotations should be synchronized from start to end of the rotation.
|
||||||
|
assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation()));
|
||||||
|
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
|
||||||
|
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
|
||||||
|
v->set_instance_offset(Z, volume->get_instance_offset().z());
|
||||||
|
break;
|
||||||
|
#else
|
||||||
// z only rotation -> keep instance z
|
// z only rotation -> keep instance z
|
||||||
// The X,Y rotations should be synchronized from start to end of the rotation.
|
// The X,Y rotations should be synchronized from start to end of the rotation.
|
||||||
assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation()));
|
assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation()));
|
||||||
break;
|
break;
|
||||||
|
#endif // ENABLE_ALLOW_NEGATIVE_Z
|
||||||
|
}
|
||||||
case SYNC_ROTATION_FULL:
|
case SYNC_ROTATION_FULL:
|
||||||
// rotation comes from place on face -> force given z
|
// rotation comes from place on face -> force given z
|
||||||
v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2)));
|
v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() });
|
||||||
break;
|
break;
|
||||||
case SYNC_ROTATION_GENERAL:
|
case SYNC_ROTATION_GENERAL:
|
||||||
// generic rotation -> update instance z with the delta of the rotation.
|
// generic rotation -> update instance z with the delta of the rotation.
|
||||||
double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation());
|
const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation());
|
||||||
v->set_instance_rotation(Vec3d(rotation(0), rotation(1), rotation(2) + z_diff));
|
v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() + z_diff });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2186,27 +2155,25 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
|
||||||
|
|
||||||
void Selection::synchronize_unselected_volumes()
|
void Selection::synchronize_unselected_volumes()
|
||||||
{
|
{
|
||||||
for (unsigned int i : m_list)
|
for (unsigned int i : m_list) {
|
||||||
{
|
|
||||||
const GLVolume* volume = (*m_volumes)[i];
|
const GLVolume* volume = (*m_volumes)[i];
|
||||||
int object_idx = volume->object_idx();
|
const int object_idx = volume->object_idx();
|
||||||
if (object_idx >= 1000)
|
if (object_idx >= 1000)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int volume_idx = volume->volume_idx();
|
const int volume_idx = volume->volume_idx();
|
||||||
const Vec3d& offset = volume->get_volume_offset();
|
const Vec3d& offset = volume->get_volume_offset();
|
||||||
const Vec3d& rotation = volume->get_volume_rotation();
|
const Vec3d& rotation = volume->get_volume_rotation();
|
||||||
const Vec3d& scaling_factor = volume->get_volume_scaling_factor();
|
const Vec3d& scaling_factor = volume->get_volume_scaling_factor();
|
||||||
const Vec3d& mirror = volume->get_volume_mirror();
|
const Vec3d& mirror = volume->get_volume_mirror();
|
||||||
|
|
||||||
// Process unselected volumes.
|
// Process unselected volumes.
|
||||||
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j)
|
for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) {
|
||||||
{
|
|
||||||
if (j == i)
|
if (j == i)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GLVolume* v = (*m_volumes)[j];
|
GLVolume* v = (*m_volumes)[j];
|
||||||
if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx))
|
if (v->object_idx() != object_idx || v->volume_idx() != volume_idx)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
v->set_volume_offset(offset);
|
v->set_volume_offset(offset);
|
||||||
|
|
@ -2222,10 +2189,8 @@ void Selection::ensure_on_bed()
|
||||||
typedef std::map<std::pair<int, int>, double> InstancesToZMap;
|
typedef std::map<std::pair<int, int>, double> InstancesToZMap;
|
||||||
InstancesToZMap instances_min_z;
|
InstancesToZMap instances_min_z;
|
||||||
|
|
||||||
for (GLVolume* volume : *m_volumes)
|
for (GLVolume* volume : *m_volumes) {
|
||||||
{
|
if (!volume->is_wipe_tower && !volume->is_modifier) {
|
||||||
if (!volume->is_wipe_tower && !volume->is_modifier)
|
|
||||||
{
|
|
||||||
double min_z = volume->transformed_convex_hull_bounding_box().min(2);
|
double min_z = volume->transformed_convex_hull_bounding_box().min(2);
|
||||||
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
|
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
|
||||||
InstancesToZMap::iterator it = instances_min_z.find(instance);
|
InstancesToZMap::iterator it = instances_min_z.find(instance);
|
||||||
|
|
@ -2236,8 +2201,7 @@ void Selection::ensure_on_bed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (GLVolume* volume : *m_volumes)
|
for (GLVolume* volume : *m_volumes) {
|
||||||
{
|
|
||||||
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
|
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
|
||||||
InstancesToZMap::iterator it = instances_min_z.find(instance);
|
InstancesToZMap::iterator it = instances_min_z.find(instance);
|
||||||
if (it != instances_min_z.end())
|
if (it != instances_min_z.end())
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ private:
|
||||||
TransformCache m_instance;
|
TransformCache m_instance;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VolumeCache() {}
|
VolumeCache() = default;
|
||||||
VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform);
|
VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform);
|
||||||
|
|
||||||
const Vec3d& get_volume_position() const { return m_volume.position; }
|
const Vec3d& get_volume_position() const { return m_volume.position; }
|
||||||
|
|
|
||||||
|
|
@ -940,7 +940,7 @@ void Tab::update_visibility()
|
||||||
page->update_visibility(m_mode, page.get() == m_active_page);
|
page->update_visibility(m_mode, page.get() == m_active_page);
|
||||||
rebuild_page_tree();
|
rebuild_page_tree();
|
||||||
|
|
||||||
if (this->m_type == Preset::TYPE_SLA_PRINT)
|
if (m_type == Preset::TYPE_SLA_PRINT)
|
||||||
update_description_lines();
|
update_description_lines();
|
||||||
|
|
||||||
Layout();
|
Layout();
|
||||||
|
|
@ -1217,9 +1217,8 @@ void Tab::apply_config_from_cache()
|
||||||
// to update number of "filament" selection boxes when the number of extruders change.
|
// to update number of "filament" selection boxes when the number of extruders change.
|
||||||
void Tab::on_presets_changed()
|
void Tab::on_presets_changed()
|
||||||
{
|
{
|
||||||
if (wxGetApp().plater() == nullptr) {
|
if (wxGetApp().plater() == nullptr)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets
|
// Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets
|
||||||
wxGetApp().plater()->sidebar().update_presets(m_type);
|
wxGetApp().plater()->sidebar().update_presets(m_type);
|
||||||
|
|
@ -1237,6 +1236,10 @@ void Tab::on_presets_changed()
|
||||||
// clear m_dependent_tabs after first update from select_preset()
|
// clear m_dependent_tabs after first update from select_preset()
|
||||||
// to avoid needless preset loading from update() function
|
// to avoid needless preset loading from update() function
|
||||||
m_dependent_tabs.clear();
|
m_dependent_tabs.clear();
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
wxGetApp().plater()->update_project_dirty_from_presets();
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup)
|
void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup)
|
||||||
|
|
@ -2113,10 +2116,16 @@ wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticTex
|
||||||
return sizer;
|
return sizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool Tab::saved_preset_is_dirty() const { return m_presets->saved_is_dirty(); }
|
||||||
|
void Tab::update_saved_preset_from_current_preset() { m_presets->update_saved_preset_from_current_preset(); }
|
||||||
|
bool Tab::current_preset_is_dirty() const { return m_presets->current_is_dirty(); }
|
||||||
|
#else
|
||||||
bool Tab::current_preset_is_dirty()
|
bool Tab::current_preset_is_dirty()
|
||||||
{
|
{
|
||||||
return m_presets->current_is_dirty();
|
return m_presets->current_is_dirty();
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
void TabPrinter::build()
|
void TabPrinter::build()
|
||||||
{
|
{
|
||||||
|
|
@ -3141,8 +3150,8 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
|
||||||
if (preset_name.empty()) {
|
if (preset_name.empty()) {
|
||||||
if (delete_current) {
|
if (delete_current) {
|
||||||
// Find an alternate preset to be selected after the current preset is deleted.
|
// Find an alternate preset to be selected after the current preset is deleted.
|
||||||
const std::deque<Preset> &presets = this->m_presets->get_presets();
|
const std::deque<Preset> &presets = m_presets->get_presets();
|
||||||
size_t idx_current = this->m_presets->get_idx_selected();
|
size_t idx_current = m_presets->get_idx_selected();
|
||||||
// Find the next visible preset.
|
// Find the next visible preset.
|
||||||
size_t idx_new = idx_current + 1;
|
size_t idx_new = idx_current + 1;
|
||||||
if (idx_new < presets.size())
|
if (idx_new < presets.size())
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,11 @@ public:
|
||||||
Preset::Type type() const { return m_type; }
|
Preset::Type type() const { return m_type; }
|
||||||
// The tab is already constructed.
|
// The tab is already constructed.
|
||||||
bool completed() const { return m_completed; }
|
bool completed() const { return m_completed; }
|
||||||
virtual bool supports_printer_technology(const PrinterTechnology tech) = 0;
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
virtual bool supports_printer_technology(const PrinterTechnology tech) const = 0;
|
||||||
|
#else
|
||||||
|
virtual bool supports_printer_technology(const PrinterTechnology tech) = 0;
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
void create_preset_tab();
|
void create_preset_tab();
|
||||||
void add_scaled_button(wxWindow* parent, ScalableButton** btn, const std::string& icon_name,
|
void add_scaled_button(wxWindow* parent, ScalableButton** btn, const std::string& icon_name,
|
||||||
|
|
@ -333,7 +337,13 @@ public:
|
||||||
Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1);
|
Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1);
|
||||||
void toggle_option(const std::string& opt_key, bool toggle, int opt_index = -1);
|
void toggle_option(const std::string& opt_key, bool toggle, int opt_index = -1);
|
||||||
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString);
|
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString);
|
||||||
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool current_preset_is_dirty() const;
|
||||||
|
bool saved_preset_is_dirty() const;
|
||||||
|
void update_saved_preset_from_current_preset();
|
||||||
|
#else
|
||||||
bool current_preset_is_dirty();
|
bool current_preset_is_dirty();
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
DynamicPrintConfig* get_config() { return m_config; }
|
DynamicPrintConfig* get_config() { return m_config; }
|
||||||
PresetCollection* get_presets() { return m_presets; }
|
PresetCollection* get_presets() { return m_presets; }
|
||||||
|
|
@ -377,8 +387,8 @@ class TabPrint : public Tab
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TabPrint(wxNotebook* parent) :
|
TabPrint(wxNotebook* parent) :
|
||||||
// Tab(parent, _(L("Print Settings")), L("print")) {}
|
// Tab(parent, _L("Print Settings"), L("print")) {}
|
||||||
Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_PRINT) {}
|
Tab(parent, _L("Print Settings"), Slic3r::Preset::TYPE_PRINT) {}
|
||||||
~TabPrint() {}
|
~TabPrint() {}
|
||||||
|
|
||||||
void build() override;
|
void build() override;
|
||||||
|
|
@ -387,7 +397,11 @@ public:
|
||||||
void toggle_options() override;
|
void toggle_options() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void clear_pages() override;
|
void clear_pages() override;
|
||||||
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; }
|
||||||
|
#else
|
||||||
|
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr;
|
ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr;
|
||||||
|
|
@ -407,8 +421,8 @@ private:
|
||||||
std::map<std::string, wxCheckBox*> m_overrides_options;
|
std::map<std::string, wxCheckBox*> m_overrides_options;
|
||||||
public:
|
public:
|
||||||
TabFilament(wxNotebook* parent) :
|
TabFilament(wxNotebook* parent) :
|
||||||
// Tab(parent, _(L("Filament Settings")), L("filament")) {}
|
// Tab(parent, _L("Filament Settings"), L("filament")) {}
|
||||||
Tab(parent, _(L("Filament Settings")), Slic3r::Preset::TYPE_FILAMENT) {}
|
Tab(parent, _L("Filament Settings"), Slic3r::Preset::TYPE_FILAMENT) {}
|
||||||
~TabFilament() {}
|
~TabFilament() {}
|
||||||
|
|
||||||
void build() override;
|
void build() override;
|
||||||
|
|
@ -417,7 +431,11 @@ public:
|
||||||
void toggle_options() override;
|
void toggle_options() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void clear_pages() override;
|
void clear_pages() override;
|
||||||
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; }
|
||||||
|
#else
|
||||||
|
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
};
|
};
|
||||||
|
|
||||||
class TabPrinter : public Tab
|
class TabPrinter : public Tab
|
||||||
|
|
@ -450,7 +468,7 @@ public:
|
||||||
|
|
||||||
// TabPrinter(wxNotebook* parent) : Tab(parent, _(L("Printer Settings")), L("printer")) {}
|
// TabPrinter(wxNotebook* parent) : Tab(parent, _(L("Printer Settings")), L("printer")) {}
|
||||||
TabPrinter(wxNotebook* parent) :
|
TabPrinter(wxNotebook* parent) :
|
||||||
Tab(parent, _(L("Printer Settings")), Slic3r::Preset::TYPE_PRINTER) {}
|
Tab(parent, _L("Printer Settings"), Slic3r::Preset::TYPE_PRINTER) {}
|
||||||
~TabPrinter() {}
|
~TabPrinter() {}
|
||||||
|
|
||||||
void build() override;
|
void build() override;
|
||||||
|
|
@ -472,7 +490,11 @@ public:
|
||||||
void init_options_list() override;
|
void init_options_list() override;
|
||||||
void msw_rescale() override;
|
void msw_rescale() override;
|
||||||
void sys_color_changed() override;
|
void sys_color_changed() override;
|
||||||
bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; }
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool supports_printer_technology(const PrinterTechnology /* tech */) const override { return true; }
|
||||||
|
#else
|
||||||
|
bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
|
||||||
wxSizer* create_bed_shape_widget(wxWindow* parent);
|
wxSizer* create_bed_shape_widget(wxWindow* parent);
|
||||||
void cache_extruder_cnt();
|
void cache_extruder_cnt();
|
||||||
|
|
@ -483,8 +505,8 @@ class TabSLAMaterial : public Tab
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TabSLAMaterial(wxNotebook* parent) :
|
TabSLAMaterial(wxNotebook* parent) :
|
||||||
// Tab(parent, _(L("Material Settings")), L("sla_material")) {}
|
// Tab(parent, _L("Material Settings"), L("sla_material")) {}
|
||||||
Tab(parent, _(L("Material Settings")), Slic3r::Preset::TYPE_SLA_MATERIAL) {}
|
Tab(parent, _L("Material Settings"), Slic3r::Preset::TYPE_SLA_MATERIAL) {}
|
||||||
~TabSLAMaterial() {}
|
~TabSLAMaterial() {}
|
||||||
|
|
||||||
void build() override;
|
void build() override;
|
||||||
|
|
@ -492,15 +514,19 @@ public:
|
||||||
void toggle_options() override {};
|
void toggle_options() override {};
|
||||||
void update() override;
|
void update() override;
|
||||||
void init_options_list() override;
|
void init_options_list() override;
|
||||||
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; }
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; }
|
||||||
|
#else
|
||||||
|
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
};
|
};
|
||||||
|
|
||||||
class TabSLAPrint : public Tab
|
class TabSLAPrint : public Tab
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TabSLAPrint(wxNotebook* parent) :
|
TabSLAPrint(wxNotebook* parent) :
|
||||||
// Tab(parent, _(L("Print Settings")), L("sla_print")) {}
|
// Tab(parent, _L("Print Settings"), L("sla_print")) {}
|
||||||
Tab(parent, _(L("Print Settings")), Slic3r::Preset::TYPE_SLA_PRINT) {}
|
Tab(parent, _L("Print Settings"), Slic3r::Preset::TYPE_SLA_PRINT) {}
|
||||||
~TabSLAPrint() {}
|
~TabSLAPrint() {}
|
||||||
|
|
||||||
ogStaticText* m_support_object_elevation_description_line = nullptr;
|
ogStaticText* m_support_object_elevation_description_line = nullptr;
|
||||||
|
|
@ -511,7 +537,11 @@ public:
|
||||||
void toggle_options() override;
|
void toggle_options() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void clear_pages() override;
|
void clear_pages() override;
|
||||||
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; }
|
#if ENABLE_PROJECT_DIRTY_STATE
|
||||||
|
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; }
|
||||||
|
#else
|
||||||
|
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; }
|
||||||
|
#endif // ENABLE_PROJECT_DIRTY_STATE
|
||||||
};
|
};
|
||||||
|
|
||||||
} // GUI
|
} // GUI
|
||||||
|
|
|
||||||
|
|
@ -1694,6 +1694,9 @@ void DiffPresetDialog::update_compatibility(const std::string& preset_name, Pres
|
||||||
technology_changed = old_printer_technology != new_printer_technology;
|
technology_changed = old_printer_technology != new_printer_technology;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// select preset
|
||||||
|
presets->select_preset_by_name(preset_name, false);
|
||||||
|
|
||||||
// Mark the print & filament enabled if they are compatible with the currently selected preset.
|
// Mark the print & filament enabled if they are compatible with the currently selected preset.
|
||||||
// The following method should not discard changes of current print or filament presets on change of a printer profile,
|
// The following method should not discard changes of current print or filament presets on change of a printer profile,
|
||||||
// if they are compatible with the current printer.
|
// if they are compatible with the current printer.
|
||||||
|
|
|
||||||
|
|
@ -755,7 +755,7 @@ namespace UndoRedo {
|
||||||
|
|
||||||
template<typename T> std::shared_ptr<const T>& ImmutableObjectHistory<T>::shared_ptr(StackImpl &stack)
|
template<typename T> std::shared_ptr<const T>& ImmutableObjectHistory<T>::shared_ptr(StackImpl &stack)
|
||||||
{
|
{
|
||||||
if (m_shared_object.get() == nullptr && ! this->m_serialized.empty()) {
|
if (m_shared_object.get() == nullptr && ! m_serialized.empty()) {
|
||||||
// Deserialize the object.
|
// Deserialize the object.
|
||||||
std::istringstream iss(m_serialized);
|
std::istringstream iss(m_serialized);
|
||||||
{
|
{
|
||||||
|
|
@ -897,7 +897,7 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GU
|
||||||
this->load_mutable_object<Slic3r::GUI::GLGizmosManager>(gizmos.id(), gizmos);
|
this->load_mutable_object<Slic3r::GUI::GLGizmosManager>(gizmos.id(), gizmos);
|
||||||
// Sort the volumes so that we may use binary search.
|
// Sort the volumes so that we may use binary search.
|
||||||
std::sort(m_selection.volumes_and_instances.begin(), m_selection.volumes_and_instances.end());
|
std::sort(m_selection.volumes_and_instances.begin(), m_selection.volumes_and_instances.end());
|
||||||
this->m_active_snapshot_time = timestamp;
|
m_active_snapshot_time = timestamp;
|
||||||
assert(this->valid());
|
assert(this->valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,22 +15,6 @@ using namespace Slic3r;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
ClipperLib::Path mittered_offset_path_scaled(const Points& contour, const std::vector<float>& deltas, double miter_limit);
|
ClipperLib::Path mittered_offset_path_scaled(const Points& contour, const std::vector<float>& deltas, double miter_limit);
|
||||||
|
|
||||||
#if 0
|
|
||||||
static Points mittered_offset_path_scaled_points(const Points& contour, const std::vector<float>& deltas, double miter_limit)
|
|
||||||
{
|
|
||||||
Points out;
|
|
||||||
ClipperLib::Path scaled = mittered_offset_path_scaled(contour, deltas, miter_limit);
|
|
||||||
for (ClipperLib::IntPoint& pt : scaled) {
|
|
||||||
pt.X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
|
||||||
pt.Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
|
|
||||||
pt.X >>= CLIPPER_OFFSET_POWER_OF_2;
|
|
||||||
pt.Y >>= CLIPPER_OFFSET_POWER_OF_2;
|
|
||||||
out.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExPolygon spirograph_gear_1mm()
|
static ExPolygon spirograph_gear_1mm()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,112 @@
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
SCENARIO("Converted Perl tests", "[Polygon]") {
|
||||||
|
GIVEN("ccw_square") {
|
||||||
|
Polygon ccw_square{ { 100, 100 }, { 200, 100 }, { 200, 200 }, { 100, 200 } };
|
||||||
|
Polygon cw_square(ccw_square);
|
||||||
|
cw_square.reverse();
|
||||||
|
|
||||||
|
THEN("ccw_square is valid") {
|
||||||
|
REQUIRE(ccw_square.is_valid());
|
||||||
|
}
|
||||||
|
THEN("cw_square is valid") {
|
||||||
|
REQUIRE(cw_square.is_valid());
|
||||||
|
}
|
||||||
|
THEN("ccw_square.area") {
|
||||||
|
REQUIRE(ccw_square.area() == 100 * 100);
|
||||||
|
}
|
||||||
|
THEN("cw_square.area") {
|
||||||
|
REQUIRE(cw_square.area() == - 100 * 100);
|
||||||
|
}
|
||||||
|
THEN("ccw_square.centroid") {
|
||||||
|
REQUIRE(ccw_square.centroid() == Point { 150, 150 });
|
||||||
|
}
|
||||||
|
THEN("cw_square.centroid") {
|
||||||
|
REQUIRE(cw_square.centroid() == Point { 150, 150 });
|
||||||
|
}
|
||||||
|
THEN("ccw_square.contains_point(150, 150)") {
|
||||||
|
REQUIRE(ccw_square.contains({ 150, 150 }));
|
||||||
|
}
|
||||||
|
THEN("cw_square.contains_point(150, 150)") {
|
||||||
|
REQUIRE(cw_square.contains({ 150, 150 }));
|
||||||
|
}
|
||||||
|
THEN("conversion to lines") {
|
||||||
|
REQUIRE(ccw_square.lines() == Lines{
|
||||||
|
{ { 100, 100 }, { 200, 100 } },
|
||||||
|
{ { 200, 100 }, { 200, 200 } },
|
||||||
|
{ { 200, 200 }, { 100, 200 } },
|
||||||
|
{ { 100, 200 }, { 100, 100 } } });
|
||||||
|
}
|
||||||
|
THEN("split_at_first_point") {
|
||||||
|
REQUIRE(ccw_square.split_at_first_point() == Polyline { ccw_square[0], ccw_square[1], ccw_square[2], ccw_square[3], ccw_square[0] });
|
||||||
|
}
|
||||||
|
THEN("split_at_index(2)") {
|
||||||
|
REQUIRE(ccw_square.split_at_index(2) == Polyline { ccw_square[2], ccw_square[3], ccw_square[0], ccw_square[1], ccw_square[2] });
|
||||||
|
}
|
||||||
|
THEN("split_at_vertex(ccw_square[2])") {
|
||||||
|
REQUIRE(ccw_square.split_at_vertex(ccw_square[2]) == Polyline { ccw_square[2], ccw_square[3], ccw_square[0], ccw_square[1], ccw_square[2] });
|
||||||
|
}
|
||||||
|
THEN("is_counter_clockwise") {
|
||||||
|
REQUIRE(ccw_square.is_counter_clockwise());
|
||||||
|
}
|
||||||
|
THEN("! is_counter_clockwise") {
|
||||||
|
REQUIRE(! cw_square.is_counter_clockwise());
|
||||||
|
}
|
||||||
|
THEN("make_counter_clockwise") {
|
||||||
|
cw_square.make_counter_clockwise();
|
||||||
|
REQUIRE(cw_square.is_counter_clockwise());
|
||||||
|
}
|
||||||
|
THEN("make_counter_clockwise^2") {
|
||||||
|
cw_square.make_counter_clockwise();
|
||||||
|
cw_square.make_counter_clockwise();
|
||||||
|
REQUIRE(cw_square.is_counter_clockwise());
|
||||||
|
}
|
||||||
|
THEN("first_point") {
|
||||||
|
REQUIRE(&ccw_square.first_point() == &ccw_square.points.front());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GIVEN("Triangulating hexagon") {
|
||||||
|
Polygon hexagon{ { 100, 0 } };
|
||||||
|
for (size_t i = 1; i < 6; ++ i) {
|
||||||
|
Point p = hexagon.points.front();
|
||||||
|
p.rotate(PI / 3 * i);
|
||||||
|
hexagon.points.emplace_back(p);
|
||||||
|
}
|
||||||
|
Polygons triangles;
|
||||||
|
hexagon.triangulate_convex(&triangles);
|
||||||
|
THEN("right number of triangles") {
|
||||||
|
REQUIRE(triangles.size() == 4);
|
||||||
|
}
|
||||||
|
THEN("all triangles are ccw") {
|
||||||
|
auto it = std::find_if(triangles.begin(), triangles.end(), [](const Polygon &tri) { return tri.is_clockwise(); });
|
||||||
|
REQUIRE(it == triangles.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GIVEN("General triangle") {
|
||||||
|
Polygon polygon { { 50000000, 100000000 }, { 300000000, 102000000 }, { 50000000, 104000000 } };
|
||||||
|
Line line { { 175992032, 102000000 }, { 47983964, 102000000 } };
|
||||||
|
Point intersection;
|
||||||
|
bool has_intersection = polygon.intersection(line, &intersection);
|
||||||
|
THEN("Intersection with line") {
|
||||||
|
REQUIRE(has_intersection);
|
||||||
|
REQUIRE(intersection == Point { 50000000, 102000000 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Centroid of Trapezoid must be inside", "[Polygon][Utils]")
|
||||||
|
{
|
||||||
|
Slic3r::Polygon trapezoid {
|
||||||
|
{ 4702134, 1124765853 },
|
||||||
|
{ -4702134, 1124765853 },
|
||||||
|
{ -9404268, 1049531706 },
|
||||||
|
{ 9404268, 1049531706 },
|
||||||
|
};
|
||||||
|
Point centroid = trapezoid.centroid();
|
||||||
|
CHECK(trapezoid.contains(centroid));
|
||||||
|
}
|
||||||
|
|
||||||
// This test currently only covers remove_collinear_points.
|
// This test currently only covers remove_collinear_points.
|
||||||
// All remaining tests are to be ported from xs/t/06_polygon.t
|
// All remaining tests are to be ported from xs/t/06_polygon.t
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,8 @@
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use List::Util qw(first);
|
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 21;
|
use Test::More tests => 3;
|
||||||
|
|
||||||
use constant PI => 4 * atan2(1, 1);
|
|
||||||
|
|
||||||
my $square = [ # ccw
|
my $square = [ # ccw
|
||||||
[100, 100],
|
[100, 100],
|
||||||
|
|
@ -17,81 +14,8 @@ my $square = [ # ccw
|
||||||
];
|
];
|
||||||
|
|
||||||
my $polygon = Slic3r::Polygon->new(@$square);
|
my $polygon = Slic3r::Polygon->new(@$square);
|
||||||
my $cw_polygon = $polygon->clone;
|
|
||||||
$cw_polygon->reverse;
|
|
||||||
|
|
||||||
ok $polygon->is_valid, 'is_valid';
|
|
||||||
is_deeply $polygon->pp, $square, 'polygon roundtrip';
|
|
||||||
|
|
||||||
is ref($polygon->arrayref), 'ARRAY', 'polygon arrayref is unblessed';
|
is ref($polygon->arrayref), 'ARRAY', 'polygon arrayref is unblessed';
|
||||||
isa_ok $polygon->[0], 'Slic3r::Point::Ref', 'polygon point is blessed';
|
isa_ok $polygon->[0], 'Slic3r::Point::Ref', 'polygon point is blessed';
|
||||||
|
|
||||||
my $lines = $polygon->lines;
|
|
||||||
is_deeply [ map $_->pp, @$lines ], [
|
|
||||||
[ [100, 100], [200, 100] ],
|
|
||||||
[ [200, 100], [200, 200] ],
|
|
||||||
[ [200, 200], [100, 200] ],
|
|
||||||
[ [100, 200], [100, 100] ],
|
|
||||||
], 'polygon lines';
|
|
||||||
|
|
||||||
is_deeply $polygon->split_at_first_point->pp, [ @$square[0,1,2,3,0] ], 'split_at_first_point';
|
|
||||||
is_deeply $polygon->split_at_index(2)->pp, [ @$square[2,3,0,1,2] ], 'split_at_index';
|
|
||||||
is_deeply $polygon->split_at_vertex(Slic3r::Point->new(@{$square->[2]}))->pp, [ @$square[2,3,0,1,2] ], 'split_at';
|
|
||||||
is $polygon->area, 100*100, 'area';
|
|
||||||
|
|
||||||
ok $polygon->is_counter_clockwise, 'is_counter_clockwise';
|
|
||||||
ok !$cw_polygon->is_counter_clockwise, 'is_counter_clockwise';
|
|
||||||
{
|
|
||||||
my $clone = $polygon->clone;
|
|
||||||
$clone->reverse;
|
|
||||||
ok !$clone->is_counter_clockwise, 'is_counter_clockwise';
|
|
||||||
$clone->make_counter_clockwise;
|
|
||||||
ok $clone->is_counter_clockwise, 'make_counter_clockwise';
|
|
||||||
$clone->make_counter_clockwise;
|
|
||||||
ok $clone->is_counter_clockwise, 'make_counter_clockwise';
|
|
||||||
}
|
|
||||||
|
|
||||||
ok ref($polygon->first_point) eq 'Slic3r::Point', 'first_point';
|
ok ref($polygon->first_point) eq 'Slic3r::Point', 'first_point';
|
||||||
|
|
||||||
ok $polygon->contains_point(Slic3r::Point->new(150,150)), 'ccw contains_point';
|
|
||||||
ok $cw_polygon->contains_point(Slic3r::Point->new(150,150)), 'cw contains_point';
|
|
||||||
|
|
||||||
{
|
|
||||||
my @points = (Slic3r::Point->new(100,0));
|
|
||||||
foreach my $i (1..5) {
|
|
||||||
my $point = $points[0]->clone;
|
|
||||||
$point->rotate(PI/3*$i, [0,0]);
|
|
||||||
push @points, $point;
|
|
||||||
}
|
|
||||||
my $hexagon = Slic3r::Polygon->new(@points);
|
|
||||||
my $triangles = $hexagon->triangulate_convex;
|
|
||||||
is scalar(@$triangles), 4, 'right number of triangles';
|
|
||||||
ok !(defined first { $_->is_clockwise } @$triangles), 'all triangles are ccw';
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
is_deeply $polygon->centroid->pp, [150,150], 'centroid';
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my $polygon = Slic3r::Polygon->new(
|
|
||||||
[50000000, 100000000],
|
|
||||||
[300000000, 102000000],
|
|
||||||
[50000000, 104000000],
|
|
||||||
);
|
|
||||||
my $line = Slic3r::Line->new([175992032,102000000], [47983964,102000000]);
|
|
||||||
my $intersection = $polygon->intersection($line);
|
|
||||||
is_deeply $intersection->pp, [50000000, 102000000], 'polygon-line intersection';
|
|
||||||
}
|
|
||||||
|
|
||||||
# this is not a test: this just demonstrates bad usage, where $polygon->clone gets
|
|
||||||
# DESTROY'ed before the derived object ($point), causing bad memory access
|
|
||||||
if (0) {
|
|
||||||
my $point;
|
|
||||||
{
|
|
||||||
$point = $polygon->clone->[0];
|
|
||||||
}
|
|
||||||
$point->scale(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
|
||||||
|
|
@ -41,18 +41,6 @@ offset_ex(polygons, delta, joinType = Slic3r::ClipperLib::jtMiter, miterLimit =
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
Polygons
|
|
||||||
offset2(polygons, delta1, delta2, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = 3)
|
|
||||||
Polygons polygons
|
|
||||||
const float delta1
|
|
||||||
const float delta2
|
|
||||||
Slic3r::ClipperLib::JoinType joinType
|
|
||||||
double miterLimit
|
|
||||||
CODE:
|
|
||||||
RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit);
|
|
||||||
OUTPUT:
|
|
||||||
RETVAL
|
|
||||||
|
|
||||||
ExPolygons
|
ExPolygons
|
||||||
offset2_ex(polygons, delta1, delta2, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = 3)
|
offset2_ex(polygons, delta1, delta2, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = 3)
|
||||||
Polygons polygons
|
Polygons polygons
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@
|
||||||
// owned by Layer, no constructor/destructor
|
// owned by Layer, no constructor/destructor
|
||||||
|
|
||||||
Ref<Layer> layer();
|
Ref<Layer> layer();
|
||||||
Ref<PrintRegion> region();
|
Ref<PrintRegion> region()
|
||||||
|
%code%{ RETVAL = &THIS->region(); %};
|
||||||
|
|
||||||
Ref<SurfaceCollection> slices()
|
Ref<SurfaceCollection> slices()
|
||||||
%code%{ RETVAL = &THIS->slices; %};
|
%code%{ RETVAL = &THIS->slices; %};
|
||||||
|
|
|
||||||
|
|
@ -32,15 +32,11 @@ _constant()
|
||||||
|
|
||||||
Ref<StaticPrintConfig> config()
|
Ref<StaticPrintConfig> config()
|
||||||
%code%{ RETVAL = &THIS->config(); %};
|
%code%{ RETVAL = &THIS->config(); %};
|
||||||
Ref<Print> print();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
%name{Slic3r::Print::Object} class PrintObject {
|
%name{Slic3r::Print::Object} class PrintObject {
|
||||||
// owned by Print, no constructor/destructor
|
// owned by Print, no constructor/destructor
|
||||||
|
|
||||||
int region_count()
|
|
||||||
%code%{ RETVAL = THIS->print()->regions().size(); %};
|
|
||||||
|
|
||||||
Ref<Print> print();
|
Ref<Print> print();
|
||||||
Ref<ModelObject> model_object();
|
Ref<ModelObject> model_object();
|
||||||
Ref<StaticPrintConfig> config()
|
Ref<StaticPrintConfig> config()
|
||||||
|
|
@ -99,11 +95,7 @@ _constant()
|
||||||
%code%{ RETVAL = THIS->objects().size(); %};
|
%code%{ RETVAL = THIS->objects().size(); %};
|
||||||
|
|
||||||
PrintRegionPtrs* regions()
|
PrintRegionPtrs* regions()
|
||||||
%code%{ RETVAL = const_cast<PrintRegionPtrs*>(&THIS->regions_mutable()); %};
|
%code%{ RETVAL = const_cast<PrintRegionPtrs*>(&THIS->print_regions_mutable()); %};
|
||||||
Ref<PrintRegion> get_region(int idx)
|
|
||||||
%code%{ RETVAL = THIS->regions_mutable()[idx]; %};
|
|
||||||
size_t region_count()
|
|
||||||
%code%{ RETVAL = THIS->regions().size(); %};
|
|
||||||
|
|
||||||
bool step_done(PrintStep step)
|
bool step_done(PrintStep step)
|
||||||
%code%{ RETVAL = THIS->is_step_done(step); %};
|
%code%{ RETVAL = THIS->is_step_done(step); %};
|
||||||
|
|
@ -113,7 +105,7 @@ _constant()
|
||||||
SV* filament_stats()
|
SV* filament_stats()
|
||||||
%code%{
|
%code%{
|
||||||
HV* hv = newHV();
|
HV* hv = newHV();
|
||||||
for (std::map<size_t,float>::const_iterator it = THIS->print_statistics().filament_stats.begin(); it != THIS->print_statistics().filament_stats.end(); ++it) {
|
for (std::map<size_t,double>::const_iterator it = THIS->print_statistics().filament_stats.begin(); it != THIS->print_statistics().filament_stats.end(); ++it) {
|
||||||
// stringify extruder_id
|
// stringify extruder_id
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << it->first;
|
ss << it->first;
|
||||||
|
|
@ -123,7 +115,6 @@ _constant()
|
||||||
RETVAL = newRV_noinc((SV*)hv);
|
RETVAL = newRV_noinc((SV*)hv);
|
||||||
}
|
}
|
||||||
%};
|
%};
|
||||||
double max_allowed_layer_height() const;
|
|
||||||
bool has_support_material() const;
|
bool has_support_material() const;
|
||||||
void auto_assign_extruders(ModelObject* model_object);
|
void auto_assign_extruders(ModelObject* model_object);
|
||||||
std::string output_filepath(std::string path = "")
|
std::string output_filepath(std::string path = "")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue