Merge remote-tracking branch 'PRIVATE/master' into ys_cut

This commit is contained in:
YuSanka 2022-10-03 11:30:49 +02:00
commit b4f38883a8
40 changed files with 2703 additions and 3057 deletions

View File

@ -71,7 +71,7 @@ is currently unsupported because some of the dependencies don't support this, mo
Please note that the `CMAKE_OSX_DEPLOYMENT_TARGET` and `CMAKE_OSX_SYSROOT` options need to be set the same Please note that the `CMAKE_OSX_DEPLOYMENT_TARGET` and `CMAKE_OSX_SYSROOT` options need to be set the same
on both the dependencies bundle as well as PrusaSlicer itself. on both the dependencies bundle as well as PrusaSlicer itself.
Official Mac PrusaSlicer builds are currently built against SDK 10.9 to ensure compatibility with older Macs. Official macOS PrusaSlicer builds are currently (as of PrusaSlicer 2.5) built against SDK 10.12 to ensure compatibility with older Macs.
_Warning:_ XCode may be set such that it rejects SDKs bellow some version (silently, more or less). _Warning:_ XCode may be set such that it rejects SDKs bellow some version (silently, more or less).
This is set in the property list file This is set in the property list file

View File

@ -1,2 +1,3 @@
min_slic3r_version = 2.4.1-alpha0 min_slic3r_version = 2.4.1-alpha0
1.0.1 Various fixes and improvements. Commented filament sensor initialisation for v-Minion (optional HW).
1.0.0 Initial RatRig bundle 1.0.0 Initial RatRig bundle

View File

@ -9,7 +9,7 @@
name = RatRig name = RatRig
# Configuration version of this file. Config file will only be installed, if the config_version differs. # Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the Slic3r configuration to be downgraded. # This means, the server may force the Slic3r configuration to be downgraded.
config_version = 1.0.0 config_version = 1.0.1
# Where to get the updates from? # Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/RatRig/ config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/RatRig/
# The printer models will be shown by the Configuration Wizard in this order, # The printer models will be shown by the Configuration Wizard in this order,
@ -25,7 +25,7 @@ technology = FFF
family = V-Core-3 family = V-Core-3
bed_model = rr-vc-300.stl bed_model = rr-vc-300.stl
bed_texture = rr-vc-300.svg bed_texture = rr-vc-300.svg
default_materials = Generic PETG V6 @RatRig; Generic PETG HF @RatRig; Generic PLA @RatRig; Esun ePA-CF @RatRig; Generic ABS @RatRig default_materials = Generic PETG V6 @RatRig; Generic PETG HF @RatRig; Generic PLA @RatRig; Esun ePA-CF @RatRig; Generic ABS @RatRig; Generic TPU @RatRig
[printer_model:VC3_400] [printer_model:VC3_400]
name = RatRig V-Core-3 400mm name = RatRig V-Core-3 400mm
@ -33,7 +33,7 @@ variants = 0.4
family = V-Core-3 family = V-Core-3
bed_model = rr-vc-400.stl bed_model = rr-vc-400.stl
bed_texture = rr-vc-400.svg bed_texture = rr-vc-400.svg
default_materials = Generic PETG V6 @RatRig; Generic PETG HF @RatRig; Generic PLA @RatRig; Esun ePA-CF @RatRig; Generic ABS @RatRig default_materials = Generic PETG V6 @RatRig; Generic PETG HF @RatRig; Generic PLA @RatRig; Esun ePA-CF @RatRig; Generic ABS @RatRig; Generic TPU @RatRig
[printer_model:VC3_500] [printer_model:VC3_500]
name = RatRig V-Core-3 500mm name = RatRig V-Core-3 500mm
@ -41,7 +41,7 @@ variants = 0.4
family = V-Core-3 family = V-Core-3
bed_model = rr-vc-500.stl bed_model = rr-vc-500.stl
bed_texture = rr-vc-500.svg bed_texture = rr-vc-500.svg
default_materials = Generic PETG V6 @RatRig; Generic PETG HF @RatRig; Generic PLA @RatRig; Esun ePA-CF @RatRig; Generic ABS @RatRig default_materials = Generic PETG V6 @RatRig; Generic PETG HF @RatRig; Generic PLA @RatRig; Esun ePA-CF @RatRig; Generic ABS @RatRig; Generic TPU @RatRig
[printer_model:VMINION] [printer_model:VMINION]
name = RatRig V-Minion name = RatRig V-Minion
@ -50,7 +50,7 @@ technology = FFF
family = V-Minion family = V-Minion
bed_model = rr-vminion.stl bed_model = rr-vminion.stl
bed_texture = rr-vminion.svg bed_texture = rr-vminion.svg
default_materials = Generic PETG V6 @RatRig; Generic PETG HF @RatRig; Generic PLA @RatRig; @Esun ePA-CF RatRig; Generic ABS @RatRig default_materials = Generic PETG V6 @RatRig; Generic PETG HF @RatRig; Generic PLA @RatRig; @Esun ePA-CF RatRig; Generic ABS @RatRig; Generic TPU @RatRig
[print:*common*] [print:*common*]
compatible_printers_condition = printer_model=~/.*VC3_.*/ and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_model=~/.*VC3_.*/ and nozzle_diameter[0]==0.4
@ -75,7 +75,6 @@ dont_support_bridges = 1
draft_shield = 0 draft_shield = 0
ensure_vertical_shell_thickness = 1 ensure_vertical_shell_thickness = 1
exact_last_layer_height = 0 exact_last_layer_height = 0
external_infill_margin = 150%
external_perimeter_extrusion_width = 0.4 external_perimeter_extrusion_width = 0.4
external_perimeter_speed = 90% external_perimeter_speed = 90%
extruder_clearance_height = 25 extruder_clearance_height = 25
@ -116,7 +115,6 @@ notes =
only_retract_when_crossing_perimeters = 0 only_retract_when_crossing_perimeters = 0
ooze_prevention = 0 ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{print_time}.gcode output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{print_time}.gcode
over_bridge_flow_ratio = 100%
perimeter_acceleration = 3000 perimeter_acceleration = 3000
perimeter_extruder = 1 perimeter_extruder = 1
perimeter_extrusion_width = 0.4 perimeter_extrusion_width = 0.4
@ -163,7 +161,6 @@ support_material_threshold = 65
support_material_with_sheath = 0 support_material_with_sheath = 0
support_material_xy_spacing = 60% support_material_xy_spacing = 60%
thin_walls = 0 thin_walls = 0
threads = 4
top_fill_pattern = monotonic top_fill_pattern = monotonic
top_infill_extrusion_width = 0.4 top_infill_extrusion_width = 0.4
top_solid_infill_speed = 60% top_solid_infill_speed = 60%
@ -437,7 +434,7 @@ retract_speed = 40
silent_mode = 0 silent_mode = 0
single_extruder_multi_material = 0 single_extruder_multi_material = 0
start_gcode = M190 S0 ; Prevents prusaslicer from prepending m190 to the gcode interfering with the macro\nM109 S0 ; Prevents prusaslicer from prepending m109 to the gcode interfering with the macro\nSTART_PRINT EXTRUDER_TEMP=[first_layer_temperature] BED_TEMP=[first_layer_bed_temperature]\n;enable this if you have a BTT Smart Filament Sensor\nSET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=0\n start_gcode = M190 S0 ; Prevents prusaslicer from prepending m190 to the gcode interfering with the macro\nM109 S0 ; Prevents prusaslicer from prepending m109 to the gcode interfering with the macro\nSTART_PRINT EXTRUDER_TEMP=[first_layer_temperature] BED_TEMP=[first_layer_bed_temperature]\n;enable this if you have a BTT Smart Filament Sensor\nSET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=0\n
thumbnails = 16x16,220x220 thumbnails = 64x64,400x300
toolchange_gcode = toolchange_gcode =
use_firmware_retraction = 0 use_firmware_retraction = 0
use_relative_e_distances = 1 use_relative_e_distances = 1
@ -499,10 +496,10 @@ retract_restart_extra_toolchange = 0
retract_speed = 40 retract_speed = 40
silent_mode = 0 silent_mode = 0
single_extruder_multi_material = 0 single_extruder_multi_material = 0
start_gcode = M190 S0 ; Prevents prusaslicer from prepending m190 to the gcode interfering with the macro\nM109 S0 ; Prevents prusaslicer from prepending m109 to the gcode interfering with the macro\nSTART_PRINT EXTRUDER_TEMP=[first_layer_temperature] BED_TEMP=[first_layer_bed_temperature]\n;enable this if you have a BTT Smart Filament Sensor\nSET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=0\n start_gcode = M190 S0 ; Prevents prusaslicer from prepending m190 to the gcode interfering with the macro\nM109 S0 ; Prevents prusaslicer from prepending m109 to the gcode interfering with the macro\nSTART_PRINT EXTRUDER_TEMP=[first_layer_temperature] BED_TEMP=[first_layer_bed_temperature]\n;enable this if you have a BTT Smart Filament Sensor\n; SET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=0\n
start_gcode_manual = 0 start_gcode_manual = 0
template_custom_gcode = template_custom_gcode =
thumbnails = 16x16,220x220 thumbnails = 64x64,400x300
thumbnails_color = #018aff thumbnails_color = #018aff
thumbnails_custom_color = 0 thumbnails_custom_color = 0
thumbnails_with_bed = 1 thumbnails_with_bed = 1
@ -745,3 +742,86 @@ min_print_speed = 15
slowdown_below_layer_time = 10 slowdown_below_layer_time = 10
start_filament_gcode = "; Filament gcode\nSET_GCODE_OFFSET Z=0.0\n\n{if nozzle_diameter[0]==0.4} SET_PRESSURE_ADVANCE ADVANCE=0.045{elsif nozzle_diameter[0]==0.6}SET_PRESSURE_ADVANCE ADVANCE=0.02{endif}\n\n" start_filament_gcode = "; Filament gcode\nSET_GCODE_OFFSET Z=0.0\n\n{if nozzle_diameter[0]==0.4} SET_PRESSURE_ADVANCE ADVANCE=0.045{elsif nozzle_diameter[0]==0.6}SET_PRESSURE_ADVANCE ADVANCE=0.02{endif}\n\n"
temperature = 270 temperature = 270
[filament:Generic TPU @RatRig]
inherits = Generic PLA @RatRig
bed_temperature = 30
bridge_fan_speed = 100
bridge_internal_fan_speed = -1
chamber_temperature = 0
compatible_printers =
compatible_printers_condition =
compatible_prints =
compatible_prints_condition =
cooling = 1
disable_fan_first_layers = 2
end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n"
external_perimeter_fan_speed = -1
extrusion_multiplier = 0.89
fan_always_on = 1
fan_below_layer_time = 100
filament_colour = #FF00FF
filament_cooling_final_speed = 3.4
filament_cooling_initial_speed = 2.2
filament_cooling_moves = 4
filament_cooling_zone_pause = 0
filament_cost = 20
filament_custom_variables = ""
filament_density = 1.24
filament_deretract_speed = nil
filament_diameter = 1.75
filament_dip_extraction_speed = 70
filament_dip_insertion_speed = 33
filament_enable_toolchange_part_fan = 0
filament_enable_toolchange_temp = 0
filament_load_time = 0
filament_loading_speed = 28
filament_loading_speed_start = 3
filament_max_overlap = 100%
filament_max_speed = 0
filament_max_volumetric_speed = 5
filament_max_wipe_tower_speed = 0
filament_melt_zone_pause = 0
filament_minimal_purge_on_wipe_tower = 15
filament_notes = "This is a profile for TPU95A. It was tested with Anycubic TPU95A, but most of the economy TPU filaments behave pretty similar.\n\nYou will want to optimize the temperature. Even different colors of the same brand can require 10° more or less.\n\nIf you experience clogs or grinding, reduce the maximum volumetric speed!"
filament_ramming_parameters = "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"
filament_retract_before_travel = nil
filament_retract_before_wipe = nil
filament_retract_layer_change = nil
filament_retract_length = 1
filament_retract_lift = 0.1
filament_retract_lift_above = nil
filament_retract_lift_below = nil
filament_retract_restart_extra = nil
filament_retract_speed = nil
filament_seam_gap = nil
filament_shrink = 100%
filament_skinnydip_distance = 31
filament_soluble = 0
filament_spool_weight = 0
filament_toolchange_delay = 0
filament_toolchange_part_fan_speed = 50
filament_toolchange_temp = 200
filament_type = FLEX
filament_unload_time = 0
filament_unloading_speed = 90
filament_unloading_speed_start = 100
filament_use_fast_skinnydip = 0
filament_use_skinnydip = 0
filament_vendor = Generic
filament_wipe = nil
filament_wipe_advanced_pigment = 0.5
filament_wipe_extra_perimeter = nil
filament_wipe_only_crossing = nil
filament_wipe_speed = nil
first_layer_bed_temperature = 30
first_layer_temperature = 220
full_fan_speed_layer = 4
max_fan_speed = 100
max_speed_reduction = 95%
min_fan_speed = 100
min_print_speed = 10
slowdown_below_layer_time = 8
start_filament_gcode = "; Filament gcode\nSET_GCODE_OFFSET Z=0\nSET_PRESSURE_ADVANCE ADVANCE=0.1"
temperature = 220
top_fan_speed = -1

View File

@ -83,6 +83,13 @@ public:
// to split around. // to split around.
template<typename SourceNode> template<typename SourceNode>
void build(std::vector<SourceNode> &&input) void build(std::vector<SourceNode> &&input)
{
this->build_modify_input(input);
input.clear();
}
template<typename SourceNode>
void build_modify_input(std::vector<SourceNode> &input)
{ {
if (input.empty()) if (input.empty())
clear(); clear();
@ -91,7 +98,6 @@ public:
m_nodes.assign(next_highest_power_of_2(input.size()) * 2 - 1, Node()); m_nodes.assign(next_highest_power_of_2(input.size()) * 2 - 1, Node());
build_recursive(input, 0, 0, input.size() - 1); build_recursive(input, 0, 0, input.size() - 1);
} }
input.clear();
} }
const std::vector<Node>& nodes() const { return m_nodes; } const std::vector<Node>& nodes() const { return m_nodes; }

View File

@ -710,6 +710,8 @@ Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygon
{ return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); } { return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip) Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); } { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip) Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); } { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip) Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip)

View File

@ -437,6 +437,7 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip);

View File

@ -6,6 +6,66 @@
namespace Slic3r { namespace Slic3r {
class InfillPolylineClipper : public FillPlanePath::InfillPolylineOutput {
public:
InfillPolylineClipper(const BoundingBox bbox, const double scale_out) : FillPlanePath::InfillPolylineOutput(scale_out), m_bbox(bbox) {}
void add_point(const Vec2d &pt);
Points&& result() { return std::move(m_out); }
bool clips() const override { return true; }
private:
enum class Side {
Left = 1,
Right = 2,
Top = 4,
Bottom = 8
};
int sides(const Point &p) const {
return int(p.x() < m_bbox.min.x()) * int(Side::Left) +
int(p.x() > m_bbox.max.x()) * int(Side::Right) +
int(p.y() < m_bbox.min.y()) * int(Side::Bottom) +
int(p.y() > m_bbox.max.y()) * int(Side::Top);
};
// Bounding box to clip the polyline with.
BoundingBox m_bbox;
// Classification of the two last points processed.
int m_sides_prev;
int m_sides_this;
};
void InfillPolylineClipper::add_point(const Vec2d &fpt)
{
const Point pt{ this->scaled(fpt) };
if (m_out.size() < 2) {
// Collect the two first points and their status.
(m_out.empty() ? m_sides_prev : m_sides_this) = sides(pt);
m_out.emplace_back(pt);
} else {
// Classify the last inserted point, possibly remove it.
int sides_next = sides(pt);
if (// This point is inside. Take it.
m_sides_this == 0 ||
// Either this point is outside and previous or next is inside, or
// the edge possibly cuts corner of the bounding box.
(m_sides_prev & m_sides_this & sides_next) == 0) {
// Keep the last point.
m_sides_prev = m_sides_this;
} else {
// All the three points (this, prev, next) are outside at the same side.
// Ignore the last point.
m_out.pop_back();
}
// And save the current point.
m_out.emplace_back(pt);
m_sides_this = sides_next;
}
}
void FillPlanePath::_fill_surface_single( void FillPlanePath::_fill_surface_single(
const FillParams &params, const FillParams &params,
unsigned int thickness_layers, unsigned int thickness_layers,
@ -13,37 +73,52 @@ void FillPlanePath::_fill_surface_single(
ExPolygon expolygon, ExPolygon expolygon,
Polylines &polylines_out) Polylines &polylines_out)
{ {
expolygon.rotate(- direction.first); expolygon.rotate(-direction.first);
coord_t distance_between_lines = coord_t(scale_(this->spacing) / params.density); //FIXME Vojtech: We are not sure whether the user expects the fill patterns on visible surfaces to be aligned across all the islands of a single layer.
// One may align for this->centered() to align the patterns for Archimedean Chords and Octagram Spiral patterns.
const bool align = params.density < 0.995;
// align infill across layers using the object's bounding box BoundingBox snug_bounding_box = get_extents(expolygon).inflated(SCALED_EPSILON);
// Rotated bounding box of the whole object.
BoundingBox bounding_box = this->bounding_box.rotated(- direction.first);
Point shift = this->_centered() ? // Rotated bounding box of the area to fill in with the pattern.
BoundingBox bounding_box = align ?
// Sparse infill needs to be aligned across layers. Align infill across layers using the object's bounding box.
this->bounding_box.rotated(-direction.first) :
// Solid infill does not need to be aligned across layers, generate the infill pattern
// around the clipping expolygon only.
snug_bounding_box;
Point shift = this->centered() ?
bounding_box.center() : bounding_box.center() :
bounding_box.min; bounding_box.min;
expolygon.translate(-shift.x(), -shift.y()); expolygon.translate(-shift.x(), -shift.y());
bounding_box.translate(-shift.x(), -shift.y()); bounding_box.translate(-shift.x(), -shift.y());
Pointfs pts = _generate( Polyline polyline;
coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines)), {
coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines)), auto distance_between_lines = scaled<double>(this->spacing) / params.density;
coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)), auto min_x = coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines));
coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)), auto min_y = coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines));
params.resolution); auto max_x = coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines));
auto max_y = coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines));
auto resolution = scaled<double>(params.resolution) / distance_between_lines;
if (align) {
// Filling in a bounding box over the whole object, clip generated polyline against the snug bounding box.
snug_bounding_box.translate(-shift.x(), -shift.y());
InfillPolylineClipper output(snug_bounding_box, distance_between_lines);
this->generate(min_x, min_y, max_x, max_y, resolution, output);
polyline.points = std::move(output.result());
} else {
// Filling in a snug bounding box, no need to clip.
InfillPolylineOutput output(distance_between_lines);
this->generate(min_x, min_y, max_x, max_y, resolution, output);
polyline.points = std::move(output.result());
}
}
if (pts.size() >= 2) { if (polyline.size() >= 2) {
// Convert points to a polyline, upscale. Polylines polylines = intersection_pl(polyline, expolygon);
Polylines polylines(1, Polyline());
Polyline &polyline = polylines.front();
polyline.points.reserve(pts.size());
for (const Vec2d &pt : pts)
polyline.points.emplace_back(
coord_t(floor(pt.x() * distance_between_lines + 0.5)),
coord_t(floor(pt.y() * distance_between_lines + 0.5)));
polylines = intersection_pl(polylines, expolygon);
Polylines chained; Polylines chained;
if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1) if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1)
chained = chain_polylines(std::move(polylines)); chained = chain_polylines(std::move(polylines));
@ -59,7 +134,8 @@ void FillPlanePath::_fill_surface_single(
} }
// Follow an Archimedean spiral, in polar coordinates: r=a+b\theta // Follow an Archimedean spiral, in polar coordinates: r=a+b\theta
Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) template<typename Output>
static void generate_archimedean_chords(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, Output &output)
{ {
// Radius to achieve. // Radius to achieve.
coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
@ -70,15 +146,22 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m
coordf_t r = 1; coordf_t r = 1;
Pointfs out; Pointfs out;
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center. //FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
out.emplace_back(0, 0); output.add_point({ 0, 0 });
out.emplace_back(1, 0); output.add_point({ 1, 0 });
while (r < rmax) { while (r < rmax) {
// Discretization angle to achieve a discretization error lower than resolution. // Discretization angle to achieve a discretization error lower than resolution.
theta += 2. * acos(1. - resolution / r); theta += 2. * acos(1. - resolution / r);
r = a + b * theta; r = a + b * theta;
out.emplace_back(r * cos(theta), r * sin(theta)); output.add_point({ r * cos(theta), r * sin(theta) });
} }
return out; }
void FillArchimedeanChords::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output)
{
if (output.clips())
generate_archimedean_chords(min_x, min_y, max_x, max_y, resolution, static_cast<InfillPolylineClipper&>(output));
else
generate_archimedean_chords(min_x, min_y, max_x, max_y, resolution, output);
} }
// Adapted from // Adapted from
@ -126,7 +209,8 @@ static inline Point hilbert_n_to_xy(const size_t n)
return Point(x, y); return Point(x, y);
} }
Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */) template<typename Output>
static void generate_hilbert_curve(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, Output &output)
{ {
// Minimum power of two square to fit the domain. // Minimum power of two square to fit the domain.
size_t sz = 2; size_t sz = 2;
@ -140,46 +224,59 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x,
} }
size_t sz2 = sz * sz; size_t sz2 = sz * sz;
Pointfs line; output.reserve(sz2);
line.reserve(sz2);
for (size_t i = 0; i < sz2; ++ i) { for (size_t i = 0; i < sz2; ++ i) {
Point p = hilbert_n_to_xy(i); Point p = hilbert_n_to_xy(i);
line.emplace_back(p.x() + min_x, p.y() + min_y); output.add_point({ p.x() + min_x, p.y() + min_y });
} }
return line;
} }
Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */) void FillHilbertCurve::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */, InfillPolylineOutput &output)
{
if (output.clips())
generate_hilbert_curve(min_x, min_y, max_x, max_y, static_cast<InfillPolylineClipper&>(output));
else
generate_hilbert_curve(min_x, min_y, max_x, max_y, output);
}
template<typename Output>
static void generate_octagram_spiral(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, Output &output)
{ {
// Radius to achieve. // Radius to achieve.
coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
// Now unwind the spiral. // Now unwind the spiral.
coordf_t r = 0; coordf_t r = 0;
coordf_t r_inc = sqrt(2.); coordf_t r_inc = sqrt(2.);
Pointfs out; output.add_point({ 0., 0. });
out.emplace_back(0., 0.);
while (r < rmax) { while (r < rmax) {
r += r_inc; r += r_inc;
coordf_t rx = r / sqrt(2.); coordf_t rx = r / sqrt(2.);
coordf_t r2 = r + rx; coordf_t r2 = r + rx;
out.emplace_back( r, 0.); output.add_point({ r, 0. });
out.emplace_back( r2, rx); output.add_point({ r2, rx });
out.emplace_back( rx, rx); output.add_point({ rx, rx });
out.emplace_back( rx, r2); output.add_point({ rx, r2 });
out.emplace_back( 0., r); output.add_point({ 0., r });
out.emplace_back(-rx, r2); output.add_point({-rx, r2 });
out.emplace_back(-rx, rx); output.add_point({-rx, rx });
out.emplace_back(-r2, rx); output.add_point({-r2, rx });
out.emplace_back(- r, 0.); output.add_point({- r, 0. });
out.emplace_back(-r2, -rx); output.add_point({-r2, -rx });
out.emplace_back(-rx, -rx); output.add_point({-rx, -rx });
out.emplace_back(-rx, -r2); output.add_point({-rx, -r2 });
out.emplace_back( 0., -r); output.add_point({ 0., -r });
out.emplace_back( rx, -r2); output.add_point({ rx, -r2 });
out.emplace_back( rx, -rx); output.add_point({ rx, -rx });
out.emplace_back( r2+r_inc, -rx); output.add_point({ r2+r_inc, -rx });
} }
return out; }
void FillOctagramSpiral::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */, InfillPolylineOutput &output)
{
if (output.clips())
generate_octagram_spiral(min_x, min_y, max_x, max_y, static_cast<InfillPolylineClipper&>(output));
else
generate_octagram_spiral(min_x, min_y, max_x, max_y, output);
} }
} // namespace Slic3r } // namespace Slic3r

View File

@ -27,8 +27,30 @@ protected:
Polylines &polylines_out) override; Polylines &polylines_out) override;
float _layer_angle(size_t idx) const override { return 0.f; } float _layer_angle(size_t idx) const override { return 0.f; }
virtual bool _centered() const = 0; virtual bool centered() const = 0;
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) = 0;
friend class InfillPolylineClipper;
class InfillPolylineOutput {
public:
InfillPolylineOutput(const double scale_out) : m_scale_out(scale_out) {}
void reserve(size_t n) { m_out.reserve(n); }
void add_point(const Vec2d& pt) { m_out.emplace_back(this->scaled(pt)); }
Points&& result() { return std::move(m_out); }
virtual bool clips() const { return false; }
protected:
const Point scaled(const Vec2d &fpt) const { return { coord_t(floor(fpt.x() * m_scale_out + 0.5)), coord_t(floor(fpt.y() * m_scale_out + 0.5)) }; }
// Output polyline.
Points m_out;
private:
// Scaling coefficient of the generated points before tested against m_bbox and clipped by bbox.
double m_scale_out;
};
virtual void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) = 0;
}; };
class FillArchimedeanChords : public FillPlanePath class FillArchimedeanChords : public FillPlanePath
@ -38,8 +60,8 @@ public:
~FillArchimedeanChords() override = default; ~FillArchimedeanChords() override = default;
protected: protected:
bool _centered() const override { return true; } bool centered() const override { return true; }
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override; void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override;
}; };
class FillHilbertCurve : public FillPlanePath class FillHilbertCurve : public FillPlanePath
@ -49,8 +71,8 @@ public:
~FillHilbertCurve() override = default; ~FillHilbertCurve() override = default;
protected: protected:
bool _centered() const override { return false; } bool centered() const override { return false; }
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override; void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override;
}; };
class FillOctagramSpiral : public FillPlanePath class FillOctagramSpiral : public FillPlanePath
@ -60,8 +82,8 @@ public:
~FillOctagramSpiral() override = default; ~FillOctagramSpiral() override = default;
protected: protected:
bool _centered() const override { return true; } bool centered() const override { return true; }
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override; void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override;
}; };
} // namespace Slic3r } // namespace Slic3r

View File

@ -9,6 +9,7 @@
#include "GCode/WipeTower.hpp" #include "GCode/WipeTower.hpp"
#include "ShortestPath.hpp" #include "ShortestPath.hpp"
#include "Print.hpp" #include "Print.hpp"
#include "Thread.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "libslic3r.h" #include "libslic3r.h"
@ -34,8 +35,6 @@
#include "SVG.hpp" #include "SVG.hpp"
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <tbb/task_scheduler_observer.h>
#include <tbb/enumerable_thread_specific.h>
// Intel redesigned some TBB interface considerably when merging TBB with their oneAPI set of libraries, see GH #7332. // Intel redesigned some TBB interface considerably when merging TBB with their oneAPI set of libraries, see GH #7332.
// We are using quite an old TBB 2017 U7. Before we update our build servers, let's use the old API, which is deprecated in up to date TBB. // We are using quite an old TBB 2017 U7. Before we update our build servers, let's use the old API, which is deprecated in up to date TBB.
@ -1469,32 +1468,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
print.throw_if_canceled(); print.throw_if_canceled();
} }
// For unknown reasons and in sporadic cases when GCode export is processing, some participating thread
// in tbb::parallel_pipeline has not set locales to "C", probably because this thread is newly spawned.
// So in this class method on_scheduler_entry is called for every thread before it starts participating
// in tbb::parallel_pipeline to ensure that locales are set correctly
// For tbb::parallel_pipeline, it seems that on_scheduler_entry is called for every layer and every filter.
// We ensure using thread-local storage that locales will be set to "C" just once for any participating thread.
class TBBLocalesSetter : public tbb::task_scheduler_observer
{
public:
TBBLocalesSetter() { this->observe(true); }
~TBBLocalesSetter() override { this->observe(false); };
void on_scheduler_entry(bool is_worker) override
{
if (bool &is_locales_sets = m_is_locales_sets.local(); !is_locales_sets) {
// Set locales of the worker thread to "C".
set_c_locales();
is_locales_sets = true;
}
}
private:
tbb::enumerable_thread_specific<bool, tbb::cache_aligned_allocator<bool>, tbb::ets_key_usage_type::ets_key_per_instance> m_is_locales_sets{false};
};
// Process all layers of all objects (non-sequential mode) with a parallel pipeline: // Process all layers of all objects (non-sequential mode) with a parallel pipeline:
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
// and export G-code into file. // and export G-code into file.

View File

@ -199,9 +199,7 @@ void GCodeProcessor::TimeMachine::reset()
max_travel_acceleration = 0.0f; max_travel_acceleration = 0.0f;
extrude_factor_override_percentage = 1.0f; extrude_factor_override_percentage = 1.0f;
time = 0.0f; time = 0.0f;
#if ENABLE_TRAVEL_TIME
travel_time = 0.0f; travel_time = 0.0f;
#endif // ENABLE_TRAVEL_TIME
stop_times = std::vector<StopTime>(); stop_times = std::vector<StopTime>();
curr.reset(); curr.reset();
prev.reset(); prev.reset();
@ -317,17 +315,12 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, floa
block_time += additional_time; block_time += additional_time;
time += block_time; time += block_time;
#if ENABLE_TRAVEL_TIME
if (block.move_type == EMoveType::Travel) if (block.move_type == EMoveType::Travel)
travel_time += block_time; travel_time += block_time;
else else
roles_time[static_cast<size_t>(block.role)] += block_time; roles_time[static_cast<size_t>(block.role)] += block_time;
#endif // ENABLE_TRAVEL_TIME
gcode_time.cache += block_time; gcode_time.cache += block_time;
moves_time[static_cast<size_t>(block.move_type)] += block_time; moves_time[static_cast<size_t>(block.move_type)] += block_time;
#if !ENABLE_TRAVEL_TIME
roles_time[static_cast<size_t>(block.role)] += block_time;
#endif // !ENABLE_TRAVEL_TIME
if (block.layer_id >= layers_time.size()) { if (block.layer_id >= layers_time.size()) {
const size_t curr_size = layers_time.size(); const size_t curr_size = layers_time.size();
layers_time.resize(block.layer_id); layers_time.resize(block.layer_id);
@ -1465,7 +1458,6 @@ std::string GCodeProcessor::get_time_dhm(PrintEstimatedStatistics::ETimeMode mod
return (mode < PrintEstimatedStatistics::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");
} }
#if ENABLE_TRAVEL_TIME
float GCodeProcessor::get_travel_time(PrintEstimatedStatistics::ETimeMode mode) const float GCodeProcessor::get_travel_time(PrintEstimatedStatistics::ETimeMode mode) const
{ {
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].travel_time : 0.0f; return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].travel_time : 0.0f;
@ -1475,7 +1467,6 @@ std::string GCodeProcessor::get_travel_time_dhm(PrintEstimatedStatistics::ETimeM
{ {
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].travel_time)) : std::string("N/A"); return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].travel_time)) : std::string("N/A");
} }
#endif // ENABLE_TRAVEL_TIME
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>>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const
{ {
@ -1769,7 +1760,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool
switch (cmd[1]) { switch (cmd[1]) {
case '1': case '1':
switch (cmd[2]) { switch (cmd[2]) {
case '0': { process_G10(line); break; } // Retract case '0': { process_G10(line); break; } // Retract or Set tool temperature
case '1': { process_G11(line); break; } // Unretract case '1': { process_G11(line); break; } // Unretract
default: break; default: break;
} }
@ -3232,6 +3223,23 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc
void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line)
{ {
if (m_flavor == gcfRepRapFirmware) {
// similar to M104/M109
float new_temp;
if (line.has_value('S', new_temp)) {
size_t id = m_extruder_id;
float val;
if (line.has_value('P', val)) {
const size_t eid = static_cast<size_t>(val);
if (eid < m_extruder_temps.size())
id = eid;
}
m_extruder_temps[id] = new_temp;
return;
}
}
// stores retract move // stores retract move
store_move_vertex(EMoveType::Retract); store_move_vertex(EMoveType::Retract);
} }
@ -3441,18 +3449,22 @@ void GCodeProcessor::process_M108(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_M109(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M109(const GCodeReader::GCodeLine& line)
{ {
float new_temp; float new_temp;
size_t id = (size_t)-1;
if (line.has_value('R', new_temp)) { if (line.has_value('R', new_temp)) {
float val; float val;
if (line.has_value('T', val)) { if (line.has_value('T', val)) {
const size_t eid = static_cast<size_t>(val); const size_t eid = static_cast<size_t>(val);
if (eid < m_extruder_temps.size()) if (eid < m_extruder_temps.size())
m_extruder_temps[eid] = new_temp; id = eid;
} }
else else
m_extruder_temps[m_extruder_id] = new_temp; id = m_extruder_id;
} }
else if (line.has_value('S', new_temp)) else if (line.has_value('S', new_temp))
m_extruder_temps[m_extruder_id] = new_temp; id = m_extruder_id;
if (id != (size_t)-1)
m_extruder_temps[id] = new_temp;
} }
void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line)
@ -4306,9 +4318,7 @@ void GCodeProcessor::update_estimated_times_stats()
auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) { auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) {
PrintEstimatedStatistics::Mode& data = m_result.print_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);
#if ENABLE_TRAVEL_TIME
data.travel_time = get_travel_time(mode); data.travel_time = get_travel_time(mode);
#endif // ENABLE_TRAVEL_TIME
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);
data.roles_times = get_roles_time(mode); data.roles_times = get_roles_time(mode);

View File

@ -44,9 +44,7 @@ namespace Slic3r {
struct Mode struct Mode
{ {
float time; float time;
#if ENABLE_TRAVEL_TIME
float travel_time; float travel_time;
#endif // ENABLE_TRAVEL_TIME
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> custom_gcode_times; std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> custom_gcode_times;
std::vector<std::pair<EMoveType, float>> moves_times; std::vector<std::pair<EMoveType, float>> moves_times;
std::vector<std::pair<ExtrusionRole, float>> roles_times; std::vector<std::pair<ExtrusionRole, float>> roles_times;
@ -54,9 +52,7 @@ namespace Slic3r {
void reset() { void reset() {
time = 0.0f; time = 0.0f;
#if ENABLE_TRAVEL_TIME
travel_time = 0.0f; travel_time = 0.0f;
#endif // ENABLE_TRAVEL_TIME
custom_gcode_times.clear(); custom_gcode_times.clear();
moves_times.clear(); moves_times.clear();
roles_times.clear(); roles_times.clear();
@ -307,9 +303,7 @@ namespace Slic3r {
float max_travel_acceleration; // mm/s^2 float max_travel_acceleration; // mm/s^2
float extrude_factor_override_percentage; float extrude_factor_override_percentage;
float time; // s float time; // s
#if ENABLE_TRAVEL_TIME
float travel_time; // s float travel_time; // s
#endif // ENABLE_TRAVEL_TIME
struct StopTime struct StopTime
{ {
unsigned int g1_line_id; unsigned int g1_line_id;
@ -635,10 +629,8 @@ namespace Slic3r {
float get_time(PrintEstimatedStatistics::ETimeMode mode) const; float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const; std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
#if ENABLE_TRAVEL_TIME
float get_travel_time(PrintEstimatedStatistics::ETimeMode mode) const; float get_travel_time(PrintEstimatedStatistics::ETimeMode mode) const;
std::string get_travel_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const; std::string get_travel_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
#endif // ENABLE_TRAVEL_TIME
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<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(PrintEstimatedStatistics::ETimeMode mode) const; std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const;
@ -674,7 +666,7 @@ namespace Slic3r {
void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise); void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise);
#endif // ENABLE_PROCESS_G2_G3_LINES #endif // ENABLE_PROCESS_G2_G3_LINES
// Retract // Retract or Set tool temperature
void process_G10(const GCodeReader::GCodeLine& line); void process_G10(const GCodeReader::GCodeLine& line);
// Unretract // Unretract

View File

@ -1619,7 +1619,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
size_t counter = 1; size_t counter = 1;
for (TriangleMesh &mesh : meshes) { for (TriangleMesh &mesh : meshes) {
// FIXME: crashes if not satisfied // FIXME: crashes if not satisfied
if (mesh.facets_count() < 3) if (mesh.facets_count() < 3 || mesh.has_zero_volume())
continue; continue;
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed? // XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
@ -2093,7 +2093,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
const Vec3d offset = this->get_offset(); const Vec3d offset = this->get_offset();
for (TriangleMesh &mesh : meshes) { for (TriangleMesh &mesh : meshes) {
if (mesh.empty()) if (mesh.empty() || mesh.has_zero_volume())
// Repair may have removed unconnected triangles, thus emptying the mesh. // Repair may have removed unconnected triangles, thus emptying the mesh.
continue; continue;

View File

@ -2198,8 +2198,7 @@ void PrintObject::combine_infill()
void PrintObject::_generate_support_material() void PrintObject::_generate_support_material()
{ {
if (m_config.support_material_style == smsTree) { if (m_config.support_material_style == smsTree) {
TreeSupport tree_support; fff_tree_support_generate(*this, std::function<void()>([this](){ this->throw_if_canceled(); }));
tree_support.generateSupportAreas(*this);
} else { } else {
PrintObjectSupportMaterial support_material(this, m_slicing_params); PrintObjectSupportMaterial support_material(this, m_slicing_params);
support_material.generate(*this); support_material.generate(*this);

View File

@ -39,14 +39,6 @@
//==================== //====================
#define ENABLE_2_5_0_ALPHA1 1 #define ENABLE_2_5_0_ALPHA1 1
// Enable changes in preview layout
#define ENABLE_PREVIEW_LAYOUT (1 && ENABLE_2_5_0_ALPHA1)
// Enable drawing the items in legend toolbar using icons
#define ENABLE_LEGEND_TOOLBAR_ICONS (1 && ENABLE_PREVIEW_LAYOUT)
// Enable coloring of toolpaths in preview by layer time
#define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_5_0_ALPHA1)
// Enable showing time estimate for travel moves in legend
#define ENABLE_TRAVEL_TIME (1 && ENABLE_2_5_0_ALPHA1)
// Enable removal of wipe tower magic object_id equal to 1000 // Enable removal of wipe tower magic object_id equal to 1000
#define ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL (1 && ENABLE_2_5_0_ALPHA1) #define ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
// Enable removal of legacy OpenGL calls // Enable removal of legacy OpenGL calls
@ -61,8 +53,6 @@
#define ENABLE_GLMODEL_STATISTICS (0 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_GLMODEL_STATISTICS (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
// Enable rework of Reload from disk command // Enable rework of Reload from disk command
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1) #define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1)
// Enable recalculating toolpaths when switching to/from volumetric rate visualization
#define ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC (1 && ENABLE_2_5_0_ALPHA1)
// Enable editing volumes transformation in world coordinates and instances in local coordinates // Enable editing volumes transformation in world coordinates and instances in local coordinates
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1) #define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1)
// Enable modified camera control using mouse // Enable modified camera control using mouse

View File

@ -4,6 +4,9 @@
#else #else
// any posix system // any posix system
#include <pthread.h> #include <pthread.h>
#ifdef __APPLE__
#include <pthread/qos.h>
#endif // __APPLE__
#endif #endif
#include <atomic> #include <atomic>
@ -241,4 +244,26 @@ void name_tbb_thread_pool_threads_set_locale()
}); });
} }
void set_current_thread_qos()
{
#ifdef __APPLE__
// OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance
// cores if available.
pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
#endif // __APPLE__
}
void TBBLocalesSetter::on_scheduler_entry(bool is_worker)
{
// static std::atomic<int> cnt = 0;
// std::cout << "TBBLocalesSetter Entering " << cnt ++ << " ID " << std::this_thread::get_id() << "\n";
if (bool& is_locales_sets = m_is_locales_sets.local(); !is_locales_sets) {
// Set locales of the worker thread to "C".
set_c_locales();
// OSX specific: Elevate QOS on Apple Silicon.
set_current_thread_qos();
is_locales_sets = true;
}
}
} }

View File

@ -6,6 +6,9 @@
#include <thread> #include <thread>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <tbb/task_scheduler_observer.h>
#include <tbb/enumerable_thread_specific.h>
namespace Slic3r { namespace Slic3r {
// Set / get thread name. // Set / get thread name.
@ -26,6 +29,10 @@ inline bool set_thread_name(boost::thread &thread, const std::string &thread_nam
bool set_current_thread_name(const char *thread_name); bool set_current_thread_name(const char *thread_name);
inline bool set_current_thread_name(const std::string &thread_name) { return set_current_thread_name(thread_name.c_str()); } inline bool set_current_thread_name(const std::string &thread_name) { return set_current_thread_name(thread_name.c_str()); }
// OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance
// cores if available.
void set_current_thread_qos();
// Returns nullopt if not supported. // Returns nullopt if not supported.
// Not supported by OSX. // Not supported by OSX.
// Naming threads is only supported on newer Windows 10. // Naming threads is only supported on newer Windows 10.
@ -53,6 +60,25 @@ template<class Fn> inline boost::thread create_thread(Fn &&fn)
return create_thread(attrs, std::forward<Fn>(fn)); return create_thread(attrs, std::forward<Fn>(fn));
} }
// For unknown reasons and in sporadic cases when GCode export is processing, some participating thread
// in tbb::parallel_pipeline has not set locales to "C", probably because this thread is newly spawned.
// So in this class method on_scheduler_entry is called for every thread before it starts participating
// in tbb::parallel_pipeline to ensure that locales are set correctly
//
// For tbb::parallel_pipeline, it seems that on_scheduler_entry is called for every layer and every filter.
// We ensure using thread-local storage that locales will be set to "C" just once for any participating thread.
class TBBLocalesSetter : public tbb::task_scheduler_observer
{
public:
TBBLocalesSetter() { this->observe(true); }
~TBBLocalesSetter() override { this->observe(false); };
void on_scheduler_entry(bool is_worker) override;
private:
tbb::enumerable_thread_specific<bool, tbb::cache_aligned_allocator<bool>, tbb::ets_key_usage_type::ets_key_per_instance> m_is_locales_sets{ false };
};
} }
#endif // GUI_THREAD_HPP #endif // GUI_THREAD_HPP

View File

@ -23,7 +23,7 @@
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <tbb/task_group.h> #include <tbb/task_group.h>
namespace Slic3r namespace Slic3r::FFFTreeSupport
{ {
// or warning // or warning
@ -106,7 +106,10 @@ TreeModelVolumes::TreeModelVolumes(
const PrintObject &print_object, const PrintObject &print_object,
const BuildVolume &build_volume, const BuildVolume &build_volume,
const coord_t max_move, const coord_t max_move_slow, size_t current_mesh_idx, const coord_t max_move, const coord_t max_move_slow, size_t current_mesh_idx,
double progress_multiplier, double progress_offset, const std::vector<Polygons>& additional_excluded_areas) : #ifdef SLIC3R_TREESUPPORTS_PROGRESS
double progress_multiplier, double progress_offset,
#endif // SLIC3R_TREESUPPORTS_PROGRESS
const std::vector<Polygons>& additional_excluded_areas) :
// -2 to avoid rounding errors // -2 to avoid rounding errors
m_max_move{ std::max<coord_t>(max_move - 2, 0) }, m_max_move_slow{ std::max<coord_t>(max_move_slow - 2, 0) }, m_max_move{ std::max<coord_t>(max_move - 2, 0) }, m_max_move_slow{ std::max<coord_t>(max_move_slow - 2, 0) },
#ifdef SLIC3R_TREESUPPORTS_PROGRESS #ifdef SLIC3R_TREESUPPORTS_PROGRESS
@ -161,7 +164,7 @@ TreeModelVolumes::TreeModelVolumes(
m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution); m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution);
} }
const TreeSupport::TreeSupportSettings config{ m_layer_outlines[m_current_outline_idx].first }; const TreeSupportSettings config{ m_layer_outlines[m_current_outline_idx].first };
m_current_min_xy_dist = config.xy_min_distance; m_current_min_xy_dist = config.xy_min_distance;
m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist; m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist;
assert(m_current_min_xy_dist_delta >= 0); assert(m_current_min_xy_dist_delta >= 0);
@ -206,7 +209,7 @@ void TreeModelVolumes::precalculate(const coord_t max_layer)
// Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant. // Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant.
// Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex // Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex
// like inital layer diameter are only done in once. // like inital layer diameter are only done in once.
TreeSupport::TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first); TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first);
{ {
// calculate which radius each layer in the tip may have. // calculate which radius each layer in the tip may have.
@ -297,7 +300,7 @@ const Polygons& TreeModelVolumes::getCollision(const coord_t orig_radius, LayerI
return (*result).get(); return (*result).get();
if (m_precalculated) { if (m_precalculated) {
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Collision requested.", false); tree_supports_show_error("Not precalculated Collision requested.", false);
} }
const_cast<TreeModelVolumes*>(this)->calculateCollision(radius, layer_idx); const_cast<TreeModelVolumes*>(this)->calculateCollision(radius, layer_idx);
return getCollision(orig_radius, layer_idx, min_xy_dist); return getCollision(orig_radius, layer_idx, min_xy_dist);
@ -312,7 +315,7 @@ const Polygons& TreeModelVolumes::getCollisionHolefree(coord_t radius, LayerInde
return (*result).get(); return (*result).get();
if (m_precalculated) { if (m_precalculated) {
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision holefree at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision holefree at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Holefree Collision requested.", false); tree_supports_show_error("Not precalculated Holefree Collision requested.", false);
} }
const_cast<TreeModelVolumes*>(this)->calculateCollisionHolefree({ radius, layer_idx }); const_cast<TreeModelVolumes*>(this)->calculateCollisionHolefree({ radius, layer_idx });
return getCollisionHolefree(radius, layer_idx); return getCollisionHolefree(radius, layer_idx);
@ -336,10 +339,10 @@ const Polygons& TreeModelVolumes::getAvoidance(const coord_t orig_radius, LayerI
if (m_precalculated) { if (m_precalculated) {
if (to_model) { if (to_model) {
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance to model at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance to model at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Avoidance(to model) requested.", false); tree_supports_show_error("Not precalculated Avoidance(to model) requested.", false);
} else { } else {
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Avoidance(to buildplate) requested.", false); tree_supports_show_error("Not precalculated Avoidance(to buildplate) requested.", false);
} }
} }
const_cast<TreeModelVolumes*>(this)->calculateAvoidance({ radius, layer_idx }, ! to_model, to_model); const_cast<TreeModelVolumes*>(this)->calculateAvoidance({ radius, layer_idx }, ! to_model, to_model);
@ -357,7 +360,7 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L
return (*result).get(); return (*result).get();
if (m_precalculated) { if (m_precalculated) {
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError("Not precalculated Placeable areas requested.", false); tree_supports_show_error("Not precalculated Placeable areas requested.", false);
} }
const_cast<TreeModelVolumes*>(this)->calculatePlaceables(radius, layer_idx); const_cast<TreeModelVolumes*>(this)->calculatePlaceables(radius, layer_idx);
return getPlaceableAreas(orig_radius, layer_idx); return getPlaceableAreas(orig_radius, layer_idx);
@ -380,7 +383,7 @@ const Polygons& TreeModelVolumes::getWallRestriction(const coord_t orig_radius,
return (*result).get(); return (*result).get();
if (m_precalculated) { if (m_precalculated) {
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Wall restricions at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Wall restricions at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
TreeSupport::showError( tree_supports_show_error(
min_xy_dist ? min_xy_dist ?
"Not precalculated Wall restriction of minimum xy distance requested )." : "Not precalculated Wall restriction of minimum xy distance requested )." :
"Not precalculated Wall restriction requested )." "Not precalculated Wall restriction requested )."
@ -774,4 +777,4 @@ coord_t TreeModelVolumes::ceilRadius(const coord_t radius) const
return out; return out;
} }
} } // namespace Slic3r::FFFTreeSupport

View File

@ -22,11 +22,14 @@
namespace Slic3r namespace Slic3r
{ {
using LayerIndex = int;
class BuildVolume; class BuildVolume;
class PrintObject; class PrintObject;
namespace FFFTreeSupport
{
using LayerIndex = int;
struct TreeSupportMeshGroupSettings { struct TreeSupportMeshGroupSettings {
TreeSupportMeshGroupSettings() = default; TreeSupportMeshGroupSettings() = default;
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object); explicit TreeSupportMeshGroupSettings(const PrintObject &print_object);
@ -194,15 +197,19 @@ class TreeModelVolumes
public: public:
TreeModelVolumes() = default; TreeModelVolumes() = default;
explicit TreeModelVolumes(const PrintObject &print_object, const BuildVolume &build_volume, explicit TreeModelVolumes(const PrintObject &print_object, const BuildVolume &build_volume,
coord_t max_move, coord_t max_move_slow, size_t current_mesh_idx, double progress_multiplier, coord_t max_move, coord_t max_move_slow, size_t current_mesh_idx,
double progress_offset, const std::vector<Polygons> &additional_excluded_areas = {}); #ifdef SLIC3R_TREESUPPORTS_PROGRESS
double progress_multiplier,
double progress_offset,
#endif // SLIC3R_TREESUPPORTS_PROGRESS
const std::vector<Polygons> &additional_excluded_areas = {});
TreeModelVolumes(TreeModelVolumes&&) = default; TreeModelVolumes(TreeModelVolumes&&) = default;
TreeModelVolumes& operator=(TreeModelVolumes&&) = default; TreeModelVolumes& operator=(TreeModelVolumes&&) = default;
TreeModelVolumes(const TreeModelVolumes&) = delete; TreeModelVolumes(const TreeModelVolumes&) = delete;
TreeModelVolumes& operator=(const TreeModelVolumes&) = delete; TreeModelVolumes& operator=(const TreeModelVolumes&) = delete;
enum class AvoidanceType enum class AvoidanceType : int8_t
{ {
Slow, Slow,
FastSafe, FastSafe,
@ -605,6 +612,7 @@ private:
#endif // SLIC3R_TREESUPPORTS_PROGRESS #endif // SLIC3R_TREESUPPORTS_PROGRESS
}; };
} } // namespace FFFTreeSupport
} // namespace Slic3r
#endif //slic3r_TreeModelVolumes_hpp #endif //slic3r_TreeModelVolumes_hpp

File diff suppressed because it is too large Load Diff

View File

@ -12,15 +12,13 @@
#include "TreeModelVolumes.hpp" #include "TreeModelVolumes.hpp"
#include "Point.hpp" #include "Point.hpp"
#include <boost/functional/hash.hpp> // For combining hashes #include <boost/container/small_vector.hpp>
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#define TREE_SUPPORT_SHOW_ERRORS #define TREE_SUPPORT_SHOW_ERRORS
#define SUPPORT_TREE_CIRCLE_RESOLUTION 25 // The number of vertices in each circle.
#ifdef SLIC3R_TREESUPPORTS_PROGRESS #ifdef SLIC3R_TREESUPPORTS_PROGRESS
// The various stages of the process can be weighted differently in the progress bar. // The various stages of the process can be weighted differently in the progress bar.
// These weights are obtained experimentally using a small sample size. Sensible weights can differ drastically based on the assumed default settings and model. // These weights are obtained experimentally using a small sample size. Sensible weights can differ drastically based on the assumed default settings and model.
@ -35,189 +33,117 @@
#define TREE_PROGRESS_FINALIZE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3 #define TREE_PROGRESS_FINALIZE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#endif // SLIC3R_TREESUPPORTS_PROGRESS #endif // SLIC3R_TREESUPPORTS_PROGRESS
#define SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL false
#define SUPPORT_TREE_AVOID_SUPPORT_BLOCKER true
namespace Slic3r namespace Slic3r
{ {
// Forward declarations
class Print;
class PrintObject;
class SupportGeneratorLayer;
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
namespace FFFTreeSupport
{
using LayerIndex = int; using LayerIndex = int;
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5; static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
static constexpr const coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled<coord_t>(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR); static constexpr const coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled<coord_t>(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR);
static constexpr const coord_t SUPPORT_TREE_COLLISION_RESOLUTION = scaled<coord_t>(0.5); static constexpr const coord_t SUPPORT_TREE_COLLISION_RESOLUTION = scaled<coord_t>(0.5);
//FIXME // The number of vertices in each circle.
class Print; static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
class PrintObject; static constexpr const bool SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true;
class SupportGeneratorLayer;
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
/*!
* \brief Generates a tree structure to support your models.
*/
class TreeSupport enum class InterfacePreference
{ {
public: InterfaceAreaOverwritesSupport,
using AvoidanceType = TreeModelVolumes::AvoidanceType; SupportAreaOverwritesInterface,
enum class InterfacePreference InterfaceLinesOverwriteSupport,
{ SupportLinesOverwriteInterface,
INTERFACE_AREA_OVERWRITES_SUPPORT, Nothing
SUPPORT_AREA_OVERWRITES_INTERFACE, };
INTERFACE_LINES_OVERWRITE_SUPPORT,
SUPPORT_LINES_OVERWRITE_INTERFACE,
NOTHING
};
/*! struct AreaIncreaseSettings
* \brief Creates an instance of the tree support generator. {
*/ AreaIncreaseSettings(
TreeSupport() = default; TreeModelVolumes::AvoidanceType type = TreeModelVolumes::AvoidanceType::Fast, coord_t increase_speed = 0,
bool increase_radius = false, bool no_error = false, bool use_min_distance = false, bool move = false) :
increase_speed{ increase_speed }, type{ type }, increase_radius{ increase_radius }, no_error{ no_error }, use_min_distance{ use_min_distance }, move{ move } {}
/*! coord_t increase_speed;
* \brief Create the areas that need support. // Packing for smaller memory footprint of SupportElementState && SupportElementMerging
* TreeModelVolumes::AvoidanceType type;
* These areas are stored inside the given SliceDataStorage object. bool increase_radius : 1;
* \param storage The data storage where the mesh data is gotten from and bool no_error : 1;
* where the resulting support areas are stored. bool use_min_distance : 1;
*/ bool move : 1;
void generateSupportAreas(Print &print, const BuildVolume &build_volume, const std::vector<size_t>& print_object_ids);
void generateSupportAreas(PrintObject &print_object);
//todo Remove! Only relevant for public BETA!
static bool inline showed_critical=false;
static bool inline showed_performance=false;
static void showError(std::string message,bool critical);
struct TreeSupportSettings; // forward declaration as we need some config values in the merge case
struct AreaIncreaseSettings
{
AvoidanceType type { AvoidanceType::Fast };
coord_t increase_speed { 0 };
bool increase_radius { false };
bool no_error { false };
bool use_min_distance { false };
bool move { false };
bool operator==(const AreaIncreaseSettings& other) const bool operator==(const AreaIncreaseSettings& other) const
{ {
return increase_radius == other.increase_radius && increase_speed == other.increase_speed && type == other.type && return type == other.type &&
no_error == other.no_error && use_min_distance == other.use_min_distance && move == other.move; increase_speed == other.increase_speed &&
increase_radius == other.increase_radius &&
no_error == other.no_error &&
use_min_distance == other.use_min_distance &&
move == other.move;
} }
}; };
struct SupportElement struct TreeSupportSettings;
{
explicit SupportElement(
coord_t distance_to_top, size_t target_height, Point target_position, bool to_buildplate, bool to_model_gracious, bool use_min_xy_dist, size_t dont_move_until,
bool supports_roof, bool can_use_safe_radius, bool force_tips_to_roof, bool skip_ovalisation) :
target_height(target_height), target_position(target_position), next_position(target_position), next_height(target_height), effective_radius_height(distance_to_top),
to_buildplate(to_buildplate), distance_to_top(distance_to_top), area(nullptr), result_on_layer(target_position), increased_to_model_radius(0), to_model_gracious(to_model_gracious),
elephant_foot_increases(0), use_min_xy_dist(use_min_xy_dist), supports_roof(supports_roof), dont_move_until(dont_move_until), can_use_safe_radius(can_use_safe_radius),
last_area_increase(AreaIncreaseSettings{ AvoidanceType::Fast, 0, false, false, false, false }), missing_roof_layers(force_tips_to_roof ? dont_move_until : 0), skip_ovalisation(skip_ovalisation)
{
}
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
explicit SupportElement(const SupportElement& elem, Polygons* newArea = nullptr) struct SupportElementStateBits {
: // copy constructor with possibility to set a new area SupportElementStateBits() :
target_height(elem.target_height), to_buildplate(false),
target_position(elem.target_position), to_model_gracious(false),
next_position(elem.next_position), use_min_xy_dist(false),
next_height(elem.next_height), supports_roof(false),
effective_radius_height(elem.effective_radius_height), can_use_safe_radius(false),
to_buildplate(elem.to_buildplate), skip_ovalisation(false),
distance_to_top(elem.distance_to_top), deleted(false),
area(newArea != nullptr ? newArea : elem.area), marked(false)
result_on_layer(elem.result_on_layer), {}
increased_to_model_radius(elem.increased_to_model_radius),
to_model_gracious(elem.to_model_gracious),
elephant_foot_increases(elem.elephant_foot_increases),
use_min_xy_dist(elem.use_min_xy_dist),
supports_roof(elem.supports_roof),
dont_move_until(elem.dont_move_until),
can_use_safe_radius(elem.can_use_safe_radius),
last_area_increase(elem.last_area_increase),
missing_roof_layers(elem.missing_roof_layers),
skip_ovalisation(elem.skip_ovalisation)
{
parents.insert(parents.begin(), elem.parents.begin(), elem.parents.end());
}
/*! /*!
* \brief Create a new Element for one layer below the element of the pointer supplied. * \brief The element trys to reach the buildplate
*/ */
bool to_buildplate : 1;
explicit SupportElement(SupportElement* element_above) /*!
: target_height(element_above->target_height), * \brief Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
target_position(element_above->target_position), */
next_position(element_above->next_position), bool to_model_gracious : 1;
next_height(element_above->next_height),
effective_radius_height(element_above->effective_radius_height),
to_buildplate(element_above->to_buildplate),
distance_to_top(element_above->distance_to_top + 1),
area(element_above->area),
result_on_layer(Point(-1, -1)), // set to invalid as we are a new node on a new layer
increased_to_model_radius(element_above->increased_to_model_radius),
to_model_gracious(element_above->to_model_gracious),
elephant_foot_increases(element_above->elephant_foot_increases),
use_min_xy_dist(element_above->use_min_xy_dist),
supports_roof(element_above->supports_roof),
dont_move_until(element_above->dont_move_until),
can_use_safe_radius(element_above->can_use_safe_radius),
last_area_increase(element_above->last_area_increase),
missing_roof_layers(element_above->missing_roof_layers),
skip_ovalisation(false)
{
parents = { element_above };
}
// ONLY to be called in merge as it assumes a few assurances made by it. /*!
explicit SupportElement( * \brief Whether the min_xy_distance can be used to get avoidance or similar. Will only be true if support_xy_overrides_z=Z overrides X/Y.
const SupportElement& first, const SupportElement& second, size_t next_height, Point next_position, */
coord_t increased_to_model_radius, const TreeSupportSettings& config) : bool use_min_xy_dist : 1;
next_position(next_position), next_height(next_height), area(nullptr), increased_to_model_radius(increased_to_model_radius),
use_min_xy_dist(first.use_min_xy_dist || second.use_min_xy_dist), supports_roof(first.supports_roof || second.supports_roof),
dont_move_until(std::max(first.dont_move_until, second.dont_move_until)), can_use_safe_radius(first.can_use_safe_radius || second.can_use_safe_radius),
missing_roof_layers(std::min(first.missing_roof_layers, second.missing_roof_layers)), skip_ovalisation(false)
{ /*!
if (first.target_height > second.target_height) { * \brief True if this Element or any parent provides support to a support roof.
target_height = first.target_height; */
target_position = first.target_position; bool supports_roof : 1;
} else {
target_height = second.target_height;
target_position = second.target_position;
}
effective_radius_height = std::max(first.effective_radius_height, second.effective_radius_height);
distance_to_top = std::max(first.distance_to_top, second.distance_to_top);
to_buildplate = first.to_buildplate && second.to_buildplate; /*!
to_model_gracious = first.to_model_gracious && second.to_model_gracious; // valid as we do not merge non-gracious with gracious * \brief An influence area is considered safe when it can use the holefree avoidance <=> It will not have to encounter holes on its way downward.
*/
bool can_use_safe_radius : 1;
AddParents(first.parents); /*!
AddParents(second.parents); * \brief Skip the ovalisation to parent and children when generating the final circles.
*/
bool skip_ovalisation : 1;
elephant_foot_increases = 0; // Not valid anymore, to be deleted.
if (config.diameter_scale_bp_radius > 0) { bool deleted : 1;
coord_t foot_increase_radius = std::abs(std::max(config.getCollisionRadius(second), config.getCollisionRadius(first)) - config.getCollisionRadius(*this));
// elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch
// the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value.
elephant_foot_increases = foot_increase_radius / (config.branch_radius * (config.diameter_scale_bp_radius - config.diameter_angle_scale_factor));
}
// set last settings to the best out of both parents. If this is wrong, it will only cause a small performance penalty instead of weird behavior. // General purpose flag marking a visited element.
last_area_increase = { bool marked : 1;
std::min(first.last_area_increase.type, second.last_area_increase.type), };
std::min(first.last_area_increase.increase_speed, second.last_area_increase.increase_speed),
first.last_area_increase.increase_radius || second.last_area_increase.increase_radius,
first.last_area_increase.no_error || second.last_area_increase.no_error,
first.last_area_increase.use_min_distance && second.last_area_increase.use_min_distance,
first.last_area_increase.move || second.last_area_increase.move };
}
struct SupportElementState : public SupportElementStateBits
{
/*! /*!
* \brief The layer this support elements wants reach * \brief The layer this support elements wants reach
*/ */
@ -233,78 +159,42 @@ public:
*/ */
Point next_position; Point next_position;
/*! /*!
* \brief The next height this support elements wants to reach * \brief The next height this support elements wants to reach
*/ */
LayerIndex next_height; LayerIndex layer_idx;
/*! /*!
* \brief The Effective distance to top of this element regarding radius increases and collision calculations. * \brief The Effective distance to top of this element regarding radius increases and collision calculations.
*/ */
uint32_t effective_radius_height;
size_t effective_radius_height;
/*!
* \brief The element trys to reach the buildplate
*/
bool to_buildplate;
/*!
* \brief All elements in the layer above the current one that are supported by this element
*/
std::vector<SupportElement*> parents;
/*! /*!
* \brief The amount of layers this element is below the topmost layer of this branch. * \brief The amount of layers this element is below the topmost layer of this branch.
*/ */
size_t distance_to_top; uint32_t distance_to_top;
/*!
* \brief The resulting influence area.
* Will only be set in the results of createLayerPathing, and will be nullptr inside!
*/
Polygons* area;
/*! /*!
* \brief The resulting center point around which a circle will be drawn later. * \brief The resulting center point around which a circle will be drawn later.
* Will be set by setPointsOnAreas * Will be set by setPointsOnAreas
*/ */
Point result_on_layer = Point(-1, -1); Point result_on_layer { std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
/*! /*!
* \brief The amount of extra radius we got from merging branches that could have reached the buildplate, but merged with ones that can not. * \brief The amount of extra radius we got from merging branches that could have reached the buildplate, but merged with ones that can not.
*/ */
coord_t increased_to_model_radius; // how much to model we increased only relevant for merging coord_t increased_to_model_radius; // how much to model we increased only relevant for merging
/*!
* \brief Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
*/
bool to_model_gracious;
/*! /*!
* \brief Counter about the times the elephant foot was increased. Can be fractions for merge reasons. * \brief Counter about the times the elephant foot was increased. Can be fractions for merge reasons.
*/ */
double elephant_foot_increases; double elephant_foot_increases;
/*!
* \brief Whether the min_xy_distance can be used to get avoidance or similar. Will only be true if support_xy_overrides_z=Z overrides X/Y.
*/
bool use_min_xy_dist;
/*!
* \brief True if this Element or any parent provides support to a support roof.
*/
bool supports_roof;
/*! /*!
* \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move. * \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move.
*/ */
size_t dont_move_until; uint32_t dont_move_until;
/*!
* \brief An influence area is considered safe when it can use the holefree avoidance <=> It will not have to encounter holes on its way downward.
*/
bool can_use_safe_radius;
/*! /*!
* \brief Settings used to increase the influence area to its current state. * \brief Settings used to increase the influence area to its current state.
@ -314,47 +204,57 @@ public:
/*! /*!
* \brief Amount of roof layers that were not yet added, because the branch needed to move. * \brief Amount of roof layers that were not yet added, because the branch needed to move.
*/ */
size_t missing_roof_layers; uint32_t missing_roof_layers;
// called by increase_single_area() and increaseAreas()
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState &src)
{
SupportElementState dst{ src };
++ dst.distance_to_top;
-- dst.layer_idx;
// set to invalid as we are a new node on a new layer
dst.result_on_layer_reset();
dst.skip_ovalisation = false;
return dst;
}
};
struct SupportElement
{
using ParentIndices =
#ifdef NDEBUG
// To reduce memory allocation in release mode.
boost::container::small_vector<int32_t, 4>;
#else // NDEBUG
// To ease debugging.
std::vector<int32_t>;
#endif // NDEBUG
// SupportElement(const SupportElementState &state) : SupportElementState(state) {}
SupportElement(const SupportElementState &state, Polygons &&influence_area) : state(state), influence_area(std::move(influence_area)) {}
SupportElement(const SupportElementState &state, ParentIndices &&parents, Polygons &&influence_area) :
state(state), parents(std::move(parents)), influence_area(std::move(influence_area)) {}
SupportElementState state;
/*! /*!
* \brief Skip the ovalisation to parent and children when generating the final circles. * \brief All elements in the layer above the current one that are supported by this element
*/ */
bool skip_ovalisation; ParentIndices parents;
bool operator==(const SupportElement& other) const
{
return target_position == other.target_position && target_height == other.target_height;
}
bool operator<(const SupportElement& other) const // true if me < other
{
return !(*this == other) && !(*this > other);
}
bool operator>(const SupportElement& other) const
{
// Doesn't really have to make sense, only required for ordering in maps to ensure deterministic behavior.
if (*this == other)
return false;
if (other.target_height != target_height)
return other.target_height < target_height;
return other.target_position.x() == target_position.x() ? other.target_position.y() < target_position.y() : other.target_position.x() < target_position.x();
}
void AddParents(const std::vector<SupportElement*>& adding)
{
for (SupportElement* ptr : adding)
{
parents.emplace_back(ptr);
}
}
};
/*! /*!
* \brief The resulting influence area.
* Will only be set in the results of createLayerPathing, and will be nullptr inside!
*/
Polygons influence_area;
};
/*!
* \brief This struct contains settings used in the tree support. Thanks to this most functions do not need to know of meshes etc. Also makes the code shorter. * \brief This struct contains settings used in the tree support. Thanks to this most functions do not need to know of meshes etc. Also makes the code shorter.
*/ */
struct TreeSupportSettings struct TreeSupportSettings
{ {
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupport class. TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class.
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings) explicit TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings)
: angle(mesh_group_settings.support_tree_angle), : angle(mesh_group_settings.support_tree_angle),
@ -397,7 +297,7 @@ public:
{ {
layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius);
if (TreeSupport::TreeSupportSettings::soluble) { if (TreeSupportSettings::soluble) {
// safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely // safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely
// When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size // When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size
// This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance. // This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance.
@ -406,19 +306,19 @@ public:
} }
// const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE }, { "interface_area_overwrite_support_area", InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT }, { "support_lines_overwrite_interface_area", InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE }, { "interface_lines_overwrite_support_area", InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT }, { "nothing", InterfacePreference::NOTHING } }; // const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } };
// interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority")); // interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority"));
//FIXME this was the default //FIXME this was the default
// interface_preference = InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE; // interface_preference = InterfacePreference::SupportLinesOverwriteInterface;
interface_preference = InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE; interface_preference = InterfacePreference::SupportAreaOverwritesInterface;
} }
private: private:
double angle; double angle;
double angle_slow; double angle_slow;
std::vector<coord_t> known_z; std::vector<coord_t> known_z;
public: public:
// some static variables dependent on other meshes that are not currently processed. // some static variables dependent on other meshes that are not currently processed.
// Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy. // Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy.
inline static bool soluble = false; inline static bool soluble = false;
@ -580,7 +480,7 @@ public:
&& min_feature_size == other.min_feature_size // interface_preference should be identical to ensure the tree will correctly interact with the roof. && min_feature_size == other.min_feature_size // interface_preference should be identical to ensure the tree will correctly interact with the roof.
// The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry // The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry
#if 0 #if 0
&& (interface_preference == InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT || interface_preference == InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE && (interface_preference == InterfacePreference::InterfaceAreaOverwritesSupport || interface_preference == InterfacePreference::SupportAreaOverwritesInterface
// Perimeter generator parameters // Perimeter generator parameters
|| ||
(settings.get<bool>("fill_outline_gaps") == other.settings.get<bool>("fill_outline_gaps") && (settings.get<bool>("fill_outline_gaps") == other.settings.get<bool>("fill_outline_gaps") &&
@ -599,13 +499,12 @@ public:
; ;
} }
/*! /*!
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch. * \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
* \param elem[in] The SupportElement one wants to know the effectiveDTT * \param elem[in] The SupportElement one wants to know the effectiveDTT
* \return The Effective DTT. * \return The Effective DTT.
*/ */
[[nodiscard]] inline size_t getEffectiveDTT(const TreeSupport::SupportElement& elem) const [[nodiscard]] inline size_t getEffectiveDTT(const SupportElementState &elem) const
{ {
return elem.effective_radius_height < increase_radius_until_layer ? (elem.distance_to_top < increase_radius_until_layer ? elem.distance_to_top : increase_radius_until_layer) : elem.effective_radius_height; return elem.effective_radius_height < increase_radius_until_layer ? (elem.distance_to_top < increase_radius_until_layer ? elem.distance_to_top : increase_radius_until_layer) : elem.effective_radius_height;
} }
@ -630,17 +529,17 @@ public:
* \param elem[in] The Element. * \param elem[in] The Element.
* \return The radius the element has. * \return The radius the element has.
*/ */
[[nodiscard]] inline coord_t getRadius(const TreeSupport::SupportElement& elem) const [[nodiscard]] inline coord_t getRadius(const SupportElementState &elem) const
{ { return getRadius(getEffectiveDTT(elem), elem.elephant_foot_increases); }
return getRadius(getEffectiveDTT(elem), elem.elephant_foot_increases); [[nodiscard]] inline coord_t getRadius(const SupportElement &elem) const
} { return this->getRadius(elem.state); }
/*! /*!
* \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model. * \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.
* \param elem[in] The Element. * \param elem[in] The Element.
* \return The collision radius the element has. * \return The collision radius the element has.
*/ */
[[nodiscard]] inline coord_t getCollisionRadius(const TreeSupport::SupportElement& elem) const [[nodiscard]] inline coord_t getCollisionRadius(const SupportElementState &elem) const
{ {
return getRadius(elem.effective_radius_height, elem.elephant_foot_increases); return getRadius(elem.effective_radius_height, elem.elephant_foot_increases);
} }
@ -675,202 +574,15 @@ public:
{ {
known_z = z; known_z = z;
} }
};
private:
/*!
* \brief Creates the initial influence areas (that can later be propagated down) by placing them below the overhang.
*
* Generates Points where the Model should be supported and creates the areas where these points have to be placed.
*
* \param mesh[in] The mesh that is currently processed.
* \param move_bounds[out] Storage for the influence areas.
* \param storage[in] Background storage, required for adding roofs.
*/
void generateInitialAreas(const PrintObject &print_object,
const std::vector<Polygons> &overhangs,
std::vector<std::set<SupportElement*>> &move_bounds,
SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &top_interface_layers,
SupportGeneratorLayerStorage &layer_storage);
/*!
* \brief Checks if an influence area contains a valid subsection and returns the corresponding metadata and the new Influence area.
*
* Calculates an influence areas of the layer below, based on the influence area of one element on the current layer.
* Increases every influence area by maximum_move_distance_slow. If this is not enough, as in we would change our gracious or to_buildplate status the influence areas are instead increased by maximum_move_distance_slow.
* Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this were the case, the radius is not increased instead.
*
* Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was done to avoid not needed heap allocations.
*
* \param settings[in] Which settings have to be used to check validity.
* \param layer_idx[in] Number of the current layer.
* \param parent[in] The metadata of the parents influence area.
* \param relevant_offset[in] The maximal possible influence area. No guarantee regarding validity with current layer collision required, as it is ensured in-function!
* \param to_bp_data[out] The part of the Influence area that can reach the buildplate.
* \param to_model_data[out] The part of the Influence area that do not have to reach the buildplate. This has overlap with new_layer_data.
* \param increased[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the user-supplied settings.
* \param overspeed[in] How much should the already offset area be offset again. Usually this is 0.
* \param mergelayer[in] Will the merge method be called on this layer. This information is required as some calculation can be avoided if they are not required for merging.
* \return A valid support element for the next layer regarding the calculated influence areas. Empty if no influence are can be created using the supplied influence area and settings.
*/
std::optional<TreeSupport::SupportElement> increaseSingleArea(AreaIncreaseSettings settings, LayerIndex layer_idx, SupportElement* parent, const Polygons& relevant_offset, Polygons& to_bp_data, Polygons& to_model_data, Polygons& increased, const coord_t overspeed, const bool mergelayer);
/*!
* \brief Increases influence areas as far as required.
*
* Calculates influence areas of the layer below, based on the influence areas of the current layer.
* Increases every influence area by maximum_move_distance_slow. If this is not enough, as in it would change the gracious or to_buildplate status, the influence areas are instead increased by maximum_move_distance.
* Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this were the case, the radius is not increased instead.
*
* Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was done to avoid not needed heap allocations.
*
* \param to_bp_areas[out] Influence areas that can reach the buildplate
* \param to_model_areas[out] Influence areas that do not have to reach the buildplate. This has overlap with new_layer_data, as areas that can reach the buildplate are also considered valid areas to the model.
* This redundancy is required if a to_buildplate influence area is allowed to merge with a to model influence area.
* \param influence_areas[out] Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the user-supplied settings.
* \param bypass_merge_areas[out] Influence areas ready to be added to the layer below that do not need merging.
* \param last_layer[in] Influence areas of the current layer.
* \param layer_idx[in] Number of the current layer.
* \param mergelayer[in] Will the merge method be called on this layer. This information is required as some calculation can be avoided if they are not required for merging.
*/
void increaseAreas(std::unordered_map<SupportElement, Polygons>& to_bp_areas, std::unordered_map<SupportElement, Polygons>& to_model_areas, std::map<SupportElement, Polygons>& influence_areas, std::vector<SupportElement*>& bypass_merge_areas, const std::vector<SupportElement*>& last_layer, const LayerIndex layer_idx, const bool mergelayer);
/*!
* \brief Propagates influence downwards, and merges overlapping ones.
*
* \param move_bounds[in,out] All currently existing influence areas
*/
void createLayerPathing(std::vector<std::set<SupportElement*>>& move_bounds);
/*!
* \brief Sets the result_on_layer for all parents based on the SupportElement supplied.
*
* \param elem[in] The SupportElements, which parent's position should be determined.
*/
void setPointsOnAreas(const SupportElement* elem);
/*!
* \brief Get the best point to connect to the model and set the result_on_layer of the relevant SupportElement accordingly.
*
* \param move_bounds[in,out] All currently existing influence areas
* \param first_elem[in,out] SupportElement that did not have its result_on_layer set meaning that it does not have a child element.
* \param layer_idx[in] The current layer.
* \return Should elem be deleted.
*/
bool setToModelContact(std::vector<std::set<SupportElement*>>& move_bounds, SupportElement* first_elem, const LayerIndex layer_idx);
/*!
* \brief Set the result_on_layer point for all influence areas
*
* \param move_bounds[in,out] All currently existing influence areas
*/
void createNodesFromArea(std::vector<std::set<SupportElement*>>& move_bounds);
/*!
* \brief Draws circles around result_on_layer points of the influence areas
*
* \param linear_data[in] All currently existing influence areas with the layer they are on
* \param layer_tree_polygons[out] Resulting branch areas with the layerindex they appear on. layer_tree_polygons.size() has to be at least linear_data.size() as each Influence area in linear_data will save have at least one (that's why it's a vector<vector>) corresponding branch area in layer_tree_polygons.
* \param inverse_tree_order[in] A mapping that returns the child of every influence area.
*/
void generateBranchAreas(std::vector<std::pair<LayerIndex, SupportElement*>>& linear_data, std::vector<std::unordered_map<SupportElement*, Polygons>>& layer_tree_polygons, const std::map<SupportElement*, SupportElement*>& inverse_tree_order);
/*!
* \brief Applies some smoothing to the outer wall, intended to smooth out sudden jumps as they can happen when a branch moves though a hole.
*
* \param layer_tree_polygons[in,out] Resulting branch areas with the layerindex they appear on.
*/
void smoothBranchAreas(std::vector<std::unordered_map<SupportElement*, Polygons>>& layer_tree_polygons);
/*!
* \brief Drop down areas that do rest non-gracefully on the model to ensure the branch actually rests on something.
*
* \param layer_tree_polygons[in] Resulting branch areas with the layerindex they appear on.
* \param linear_data[in] All currently existing influence areas with the layer they are on
* \param dropped_down_areas[out] Areas that have to be added to support all non-graceful areas.
* \param inverse_tree_order[in] A mapping that returns the child of every influence area.
*/
void dropNonGraciousAreas(std::vector<std::unordered_map<SupportElement*, Polygons>>& layer_tree_polygons, const std::vector<std::pair<LayerIndex, SupportElement*>>& linear_data, std::vector<std::vector<std::pair<LayerIndex, Polygons>>>& dropped_down_areas, const std::map<SupportElement*, SupportElement*>& inverse_tree_order);
/*!
* \brief Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as support to storage
*
* \param support_layer_storage[in] Areas where support should be generated.
* \param support_roof_storage[in] Areas where support was replaced with roof.
* \param storage[in,out] The storage where the support should be stored.
*/
void finalizeInterfaceAndSupportAreas(
const PrintObject &print_object,
const std::vector<Polygons> &overhangs,
std::vector<Polygons> &support_layer_storage,
std::vector<Polygons> &support_roof_storage,
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage);
/*!
* \brief Draws circles around result_on_layer points of the influence areas and applies some post processing.
*
* \param move_bounds[in] All currently existing influence areas
* \param storage[in,out] The storage where the support should be stored.
*/
void drawAreas(
PrintObject &print_object,
const std::vector<Polygons> &overhangs,
std::vector<std::set<SupportElement*>> &move_bounds,
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage);
/*!
* \brief Settings with the indexes of meshes that use these settings.
*
*/
std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> m_grouped_meshes;
/*!
* \brief Generator for model collision, avoidance and internal guide volumes.
*
*/
TreeModelVolumes m_volumes;
/*!
* \brief Contains config settings to avoid loading them in every function. This was done to improve readability of the code.
*/
TreeSupportSettings m_config;
/*!
* \brief The progress multiplier of all values added progress bar.
* Required for the progress bar the behave as expected when areas have to be calculated multiple times
*/
double m_progress_multiplier = 1;
/*!
* \brief The progress offset added to all values communicated to the progress bar.
* Required for the progress bar the behave as expected when areas have to be calculated multiple times
*/
double m_progress_offset = 0;
}; };
// todo Remove! ONLY FOR PUBLIC BETA!!
void tree_supports_show_error(std::string message, bool critical);
} // namespace FFFTreeSupport
void fff_tree_support_generate(PrintObject &print_object, std::function<void()> throw_on_cancel = []{});
} // namespace Slic3r } // namespace Slic3r
namespace std
{
template <>
struct hash<Slic3r::TreeSupport::SupportElement>
{
size_t operator()(const Slic3r::TreeSupport::SupportElement& node) const
{
size_t hash_node = Slic3r::PointHash{}(node.target_position);
boost::hash_combine(hash_node, size_t(node.target_height));
return hash_node;
}
};
} // namespace std
#endif /* slic3r_TreeSupport_hpp */ #endif /* slic3r_TreeSupport_hpp */

View File

@ -378,6 +378,14 @@ bool TriangleMesh::is_splittable() const
return its_is_splittable(this->its); return its_is_splittable(this->its);
} }
bool TriangleMesh::has_zero_volume() const
{
const Vec3d sz = size();
const double volume_val = sz.x() * sz.y() * sz.z();
return is_approx(volume_val, 0.0);
}
std::vector<TriangleMesh> TriangleMesh::split() const std::vector<TriangleMesh> TriangleMesh::split() const
{ {
std::vector<indexed_triangle_set> itss = its_split(this->its); std::vector<indexed_triangle_set> itss = its_split(this->its);

View File

@ -139,6 +139,7 @@ public:
bool empty() const { return this->facets_count() == 0; } bool empty() const { return this->facets_count() == 0; }
bool repaired() const; bool repaired() const;
bool is_splittable() const; bool is_splittable() const;
bool has_zero_volume() const;
// Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation. // Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation.
size_t memsize() const; size_t memsize() const;

View File

@ -111,7 +111,7 @@
</dict> </dict>
</array> </array>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>10.10</string> <string>10.12</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>NSApplication</string>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>

View File

@ -69,80 +69,7 @@ bool Version::is_current_slic3r_downgrade() const
{ {
return Slic3r::SEMVER < min_slic3r_version; return Slic3r::SEMVER < min_slic3r_version;
} }
#if 0
//TODO: This test should be moved to a unit test, once we have C++ unit tests in place.
static int version_test()
{
Version v;
v.config_version = *Semver::parse("1.1.2");
v.min_slic3r_version = *Semver::parse("1.38.0");
v.max_slic3r_version = Semver::inf();
assert(v.is_slic3r_supported(*Semver::parse("1.38.0")));
assert(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha")));
assert(! v.is_slic3r_supported(*Semver::parse("1.37.0-alpha")));
// Test the prerelease status.
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-alpha");
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-alpha1");
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-beta");
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-rc");
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-rc2");
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
// Test the upper boundary.
v.config_version = *Semver::parse("1.1.2");
v.max_slic3r_version = *Semver::parse("1.39.3-beta1");
assert(v.is_slic3r_supported(*Semver::parse("1.38.0")));
assert(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha")));
assert(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha1")));
assert(! v.is_slic3r_supported(*Semver::parse("1.37.0-alpha")));
return 0;
}
static int version_test_run = version_test();
#endif
inline char* left_trim(char *c) inline char* left_trim(char *c)
{ {

View File

@ -211,6 +211,12 @@ void BackgroundSlicingProcess::thread_proc()
set_current_thread_name("slic3r_BgSlcPcs"); set_current_thread_name("slic3r_BgSlcPcs");
name_tbb_thread_pool_threads_set_locale(); name_tbb_thread_pool_threads_set_locale();
// Set "C" locales and enforce OSX QoS level on all threads entering an arena.
// The cost of the callback is quite low: The callback is called once per thread
// entering a parallel loop and the callback is guarded with a thread local
// variable to be executed just once.
TBBLocalesSetter setter;
assert(m_print != nullptr); assert(m_print != nullptr);
assert(m_print == m_fff_print || m_print == m_sla_print); assert(m_print == m_fff_print || m_print == m_sla_print);
std::unique_lock<std::mutex> lck(m_mutex); std::unique_lock<std::mutex> lck(m_mutex);

View File

@ -34,8 +34,11 @@ void BitmapCache::clear()
{ {
for (std::pair<const std::string, wxBitmap*> &bitmap : m_map) for (std::pair<const std::string, wxBitmap*> &bitmap : m_map)
delete bitmap.second; delete bitmap.second;
m_map.clear(); m_map.clear();
for (std::pair<const std::string, wxBitmapBundle*> &bitmap_bundle : m_bndl_map)
delete bitmap_bundle.second;
m_bndl_map.clear();
} }
static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image, float scale = 1.0f) static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image, float scale = 1.0f)

View File

@ -116,11 +116,7 @@ void GCodeViewer::IBuffer::reset()
count = 0; count = 0;
} }
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move, bool account_for_volumetric_rate) const bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move, bool account_for_volumetric_rate) const
#else
bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move) const
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
{ {
auto matches_percent = [](float value1, float value2, float max_percent) { auto matches_percent = [](float value1, float value2, float max_percent) {
return std::abs(value2 - value1) / value1 <= max_percent; return std::abs(value2 - value1) / value1 <= max_percent;
@ -137,7 +133,6 @@ bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move) co
case EMoveType::Seam: case EMoveType::Seam:
case EMoveType::Extrude: { case EMoveType::Extrude: {
// use rounding to reduce the number of generated paths // use rounding to reduce the number of generated paths
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
if (account_for_volumetric_rate) if (account_for_volumetric_rate)
return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role && return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed && move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed &&
@ -147,12 +142,6 @@ bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move) co
return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role && return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed && move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed &&
height == round_to_bin(move.height) && width == round_to_bin(move.width); height == round_to_bin(move.height) && width == round_to_bin(move.width);
#else
return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed &&
height == round_to_bin(move.height) && width == round_to_bin(move.width) &&
matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f);
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
} }
case EMoveType::Travel: { case EMoveType::Travel: {
return type == move.type && feedrate == move.feedrate && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id; return type == move.type && feedrate == move.feedrate && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id;
@ -261,7 +250,6 @@ void GCodeViewer::COG::render()
//ImGui::PopStyleVar(); //ImGui::PopStyleVar();
} }
#if ENABLE_PREVIEW_LAYER_TIME
float GCodeViewer::Extrusions::Range::step_size(EType type) const float GCodeViewer::Extrusions::Range::step_size(EType type) const
{ {
switch (type) switch (type)
@ -273,12 +261,8 @@ float GCodeViewer::Extrusions::Range::step_size(EType type) const
} }
ColorRGBA GCodeViewer::Extrusions::Range::get_color_at(float value, EType type) const ColorRGBA GCodeViewer::Extrusions::Range::get_color_at(float value, EType type) const
#else
ColorRGBA GCodeViewer::Extrusions::Range::get_color_at(float value) const
#endif // ENABLE_PREVIEW_LAYER_TIME
{ {
// Input value scaled to the colors range // Input value scaled to the colors range
#if ENABLE_PREVIEW_LAYER_TIME
float global_t = 0.0f; float global_t = 0.0f;
const float step = step_size(type); const float step = step_size(type);
if (step > 0.0f) { if (step > 0.0f) {
@ -289,10 +273,6 @@ ColorRGBA GCodeViewer::Extrusions::Range::get_color_at(float value) const
case EType::Logarithmic: { global_t = (value > min && min > 0.0f) ? ::log(value / min) / step : 0.0f; break; } case EType::Logarithmic: { global_t = (value > min && min > 0.0f) ? ::log(value / min) / step : 0.0f; break; }
} }
} }
#else
const float step = step_size();
const float global_t = (step != 0.0f) ? std::max(0.0f, value - min) / step : 0.0f; // lower limit of 0.0f
#endif // ENABLE_PREVIEW_LAYER_TIME
const size_t color_max_idx = Range_Colors.size() - 1; const size_t color_max_idx = Range_Colors.size() - 1;
@ -771,19 +751,12 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr
#endif // ENABLE_LEGACY_OPENGL_REMOVAL #endif // ENABLE_LEGACY_OPENGL_REMOVAL
{ {
// avoid processing if called with the same gcode_result // avoid processing if called with the same gcode_result
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
if (m_last_result_id == gcode_result.id && if (m_last_result_id == gcode_result.id &&
(m_last_view_type == m_view_type || (m_last_view_type != EViewType::VolumetricRate && m_view_type != EViewType::VolumetricRate))) (m_last_view_type == m_view_type || (m_last_view_type != EViewType::VolumetricRate && m_view_type != EViewType::VolumetricRate)))
return; return;
#else
if (m_last_result_id == gcode_result.id)
return;
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
m_last_result_id = gcode_result.id; m_last_result_id = gcode_result.id;
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
m_last_view_type = m_view_type; m_last_view_type = m_view_type;
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
// release gpu memory, if used // release gpu memory, if used
reset(); reset();
@ -919,7 +892,6 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v
} }
} }
#if ENABLE_PREVIEW_LAYER_TIME
for (size_t i = 0; i < gcode_result.print_statistics.modes.size(); ++i) { for (size_t i = 0; i < gcode_result.print_statistics.modes.size(); ++i) {
m_layers_times[i] = gcode_result.print_statistics.modes[i].layers_times; m_layers_times[i] = gcode_result.print_statistics.modes[i].layers_times;
} }
@ -929,28 +901,16 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v
m_extrusions.ranges.layer_time[i].update_from(time); m_extrusions.ranges.layer_time[i].update_from(time);
} }
} }
#endif // ENABLE_PREVIEW_LAYER_TIME
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
m_statistics.refresh_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time).count(); m_statistics.refresh_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time).count();
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
// update buffers' render paths // update buffers' render paths
#if ENABLE_PREVIEW_LAYOUT
refresh_render_paths(false, false); refresh_render_paths(false, false);
#else
refresh_render_paths();
#endif // ENABLE_PREVIEW_LAYOUT
log_memory_used("Refreshed G-code extrusion paths, "); log_memory_used("Refreshed G-code extrusion paths, ");
} }
#if !ENABLE_PREVIEW_LAYOUT
void GCodeViewer::refresh_render_paths()
{
refresh_render_paths(false, false);
}
#endif // !ENABLE_PREVIEW_LAYOUT
void GCodeViewer::update_shells_color_by_extruder(const DynamicPrintConfig* config) void GCodeViewer::update_shells_color_by_extruder(const DynamicPrintConfig* config)
{ {
if (config != nullptr) if (config != nullptr)
@ -978,20 +938,16 @@ void GCodeViewer::reset()
m_layers_z_range = { 0, 0 }; m_layers_z_range = { 0, 0 };
m_roles = std::vector<ExtrusionRole>(); m_roles = std::vector<ExtrusionRole>();
m_print_statistics.reset(); m_print_statistics.reset();
#if ENABLE_PREVIEW_LAYER_TIME
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
m_layers_times[i] = std::vector<float>(); m_layers_times[i] = std::vector<float>();
} }
#endif // ENABLE_PREVIEW_LAYER_TIME
m_custom_gcode_per_print_z = std::vector<CustomGCode::Item>(); m_custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
m_sequential_view.gcode_window.reset(); m_sequential_view.gcode_window.reset();
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
m_statistics.reset_all(); m_statistics.reset_all();
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
m_contained_in_bed = true; m_contained_in_bed = true;
#if ENABLE_PREVIEW_LAYOUT
m_legend_resizer.reset(); m_legend_resizer.reset();
#endif // ENABLE_PREVIEW_LAYOUT
} }
void GCodeViewer::render() void GCodeViewer::render()
@ -1104,9 +1060,6 @@ unsigned int GCodeViewer::get_options_visibility_flags() const
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::CenterOfGravity), m_cog.is_visible()); flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::CenterOfGravity), m_cog.is_visible());
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Shells), m_shells.visible); flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Shells), m_shells.visible);
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible()); flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::ToolMarker), m_sequential_view.marker.is_visible());
#if !ENABLE_PREVIEW_LAYOUT
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Legend), is_legend_enabled());
#endif // !ENABLE_PREVIEW_LAYOUT
return flags; return flags;
} }
@ -1128,9 +1081,6 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags)
m_cog.set_visible(is_flag_set(static_cast<unsigned int>(Preview::OptionType::CenterOfGravity))); m_cog.set_visible(is_flag_set(static_cast<unsigned int>(Preview::OptionType::CenterOfGravity)));
m_shells.visible = is_flag_set(static_cast<unsigned int>(Preview::OptionType::Shells)); m_shells.visible = is_flag_set(static_cast<unsigned int>(Preview::OptionType::Shells));
m_sequential_view.marker.set_visible(is_flag_set(static_cast<unsigned int>(Preview::OptionType::ToolMarker))); m_sequential_view.marker.set_visible(is_flag_set(static_cast<unsigned int>(Preview::OptionType::ToolMarker)));
#if !ENABLE_PREVIEW_LAYOUT
enable_legend(is_flag_set(static_cast<unsigned int>(Preview::OptionType::Legend)));
#endif // !ENABLE_PREVIEW_LAYOUT
} }
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range) void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
@ -1360,15 +1310,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
// add current vertex // add current vertex
add_vertex(curr); add_vertex(curr);
}; };
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
auto add_indices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, auto add_indices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer,
unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id, bool account_for_volumetric_rate) { unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id, bool account_for_volumetric_rate) {
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr, account_for_volumetric_rate)) { if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr, account_for_volumetric_rate)) {
#else
auto add_indices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer,
unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) {
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
// add starting index // add starting index
indices.push_back(static_cast<IBufferType>(indices.size())); indices.push_back(static_cast<IBufferType>(indices.size()));
buffer.add_path(curr, ibuffer_id, indices.size() - 1, move_id - 1); buffer.add_path(curr, ibuffer_id, indices.size() - 1, move_id - 1);
@ -1387,13 +1331,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
}; };
// format data into the buffers to be rendered as solid // format data into the buffers to be rendered as solid
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
auto add_vertices_as_solid = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer, auto add_vertices_as_solid = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer,
unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id, bool account_for_volumetric_rate) { unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id, bool account_for_volumetric_rate) {
#else
auto add_vertices_as_solid = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer,
unsigned int vbuffer_id, VertexBuffer& vertices, size_t move_id) {
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) { auto store_vertex = [](VertexBuffer& vertices, const Vec3f& position, const Vec3f& normal) {
// append position // append position
vertices.push_back(position.x()); vertices.push_back(position.x());
@ -1405,11 +1344,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
vertices.push_back(normal.z()); vertices.push_back(normal.z());
}; };
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr, account_for_volumetric_rate)) { if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr, account_for_volumetric_rate)) {
#else
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) {
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
buffer.add_path(curr, vbuffer_id, vertices.size(), move_id - 1); buffer.add_path(curr, vbuffer_id, vertices.size(), move_id - 1);
buffer.paths.back().sub_paths.back().first.position = prev.position; buffer.paths.back().sub_paths.back().first.position = prev.position;
} }
@ -1454,15 +1389,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
last_path.sub_paths.back().last = { vbuffer_id, vertices.size(), move_id, curr.position }; last_path.sub_paths.back().last = { vbuffer_id, vertices.size(), move_id, curr.position };
}; };
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
auto add_indices_as_solid = [&](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, auto add_indices_as_solid = [&](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr,
const GCodeProcessorResult::MoveVertex* next, TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id, const GCodeProcessorResult::MoveVertex* next, TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id,
IndexBuffer& indices, size_t move_id, bool account_for_volumetric_rate) { IndexBuffer& indices, size_t move_id, bool account_for_volumetric_rate) {
#else
auto add_indices_as_solid = [&](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr,
const GCodeProcessorResult::MoveVertex* next, TBuffer& buffer, size_t& vbuffer_size, unsigned int ibuffer_id,
IndexBuffer& indices, size_t move_id) {
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
static Vec3f prev_dir; static Vec3f prev_dir;
static Vec3f prev_up; static Vec3f prev_up;
static float sq_prev_length; static float sq_prev_length;
@ -1507,11 +1436,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
store_triangle(indices, v_offsets[4], v_offsets[5], v_offsets[6]); store_triangle(indices, v_offsets[4], v_offsets[5], v_offsets[6]);
}; };
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr, account_for_volumetric_rate)) { if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr, account_for_volumetric_rate)) {
#else
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) {
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
buffer.add_path(curr, ibuffer_id, indices.size(), move_id - 1); buffer.add_path(curr, ibuffer_id, indices.size(), move_id - 1);
buffer.paths.back().sub_paths.back().first.position = prev.position; buffer.paths.back().sub_paths.back().first.position = prev.position;
} }
@ -1595,11 +1520,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
vbuffer_size += 6; vbuffer_size += 6;
} }
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
if (next != nullptr && (curr.type != next->type || !last_path.matches(*next, account_for_volumetric_rate))) if (next != nullptr && (curr.type != next->type || !last_path.matches(*next, account_for_volumetric_rate)))
#else
if (next != nullptr && (curr.type != next->type || !last_path.matches(*next)))
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
// ending cap triangles // ending cap triangles
append_ending_cap_triangles(indices, is_first_segment ? first_seg_v_offsets : non_first_seg_v_offsets); append_ending_cap_triangles(indices, is_first_segment ? first_seg_v_offsets : non_first_seg_v_offsets);
@ -1737,9 +1658,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
m_sequential_view.gcode_ids.push_back(move.gcode_id); m_sequential_view.gcode_ids.push_back(move.gcode_id);
} }
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
bool account_for_volumetric_rate = m_view_type == EViewType::VolumetricRate; bool account_for_volumetric_rate = m_view_type == EViewType::VolumetricRate;
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
std::vector<MultiVertexBuffer> vertices(m_buffers.size()); std::vector<MultiVertexBuffer> vertices(m_buffers.size());
std::vector<MultiIndexBuffer> indices(m_buffers.size()); std::vector<MultiIndexBuffer> indices(m_buffers.size());
@ -1803,11 +1722,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
v_multibuffer.push_back(VertexBuffer()); v_multibuffer.push_back(VertexBuffer());
if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
Path& last_path = t_buffer.paths.back(); Path& last_path = t_buffer.paths.back();
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
if (prev.type == curr.type && last_path.matches(curr, account_for_volumetric_rate)) if (prev.type == curr.type && last_path.matches(curr, account_for_volumetric_rate))
#else
if (prev.type == curr.type && last_path.matches(curr))
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, move_id - 1); last_path.add_sub_path(prev, static_cast<unsigned int>(v_multibuffer.size()) - 1, 0, move_id - 1);
} }
} }
@ -1817,11 +1732,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
switch (t_buffer.render_primitive_type) switch (t_buffer.render_primitive_type)
{ {
case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; } case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; }
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id, account_for_volumetric_rate); break; } case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id, account_for_volumetric_rate); break; }
#else
case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id); break; }
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
case TBuffer::ERenderPrimitiveType::InstancedModel: case TBuffer::ERenderPrimitiveType::InstancedModel:
{ {
add_model_instance(curr, inst_buffer, inst_id_buffer, move_id); add_model_instance(curr, inst_buffer, inst_id_buffer, move_id);
@ -2220,20 +2131,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
switch (t_buffer.render_primitive_type) switch (t_buffer.render_primitive_type)
{ {
case TBuffer::ERenderPrimitiveType::Line: { case TBuffer::ERenderPrimitiveType::Line: {
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id, account_for_volumetric_rate); add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id, account_for_volumetric_rate);
#else
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
curr_vertex_buffer.second += t_buffer.max_vertices_per_segment(); curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
break; break;
} }
case TBuffer::ERenderPrimitiveType::Triangle: { case TBuffer::ERenderPrimitiveType::Triangle: {
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id, account_for_volumetric_rate); add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id, account_for_volumetric_rate);
#else
add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
break; break;
} }
case TBuffer::ERenderPrimitiveType::BatchedModel: { case TBuffer::ERenderPrimitiveType::BatchedModel: {
@ -2500,7 +2403,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; }
case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; }
case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; } case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; }
#if ENABLE_PREVIEW_LAYER_TIME
case EViewType::LayerTimeLinear: case EViewType::LayerTimeLinear:
case EViewType::LayerTimeLogarithmic: { case EViewType::LayerTimeLogarithmic: {
const Path::Sub_Path& sub_path = path.sub_paths.front(); const Path::Sub_Path& sub_path = path.sub_paths.front();
@ -2519,7 +2421,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
} }
break; break;
} }
#endif // ENABLE_PREVIEW_LAYER_TIME
case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; }
case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; }
case EViewType::ColorPrint: { case EViewType::ColorPrint: {
@ -3514,11 +3415,7 @@ void GCodeViewer::render_legend(float& legend_height)
const float max_height = 0.75f * static_cast<float>(cnv_size.get_height()); const float max_height = 0.75f * static_cast<float>(cnv_size.get_height());
const float child_height = 0.3333f * max_height; const float child_height = 0.3333f * max_height;
ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, max_height }); ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, max_height });
#if ENABLE_PREVIEW_LAYOUT
imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove);
#else
imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove);
#endif // ENABLE_PREVIEW_LAYOUT
enum class EItemType : unsigned char enum class EItemType : unsigned char
{ {
@ -3529,14 +3426,9 @@ void GCodeViewer::render_legend(float& legend_height)
}; };
const PrintEstimatedStatistics::Mode& time_mode = m_print_statistics.modes[static_cast<size_t>(m_time_estimate_mode)]; const PrintEstimatedStatistics::Mode& time_mode = m_print_statistics.modes[static_cast<size_t>(m_time_estimate_mode)];
#if ENABLE_PREVIEW_LAYER_TIME
bool show_estimated_time = time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || bool show_estimated_time = time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType ||
m_view_type == EViewType::LayerTimeLinear || m_view_type == EViewType::LayerTimeLogarithmic || m_view_type == EViewType::LayerTimeLinear || m_view_type == EViewType::LayerTimeLogarithmic ||
(m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty())); (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty()));
#else
bool show_estimated_time = time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType ||
(m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty()));
#endif // ENABLE_PREVIEW_LAYER_TIME
const float icon_size = ImGui::GetTextLineHeight(); const float icon_size = ImGui::GetTextLineHeight();
const float percent_bar_size = 2.0f * ImGui::GetTextLineHeight(); const float percent_bar_size = 2.0f * ImGui::GetTextLineHeight();
@ -3622,7 +3514,6 @@ void GCodeViewer::render_legend(float& legend_height)
} }
else { else {
imgui.text(label); imgui.text(label);
#if ENABLE_TRAVEL_TIME
if (!time.empty()) { if (!time.empty()) {
ImGui::SameLine(offsets[0]); ImGui::SameLine(offsets[0]);
imgui.text(time); imgui.text(time);
@ -3638,9 +3529,6 @@ void GCodeViewer::render_legend(float& legend_height)
ImGui::TextUnformatted((percent > 0.0f) ? buf : ""); ImGui::TextUnformatted((percent > 0.0f) ? buf : "");
} }
else if (used_filament_m > 0.0) { else if (used_filament_m > 0.0) {
#else
if (used_filament_m > 0.0) {
#endif // ENABLE_TRAVEL_TIME
char buf[64]; char buf[64];
ImGui::SameLine(offsets[0]); ImGui::SameLine(offsets[0]);
::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", used_filament_m); ::sprintf(buf, imperial_units ? "%.2f in" : "%.2f m", used_filament_m);
@ -3678,7 +3566,6 @@ void GCodeViewer::render_legend(float& legend_height)
} }
}; };
#if ENABLE_PREVIEW_LAYER_TIME
auto append_time_range = [append_item](const Extrusions::Range& range, Extrusions::Range::EType type) { auto append_time_range = [append_item](const Extrusions::Range& range, Extrusions::Range::EType type) {
auto append_range_item = [append_item](int i, float value) { auto append_range_item = [append_item](int i, float value) {
std::string str_value = get_time_dhms(value); std::string str_value = get_time_dhms(value);
@ -3709,7 +3596,6 @@ void GCodeViewer::render_legend(float& legend_height)
} }
} }
}; };
#endif // ENABLE_PREVIEW_LAYER_TIME
auto append_headers = [&imgui](const std::array<std::string, 5>& texts, const std::array<float, 4>& offsets) { auto append_headers = [&imgui](const std::array<std::string, 5>& texts, const std::array<float, 4>& offsets) {
size_t i = 0; size_t i = 0;
@ -3883,7 +3769,6 @@ void GCodeViewer::render_legend(float& legend_height)
offsets = calculate_offsets(labels, times, { "Extruder NNN", longest_used_filament_string }, icon_size); offsets = calculate_offsets(labels, times, { "Extruder NNN", longest_used_filament_string }, icon_size);
} }
#if ENABLE_PREVIEW_LAYOUT
// selection section // selection section
bool view_type_changed = false; bool view_type_changed = false;
int old_view_type = static_cast<int>(get_view_type()); int old_view_type = static_cast<int>(get_view_type());
@ -3901,10 +3786,8 @@ void GCodeViewer::render_legend(float& legend_height)
_u8L("Fan speed (%)"), _u8L("Fan speed (%)"),
_u8L("Temperature (°C)"), _u8L("Temperature (°C)"),
_u8L("Volumetric flow rate (mm³/s)"), _u8L("Volumetric flow rate (mm³/s)"),
#if ENABLE_PREVIEW_LAYER_TIME
_u8L("Layer time (linear)"), _u8L("Layer time (linear)"),
_u8L("Layer time (logarithmic)"), _u8L("Layer time (logarithmic)"),
#endif // ENABLE_PREVIEW_LAYER_TIME
_u8L("Tool"), _u8L("Tool"),
_u8L("Color Print") }, view_type, ImGuiComboFlags_HeightLargest); _u8L("Color Print") }, view_type, ImGuiComboFlags_HeightLargest);
ImGui::PopStyleColor(2); ImGui::PopStyleColor(2);
@ -3923,45 +3806,14 @@ void GCodeViewer::render_legend(float& legend_height)
append_headers({ _u8L(""), _u8L("Used filament"), _u8L(""), _u8L("") }, offsets); append_headers({ _u8L(""), _u8L("Used filament"), _u8L(""), _u8L("") }, offsets);
else else
ImGui::Separator(); ImGui::Separator();
#else
// extrusion paths section -> title
switch (m_view_type)
{
case EViewType::FeatureType:
{
append_headers({ _u8L("Feature type"), _u8L("Time"), _u8L("Percentage"), _u8L("Used filament") }, offsets);
break;
}
case EViewType::Height: { imgui.title(_u8L("Height (mm)")); break; }
case EViewType::Width: { imgui.title(_u8L("Width (mm)")); break; }
case EViewType::Feedrate: { imgui.title(_u8L("Speed (mm/s)")); break; }
case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; }
case EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; }
case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; }
#if ENABLE_PREVIEW_LAYER_TIME
case EViewType::LayerTimeLinear: { imgui.title(_u8L("Layer time (linear)")); break; }
case EViewType::LayerTimeLogarithmic: { imgui.title(_u8L("Layer time (logarithmic)")); break; }
#endif // ENABLE_PREVIEW_LAYER_TIME
case EViewType::Tool: {
append_headers({ _u8L("Tool"), _u8L("Used filament") }, offsets);
break;
}
case EViewType::ColorPrint: { imgui.title(_u8L("Color Print")); break; }
default: { break; }
}
#endif // ENABLE_PREVIEW_LAYOUT
#if ENABLE_PREVIEW_LAYOUT
if (!view_type_changed) { if (!view_type_changed) {
#endif // ENABLE_PREVIEW_LAYOUT
// extrusion paths section -> items // extrusion paths section -> items
switch (m_view_type) switch (m_view_type)
{ {
case EViewType::FeatureType: case EViewType::FeatureType:
{ {
#if ENABLE_TRAVEL_TIME
max_time_percent = std::max(max_time_percent, time_mode.travel_time / time_mode.time); max_time_percent = std::max(max_time_percent, time_mode.travel_time / time_mode.time);
#endif // ENABLE_TRAVEL_TIME
for (size_t i = 0; i < m_roles.size(); ++i) { for (size_t i = 0; i < m_roles.size(); ++i) {
ExtrusionRole role = m_roles[i]; ExtrusionRole role = m_roles[i];
@ -3975,18 +3827,13 @@ void GCodeViewer::render_legend(float& legend_height)
refresh_render_paths(false, false); refresh_render_paths(false, false);
wxGetApp().plater()->update_preview_moves_slider(); wxGetApp().plater()->update_preview_moves_slider();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
#if !ENABLE_PREVIEW_LAYOUT
wxGetApp().plater()->update_preview_bottom_toolbar();
#endif // !ENABLE_PREVIEW_LAYOUT
} }
); );
} }
#if ENABLE_TRAVEL_TIME
if (m_buffers[buffer_id(EMoveType::Travel)].visible) if (m_buffers[buffer_id(EMoveType::Travel)].visible)
append_item(EItemType::Line, Travel_Colors[0], _u8L("Travel"), true, short_time(get_time_dhms(time_mode.travel_time)), append_item(EItemType::Line, Travel_Colors[0], _u8L("Travel"), true, short_time(get_time_dhms(time_mode.travel_time)),
time_mode.travel_time / time_mode.time, max_time_percent, offsets, 0.0f, 0.0f); time_mode.travel_time / time_mode.time, max_time_percent, offsets, 0.0f, 0.0f);
#endif // ENABLE_TRAVEL_TIME
break; break;
} }
@ -3996,10 +3843,8 @@ void GCodeViewer::render_legend(float& legend_height)
case EViewType::FanSpeed: { append_range(m_extrusions.ranges.fan_speed, 0); break; } case EViewType::FanSpeed: { append_range(m_extrusions.ranges.fan_speed, 0); break; }
case EViewType::Temperature: { append_range(m_extrusions.ranges.temperature, 0); break; } case EViewType::Temperature: { append_range(m_extrusions.ranges.temperature, 0); break; }
case EViewType::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 3); break; } case EViewType::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 3); break; }
#if ENABLE_PREVIEW_LAYER_TIME
case EViewType::LayerTimeLinear: { append_time_range(m_extrusions.ranges.layer_time[static_cast<size_t>(m_time_estimate_mode)], Extrusions::Range::EType::Linear); break; } case EViewType::LayerTimeLinear: { append_time_range(m_extrusions.ranges.layer_time[static_cast<size_t>(m_time_estimate_mode)], Extrusions::Range::EType::Linear); break; }
case EViewType::LayerTimeLogarithmic: { append_time_range(m_extrusions.ranges.layer_time[static_cast<size_t>(m_time_estimate_mode)], Extrusions::Range::EType::Logarithmic); break; } case EViewType::LayerTimeLogarithmic: { append_time_range(m_extrusions.ranges.layer_time[static_cast<size_t>(m_time_estimate_mode)], Extrusions::Range::EType::Logarithmic); break; }
#endif // ENABLE_PREVIEW_LAYER_TIME
case EViewType::Tool: { case EViewType::Tool: {
// shows only extruders actually used // shows only extruders actually used
for (unsigned char extruder_id : m_extruder_ids) { for (unsigned char extruder_id : m_extruder_ids) {
@ -4078,9 +3923,7 @@ void GCodeViewer::render_legend(float& legend_height)
} }
default: { break; } default: { break; }
} }
#if ENABLE_PREVIEW_LAYOUT
} }
#endif // ENABLE_PREVIEW_LAYOUT
// partial estimated printing time section // partial estimated printing time section
if (m_view_type == EViewType::ColorPrint) { if (m_view_type == EViewType::ColorPrint) {
@ -4257,89 +4100,14 @@ void GCodeViewer::render_legend(float& legend_height)
} }
} }
#if !ENABLE_PREVIEW_LAYOUT auto add_strings_row_to_table = [&imgui](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) {
// travel paths section ImGui::TableNextRow();
if (m_buffers[buffer_id(EMoveType::Travel)].visible) { ImGui::TableSetColumnIndex(0);
switch (m_view_type) imgui.text_colored(col_1_color, col_1.c_str());
{ ImGui::TableSetColumnIndex(1);
case EViewType::Feedrate: imgui.text_colored(col_2_color, col_2.c_str());
case EViewType::Tool:
case EViewType::ColorPrint: {
break;
}
default: {
// title
ImGui::Spacing();
imgui.title(_u8L("Travel"));
// items
append_item(EItemType::Line, Travel_Colors[0], _u8L("Movement"));
append_item(EItemType::Line, Travel_Colors[1], _u8L("Extrusion"));
append_item(EItemType::Line, Travel_Colors[2], _u8L("Retraction"));
break;
}
}
}
// wipe paths section
if (m_buffers[buffer_id(EMoveType::Wipe)].visible) {
switch (m_view_type)
{
case EViewType::Feedrate:
case EViewType::Tool:
case EViewType::ColorPrint: { break; }
default: {
// title
ImGui::Spacing();
imgui.title(_u8L("Wipe"));
// items
append_item(EItemType::Line, Wipe_Color, _u8L("Wipe"));
break;
}
}
}
auto any_option_available = [this]() {
auto available = [this](EMoveType type) {
const TBuffer& buffer = m_buffers[buffer_id(type)];
return buffer.visible && buffer.has_data();
}; };
return available(EMoveType::Color_change) ||
available(EMoveType::Custom_GCode) ||
available(EMoveType::Pause_Print) ||
available(EMoveType::Retract) ||
available(EMoveType::Tool_change) ||
available(EMoveType::Unretract) ||
available(EMoveType::Seam);
};
auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) {
const TBuffer& buffer = m_buffers[buffer_id(move_type)];
if (buffer.visible && buffer.has_data())
append_item(EItemType::Circle, Options_Colors[static_cast<unsigned int>(color)], text);
};
// options section
if (any_option_available()) {
// title
ImGui::Spacing();
imgui.title(_u8L("Options"));
// items
add_option(EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions"));
add_option(EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Deretractions"));
add_option(EMoveType::Seam, EOptionsColors::Seams, _u8L("Seams"));
add_option(EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes"));
add_option(EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes"));
add_option(EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Print pauses"));
add_option(EMoveType::Custom_GCode, EOptionsColors::CustomGCodes, _u8L("Custom G-codes"));
}
#endif // !ENABLE_PREVIEW_LAYOUT
// settings section // settings section
bool has_settings = false; bool has_settings = false;
has_settings |= !m_settings_ids.print.empty(); has_settings |= !m_settings_ids.print.empty();
@ -4354,48 +4122,38 @@ void GCodeViewer::render_legend(float& legend_height)
show_settings &= (m_view_type == EViewType::FeatureType || m_view_type == EViewType::Tool); show_settings &= (m_view_type == EViewType::FeatureType || m_view_type == EViewType::Tool);
show_settings &= has_settings; show_settings &= has_settings;
if (show_settings) { if (show_settings) {
auto calc_offset = [this]() {
float ret = 0.0f;
if (!m_settings_ids.printer.empty())
ret = std::max(ret, ImGui::CalcTextSize((_u8L("Printer") + std::string(":")).c_str()).x);
if (!m_settings_ids.print.empty())
ret = std::max(ret, ImGui::CalcTextSize((_u8L("Print settings") + std::string(":")).c_str()).x);
if (!m_settings_ids.filament.empty()) {
for (unsigned char i : m_extruder_ids) {
ret = std::max(ret, ImGui::CalcTextSize((_u8L("Filament") + " " + std::to_string(i + 1) + ":").c_str()).x);
}
}
if (ret > 0.0f)
ret += 2.0f * ImGui::GetStyle().ItemSpacing.x;
return ret;
};
ImGui::Spacing(); ImGui::Spacing();
imgui.title(_u8L("Settings")); imgui.title(_u8L("Settings"));
float offset = calc_offset(); auto trim_text_if_needed = [](const std::string& txt) {
const float max_length = 250.0f;
const float length = ImGui::CalcTextSize(txt.c_str()).x;
if (length > max_length) {
const size_t new_len = txt.length() * max_length / length;
return txt.substr(0, new_len) + "...";
}
return txt;
};
if (!m_settings_ids.printer.empty()) { if (ImGui::BeginTable("Settings", 2)) {
imgui.text(_u8L("Printer") + ":"); if (!m_settings_ids.printer.empty())
ImGui::SameLine(offset); add_strings_row_to_table(_u8L("Printer") + ":", ImGuiWrapper::COL_ORANGE_LIGHT,
imgui.text(m_settings_ids.printer); trim_text_if_needed(m_settings_ids.printer), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()));
} if (!m_settings_ids.print.empty())
if (!m_settings_ids.print.empty()) { add_strings_row_to_table(_u8L("Print settings") + ":", ImGuiWrapper::COL_ORANGE_LIGHT,
imgui.text(_u8L("Print settings") + ":"); trim_text_if_needed(m_settings_ids.print), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()));
ImGui::SameLine(offset);
imgui.text(m_settings_ids.print);
}
if (!m_settings_ids.filament.empty()) { if (!m_settings_ids.filament.empty()) {
for (unsigned char i : m_extruder_ids) { for (unsigned char i : m_extruder_ids) {
if (i < static_cast<unsigned char>(m_settings_ids.filament.size()) && !m_settings_ids.filament[i].empty()) { if (i < static_cast<unsigned char>(m_settings_ids.filament.size()) && !m_settings_ids.filament[i].empty()) {
std::string txt = _u8L("Filament"); std::string txt = _u8L("Filament");
txt += (m_extruder_ids.size() == 1) ? ":" : " " + std::to_string(i + 1); txt += (m_extruder_ids.size() == 1) ? ":" : " " + std::to_string(i + 1);
imgui.text(txt); add_strings_row_to_table(txt, ImGuiWrapper::COL_ORANGE_LIGHT,
ImGui::SameLine(offset); trim_text_if_needed(m_settings_ids.filament[i]), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()));
imgui.text(m_settings_ids.filament[i]);
} }
} }
} }
ImGui::EndTable();
}
} }
// total estimated printing time section // total estimated printing time section
@ -4428,33 +4186,24 @@ void GCodeViewer::render_legend(float& legend_height)
imgui.title(time_title + ":"); imgui.title(time_title + ":");
std::string first_str = _u8L("First layer"); if (ImGui::BeginTable("Times", 2)) {
std::string total_str = _u8L("Total");
float max_len = 10.0f + ImGui::GetStyle().ItemSpacing.x;
if (time_mode.layers_times.empty())
max_len += ImGui::CalcTextSize(total_str.c_str()).x;
else
max_len += std::max(ImGui::CalcTextSize(first_str.c_str()).x, ImGui::CalcTextSize(total_str.c_str()).x);
if (!time_mode.layers_times.empty()) { if (!time_mode.layers_times.empty()) {
imgui.text(first_str + ":"); add_strings_row_to_table(_u8L("First layer") + ":", ImGuiWrapper::COL_ORANGE_LIGHT,
ImGui::SameLine(max_len); short_time(get_time_dhms(time_mode.layers_times.front())), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()));
imgui.text(short_time(get_time_dhms(time_mode.layers_times.front())));
} }
imgui.text(total_str + ":"); add_strings_row_to_table(_u8L("Total") + ":", ImGuiWrapper::COL_ORANGE_LIGHT,
ImGui::SameLine(max_len); short_time(get_time_dhms(time_mode.time)), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()));
imgui.text(short_time(get_time_dhms(time_mode.time)));
ImGui::EndTable();
}
auto show_mode_button = [this, &imgui, can_show_mode_button](const wxString& label, PrintEstimatedStatistics::ETimeMode mode) { auto show_mode_button = [this, &imgui, can_show_mode_button](const wxString& label, PrintEstimatedStatistics::ETimeMode mode) {
if (can_show_mode_button(mode)) { if (can_show_mode_button(mode)) {
if (imgui.button(label)) { if (imgui.button(label)) {
m_time_estimate_mode = mode; m_time_estimate_mode = mode;
#if ENABLE_PREVIEW_LAYER_TIME
if (m_view_type == EViewType::LayerTimeLinear || m_view_type == EViewType::LayerTimeLogarithmic) if (m_view_type == EViewType::LayerTimeLinear || m_view_type == EViewType::LayerTimeLogarithmic)
refresh_render_paths(false, false); refresh_render_paths(false, false);
#endif // ENABLE_PREVIEW_LAYER_TIME
imgui.set_requires_extra_frame(); imgui.set_requires_extra_frame();
} }
} }
@ -4473,7 +4222,6 @@ void GCodeViewer::render_legend(float& legend_height)
} }
} }
#if ENABLE_PREVIEW_LAYOUT
// toolbar section // toolbar section
auto toggle_button = [this, &imgui, icon_size](Preview::OptionType type, const std::string& name, auto toggle_button = [this, &imgui, icon_size](Preview::OptionType type, const std::string& name,
std::function<void(ImGuiWindow& window, const ImVec2& pos, float size)> draw_callback) { std::function<void(ImGuiWindow& window, const ImVec2& pos, float size)> draw_callback) {
@ -4513,7 +4261,6 @@ void GCodeViewer::render_legend(float& legend_height)
} }
}; };
#if ENABLE_LEGEND_TOOLBAR_ICONS
auto image_icon = [&imgui](ImGuiWindow& window, const ImVec2& pos, float size, const wchar_t& icon_id) { auto image_icon = [&imgui](ImGuiWindow& window, const ImVec2& pos, float size, const wchar_t& icon_id) {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
const ImTextureID tex_id = io.Fonts->TexID; const ImTextureID tex_id = io.Fonts->TexID;
@ -4524,163 +4271,59 @@ void GCodeViewer::render_legend(float& legend_height)
const ImVec2 uv1 = { static_cast<float>(rect->X + rect->Width) / tex_w, static_cast<float>(rect->Y + rect->Height) / tex_h }; const ImVec2 uv1 = { static_cast<float>(rect->X + rect->Width) / tex_w, static_cast<float>(rect->Y + rect->Height) / tex_h };
window.DrawList->AddImage(tex_id, pos, { pos.x + size, pos.y + size }, uv0, uv1, ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f })); window.DrawList->AddImage(tex_id, pos, { pos.x + size, pos.y + size }, uv0, uv1, ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f }));
}; };
#else
auto circle_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const ColorRGBA& color) {
const float margin = 3.0f;
const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size));
window.DrawList->AddCircleFilled(center, 0.5f * (size - 2.0f * margin), ImGuiWrapper::to_ImU32(color), 16);
};
auto line_icon = [](ImGuiWindow& window, const ImVec2& pos, float size, const ColorRGBA& color) {
const float margin = 3.0f;
window.DrawList->AddLine({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin, pos.y + margin }, ImGuiWrapper::to_ImU32(color), 3.0f);
};
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
ImGui::Spacing(); ImGui::Spacing();
ImGui::Separator(); ImGui::Separator();
ImGui::Spacing(); ImGui::Spacing();
ImGui::Spacing(); ImGui::Spacing();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::Travel, _u8L("Travel"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::Travel, _u8L("Travel"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendTravel); image_icon(window, pos, size, ImGui::LegendTravel);
#else
toggle_button(Preview::OptionType::Travel, _u8L("Travel"), [line_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
line_icon(window, pos, size, Travel_Colors[0]);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::Wipe, _u8L("Wipe"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::Wipe, _u8L("Wipe"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendWipe); image_icon(window, pos, size, ImGui::LegendWipe);
#else
toggle_button(Preview::OptionType::Wipe, _u8L("Wipe"), [line_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
line_icon(window, pos, size, Wipe_Color);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::Retractions, _u8L("Retractions"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::Retractions, _u8L("Retractions"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendRetract); image_icon(window, pos, size, ImGui::LegendRetract);
#else
toggle_button(Preview::OptionType::Retractions, _u8L("Retractions"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
circle_icon(window, pos, size, Options_Colors[static_cast<unsigned int>(EOptionsColors::Retractions)]);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::Unretractions, _u8L("Deretractions"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::Unretractions, _u8L("Deretractions"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendDeretract); image_icon(window, pos, size, ImGui::LegendDeretract);
#else
toggle_button(Preview::OptionType::Unretractions, _u8L("Deretractions"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
circle_icon(window, pos, size, Options_Colors[static_cast<unsigned int>(EOptionsColors::Unretractions)]);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::Seams, _u8L("Seams"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::Seams, _u8L("Seams"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendSeams); image_icon(window, pos, size, ImGui::LegendSeams);
#else
toggle_button(Preview::OptionType::Seams, _u8L("Seams"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
circle_icon(window, pos, size, Options_Colors[static_cast<unsigned int>(EOptionsColors::Seams)]);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::ToolChanges, _u8L("Tool changes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::ToolChanges, _u8L("Tool changes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendToolChanges); image_icon(window, pos, size, ImGui::LegendToolChanges);
#else
toggle_button(Preview::OptionType::ToolChanges, _u8L("Tool changes"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
circle_icon(window, pos, size, Options_Colors[static_cast<unsigned int>(EOptionsColors::ToolChanges)]);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::ColorChanges, _u8L("Color changes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::ColorChanges, _u8L("Color changes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendColorChanges); image_icon(window, pos, size, ImGui::LegendColorChanges);
#else
toggle_button(Preview::OptionType::ColorChanges, _u8L("Color changes"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
circle_icon(window, pos, size, Options_Colors[static_cast<unsigned int>(EOptionsColors::ColorChanges)]);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::PausePrints, _u8L("Print pauses"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::PausePrints, _u8L("Print pauses"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendPausePrints); image_icon(window, pos, size, ImGui::LegendPausePrints);
#else
toggle_button(Preview::OptionType::PausePrints, _u8L("Print pauses"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
circle_icon(window, pos, size, Options_Colors[static_cast<unsigned int>(EOptionsColors::PausePrints)]);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::CustomGCodes, _u8L("Custom G-codes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::CustomGCodes, _u8L("Custom G-codes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendCustomGCodes); image_icon(window, pos, size, ImGui::LegendCustomGCodes);
#else
toggle_button(Preview::OptionType::CustomGCodes, _u8L("Custom G-codes"), [circle_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
circle_icon(window, pos, size, Options_Colors[static_cast<unsigned int>(EOptionsColors::CustomGCodes)]);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::CenterOfGravity, _u8L("Center of gravity"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::CenterOfGravity, _u8L("Center of gravity"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendCOG); image_icon(window, pos, size, ImGui::LegendCOG);
}); });
#else
toggle_button(Preview::OptionType::CenterOfGravity, _u8L("Center of gravity"), [](ImGuiWindow& window, const ImVec2& pos, float size) {
const ImU32 black = ImGuiWrapper::to_ImU32({ 0.0f, 0.0f, 0.0f, 1.0f });
const ImU32 white = ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f });
const float margin = 3.0f;
const ImVec2 center(0.5f * (pos.x + pos.x + size), 0.5f * (pos.y + pos.y + size));
const float radius = 0.5f * (size - 2.0f * margin);
window.DrawList->PathArcToFast(center, radius, 0, 3);
window.DrawList->PathLineTo(center);
window.DrawList->PathFillConvex(black);
window.DrawList->PathArcToFast(center, radius, 3, 6);
window.DrawList->PathLineTo(center);
window.DrawList->PathFillConvex(white);
window.DrawList->PathArcToFast(center, radius, 6, 9);
window.DrawList->PathLineTo(center);
window.DrawList->PathFillConvex(black);
window.DrawList->PathArcToFast(center, radius, 9, 12);
window.DrawList->PathLineTo(center);
window.DrawList->PathFillConvex(white);
window.DrawList->AddCircle(center, radius, black, 16);
});
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
ImGui::SameLine(); ImGui::SameLine();
if (!wxGetApp().is_gcode_viewer()) { if (!wxGetApp().is_gcode_viewer()) {
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendShells); image_icon(window, pos, size, ImGui::LegendShells);
#else
toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [](ImGuiWindow& window, const ImVec2& pos, float size) {
const ImU32 color = ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f });
const float margin = 3.0f;
const float proj = 0.25f * size;
window.DrawList->AddRect({ pos.x + margin, pos.y + size - margin }, { pos.x + size - margin - proj, pos.y + margin + proj }, color);
window.DrawList->AddLine({ pos.x + margin, pos.y + margin + proj }, { pos.x + margin + proj, pos.y + margin }, color);
window.DrawList->AddLine({ pos.x + size - margin - proj, pos.y + margin + proj }, { pos.x + size - margin, pos.y + margin }, color);
window.DrawList->AddLine({ pos.x + size - margin - proj, pos.y + size - margin }, { pos.x + size - margin, pos.y + size - margin - proj }, color);
window.DrawList->AddLine({ pos.x + margin + proj, pos.y + margin }, { pos.x + size - margin, pos.y + margin }, color);
window.DrawList->AddLine({ pos.x + size - margin, pos.y + margin }, { pos.x + size - margin, pos.y + size - margin - proj }, color);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
ImGui::SameLine(); ImGui::SameLine();
} }
#if ENABLE_LEGEND_TOOLBAR_ICONS
toggle_button(Preview::OptionType::ToolMarker, _u8L("Tool marker"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { toggle_button(Preview::OptionType::ToolMarker, _u8L("Tool marker"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) {
image_icon(window, pos, size, ImGui::LegendToolMarker); image_icon(window, pos, size, ImGui::LegendToolMarker);
#else
toggle_button(Preview::OptionType::ToolMarker, _u8L("Tool marker"), [](ImGuiWindow& window, const ImVec2& pos, float size) {
const ImU32 color = ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 0.8f });
const float margin = 3.0f;
const ImVec2 p1(0.5f * (pos.x + pos.x + size), pos.y + size - margin);
const ImVec2 p2(p1.x + 0.25f * size, p1.y - 0.25f * size);
const ImVec2 p3(p1.x - 0.25f * size, p1.y - 0.25f * size);
window.DrawList->AddTriangleFilled(p1, p2, p3, color);
const float mid_x = 0.5f * (pos.x + pos.x + size);
window.DrawList->AddRectFilled({ mid_x - 0.09375f * size, p1.y - 0.25f * size }, { mid_x + 0.09375f * size, pos.y + margin }, color);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
}); });
bool size_dirty = !ImGui::GetCurrentWindow()->ScrollbarY && ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x != ImGui::GetWindowWidth(); bool size_dirty = !ImGui::GetCurrentWindow()->ScrollbarY && ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x != ImGui::GetWindowWidth();
@ -4689,7 +4332,6 @@ void GCodeViewer::render_legend(float& legend_height)
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
} }
m_legend_resizer.dirty = size_dirty; m_legend_resizer.dirty = size_dirty;
#endif // ENABLE_PREVIEW_LAYOUT
legend_height = ImGui::GetWindowHeight(); legend_height = ImGui::GetWindowHeight();

View File

@ -220,11 +220,7 @@ class GCodeViewer
unsigned char cp_color_id{ 0 }; unsigned char cp_color_id{ 0 };
std::vector<Sub_Path> sub_paths; std::vector<Sub_Path> sub_paths;
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
bool matches(const GCodeProcessorResult::MoveVertex& move, bool account_for_volumetric_rate) const; bool matches(const GCodeProcessorResult::MoveVertex& move, bool account_for_volumetric_rate) const;
#else
bool matches(const GCodeProcessorResult::MoveVertex& move) const;
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
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;
} }
@ -436,13 +432,11 @@ class GCodeViewer
{ {
struct Range struct Range
{ {
#if ENABLE_PREVIEW_LAYER_TIME
enum class EType : unsigned char enum class EType : unsigned char
{ {
Linear, Linear,
Logarithmic Logarithmic
}; };
#endif // ENABLE_PREVIEW_LAYER_TIME
float min; float min;
float max; float max;
@ -458,13 +452,8 @@ class GCodeViewer
} }
void reset() { min = FLT_MAX; max = -FLT_MAX; count = 0; } void reset() { min = FLT_MAX; max = -FLT_MAX; count = 0; }
#if ENABLE_PREVIEW_LAYER_TIME
float step_size(EType type = EType::Linear) const; float step_size(EType type = EType::Linear) const;
ColorRGBA get_color_at(float value, EType type = EType::Linear) const; ColorRGBA get_color_at(float value, EType type = EType::Linear) const;
#else
float step_size() const { return (max - min) / (static_cast<float>(Range_Colors.size()) - 1.0f); }
ColorRGBA get_color_at(float value) const;
#endif // ENABLE_PREVIEW_LAYER_TIME
}; };
struct Ranges struct Ranges
@ -481,10 +470,8 @@ class GCodeViewer
Range volumetric_rate; Range volumetric_rate;
// Color mapping by extrusion temperature. // Color mapping by extrusion temperature.
Range temperature; Range temperature;
#if ENABLE_PREVIEW_LAYER_TIME
// Color mapping by layer time. // Color mapping by layer time.
std::array<Range, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> layer_time; std::array<Range, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> layer_time;
#endif // ENABLE_PREVIEW_LAYER_TIME
void reset() { void reset() {
height.reset(); height.reset();
@ -493,11 +480,9 @@ class GCodeViewer
fan_speed.reset(); fan_speed.reset();
volumetric_rate.reset(); volumetric_rate.reset();
temperature.reset(); temperature.reset();
#if ENABLE_PREVIEW_LAYER_TIME
for (auto& range : layer_time) { for (auto& range : layer_time) {
range.reset(); range.reset();
} }
#endif // ENABLE_PREVIEW_LAYER_TIME
} }
}; };
@ -756,10 +741,8 @@ public:
FanSpeed, FanSpeed,
Temperature, Temperature,
VolumetricRate, VolumetricRate,
#if ENABLE_PREVIEW_LAYER_TIME
LayerTimeLinear, LayerTimeLinear,
LayerTimeLogarithmic, LayerTimeLogarithmic,
#endif // ENABLE_PREVIEW_LAYER_TIME
Tool, Tool,
ColorPrint, ColorPrint,
Count Count
@ -768,9 +751,7 @@ public:
private: private:
bool m_gl_data_initialized{ false }; bool m_gl_data_initialized{ false };
unsigned int m_last_result_id{ 0 }; unsigned int m_last_result_id{ 0 };
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
EViewType m_last_view_type{ EViewType::Count }; EViewType m_last_view_type{ EViewType::Count };
#endif // ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
size_t m_moves_count{ 0 }; size_t m_moves_count{ 0 };
std::vector<TBuffer> m_buffers{ static_cast<size_t>(EMoveType::Extrude) }; std::vector<TBuffer> m_buffers{ static_cast<size_t>(EMoveType::Extrude) };
// bounding box of toolpaths // bounding box of toolpaths
@ -792,14 +773,12 @@ private:
COG m_cog; COG m_cog;
EViewType m_view_type{ EViewType::FeatureType }; EViewType m_view_type{ EViewType::FeatureType };
bool m_legend_enabled{ true }; bool m_legend_enabled{ true };
#if ENABLE_PREVIEW_LAYOUT
struct LegendResizer struct LegendResizer
{ {
bool dirty{ true }; bool dirty{ true };
void reset() { dirty = true; } void reset() { dirty = true; }
}; };
LegendResizer m_legend_resizer; LegendResizer m_legend_resizer;
#endif // ENABLE_PREVIEW_LAYOUT
PrintEstimatedStatistics m_print_statistics; PrintEstimatedStatistics m_print_statistics;
PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal }; PrintEstimatedStatistics::ETimeMode m_time_estimate_mode{ PrintEstimatedStatistics::ETimeMode::Normal };
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
@ -807,9 +786,7 @@ private:
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
GCodeProcessorResult::SettingsIds m_settings_ids; GCodeProcessorResult::SettingsIds m_settings_ids;
std::array<SequentialRangeCap, 2> m_sequential_range_caps; std::array<SequentialRangeCap, 2> m_sequential_range_caps;
#if ENABLE_PREVIEW_LAYER_TIME
std::array<std::vector<float>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> m_layers_times; std::array<std::vector<float>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> m_layers_times;
#endif // ENABLE_PREVIEW_LAYER_TIME
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z; std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
@ -829,11 +806,7 @@ public:
#endif // ENABLE_LEGACY_OPENGL_REMOVAL #endif // ENABLE_LEGACY_OPENGL_REMOVAL
// recalculate ranges in dependence of what is visible and sets tool/print colors // recalculate ranges in dependence of what is visible and sets tool/print colors
void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors); void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
#if ENABLE_PREVIEW_LAYOUT
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
#else
void refresh_render_paths();
#endif // ENABLE_PREVIEW_LAYOUT
void update_shells_color_by_extruder(const DynamicPrintConfig* config); void update_shells_color_by_extruder(const DynamicPrintConfig* config);
void reset(); void reset();
@ -878,9 +851,7 @@ public:
std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_custom_gcode_per_print_z; } std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_custom_gcode_per_print_z; }
size_t get_extruders_count() { return m_extruders_count; } size_t get_extruders_count() { return m_extruders_count; }
#if ENABLE_PREVIEW_LAYOUT
void invalidate_legend() { m_legend_resizer.reset(); } void invalidate_legend() { m_legend_resizer.reset(); }
#endif // ENABLE_PREVIEW_LAYOUT
private: private:
void load_toolpaths(const GCodeProcessorResult& gcode_result); void load_toolpaths(const GCodeProcessorResult& gcode_result);
@ -889,9 +860,6 @@ private:
#else #else
void load_shells(const Print& print, bool initialized); void load_shells(const Print& print, bool initialized);
#endif // ENABLE_LEGACY_OPENGL_REMOVAL #endif // ENABLE_LEGACY_OPENGL_REMOVAL
#if !ENABLE_PREVIEW_LAYOUT
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
#endif // !ENABLE_PREVIEW_LAYOUT
void render_toolpaths(); void render_toolpaths();
void render_shells(); void render_shells();
void render_legend(float& legend_height); void render_legend(float& legend_height);

View File

@ -2512,21 +2512,12 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co
request_extra_frame(); request_extra_frame();
} }
#if ENABLE_PREVIEW_LAYOUT
void GLCanvas3D::refresh_gcode_preview_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) void GLCanvas3D::refresh_gcode_preview_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last)
{ {
m_gcode_viewer.refresh_render_paths(keep_sequential_current_first, keep_sequential_current_last); m_gcode_viewer.refresh_render_paths(keep_sequential_current_first, keep_sequential_current_last);
set_as_dirty(); set_as_dirty();
request_extra_frame(); request_extra_frame();
} }
#else
void GLCanvas3D::refresh_gcode_preview_render_paths()
{
m_gcode_viewer.refresh_render_paths();
set_as_dirty();
request_extra_frame();
}
#endif // ENABLE_PREVIEW_LAYOUT
void GLCanvas3D::load_sla_preview() void GLCanvas3D::load_sla_preview()
{ {
@ -2833,15 +2824,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case 'k': { wxGetApp().plater()->get_camera().select_next_type(); m_dirty = true; break; } case 'k': { wxGetApp().plater()->get_camera().select_next_type(); m_dirty = true; break; }
case 'L': case 'L':
case 'l': { case 'l': {
if (!m_main_toolbar.is_enabled()) { if (!m_main_toolbar.is_enabled())
#if ENABLE_PREVIEW_LAYOUT
show_legend(!is_legend_shown()); show_legend(!is_legend_shown());
#else
m_gcode_viewer.enable_legend(!m_gcode_viewer.is_legend_enabled());
m_dirty = true;
wxGetApp().plater()->update_preview_bottom_toolbar();
#endif // ENABLE_PREVIEW_LAYOUT
}
break; break;
} }
case 'O': case 'O':
@ -4296,9 +4280,7 @@ void GLCanvas3D::set_cursor(ECursorType type)
void GLCanvas3D::msw_rescale() void GLCanvas3D::msw_rescale()
{ {
#if ENABLE_PREVIEW_LAYOUT
m_gcode_viewer.invalidate_legend(); m_gcode_viewer.invalidate_legend();
#endif // ENABLE_PREVIEW_LAYOUT
} }
void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar() void GLCanvas3D::update_tooltip_for_settings_item_in_main_toolbar()

View File

@ -817,11 +817,7 @@ public:
void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false); void reload_scene(bool refresh_immediately, bool force_full_scene_refresh = false);
void load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors); void load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
#if ENABLE_PREVIEW_LAYOUT
void refresh_gcode_preview_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last); void refresh_gcode_preview_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last);
#else
void refresh_gcode_preview_render_paths();
#endif // ENABLE_PREVIEW_LAYOUT
void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); } void set_gcode_view_preview_type(GCodeViewer::EViewType type) { return m_gcode_viewer.set_view_type(type); }
GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); } GCodeViewer::EViewType get_gcode_view_preview_type() const { return m_gcode_viewer.get_view_type(); }
void load_sla_preview(); void load_sla_preview();
@ -915,10 +911,8 @@ public:
bool are_labels_shown() const { return m_labels.is_shown(); } bool are_labels_shown() const { return m_labels.is_shown(); }
void show_labels(bool show) { m_labels.show(show); } void show_labels(bool show) { m_labels.show(show); }
#if ENABLE_PREVIEW_LAYOUT
bool is_legend_shown() const { return m_gcode_viewer.is_legend_enabled(); } bool is_legend_shown() const { return m_gcode_viewer.is_legend_enabled(); }
void show_legend(bool show) { m_gcode_viewer.enable_legend(show); m_dirty = true; } void show_legend(bool show) { m_gcode_viewer.enable_legend(show); m_dirty = true; }
#endif // ENABLE_PREVIEW_LAYOUT
bool is_using_slope() const { return m_slope.is_used(); } bool is_using_slope() const { return m_slope.is_used(); }
void use_slope(bool use) { m_slope.use(use); } void use_slope(bool use) { m_slope.use(use); }

View File

@ -208,75 +208,6 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model)
m_layers_slider_sizer = create_layers_slider_sizer(); m_layers_slider_sizer = create_layers_slider_sizer();
wxGetApp().UpdateDarkUI(m_bottom_toolbar_panel = new wxPanel(this)); wxGetApp().UpdateDarkUI(m_bottom_toolbar_panel = new wxPanel(this));
#if !ENABLE_PREVIEW_LAYOUT
m_label_view_type = new wxStaticText(m_bottom_toolbar_panel, wxID_ANY, _L("View"));
#ifdef _WIN32
wxGetApp().UpdateDarkUI(m_choice_view_type = new BitmapComboBox(m_bottom_toolbar_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY));
#else
m_choice_view_type = new wxComboBox(m_bottom_toolbar_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY);
#endif
m_choice_view_type->Append(_L("Feature type"));
m_choice_view_type->Append(_L("Height"));
m_choice_view_type->Append(_L("Width"));
m_choice_view_type->Append(_L("Speed"));
m_choice_view_type->Append(_L("Fan speed"));
m_choice_view_type->Append(_L("Temperature"));
m_choice_view_type->Append(_L("Volumetric flow rate"));
#if ENABLE_PREVIEW_LAYER_TIME
m_choice_view_type->Append(_L("Layer time (linear)"));
m_choice_view_type->Append(_L("Layer time (logarithmic)"));
#endif // ENABLE_PREVIEW_LAYER_TIME
m_choice_view_type->Append(_L("Tool"));
m_choice_view_type->Append(_L("Color Print"));
m_choice_view_type->SetSelection(0);
m_label_show = new wxStaticText(m_bottom_toolbar_panel, wxID_ANY, _L("Show"));
#ifdef _WIN32
long combo_style = wxCB_READONLY | wxBORDER_SIMPLE; //set border allows use default color instead of theme color wich is allways light under MSW
#else
long combo_style = wxCB_READONLY;
#endif
m_combochecklist_features = new wxComboCtrl();
m_combochecklist_features->Create(m_bottom_toolbar_panel, wxID_ANY, _L("Feature types"), wxDefaultPosition, wxDefaultSize, combo_style);
std::string feature_items = GUI::into_u8(
_L("Unknown") + "|1|" +
_L("Perimeter") + "|1|" +
_L("External perimeter") + "|1|" +
_L("Overhang perimeter") + "|1|" +
_L("Internal infill") + "|1|" +
_L("Solid infill") + "|1|" +
_L("Top solid infill") + "|1|" +
_L("Ironing") + "|1|" +
_L("Bridge infill") + "|1|" +
_L("Gap fill") + "|1|" +
_L("Skirt/Brim") + "|1|" +
_L("Support material") + "|1|" +
_L("Support material interface") + "|1|" +
_L("Wipe tower") + "|1|" +
_L("Custom") + "|1"
);
Slic3r::GUI::create_combochecklist(m_combochecklist_features, GUI::into_u8(_L("Feature types")), feature_items);
m_combochecklist_options = new wxComboCtrl();
m_combochecklist_options->Create(m_bottom_toolbar_panel, wxID_ANY, _L("Options"), wxDefaultPosition, wxDefaultSize, combo_style);
std::string options_items = GUI::into_u8(
get_option_type_string(OptionType::Travel) + "|0|" +
get_option_type_string(OptionType::Wipe) + "|0|" +
get_option_type_string(OptionType::Retractions) + "|0|" +
get_option_type_string(OptionType::Unretractions) + "|0|" +
get_option_type_string(OptionType::Seams) + "|0|" +
get_option_type_string(OptionType::ToolChanges) + "|0|" +
get_option_type_string(OptionType::ColorChanges) + "|0|" +
get_option_type_string(OptionType::PausePrints) + "|0|" +
get_option_type_string(OptionType::CustomGCodes) + "|0|" +
get_option_type_string(OptionType::Shells) + "|0|" +
get_option_type_string(OptionType::ToolMarker) + "|1|" +
get_option_type_string(OptionType::Legend) + "|1"
);
Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_L("Options")), options_items);
#endif // !ENABLE_PREVIEW_LAYOUT
m_left_sizer = new wxBoxSizer(wxVERTICAL); m_left_sizer = new wxBoxSizer(wxVERTICAL);
m_left_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); m_left_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0);
@ -288,19 +219,6 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model)
m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView);
wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL);
#if !ENABLE_PREVIEW_LAYOUT
bottom_toolbar_sizer->AddSpacer(5);
bottom_toolbar_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
bottom_toolbar_sizer->Add(m_choice_view_type, 0, wxALIGN_CENTER_VERTICAL, 0);
bottom_toolbar_sizer->AddSpacer(5);
bottom_toolbar_sizer->Add(m_label_show, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 5);
bottom_toolbar_sizer->Add(m_combochecklist_options, 0, wxALIGN_CENTER_VERTICAL, 0);
// change the following number if editing the layout of the bottom toolbar sizer. It is used into update_bottom_toolbar()
m_combochecklist_features_pos = 6;
bottom_toolbar_sizer->Add(m_combochecklist_features, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5);
bottom_toolbar_sizer->Hide(m_combochecklist_features);
bottom_toolbar_sizer->AddSpacer(5);
#endif // !ENABLE_PREVIEW_LAYOUT
bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 0); bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 0);
m_bottom_toolbar_panel->SetSizer(bottom_toolbar_sizer); m_bottom_toolbar_panel->SetSizer(bottom_toolbar_sizer);
@ -362,9 +280,6 @@ void Preview::load_print(bool keep_z_range)
else if (tech == ptSLA) else if (tech == ptSLA)
load_print_as_sla(); load_print_as_sla();
#if !ENABLE_PREVIEW_LAYOUT
update_bottom_toolbar();
#endif // !ENABLE_PREVIEW_LAYOUT
Layout(); Layout();
} }
@ -407,12 +322,6 @@ void Preview::refresh_print()
void Preview::msw_rescale() void Preview::msw_rescale()
{ {
#if !ENABLE_PREVIEW_LAYOUT
#ifdef _WIN32
m_choice_view_type->Rescale();
m_choice_view_type->SetMinSize(m_choice_view_type->GetSize());
#endif
#endif // !ENABLE_PREVIEW_LAYOUT
// rescale slider // rescale slider
if (m_layers_slider != nullptr) m_layers_slider->msw_rescale(); if (m_layers_slider != nullptr) m_layers_slider->msw_rescale();
if (m_moves_slider != nullptr) m_moves_slider->msw_rescale(); if (m_moves_slider != nullptr) m_moves_slider->msw_rescale();
@ -428,16 +337,8 @@ void Preview::sys_color_changed()
{ {
#ifdef _WIN32 #ifdef _WIN32
wxWindowUpdateLocker noUpdates(this); wxWindowUpdateLocker noUpdates(this);
wxGetApp().UpdateAllStaticTextDarkUI(m_bottom_toolbar_panel); wxGetApp().UpdateAllStaticTextDarkUI(m_bottom_toolbar_panel);
#if !ENABLE_PREVIEW_LAYOUT #endif // _WIN32
wxGetApp().UpdateDarkUI(m_choice_view_type);
wxGetApp().UpdateDarkUI(m_combochecklist_features);
wxGetApp().UpdateDarkUI(static_cast<wxCheckListBoxComboPopup*>(m_combochecklist_features->GetPopupControl()));
wxGetApp().UpdateDarkUI(m_combochecklist_options);
wxGetApp().UpdateDarkUI(static_cast<wxCheckListBoxComboPopup*>(m_combochecklist_options->GetPopupControl()));
#endif // !ENABLE_PREVIEW_LAYOUT
#endif
if (m_layers_slider != nullptr) if (m_layers_slider != nullptr)
m_layers_slider->sys_color_changed(); m_layers_slider->sys_color_changed();
@ -461,22 +362,12 @@ void Preview::edit_layers_slider(wxKeyEvent& evt)
void Preview::bind_event_handlers() void Preview::bind_event_handlers()
{ {
Bind(wxEVT_SIZE, &Preview::on_size, this); Bind(wxEVT_SIZE, &Preview::on_size, this);
#if !ENABLE_PREVIEW_LAYOUT
m_choice_view_type->Bind(wxEVT_COMBOBOX, &Preview::on_choice_view_type, this);
m_combochecklist_features->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this);
m_combochecklist_options->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_options, this);
#endif // !ENABLE_PREVIEW_LAYOUT
m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this);
} }
void Preview::unbind_event_handlers() void Preview::unbind_event_handlers()
{ {
Unbind(wxEVT_SIZE, &Preview::on_size, this); Unbind(wxEVT_SIZE, &Preview::on_size, this);
#if !ENABLE_PREVIEW_LAYOUT
m_choice_view_type->Unbind(wxEVT_COMBOBOX, &Preview::on_choice_view_type, this);
m_combochecklist_features->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this);
m_combochecklist_options->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_options, this);
#endif // !ENABLE_PREVIEW_LAYOUT
m_moves_slider->Unbind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); m_moves_slider->Unbind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this);
} }
@ -497,75 +388,6 @@ void Preview::on_size(wxSizeEvent& evt)
Refresh(); Refresh();
} }
#if !ENABLE_PREVIEW_LAYOUT
void Preview::on_choice_view_type(wxCommandEvent& evt)
{
int selection = m_choice_view_type->GetCurrentSelection();
if (0 <= selection && selection < static_cast<int>(GCodeViewer::EViewType::Count)) {
m_canvas->set_toolpath_view_type(static_cast<GCodeViewer::EViewType>(selection));
m_keep_current_preview_type = true;
}
refresh_print();
}
void Preview::on_combochecklist_features(wxCommandEvent& evt)
{
unsigned int flags = Slic3r::GUI::combochecklist_get_flags(m_combochecklist_features);
m_canvas->set_toolpath_role_visibility_flags(flags);
refresh_print();
}
void Preview::on_combochecklist_options(wxCommandEvent& evt)
{
const unsigned int curr_flags = m_canvas->get_gcode_options_visibility_flags();
const unsigned int new_flags = Slic3r::GUI::combochecklist_get_flags(m_combochecklist_options);
if (curr_flags == new_flags)
return;
m_canvas->set_gcode_options_visibility_from_flags(new_flags);
if (m_canvas->get_gcode_view_type() == GCodeViewer::EViewType::Feedrate) {
const unsigned int diff_flags = curr_flags ^ new_flags;
if ((diff_flags & (1 << static_cast<unsigned int>(Preview::OptionType::Travel))) != 0)
refresh_print();
else
m_canvas->refresh_gcode_preview_render_paths();
}
else
m_canvas->refresh_gcode_preview_render_paths();
update_moves_slider();
}
void Preview::update_bottom_toolbar()
{
combochecklist_set_flags(m_combochecklist_features, m_canvas->get_toolpath_role_visibility_flags());
combochecklist_set_flags(m_combochecklist_options, m_canvas->get_gcode_options_visibility_flags());
// updates visibility of features combobox
if (m_bottom_toolbar_panel->IsShown()) {
wxSizer* sizer = m_bottom_toolbar_panel->GetSizer();
bool show = !m_canvas->is_gcode_legend_enabled() || m_canvas->get_gcode_view_type() != GCodeViewer::EViewType::FeatureType;
if (show) {
if (sizer->GetItem(m_combochecklist_features) == nullptr) {
sizer->Insert(m_combochecklist_features_pos, m_combochecklist_features, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5);
sizer->Show(m_combochecklist_features);
sizer->Layout();
Refresh();
}
}
else {
if (sizer->GetItem(m_combochecklist_features) != nullptr) {
sizer->Hide(m_combochecklist_features);
sizer->Detach(m_combochecklist_features);
sizer->Layout();
Refresh();
}
}
}
}
#endif // !ENABLE_PREVIEW_LAYOUT
wxBoxSizer* Preview::create_layers_slider_sizer() wxBoxSizer* Preview::create_layers_slider_sizer()
{ {
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
@ -1024,7 +846,6 @@ void Preview::load_print_as_fff(bool keep_z_range)
m_canvas->get_custom_gcode_per_print_z(); m_canvas->get_custom_gcode_per_print_z();
const bool contains_color_gcodes = std::any_of(std::begin(gcodes), std::end(gcodes), const bool contains_color_gcodes = std::any_of(std::begin(gcodes), std::end(gcodes),
[] (auto const& item) { return item.type == CustomGCode::Type::ColorChange; }); [] (auto const& item) { return item.type == CustomGCode::Type::ColorChange; });
#if ENABLE_PREVIEW_LAYOUT
const GCodeViewer::EViewType choice = contains_color_gcodes ? const GCodeViewer::EViewType choice = contains_color_gcodes ?
GCodeViewer::EViewType::ColorPrint : GCodeViewer::EViewType::ColorPrint :
(number_extruders > 1) ? GCodeViewer::EViewType::Tool : GCodeViewer::EViewType::FeatureType; (number_extruders > 1) ? GCodeViewer::EViewType::Tool : GCodeViewer::EViewType::FeatureType;
@ -1034,21 +855,6 @@ void Preview::load_print_as_fff(bool keep_z_range)
m_keep_current_preview_type = true; m_keep_current_preview_type = true;
refresh_print(); refresh_print();
} }
#else
const wxString choice = contains_color_gcodes ?
_L("Color Print") :
(number_extruders > 1) ? _L("Tool") : _L("Feature type");
int type = m_choice_view_type->FindString(choice);
if (m_choice_view_type->GetSelection() != type) {
if (0 <= type && type < static_cast<int>(GCodeViewer::EViewType::Count)) {
m_choice_view_type->SetSelection(type);
m_canvas->set_gcode_view_preview_type(static_cast<GCodeViewer::EViewType>(type));
if (wxGetApp().is_gcode_viewer())
m_keep_current_preview_type = true;
refresh_print();
}
}
#endif // ENABLE_PREVIEW_LAYOUT
} }
if (zs.empty()) { if (zs.empty()) {
@ -1123,27 +929,5 @@ void Preview::on_moves_slider_scroll_changed(wxCommandEvent& event)
m_canvas->render(); m_canvas->render();
} }
#if !ENABLE_PREVIEW_LAYOUT
wxString Preview::get_option_type_string(OptionType type) const
{
switch (type)
{
case OptionType::Travel: { return _L("Travel"); }
case OptionType::Wipe: { return _L("Wipe"); }
case OptionType::Retractions: { return _L("Retractions"); }
case OptionType::Unretractions: { return _L("Deretractions"); }
case OptionType::Seams: { return _L("Seams"); }
case OptionType::ToolChanges: { return _L("Tool changes"); }
case OptionType::ColorChanges: { return _L("Color changes"); }
case OptionType::PausePrints: { return _L("Print pauses"); }
case OptionType::CustomGCodes: { return _L("Custom G-codes"); }
case OptionType::Shells: { return _L("Shells"); }
case OptionType::ToolMarker: { return _L("Tool marker"); }
case OptionType::Legend: { return _L("Legend/Estimated printing time"); }
default: { return ""; }
}
}
#endif // !ENABLE_PREVIEW_LAYOUT
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View File

@ -80,18 +80,6 @@ class Preview : public wxPanel
wxBoxSizer* m_left_sizer { nullptr }; wxBoxSizer* m_left_sizer { nullptr };
wxBoxSizer* m_layers_slider_sizer { nullptr }; wxBoxSizer* m_layers_slider_sizer { nullptr };
wxPanel* m_bottom_toolbar_panel { nullptr }; wxPanel* m_bottom_toolbar_panel { nullptr };
#if !ENABLE_PREVIEW_LAYOUT
wxStaticText* m_label_view_type { nullptr };
#ifdef _WIN32
BitmapComboBox* m_choice_view_type { nullptr };
#else
wxComboBox* m_choice_view_type { nullptr };
#endif
wxStaticText* m_label_show{ nullptr };
wxComboCtrl* m_combochecklist_features { nullptr };
size_t m_combochecklist_features_pos { 0 };
wxComboCtrl* m_combochecklist_options { nullptr };
#endif // !ENABLE_PREVIEW_LAYOUT
DynamicPrintConfig* m_config; DynamicPrintConfig* m_config;
BackgroundSlicingProcess* m_process; BackgroundSlicingProcess* m_process;
@ -129,9 +117,6 @@ public:
CenterOfGravity, CenterOfGravity,
Shells, Shells,
ToolMarker, ToolMarker,
#if !ENABLE_PREVIEW_LAYOUT
Legend
#endif // !ENABLE_PREVIEW_LAYOUT
}; };
Preview(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process, Preview(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process,
@ -159,17 +144,12 @@ public:
bool is_loaded() const { return m_loaded; } bool is_loaded() const { return m_loaded; }
#if !ENABLE_PREVIEW_LAYOUT
void update_bottom_toolbar();
#endif // !ENABLE_PREVIEW_LAYOUT
void update_moves_slider(); void update_moves_slider();
void enable_moves_slider(bool enable); void enable_moves_slider(bool enable);
void move_moves_slider(wxKeyEvent& evt); void move_moves_slider(wxKeyEvent& evt);
void hide_layers_slider(); void hide_layers_slider();
#if ENABLE_PREVIEW_LAYOUT
void set_keep_current_preview_type(bool value) { m_keep_current_preview_type = value; } void set_keep_current_preview_type(bool value) { m_keep_current_preview_type = value; }
#endif // ENABLE_PREVIEW_LAYOUT
private: private:
bool init(wxWindow* parent, Bed3D& bed, Model* model); bool init(wxWindow* parent, Bed3D& bed, Model* model);
@ -178,11 +158,6 @@ private:
void unbind_event_handlers(); void unbind_event_handlers();
void on_size(wxSizeEvent& evt); void on_size(wxSizeEvent& evt);
#if !ENABLE_PREVIEW_LAYOUT
void on_choice_view_type(wxCommandEvent& evt);
void on_combochecklist_features(wxCommandEvent& evt);
void on_combochecklist_options(wxCommandEvent& evt);
#endif // !ENABLE_PREVIEW_LAYOUT
// Create/Update/Reset double slider on 3dPreview // Create/Update/Reset double slider on 3dPreview
wxBoxSizer* create_layers_slider_sizer(); wxBoxSizer* create_layers_slider_sizer();
@ -199,9 +174,6 @@ private:
void on_layers_slider_scroll_changed(wxCommandEvent& event); void on_layers_slider_scroll_changed(wxCommandEvent& event);
void on_moves_slider_scroll_changed(wxCommandEvent& event); void on_moves_slider_scroll_changed(wxCommandEvent& event);
#if !ENABLE_PREVIEW_LAYOUT
wxString get_option_type_string(OptionType type) const;
#endif // !ENABLE_PREVIEW_LAYOUT
}; };
} // namespace GUI } // namespace GUI

View File

@ -64,7 +64,6 @@ static const std::map<const wchar_t, std::string> font_icons = {
}; };
static const std::map<const wchar_t, std::string> font_icons_large = { static const std::map<const wchar_t, std::string> font_icons_large = {
#if ENABLE_LEGEND_TOOLBAR_ICONS
{ImGui::LegendTravel , "legend_travel" }, {ImGui::LegendTravel , "legend_travel" },
{ImGui::LegendWipe , "legend_wipe" }, {ImGui::LegendWipe , "legend_wipe" },
{ImGui::LegendRetract , "legend_retract" }, {ImGui::LegendRetract , "legend_retract" },
@ -77,7 +76,6 @@ static const std::map<const wchar_t, std::string> font_icons_large = {
{ImGui::LegendCOG , "legend_cog" }, {ImGui::LegendCOG , "legend_cog" },
{ImGui::LegendShells , "legend_shells" }, {ImGui::LegendShells , "legend_shells" },
{ImGui::LegendToolMarker , "legend_toolmarker" }, {ImGui::LegendToolMarker , "legend_toolmarker" },
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
{ImGui::CloseNotifButton , "notification_close" }, {ImGui::CloseNotifButton , "notification_close" },
{ImGui::CloseNotifHoverButton , "notification_close_hover" }, {ImGui::CloseNotifHoverButton , "notification_close_hover" },
{ImGui::EjectButton , "notification_eject_sd" }, {ImGui::EjectButton , "notification_eject_sd" },
@ -396,7 +394,6 @@ bool ImGuiWrapper::radio_button(const wxString &label, bool active)
return ImGui::RadioButton(label_utf8.c_str(), active); return ImGui::RadioButton(label_utf8.c_str(), active);
} }
#if ENABLE_PREVIEW_LAYOUT
bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool active, bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool active,
std::function<void(ImGuiWindow& window, const ImVec2& pos, float size)> draw_callback) std::function<void(ImGuiWindow& window, const ImVec2& pos, float size)> draw_callback)
{ {
@ -430,7 +427,6 @@ bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool a
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window.DC.LastItemStatusFlags); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window.DC.LastItemStatusFlags);
return pressed; return pressed;
} }
#endif // ENABLE_PREVIEW_LAYOUT
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
{ {
@ -575,7 +571,6 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y }); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y });
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_LEGEND_TOOLBAR_ICONS
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0);
float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); float inv_tex_w = 1.0f / float(io.Fonts->TexWidth);
@ -585,13 +580,11 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float
const ImVec2 size = { float(rect->Width), float(rect->Height) }; const ImVec2 size = { float(rect->Width), float(rect->Height) };
const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h);
const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f });
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f });
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.4f, 0.4f, 0.4f, 1.0f }); ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.4f, 0.4f, 0.4f, 1.0f });
#if ENABLE_LEGEND_TOOLBAR_ICONS
const ImTextureID tex_id = io.Fonts->TexID; const ImTextureID tex_id = io.Fonts->TexID;
if (image_button(tex_id, size, uv0, uv1, -1, ImVec4(0.0, 0.0, 0.0, 0.0), ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags_PressedOnClick)) { if (image_button(tex_id, size, uv0, uv1, -1, ImVec4(0.0, 0.0, 0.0, 0.0), ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags_PressedOnClick)) {
if (!slider_editing) if (!slider_editing)
@ -600,13 +593,6 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float
ImGui::ClearActiveID(); ImGui::ClearActiveID();
this->set_requires_extra_frame(); this->set_requires_extra_frame();
} }
#else
std::wstring btn_name = ImGui::SliderFloatEditBtnIcon + boost::nowide::widen(str_label);
if (ImGui::Button(into_u8(btn_name).c_str())) {
ImGui::SetKeyboardFocusHere(-1);
this->set_requires_extra_frame();
}
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
ImGui::PopStyleColor(3); ImGui::PopStyleColor(3);
@ -688,14 +674,10 @@ bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size,
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags) bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags)
{ {
// this is to force the label to the left of the widget: // this is to force the label to the left of the widget:
#if ENABLE_PREVIEW_LAYOUT
if (!label.empty()) { if (!label.empty()) {
#endif // ENABLE_PREVIEW_LAYOUT
text(label); text(label);
ImGui::SameLine(); ImGui::SameLine();
#if ENABLE_PREVIEW_LAYOUT
} }
#endif // ENABLE_PREVIEW_LAYOUT
int selection_out = selection; int selection_out = selection;
bool res = false; bool res = false;
@ -1145,13 +1127,11 @@ bool ImGuiWrapper::want_any_input() const
return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput; return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
} }
#if ENABLE_LEGEND_TOOLBAR_ICONS
ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id) ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id)
{ {
auto item = m_custom_glyph_rects_ids.find(tex_id); auto item = m_custom_glyph_rects_ids.find(tex_id);
return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr; return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr;
} }
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color) ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color)
{ {
@ -1270,7 +1250,6 @@ void ImGuiWrapper::init_font(bool compress)
int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next
// add rectangles for the icons to the font atlas // add rectangles for the icons to the font atlas
#if ENABLE_LEGEND_TOOLBAR_ICONS
for (auto& icon : font_icons) { for (auto& icon : font_icons) {
m_custom_glyph_rects_ids[icon.first] = m_custom_glyph_rects_ids[icon.first] =
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz);
@ -1282,15 +1261,7 @@ void ImGuiWrapper::init_font(bool compress)
for (auto& icon : font_icons_extra_large) { for (auto& icon : font_icons_extra_large) {
m_custom_glyph_rects_ids[icon.first] = m_custom_glyph_rects_ids[icon.first] =
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4); io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4);
} }
#else
for (auto& icon : font_icons)
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz);
for (auto& icon : font_icons_large)
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2);
for (auto& icon : font_icons_extra_large)
io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
// Build texture atlas // Build texture atlas
unsigned char* pixels; unsigned char* pixels;

View File

@ -21,9 +21,7 @@ class wxString;
class wxMouseEvent; class wxMouseEvent;
class wxKeyEvent; class wxKeyEvent;
#if ENABLE_PREVIEW_LAYOUT
struct IMGUI_API ImGuiWindow; struct IMGUI_API ImGuiWindow;
#endif // ENABLE_PREVIEW_LAYOUT
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
@ -40,9 +38,7 @@ class ImGuiWrapper
bool m_disabled{ false }; bool m_disabled{ false };
bool m_new_frame_open{ false }; bool m_new_frame_open{ false };
bool m_requires_extra_frame{ false }; bool m_requires_extra_frame{ false };
#if ENABLE_LEGEND_TOOLBAR_ICONS
std::map<wchar_t, int> m_custom_glyph_rects_ids; std::map<wchar_t, int> m_custom_glyph_rects_ids;
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
std::string m_clipboard_text; std::string m_clipboard_text;
public: public:
@ -92,9 +88,7 @@ public:
bool button(const wxString &label); bool button(const wxString &label);
bool button(const wxString& label, float width, float height); bool button(const wxString& label, float width, float height);
bool radio_button(const wxString &label, bool active); bool radio_button(const wxString &label, bool active);
#if ENABLE_PREVIEW_LAYOUT
bool draw_radio_button(const std::string& name, float size, bool active, std::function<void(ImGuiWindow& window, const ImVec2& pos, float size)> draw_callback); bool draw_radio_button(const std::string& name, float size, bool active, std::function<void(ImGuiWindow& window, const ImVec2& pos, float size)> draw_callback);
#endif // ENABLE_PREVIEW_LAYOUT
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
@ -143,9 +137,7 @@ public:
static ColorRGBA from_ImU32(const ImU32& color); static ColorRGBA from_ImU32(const ImU32& color);
static ColorRGBA from_ImVec4(const ImVec4& color); static ColorRGBA from_ImVec4(const ImVec4& color);
#if ENABLE_LEGEND_TOOLBAR_ICONS
ImFontAtlasCustomRect* GetTextureCustomRect(const wchar_t& tex_id); ImFontAtlasCustomRect* GetTextureCustomRect(const wchar_t& tex_id);
#endif // ENABLE_LEGEND_TOOLBAR_ICONS
static const ImVec4 COL_GREY_DARK; static const ImVec4 COL_GREY_DARK;
static const ImVec4 COL_GREY_LIGHT; static const ImVec4 COL_GREY_LIGHT;

View File

@ -1494,11 +1494,9 @@ void MainFrame::init_menubar_as_editor()
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Labels") + sep + "E", _L("Show object/instance labels in 3D scene"), append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Labels") + sep + "E", _L("Show object/instance labels in 3D scene"),
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this, [this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this); [this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
#if ENABLE_PREVIEW_LAYOUT
append_menu_check_item(viewMenu, wxID_ANY, _L("Show Legen&d") + sep + "L", _L("Show legend in preview"), append_menu_check_item(viewMenu, wxID_ANY, _L("Show Legen&d") + sep + "L", _L("Show legend in preview"),
[this](wxCommandEvent&) { m_plater->show_legend(!m_plater->is_legend_shown()); }, this, [this](wxCommandEvent&) { m_plater->show_legend(!m_plater->is_legend_shown()); }, this,
[this]() { return m_plater->is_preview_shown(); }, [this]() { return m_plater->is_legend_shown(); }, this); [this]() { return m_plater->is_preview_shown(); }, [this]() { return m_plater->is_legend_shown(); }, this);
#endif // ENABLE_PREVIEW_LAYOUT
append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse Sidebar") + sep + "Shift+" + sep_space + "Tab", _L("Collapse sidebar"), append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse Sidebar") + sep + "Shift+" + sep_space + "Tab", _L("Collapse sidebar"),
[this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this, [this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this,
[]() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this); []() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this);
@ -1616,12 +1614,10 @@ void MainFrame::init_menubar_as_gcodeviewer()
if (m_plater != nullptr) { if (m_plater != nullptr) {
viewMenu = new wxMenu(); viewMenu = new wxMenu();
add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this)); add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this));
#if ENABLE_PREVIEW_LAYOUT
viewMenu->AppendSeparator(); viewMenu->AppendSeparator();
append_menu_check_item(viewMenu, wxID_ANY, _L("Show legen&d") + sep + "L", _L("Show legend"), append_menu_check_item(viewMenu, wxID_ANY, _L("Show legen&d") + sep + "L", _L("Show legend"),
[this](wxCommandEvent&) { m_plater->show_legend(!m_plater->is_legend_shown()); }, this, [this](wxCommandEvent&) { m_plater->show_legend(!m_plater->is_legend_shown()); }, this,
[this]() { return m_plater->is_preview_shown(); }, [this]() { return m_plater->is_legend_shown(); }, this); [this]() { return m_plater->is_preview_shown(); }, [this]() { return m_plater->is_legend_shown(); }, this);
#endif // ENABLE_PREVIEW_LAYOUT
} }
// helpmenu // helpmenu

View File

@ -1768,10 +1768,8 @@ struct Plater::priv
bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); } bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); }
void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); } void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); }
#if ENABLE_PREVIEW_LAYOUT
bool is_legend_shown() const { return (current_panel == preview) && preview->get_canvas3d()->is_legend_shown(); } bool is_legend_shown() const { return (current_panel == preview) && preview->get_canvas3d()->is_legend_shown(); }
void show_legend(bool show) { if (current_panel == preview) preview->get_canvas3d()->show_legend(show); } void show_legend(bool show) { if (current_panel == preview) preview->get_canvas3d()->show_legend(show); }
#endif // ENABLE_PREVIEW_LAYOUT
bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); } bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); }
void collapse_sidebar(bool collapse); void collapse_sidebar(bool collapse);
@ -1786,9 +1784,6 @@ struct Plater::priv
bool init_view_toolbar(); bool init_view_toolbar();
bool init_collapse_toolbar(); bool init_collapse_toolbar();
#if !ENABLE_PREVIEW_LAYOUT
void update_preview_bottom_toolbar();
#endif // !ENABLE_PREVIEW_LAYOUT
void update_preview_moves_slider(); void update_preview_moves_slider();
void enable_preview_moves_slider(bool enable); void enable_preview_moves_slider(bool enable);
@ -4762,13 +4757,6 @@ bool Plater::priv::init_collapse_toolbar()
return true; return true;
} }
#if !ENABLE_PREVIEW_LAYOUT
void Plater::priv::update_preview_bottom_toolbar()
{
preview->update_bottom_toolbar();
}
#endif // !ENABLE_PREVIEW_LAYOUT
void Plater::priv::update_preview_moves_slider() void Plater::priv::update_preview_moves_slider()
{ {
preview->update_moves_slider(); preview->update_moves_slider();
@ -5747,10 +5735,8 @@ bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); }
bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); } bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); }
void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); } void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); }
#if ENABLE_PREVIEW_LAYOUT
bool Plater::is_legend_shown() const { return p->is_legend_shown(); } bool Plater::is_legend_shown() const { return p->is_legend_shown(); }
void Plater::show_legend(bool show) { p->show_legend(show); } void Plater::show_legend(bool show) { p->show_legend(show); }
#endif // ENABLE_PREVIEW_LAYOUT
bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); } bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); }
void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); } void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); }
@ -7117,13 +7103,6 @@ GLToolbar& Plater::get_collapse_toolbar()
return p->collapse_toolbar; return p->collapse_toolbar;
} }
#if !ENABLE_PREVIEW_LAYOUT
void Plater::update_preview_bottom_toolbar()
{
p->update_preview_bottom_toolbar();
}
#endif // !ENABLE_PREVIEW_LAYOUT
void Plater::update_preview_moves_slider() void Plater::update_preview_moves_slider()
{ {
p->update_preview_moves_slider(); p->update_preview_moves_slider();
@ -7232,12 +7211,10 @@ bool Plater::is_render_statistic_dialog_visible() const
return p->show_render_statistic_dialog; return p->show_render_statistic_dialog;
} }
#if ENABLE_PREVIEW_LAYOUT
void Plater::set_keep_current_preview_type(bool value) void Plater::set_keep_current_preview_type(bool value)
{ {
p->preview->set_keep_current_preview_type(value); p->preview->set_keep_current_preview_type(value);
} }
#endif // ENABLE_PREVIEW_LAYOUT
Plater::TakeSnapshot::TakeSnapshot(Plater *plater, const std::string &snapshot_name) Plater::TakeSnapshot::TakeSnapshot(Plater *plater, const std::string &snapshot_name)
: TakeSnapshot(plater, from_u8(snapshot_name)) {} : TakeSnapshot(plater, from_u8(snapshot_name)) {}

View File

@ -225,10 +225,8 @@ public:
bool are_view3D_labels_shown() const; bool are_view3D_labels_shown() const;
void show_view3D_labels(bool show); void show_view3D_labels(bool show);
#if ENABLE_PREVIEW_LAYOUT
bool is_legend_shown() const; bool is_legend_shown() const;
void show_legend(bool show); void show_legend(bool show);
#endif // ENABLE_PREVIEW_LAYOUT
bool is_sidebar_collapsed() const; bool is_sidebar_collapsed() const;
void collapse_sidebar(bool show); void collapse_sidebar(bool show);
@ -390,9 +388,6 @@ public:
const GLToolbar& get_collapse_toolbar() const; const GLToolbar& get_collapse_toolbar() const;
GLToolbar& get_collapse_toolbar(); GLToolbar& get_collapse_toolbar();
#if !ENABLE_PREVIEW_LAYOUT
void update_preview_bottom_toolbar();
#endif // !ENABLE_PREVIEW_LAYOUT
void update_preview_moves_slider(); void update_preview_moves_slider();
void enable_preview_moves_slider(bool enable); void enable_preview_moves_slider(bool enable);
@ -458,9 +453,7 @@ public:
void toggle_render_statistic_dialog(); void toggle_render_statistic_dialog();
bool is_render_statistic_dialog_visible() const; bool is_render_statistic_dialog_visible() const;
#if ENABLE_PREVIEW_LAYOUT
void set_keep_current_preview_type(bool value); void set_keep_current_preview_type(bool value);
#endif // ENABLE_PREVIEW_LAYOUT
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu. // Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition); bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition);

View File

@ -66,7 +66,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
GIVEN("polyline") { GIVEN("polyline") {
Polyline polyline { { 50, 150 }, { 300, 150 } }; Polyline polyline { { 50, 150 }, { 300, 150 } };
WHEN("intersection_pl") { WHEN("intersection_pl") {
Polylines result = Slic3r::intersection_pl({ polyline }, { square, hole_in_square }); Polylines result = Slic3r::intersection_pl(polyline, ExPolygon{ square, hole_in_square });
THEN("correct number of result lines") { THEN("correct number of result lines") {
REQUIRE(result.size() == 2); REQUIRE(result.size() == 2);
} }
@ -99,7 +99,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
{ 74730000, 74730000 }, { 55270000, 74730000 }, { 55270000, 68063296 }, { 44730000, 68063296 }, { 44730000, 74730000 }, { 25270000, 74730000 }, { 25270000, 55270000 }, { 31936670, 55270000 }, { 74730000, 74730000 }, { 55270000, 74730000 }, { 55270000, 68063296 }, { 44730000, 68063296 }, { 44730000, 74730000 }, { 25270000, 74730000 }, { 25270000, 55270000 }, { 31936670, 55270000 },
{ 31936670, 44730000 }, { 25270000, 44730000 }, { 25270000, 25270000 }, { 44730000, 25270000 }, { 44730000, 31936670 } }; { 31936670, 44730000 }, { 25270000, 44730000 }, { 25270000, 25270000 }, { 44730000, 25270000 }, { 44730000, 31936670 } };
Slic3r::Polygon clip { {75200000, 45200000}, {54800000, 45200000}, {54800000, 24800000}, {75200000, 24800000} }; Slic3r::Polygon clip { {75200000, 45200000}, {54800000, 45200000}, {54800000, 24800000}, {75200000, 24800000} };
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip }); Slic3r::Polylines result = Slic3r::intersection_pl(subject, ExPolygon{ clip });
THEN("intersection_pl - result is not empty") { THEN("intersection_pl - result is not empty") {
REQUIRE(result.size() == 1); REQUIRE(result.size() == 1);
} }
@ -117,7 +117,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
GIVEN("Clipper bug #126") { GIVEN("Clipper bug #126") {
Slic3r::Polyline subject { { 200000, 19799999 }, { 200000, 200000 }, { 24304692, 200000 }, { 15102879, 17506106 }, { 13883200, 19799999 }, { 200000, 19799999 } }; Slic3r::Polyline subject { { 200000, 19799999 }, { 200000, 200000 }, { 24304692, 200000 }, { 15102879, 17506106 }, { 13883200, 19799999 }, { 200000, 19799999 } };
Slic3r::Polygon clip { { 15257205, 18493894 }, { 14350057, 20200000 }, { -200000, 20200000 }, { -200000, -200000 }, { 25196917, -200000 } }; Slic3r::Polygon clip { { 15257205, 18493894 }, { 14350057, 20200000 }, { -200000, 20200000 }, { -200000, -200000 }, { 25196917, -200000 } };
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip }); Slic3r::Polylines result = Slic3r::intersection_pl(subject, ExPolygon{ clip });
THEN("intersection_pl - result is not empty") { THEN("intersection_pl - result is not empty") {
REQUIRE(result.size() == 1); REQUIRE(result.size() == 1);
} }

View File

@ -2,6 +2,7 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests add_executable(${_TEST_NAME}_tests
${_TEST_NAME}_tests_main.cpp ${_TEST_NAME}_tests_main.cpp
slic3r_jobs_tests.cpp slic3r_jobs_tests.cpp
slic3r_version_tests.cpp
) )
# mold linker for successful linking needs also to link TBB library and link it before libslic3r. # mold linker for successful linking needs also to link TBB library and link it before libslic3r.

View File

@ -0,0 +1,83 @@
#include "catch2/catch.hpp"
#include "slic3r/Config/Version.hpp"
TEST_CASE("Check parsing and comparing of config versions", "[Version]") {
using namespace Slic3r;
GUI::Config::Version v;
v.config_version = *Semver::parse("1.1.2");
v.min_slic3r_version = *Semver::parse("1.38.0");
v.max_slic3r_version = Semver::inf();
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.38.0")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.37.0-alpha")));
// Test the prerelease status.
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-alpha");
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-alpha1");
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-beta");
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-rc");
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
v.config_version = *Semver::parse("1.1.2-rc2");
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
// Test the upper boundary.
v.config_version = *Semver::parse("1.1.2");
v.max_slic3r_version = *Semver::parse("1.39.3-beta1");
REQUIRE(v.is_slic3r_supported(*Semver::parse("1.38.0")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha1")));
REQUIRE(! v.is_slic3r_supported(*Semver::parse("1.37.0-alpha")));
}