diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp index 254675f3208..d0f2d89acdd 100644 --- a/src/libslic3r/BoundingBox.cpp +++ b/src/libslic3r/BoundingBox.cpp @@ -105,6 +105,59 @@ template void BoundingBoxBase::merge(const BoundingBoxBase &bb); template void BoundingBoxBase::merge(const BoundingBoxBase &bb); template void BoundingBoxBase::merge(const BoundingBoxBase &bb); +template +bool BoundingBoxBase::cross(const Line &line) const +{ + assert(this->defined || this->min.x() >= this->max.x() || this->min.y() >= this->max.y()); + // first just check if one point is inside and the other outside + bool cross = this->contains(line.a) != this->contains(line.b); + // now compre cross for the 4 lines + Point intersect; + if (!cross) + cross = Line(Point(this->min.x(), this->min.y()), Point(this->min.x(), this->max.y())).intersection(line, &intersect); + if (!cross) + cross = Line(Point(this->min.x(), this->min.y()), Point(this->max.x(), this->min.y())).intersection(line, &intersect); + if (!cross) + cross = Line(Point(this->max.x(), this->max.y()), Point(this->min.x(), this->max.y())).intersection(line, &intersect); + if (!cross) + cross = Line(Point(this->max.x(), this->max.y()), Point(this->max.x(), this->min.y())).intersection(line, &intersect); + return cross; +} +template bool BoundingBoxBase::cross(const Line &line) const; + +template +bool BoundingBoxBase::cross(const Polyline &lines) const +{ + assert(this->defined || this->min.x() >= this->max.x() || this->min.y() >= this->max.y()); + // first just check if one point is inside and the other outside + size_t nb_in = 0; + size_t nb_out = 0; + for (const Point &pt : lines.points) + if (this->contains(pt)) + nb_in++; + else + nb_out++; + if (nb_in > 0 && nb_out > 0) + return true; + bool cross = false; + Point intersect; + // now compare cross for the 4 lines + Line l1 = Line(Point(this->min.x(), this->min.y()), Point(this->min.x(), this->max.y())); + Line l2 = Line(Point(this->min.x(), this->min.y()), Point(this->max.x(), this->min.y())); + Line l3 = Line(Point(this->max.x(), this->max.y()), Point(this->min.x(), this->max.y())); + Line l4 = Line(Point(this->max.x(), this->max.y()), Point(this->max.x(), this->min.y())); + for (size_t next_idx = 1; next_idx < lines.size(); ++next_idx) { + Line line(lines.points[next_idx - 1], lines.points[next_idx]); + Vec2f v; + cross = l1.intersection(line, &intersect) || l2.intersection(line, &intersect) || + l3.intersection(line, &intersect) || l4.intersection(line, &intersect); + if (cross) + return true; + } + return false; +} +template bool BoundingBoxBase::cross(const Polyline &lines) const; + template void BoundingBox3Base::merge(const PointClass &point) { diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 821f8ee5ff6..571658c118c 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -61,6 +61,8 @@ class BoundingBoxBase return point(0) >= this->min(0) && point(0) <= this->max(0) && point(1) >= this->min(1) && point(1) <= this->max(1); } + bool cross(const Line &line) const; + bool cross(const Polyline &lines) const; bool contains(const BoundingBoxBase &other) const { return contains(other.min) && contains(other.max); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 8ceac39bcbd..e82af2a5d00 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -764,9 +764,9 @@ namespace DoExport { if (extruder == extruders.end()) continue; - double s = PI * sqr(0.5* extruder->filament_diameter()); + double section = PI * sqr(0.5 * extruder->filament_diameter()); double weight = volume.second * extruder->filament_density() * 0.001; - total_used_filament += volume.second/s; + total_used_filament += volume.second / section; total_weight += weight; total_cost += weight * extruder->filament_cost() * 0.001; } @@ -1308,12 +1308,20 @@ void GCode::_init_multiextruders(const Print& print, std::string& out, GCodeWrit } } +struct LockMonitor +{ + PrintStatistics &m_status_monitor; + LockMonitor(PrintStatistics &status_monitor) : m_status_monitor(status_monitor) { m_status_monitor.is_computing_gcode = true; } + ~LockMonitor(){ m_status_monitor.is_computing_gcode = false; } +}; + void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { PROFILE_FUNC(); const Print &print = print_mod; Print::StatusMonitor status_monitor{print_mod}; + LockMonitor monitor_soft_lock(status_monitor.stats()); this->m_throw_if_canceled = [&print]() { print.throw_if_canceled(); }; @@ -1348,6 +1356,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene status_monitor.stats().color_extruderid_to_used_filament.clear(); status_monitor.stats().color_extruderid_to_used_weight.clear(); + status_monitor.stats().layer_area_stats.clear(); // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. @@ -6047,7 +6056,7 @@ Polyline GCode::travel_to(std::string &gcode, const Point &point, ExtrusionRole if (may_need_avoid_crossing) { // if a retraction would be needed (with a low min_dist threshold), try to use avoid_crossing_perimeters to // plan a multi-hop travel path inside the configuration space - if (this->can_cross_perimeter(travel, can_avoid_cross_peri)) { + if (this->can_cross_perimeter(travel, true)) { this->m_throw_if_canceled(); travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled); } @@ -6057,7 +6066,7 @@ Polyline GCode::travel_to(std::string &gcode, const Point &point, ExtrusionRole bool needs_retraction = this->needs_retraction(travel, role); if (m_config.only_retract_when_crossing_perimeters && !(m_config.enforce_retract_first_layer && m_layer_index == 0)) - needs_retraction = needs_retraction && can_avoid_cross_peri && this->can_cross_perimeter(travel, false); + needs_retraction = needs_retraction && this->can_cross_perimeter(travel, false); // Re-allow avoid_crossing_perimeters for the next travel moves m_avoid_crossing_perimeters.reset_once_modifiers(); @@ -6273,7 +6282,7 @@ bool GCode::can_cross_perimeter(const Polyline& travel, bool offset) // FROM 2.7 if (m_layer_slices_offseted.layer != m_layer) { m_layer_slices_offseted.layer = m_layer; - m_layer_slices_offseted.diameter = scale_t(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0.4)); + m_layer_slices_offseted.diameter = scale_t(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0.4)) / 2; ExPolygons slices = m_layer->lslices; ExPolygons slices_offsetted = offset_ex(m_layer->lslices, -m_layer_slices_offseted.diameter * 1.5f); // remove top surfaces @@ -6299,13 +6308,38 @@ bool GCode::can_cross_perimeter(const Polyline& travel, bool offset) } } } + //{ + // static int aodfjiaqsdz = 0; + // std::stringstream stri; + // stri << this->m_layer->id() << "_avoid_" <<"_"<<(aodfjiaqsdz++) << ".svg"; + // SVG svg(stri.str()); + // svg.draw(m_layer->lslices, "grey"); + // for (auto &entry : offset ? m_layer_slices_offseted.slices_offsetted : m_layer_slices_offseted.slices) { + // bool checked = (travel.size() > 1 && + // (entry.second.contains(travel.front()) || + // entry.second.contains(travel.back()) || + // entry.second.contains(travel.points[travel.size() / 2]) || + // entry.second.cross(travel) ) + // ); + // svg.draw((entry.second.polygon().split_at_first_point()), checked?"green":"orange", scale_t(0.03)); + // int diff_count =0; + // if(checked) + // diff_count = diff_pl(travel, entry.first.contour).size(); + // svg.draw(to_polylines(entry.first), diff_count==0?"blue":diff_count==1?"teal":"yellow", scale_t(0.05)); + // } + // svg.draw(travel, "red", scale_t(0.05)); + // svg.Close(); + //} // test if a expoly contains the entire travel for (const std::pair &expoly_2_bb : offset ? m_layer_slices_offseted.slices_offsetted : m_layer_slices_offseted.slices) { // first check if it's roughtly inside the bb, to reject quickly. - if (travel.size() > 1 && expoly_2_bb.second.contains(travel.front()) && - expoly_2_bb.second.contains(travel.back()) && - expoly_2_bb.second.contains(travel.points[travel.size() / 2])) { + if (travel.size() > 1 && + (expoly_2_bb.second.contains(travel.front()) || + expoly_2_bb.second.contains(travel.back()) || + expoly_2_bb.second.contains(travel.points[travel.size() / 2]) || + expoly_2_bb.second.cross(travel) ) + ) { // first, check if it's inside the contour (still, it can go over holes) Polylines diff_result = diff_pl(travel, expoly_2_bb.first.contour); if (diff_result.size() == 1 && diff_result.front() == travel) @@ -6333,14 +6367,12 @@ bool GCode::can_cross_perimeter(const Polyline& travel, bool offset) return true; } } - //if (has_intersect) - // break; } - // if inside contour and does not inersect hole -> inside expoly, you don't need to avoid. - //if (!has_intersect) - return false; + //note: can be inside multiple contours, so we need to checl all of them } } + // never crossed a perimeter or a hole + return false; } // retract if only_retract_when_crossing_perimeters is disabled or doesn't apply diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 4ac03d5a8fc..62eb2c3dcfe 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -931,9 +931,9 @@ std::string CoolingBuffer::apply_layer_cooldown( fan_speeds[i] = default_fan_speed[i]; } //fan_speeds[0] carry the current default value. ensure it's not negative. - if (initial_default_fan_speed <= 0) { - fan_speeds[0] = 0; - } + //if (initial_default_fan_speed <= 0) { + // fan_speeds[0] = 0; + //} if (layer_time < slowdown_below_layer_time && fan_below_layer_time > 0) { // Layer time very short. Enable the fan to a full throttle. //fan_speed_new = std::max(max_fan_speed, fan_speed_new); @@ -948,7 +948,7 @@ std::string CoolingBuffer::apply_layer_cooldown( for (size_t etype_idx = 0; etype_idx < etype_can_increase_fan.size(); etype_idx++) { uint16_t idx = etype_can_increase_fan[etype_idx]; if (fan_speeds[idx] < max_fan_speed) // if max speed is lower, this will reduce speed, so don't do it. - fan_speeds[idx] = std::clamp(int(t * fan_speeds[idx] + (1. - t) * max_fan_speed + 0.5), 0, 255); + fan_speeds[idx] = std::clamp(int(t * (fan_speeds[idx] < 0 ? 0 : fan_speeds[idx]) + (1. - t) * max_fan_speed + 0.5), 0, 255); } } @@ -961,7 +961,9 @@ std::string CoolingBuffer::apply_layer_cooldown( float factor = float(int(layer_id + 1) - disable_fan_first_layers) / float(full_fan_speed_layer - disable_fan_first_layers); for (size_t etype_idx = 0; etype_idx < etype_can_ramp_up_fan.size(); etype_idx++) { uint16_t idx = etype_can_ramp_up_fan[etype_idx]; - fan_speeds[idx] = std::clamp(int(float(fan_speeds[idx]) * factor + 0.01f), 0, 255); + if (fan_speeds[idx] > 0) { + fan_speeds[idx] = std::clamp(int(float(fan_speeds[idx] < 0 ? 0 : fan_speeds[idx]) * factor + 0.01f), 0, 255); + } } } //only activate fan control if the fan speed is higher than min @@ -1020,6 +1022,7 @@ std::string CoolingBuffer::apply_layer_cooldown( const char *pos = gcode.c_str(); int current_feedrate = 0; int stored_fan_speed = m_fan_speed < 0 ? 0 : m_fan_speed; + int current_fan_speed = -1; change_extruder_set_fan(); for (const CoolingLine *line : lines) { const char *line_start = gcode.c_str() + line->line_start; @@ -1139,19 +1142,27 @@ std::string CoolingBuffer::apply_layer_cooldown( bool fan_set = false; for (size_t i = extrude_tree.size() - 1; i < extrude_tree.size(); --i) { if (fan_control[extrude_tree[i]]) { - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, - fan_speeds[extrude_tree[i]], - EXTRUDER_CONFIG(extruder_fan_offset), m_config.fan_percentage, - std::string("set fan for ") + ExtrusionEntity::role_to_string(extrude_tree[i])); + if (current_fan_speed != fan_speeds[extrude_tree[i]]) { + current_fan_speed = fan_speeds[extrude_tree[i]]; + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, + fan_speeds[extrude_tree[i]], + EXTRUDER_CONFIG(extruder_fan_offset), + m_config.fan_percentage, + std::string("set fan for ") + ExtrusionEntity::role_to_string(extrude_tree[i])); + } fan_set = true; break; } } - if (!fan_set) { - // return to default - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed < 0 ? 0 : m_fan_speed, - EXTRUDER_CONFIG(extruder_fan_offset), m_config.fan_percentage, - "set default fan"); + if (!fan_set && m_fan_speed >= 0 ) { + if (current_fan_speed != m_fan_speed && (default_fan_speed[0] >= 0 || current_fan_speed > 0)) { + current_fan_speed = m_fan_speed; + // return to default + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed, + EXTRUDER_CONFIG(extruder_fan_offset), m_config.fan_percentage, + "set default fan"); + } + fan_set = true; } fan_need_set = false; } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 521e687e704..ffe5b5db515 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -777,6 +777,7 @@ void GCodeProcessorResult::reset() { #endif // ENABLE_SPIRAL_VASE_LAYERS time = 0; computed_timestamp = std::time(0); + print_statistics.reset(); } #else void GCodeProcessorResult::reset() { @@ -795,6 +796,7 @@ void GCodeProcessorResult::reset() { spiral_vase_layers = std::vector>>(); #endif // ENABLE_SPIRAL_VASE_LAYERS computed_timestamp = std::time(0); + print_statistics.reset(); } #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index a5b60fd891d..25d97f8d4df 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -49,7 +49,6 @@ namespace Slic3r { std::vector> moves_times; std::vector> roles_times; std::vector layers_times; - std::vector layers_areas; void reset() { time = 0.0f; @@ -69,7 +68,7 @@ namespace Slic3r { PrintEstimatedStatistics() { reset(); } void reset() { - for (auto m : modes) { + for (Mode &m : modes) { m.reset(); } volumes_per_color_change.clear(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index d446bba0fd6..7e5812954f6 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -487,7 +488,7 @@ struct PrintStatistics std::string estimated_normal_print_time; std::string estimated_silent_print_time; double total_used_filament; - std::vector> color_extruderid_to_used_filament; + std::vector> color_extruderid_to_used_filament; // id -> mm (length) double total_extruded_volume; double total_cost; int total_toolchanges; @@ -499,9 +500,11 @@ struct PrintStatistics unsigned int initial_extruder_id; std::string initial_filament_type; std::string printing_filament_types; - std::map filament_stats; + std::map filament_stats; // extruder id -> volume in mm3 std::vector> layer_area_stats; // print_z to area + std::atomic_bool is_computing_gcode; + // Config with the filled in print statistics. DynamicConfig config() const; // Config with the statistics keys populated with placeholder strings. @@ -522,6 +525,7 @@ struct PrintStatistics printing_filament_types.clear(); filament_stats.clear(); printing_extruders.clear(); + is_computing_gcode = false; } }; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 45dc9ef5bf3..5f0dfd14a66 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5033,7 +5033,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm²"); def->min = 0; def->mode = comExpert | comPrusa; - def->set_default_value(new ConfigOptionFloat(70)); + def->set_default_value(new ConfigOptionFloat(4)); def = this->add("solid_infill_below_layer_area", coFloat); def->label = L("Solid infill layer threshold area"); @@ -8420,6 +8420,7 @@ std::unordered_set prusa_export_to_remove_keys = { "start_gcode_manual", "solid_infill_below_layer_area", "solid_infill_below_thickness", +"solid_infill_below_width", "support_material_angle_height", "support_material_acceleration", "support_material_contact_distance_type", diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 35409771824..4182f5a09cd 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -241,12 +241,9 @@ void Control::SetLowerValue(const int lower_tick) m_selection = ssLower; m_lower_tick = lower_tick; correct_lower_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); + + m_need_refresh_and_update = true; + m_need_fire_scroll_change = true; } void Control::SetHigherValue(const int higher_tick) @@ -254,12 +251,9 @@ void Control::SetHigherValue(const int higher_tick) m_selection = ssHigher; m_higher_tick = higher_tick; correct_higher_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); + + m_need_refresh_and_update = true; + m_need_fire_scroll_change = true; } void Control::SetSelectionSpan(const int lower_tick, const int higher_tick) @@ -268,20 +262,14 @@ void Control::SetSelectionSpan(const int lower_tick, const int higher_tick) m_higher_tick = std::max(std::min(higher_tick, m_max_tick), m_lower_tick); if (m_lower_tick < m_higher_tick) m_is_one_layer = false; - - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); + + m_need_refresh_and_update = true; + m_need_fire_scroll_change = true; } void Control::SetMaxValue(const int max_tick) { m_max_tick = max_tick; - Refresh(); - Update(); } void Control::SetSliderValues(const std::vector& values) @@ -359,9 +347,9 @@ double Control::get_double_value(const SelectedSlider& selection) return m_values[selection == ssLower ? m_lower_tick : m_higher_tick]; } -int Control::get_tick_from_value(double value, bool force_lower_bound/* = false*/) +int Control::get_tick_from_value(double value, bool force_lower_bound/* = false*/) const { - std::vector::iterator it; + std::vector::const_iterator it; if (m_is_wipe_tower && !force_lower_bound) it = std::find_if(m_values.begin(), m_values.end(), [value](const double & val) { return fabs(value - val) <= epsilon(); }); @@ -419,13 +407,12 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z) if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty()) m_ticks.mode = custom_gcode_per_print_z.mode; - - Refresh(); - Update(); + + m_need_refresh_and_update = true; } void Control::SetLayersTimes(const std::vector& layers_times, float total_time) -{ +{ m_layers_times.clear(); if (layers_times.empty()) return; @@ -447,8 +434,7 @@ void Control::SetLayersTimes(const std::vector& layers_times, float total if (m_layers_values.size() != m_layers_times.size()) for (size_t i = m_layers_times.size(); i < m_layers_values.size(); i++) m_layers_times.push_back(total_time); - Refresh(); - Update(); + m_need_refresh_and_update = true; } } @@ -466,14 +452,27 @@ void Control::SetLayersAreas(const std::vector& layers_areas) m_layers_areas.reserve(layers_areas.size()); for(float area : layers_areas) m_layers_areas.push_back(area); - if (m_layers_values.size() == m_layers_areas.size() + 1) - m_layers_areas.insert(m_layers_areas.begin(), 0.); - if (m_is_wipe_tower && m_values.size() != m_layers_areas.size()) { - // When whipe tower is used to the end of print, there is one layer which is not marked in layers_times - // So, add this value from the total print time value - for (size_t i = m_layers_areas.size(); i < m_layers_values.size(); i++) - m_layers_areas.push_back(0.); +} +bool Control::ensure_correctly_filled() const +{ + bool ok = true; + if (!m_layers_times.empty() && m_layers_times.size() != m_values.size() && m_layers_times.size() != m_values.size() - 1) { + ok = false; + assert(false); + //m_layers_times.clear(); } + if (!m_layers_areas.empty() && m_layers_areas.size() != m_values.size() && m_layers_areas.size() != m_values.size() - 1) { + ok = false; + assert(false); + //m_layers_areas.clear(); + } + if (!m_layers_values.empty()) { + ok = m_is_wipe_tower; + assert(m_is_wipe_tower); + //m_is_wipe_tower = false; + } + + return ok; } void Control::SetDrawMode(bool is_sla_print, bool is_sequential_print) @@ -545,6 +544,7 @@ void Control::draw_focus_rect() void Control::render() { + std::lock_guard lock(m_lock_data); #ifdef _WIN32 GUI::wxGetApp().UpdateDarkUI(this); #else @@ -765,12 +765,33 @@ static wxString short_and_splitted_time(const std::string& time) wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer*/) const { + if (!ensure_correctly_filled()) + return ""; const size_t value = tick; if (m_label_koef == 1.0 && m_values.empty()) return wxString::Format("%lu", static_cast(value)); if (value >= m_values.size()) return "ErrVal"; + if (m_draw_mode == dmSequentialGCodeView) + return wxString::Format("%lu", static_cast(m_alternate_values[value])); + + wxString str = m_values.empty() ? + wxString::Format("%.*f", 2, m_label_koef * value) : + wxString::Format("%.*f", 2, m_values[value]); + if (label_type == ltHeight) + return str; + + // get the layer num (gcode can have 0, but not preview) + bool is_preview_not_gcode = m_layers_times.size() == m_values.size(); + if (m_is_wipe_tower) { + is_preview_not_gcode = (m_layers_values.size() != m_values.size()); + } else { + //assert(m_layers_times.size() == m_values.size() - 1 || m_layers_times.size() == m_values.size() || m_layers_times.empty()); + //assert(m_layers_values.empty()); + } + const size_t layer_number = is_preview_not_gcode ? value + 1 : value; + const size_t time_idx = is_preview_not_gcode ? value : value - 1; // When "Print Settings -> Multiple Extruders -> No sparse layer" is enabled, then "Smart" Wipe Tower is used for wiping. // As a result, each layer with tool changes is splited for min 3 parts: first tool, wiping, second tool ... @@ -779,37 +800,27 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer // m_values contains data for all layer's parts, // but m_layers_values contains just unique Z values. // Use this function for correct conversion slider position to number of printed layer - auto get_layer_number = [this](int value, LabelType label_type) { - if (label_type == ltEstimatedTime && m_layers_times.empty()) - return size_t(-1); - double layer_print_z = m_values[is_wipe_tower_layer(value) ? std::max(value - 1, 0) : value]; - auto it = std::lower_bound(m_layers_values.begin(), m_layers_values.end(), layer_print_z - epsilon()); - if (it == m_layers_values.end()) { - it = std::lower_bound(m_values.begin(), m_values.end(), layer_print_z - epsilon()); - if (it == m_values.end()) - return size_t(-1); - return size_t(value); - } - return size_t(it - m_layers_values.begin()); - }; + //auto get_layer_number = [this](int value, LabelType label_type) -> size_t { + // if (label_type == ltEstimatedTime && m_layers_times.empty()) + // return size_t(-1); + // assert((is_wipe_tower_layer(value) ? std::max(value - 1, 0) : value) < m_values.size()); + // double layer_print_z = m_values[is_wipe_tower_layer(value) ? std::max(value - 1, 0) : value]; + // auto it = std::lower_bound(m_layers_values.begin(), m_layers_values.end(), layer_print_z - epsilon()); + // if (it == m_layers_values.end()) { + // it = std::lower_bound(m_values.begin(), m_values.end(), layer_print_z - epsilon()); + // if (it == m_values.end()) + // return size_t(-1); + // return size_t(value); + // } + // size_t res = size_t(it - m_layers_values.begin()); + // return res; + //}; - if (m_draw_mode == dmSequentialGCodeView) - return wxString::Format("%lu", static_cast(m_alternate_values[value])); - else { + { if (label_type == ltEstimatedTime) { - if (m_is_wipe_tower) { - size_t layer_number = get_layer_number(value, label_type); - return (layer_number == size_t(-1) || layer_number == m_layers_times.size()) ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number])); - } - return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : ""; + return time_idx < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[time_idx])) : ""; } - wxString str = m_values.empty() ? - wxString::Format("%.*f", 2, m_label_koef * value) : - wxString::Format("%.*f", 2, m_values[value]); - if (label_type == ltHeight) - return str; if (label_type == ltHeightWithLayer) { - size_t layer_number = m_is_wipe_tower ? get_layer_number(value, label_type) /**/ + 1 : (m_values.empty() ? value : value /* + 1 */ ); bool show_lheight = GUI::wxGetApp().app_config->get("show_layer_height_doubleslider") == "1"; bool show_ltime = GUI::wxGetApp().app_config->get("show_layer_time_doubleslider") == "1"; bool show_larea = GUI::wxGetApp().app_config->get("show_layer_area_doubleslider") == "1"; @@ -818,46 +829,42 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer if (show_lheight) { nb_lines++; double layer_height = 0; - if (layer_number >= m_values.size()) { - assert(layer_number == m_values.size()); - layer_height = m_values.empty() ? m_label_koef : m_values[m_values.size() - 1] - (m_values.size() > 1 ? m_values[m_values.size() - 2] : 0); - } else if (layer_number == 0) { - layer_height = m_values.empty() ? m_label_koef : m_values[layer_number]; - }else { - layer_height = m_values.empty() ? m_label_koef : m_values[layer_number] - (layer_number > 1 ? m_values[layer_number - 1] : 0); + if (value >= m_values.size()) { + const auto st1 = value; + const auto st2 = m_values.size(); + layer_height = m_values.empty() ? m_label_koef : m_values.back() - (m_values.size() > 1 ? m_values[m_values.size() - 2] : 0); + assert(value == m_values.size()); + } else if (value == 0) { + layer_height = m_values.empty() ? m_label_koef : m_values[value]; + } else { + layer_height = m_values.empty() ? m_label_koef : m_values[value] - (value > 1 ? m_values[value - 1] : 0); } str = str + comma + wxString::Format("%.*f", 2, layer_height); comma = "\n"; } if (show_ltime && !m_layers_times.empty()) { - if (m_layers_times.size() +1 >= m_values.size()) { - size_t layer_idx_time = layer_number; - if (m_values.size() > m_layers_times.size()) { - layer_idx_time--; - } - if (layer_idx_time < m_layers_times.size()) { + if (m_layers_times.size() + 1 >= m_values.size()) { + if (time_idx < m_layers_times.size()) { nb_lines++; - double previous_time = (layer_idx_time > 0 ? m_layers_times[layer_idx_time - 1] : 0); - wxString layer_time_wstr = short_and_splitted_time(get_time_dhms(m_layers_times[layer_idx_time] - previous_time)); + double previous_time = (time_idx > 0 ? m_layers_times[time_idx - 1] : 0); + wxString layer_time_wstr = short_and_splitted_time(get_time_dhms(m_layers_times[time_idx] - previous_time)); str = str + comma + layer_time_wstr; comma = "\n"; } } } if (show_larea && !m_layers_areas.empty()) { - if (m_layers_areas.size() +1 >= m_values.size()) { - size_t layer_idx_time = layer_number; - if (m_values.size() > m_layers_areas.size()) { - layer_idx_time--; - } - if (layer_idx_time < m_layers_areas.size()) { + if (m_layers_areas.size() + 1 >= m_values.size()) { + //assert(time_idx < m_layers_areas.size()); + assert(m_layers_areas.size() == m_layers_times.size() || m_layers_areas.size() == m_layers_times.size() - 1); + if (time_idx < m_layers_areas.size()) { nb_lines++; - str = str + comma + wxString::Format("%.*f", m_layers_areas[layer_idx_time] < 1 ? 3 : m_layers_areas[layer_idx_time] < 10 ? 2 : m_layers_areas[layer_idx_time] < 100 ? 1 : 0, m_layers_areas[layer_idx_time]); + str = str + comma + wxString::Format("%.*f", m_layers_areas[time_idx] < 1 ? 3 : m_layers_areas[time_idx] < 10 ? 2 : m_layers_areas[time_idx] < 100 ? 1 : 0, m_layers_areas[time_idx]); comma = "\n"; } } } - int nb_step_down = layer_number - m_values.size() + nb_lines - 1; + int nb_step_down = value - m_values.size() + nb_lines - 1; while (nb_step_down > 0) { str = "\n" + str; nb_step_down--; @@ -1429,44 +1436,43 @@ void Control::ChangeOneLayerLock() m_selection == ssLower ? correct_lower_value() : correct_higher_value(); if (!m_selection) m_selection = ssHigher; - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); + m_need_refresh_and_update = true; + m_need_fire_scroll_change = true; } void Control::OnLeftDown(wxMouseEvent& event) { - if (HasCapture()) - return; - this->CaptureMouse(); + { + std::lock_guard lock(m_lock_data); + if (HasCapture()) + return; + this->CaptureMouse(); - m_mouse = maNone; + m_mouse = maNone; - wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); + wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); - if (is_point_in_rect(pos, m_rect_one_layer_icon)) - m_mouse = maOneLayerIconClick; - else if (is_point_in_rect(pos, m_rect_cog_icon)) - m_mouse = maCogIconClick; - else if (m_draw_mode == dmRegular) { - if (is_point_in_rect(pos, m_rect_tick_action)) { - auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_tick : m_higher_tick }); - m_mouse = it == m_ticks.ticks.end() ? maAddTick : maDeleteTick; + if (is_point_in_rect(pos, m_rect_one_layer_icon)) + m_mouse = maOneLayerIconClick; + else if (is_point_in_rect(pos, m_rect_cog_icon)) + m_mouse = maCogIconClick; + else if (m_draw_mode == dmRegular) { + if (is_point_in_rect(pos, m_rect_tick_action)) { + auto it = m_ticks.ticks.find(TickCode{m_selection == ssLower ? m_lower_tick : m_higher_tick}); + m_mouse = it == m_ticks.ticks.end() ? maAddTick : maDeleteTick; + } else if (is_point_in_rect(pos, m_rect_revert_icon)) + m_mouse = maRevertIconClick; } - else if (is_point_in_rect(pos, m_rect_revert_icon)) - m_mouse = maRevertIconClick; - } - // move only if not in not in motion and not in dead zone (in vertical slicer) - if(is_horizontal() || (m_mouse == maNone && pos.y < m_rect_one_layer_icon.y - m_rect_one_layer_icon.height/2)) - m_is_left_down = true; + // move only if not in not in motion and not in dead zone (in vertical slicer) + if (is_horizontal() || + (m_mouse == maNone && pos.y < m_rect_one_layer_icon.y - m_rect_one_layer_icon.height / 2)) + m_is_left_down = true; - if (m_mouse == maNone) + if (m_mouse == maNone) detect_selected_slider(pos); - + } + this->fire_update_if_needed(); event.Skip(); } @@ -1594,9 +1600,10 @@ wxString Control::get_tooltip(int tick/*=-1*/) format_wxstr(_L("Extruder (tool) is changed to Extruder \"%1%\""), tick_code_it->extruder) : from_u8(format_gcode(tick_code_it->extra));// tick_code_it->type == Custom + assert(tick < m_values.size() && !m_values.empty()); // If tick is marked as a conflict (exclamation icon), // we should to explain why - ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_only_extruder, m_values[tick]); + ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_only_extruder, tick < m_values.size() ? m_values[tick] : m_values.back()); if (conflict != ctNone) tooltip += "\n\n" + _L("Note") + "! "; if (conflict == ctModeConflict) @@ -1640,70 +1647,82 @@ int Control::get_edited_tick_for_position(const wxPoint pos, Type type /*= Color return -1; } -void Control::OnMotion(wxMouseEvent& event) +void Control::OnMotion(wxMouseEvent &event) { - bool action = false; - - const wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); - int tick = -1; - - if (!m_is_left_down && !m_is_right_down) { - if (is_point_in_rect(pos, m_rect_one_layer_icon)) - m_focus = fiOneLayerIcon; - else if (is_point_in_rect(pos, m_rect_tick_action)) { - m_focus = fiActionIcon; - tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; - } - else if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) - m_focus = fiRevertIcon; - else if (is_point_in_rect(pos, m_rect_cog_icon)) - m_focus = fiCogIcon; - else if (m_mode == SingleExtruder && is_point_in_rect(pos, get_colored_band_rect()) && - get_edited_tick_for_position(pos) >= 0 ) - m_focus = fiColorBand; - else if (is_point_in_rect(pos, m_rect_lower_thumb)) - m_focus = fiLowerThumb; - else if (is_point_in_rect(pos, m_rect_higher_thumb)) - m_focus = fiHigherThumb; - else { - tick = get_tick_near_point(pos); - if (tick < 0 && m_is_wipe_tower) { - tick = get_tick_from_position(pos); - m_focus = tick > 0 && is_wipe_tower_layer(tick) && (tick == m_lower_tick || tick == m_higher_tick) ? - fiSmartWipeTower : fiTick; - } - else - m_focus = fiTick; - } - m_moving_pos = pos; - } - else if (m_is_left_down || m_is_right_down) { - if (m_selection == ssLower) { - int current_value = m_lower_tick; - m_lower_tick = get_tick_from_position(pos.x, pos.y); - correct_lower_value(); - action = (current_value != m_lower_tick); + bool action = false; + { + std::lock_guard lock(m_lock_data); + + const wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); + int tick = -1; + + if (!m_is_left_down && !m_is_right_down) { + if (is_point_in_rect(pos, m_rect_one_layer_icon)) + m_focus = fiOneLayerIcon; + else if (is_point_in_rect(pos, m_rect_tick_action)) { + m_focus = fiActionIcon; + tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; + } else if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) + m_focus = fiRevertIcon; + else if (is_point_in_rect(pos, m_rect_cog_icon)) + m_focus = fiCogIcon; + else if (m_mode == SingleExtruder && is_point_in_rect(pos, get_colored_band_rect()) && + get_edited_tick_for_position(pos) >= 0) + m_focus = fiColorBand; + else if (is_point_in_rect(pos, m_rect_lower_thumb)) + m_focus = fiLowerThumb; + else if (is_point_in_rect(pos, m_rect_higher_thumb)) + m_focus = fiHigherThumb; + else { + tick = get_tick_near_point(pos); + if (tick < 0 && m_is_wipe_tower) { + tick = get_tick_from_position(pos); + m_focus = tick > 0 && is_wipe_tower_layer(tick) && + (tick == m_lower_tick || tick == m_higher_tick) ? + fiSmartWipeTower : + fiTick; + } else + m_focus = fiTick; + } + m_moving_pos = pos; + } else if (m_is_left_down || m_is_right_down) { + if (m_selection == ssLower) { + int current_value = m_lower_tick; + m_lower_tick = get_tick_from_position(pos.x, pos.y); + correct_lower_value(); + action = (current_value != m_lower_tick); + } else if (m_selection == ssHigher) { + int current_value = m_higher_tick; + m_higher_tick = get_tick_from_position(pos.x, pos.y); + correct_higher_value(); + action = (current_value != m_higher_tick); + } + m_moving_pos = wxDefaultPosition; } - else if (m_selection == ssHigher) { - int current_value = m_higher_tick; - m_higher_tick = get_tick_from_position(pos.x, pos.y); - correct_higher_value(); - action = (current_value != m_higher_tick); + + // Set tooltips with information for each icon + if (GUI::wxGetApp().is_editor()) { + this->SetToolTip(get_tooltip(tick)); } - m_moving_pos = wxDefaultPosition; } + m_need_refresh_and_update = false; Refresh(); Update(); event.Skip(); - - // Set tooltips with information for each icon - if (GUI::wxGetApp().is_editor()) - this->SetToolTip(get_tooltip(tick)); - - if (action) { + if (action || m_need_fire_scroll_change) { + m_need_fire_scroll_change = false; + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + if (action) + e.SetString("moving"); + ProcessWindowEvent(e); + } + //need double event or the sphere won't be shown in color (while moving the thing by mouse in the lower direction). + //FIXME better than that. + if (action || m_need_fire_scroll_change) { + m_need_fire_scroll_change = false; wxCommandEvent e(wxEVT_SCROLL_CHANGED); e.SetEventObject(this); - e.SetString("moving"); ProcessWindowEvent(e); } } @@ -1744,7 +1763,8 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) { int tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; - std::set used_extruders_for_tick = m_ticks.get_used_extruders_for_tick(tick, m_only_extruder, m_values[tick]); + assert(tick < m_values.size() && !m_values.empty()); + std::set used_extruders_for_tick = m_ticks.get_used_extruders_for_tick(tick, m_only_extruder, tick < m_values.size() ? m_values[tick] : m_values.back()); wxMenu* add_color_change_menu = new wxMenu(); @@ -1769,40 +1789,35 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren void Control::OnLeftUp(wxMouseEvent& event) { - if (!HasCapture()) - return; + { + std::lock_guard lock(m_lock_data); + if (!HasCapture()) + return; + + } this->ReleaseMouse(); + //there are some event fired inside, so can't be in the lock section switch (m_mouse) { - case maNone : - if(m_is_left_down) + case maNone: + if (m_is_left_down) move_current_thumb_to_pos(event.GetLogicalPosition(wxClientDC(this))); break; - case maDeleteTick : - delete_current_tick(); - break; - case maAddTick : - add_current_tick(); - break; - case maCogIconClick : - show_cog_icon_context_menu(); - break; - case maOneLayerIconClick: - switch_one_layer_mode(); - break; - case maRevertIconClick: - discard_all_thicks(); - break; - default : - break; + case maDeleteTick: delete_current_tick(); break; + case maAddTick: add_current_tick(); break; + case maCogIconClick: show_cog_icon_context_menu(); break; + case maOneLayerIconClick: switch_one_layer_mode(); break; + case maRevertIconClick: discard_all_thicks(); break; + default: break; } m_is_left_down = false; - + m_need_refresh_and_update = false; Refresh(); Update(); event.Skip(); - + + m_need_fire_scroll_change = false; wxCommandEvent e(wxEVT_SCROLL_CHANGED); e.SetEventObject(this); ProcessWindowEvent(e); @@ -1811,8 +1826,8 @@ void Control::OnLeftUp(wxMouseEvent& event) void Control::enter_window(wxMouseEvent& event, const bool enter) { m_is_focused = enter; - Refresh(); - Update(); + m_need_refresh_and_update = true; + this->fire_update_if_needed(); event.Skip(); } @@ -1846,154 +1861,163 @@ void Control::move_current_thumb(const bool condition) m_higher_tick -= delta; correct_higher_value(); } - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); + + m_need_refresh_and_update = true; + m_need_fire_scroll_change = true; } void Control::OnWheel(wxMouseEvent& event) { - // Set nearest to the mouse thumb as a selected, if there is not selected thumb - if (m_selection == ssUndef) { - const wxPoint& pt = event.GetLogicalPosition(wxClientDC(this)); - - if (is_horizontal()) - m_selection = abs(pt.x - m_rect_lower_thumb.GetRight()) <= - abs(pt.x - m_rect_higher_thumb.GetLeft()) ? - ssLower : ssHigher; - else - m_selection = abs(pt.y - m_rect_lower_thumb.GetTop()) <= - abs(pt.y - m_rect_higher_thumb.GetBottom()) ? - ssLower : ssHigher; - } + { + std::lock_guard lock(m_lock_data); + // Set nearest to the mouse thumb as a selected, if there is not selected thumb + if (m_selection == ssUndef) { + const wxPoint &pt = event.GetLogicalPosition(wxClientDC(this)); + + if (is_horizontal()) + m_selection = abs(pt.x - m_rect_lower_thumb.GetRight()) <= abs(pt.x - m_rect_higher_thumb.GetLeft()) ? + ssLower : + ssHigher; + else + m_selection = abs(pt.y - m_rect_lower_thumb.GetTop()) <= abs(pt.y - m_rect_higher_thumb.GetBottom()) ? + ssLower : + ssHigher; + } - if (m_selection == ssLower && !is_lower_thumb_editable()) - m_selection = ssUndef; + if (m_selection == ssLower && !is_lower_thumb_editable()) + m_selection = ssUndef; - move_current_thumb((m_draw_mode == dmSequentialGCodeView) ? event.GetWheelRotation() < 0 : event.GetWheelRotation() > 0); + move_current_thumb((m_draw_mode == dmSequentialGCodeView) ? event.GetWheelRotation() < 0 : + event.GetWheelRotation() > 0); + } + this->fire_update_if_needed(); } void Control::OnKeyDown(wxKeyEvent &event) { - const int key = event.GetKeyCode(); - if (m_draw_mode != dmSequentialGCodeView && key == WXK_NUMPAD_ADD) { - // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. - // To avoid this case we should suppress second add_tick() call. - m_ticks.suppress_plus(true); - add_current_tick(true); - } - else if (m_draw_mode != dmSequentialGCodeView && (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK)) { - // OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice. - // To avoid this case we should suppress second delete_tick() call. - m_ticks.suppress_minus(true); - delete_current_tick(); - } - else if (m_draw_mode != dmSequentialGCodeView && event.GetKeyCode() == WXK_SHIFT) - UseDefaultColors(false); - else if (is_horizontal()) { - if (m_is_focused) { - if (key == WXK_LEFT || key == WXK_RIGHT) - move_current_thumb(key == WXK_LEFT); - else if (key == WXK_UP || key == WXK_DOWN) { - if (key == WXK_DOWN) - m_selection = ssHigher; - else if (key == WXK_UP && is_lower_thumb_editable()) - m_selection = ssLower; - Refresh(); + { + std::lock_guard lock(m_lock_data); + const int key = event.GetKeyCode(); + if (m_draw_mode != dmSequentialGCodeView && key == WXK_NUMPAD_ADD) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. + // To avoid this case we should suppress second add_tick() call. + m_ticks.suppress_plus(true); + add_current_tick(true); + } else if (m_draw_mode != dmSequentialGCodeView && + (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK)) { + // OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice. + // To avoid this case we should suppress second delete_tick() call. + m_ticks.suppress_minus(true); + delete_current_tick(); + } else if (m_draw_mode != dmSequentialGCodeView && event.GetKeyCode() == WXK_SHIFT) + UseDefaultColors(false); + else if (is_horizontal()) { + if (m_is_focused) { + if (key == WXK_LEFT || key == WXK_RIGHT) + move_current_thumb(key == WXK_LEFT); + else if (key == WXK_UP || key == WXK_DOWN) { + if (key == WXK_DOWN) + m_selection = ssHigher; + else if (key == WXK_UP && is_lower_thumb_editable()) + m_selection = ssLower; + m_need_refresh_and_update = true; + } + } else { + if (key == WXK_LEFT || key == WXK_RIGHT) + move_current_thumb(key == WXK_LEFT); } - } - else { - if (key == WXK_LEFT || key == WXK_RIGHT) - move_current_thumb(key == WXK_LEFT); - } - } - else { - if (m_is_focused) { - if (key == WXK_LEFT || key == WXK_RIGHT) { - if (key == WXK_LEFT) - m_selection = ssHigher; - else if (key == WXK_RIGHT && is_lower_thumb_editable()) - m_selection = ssLower; - Refresh(); + } else { + if (m_is_focused) { + if (key == WXK_LEFT || key == WXK_RIGHT) { + if (key == WXK_LEFT) + m_selection = ssHigher; + else if (key == WXK_RIGHT && is_lower_thumb_editable()) + m_selection = ssLower; + m_need_refresh_and_update = true; + } else if (key == WXK_UP || key == WXK_DOWN) + move_current_thumb(key == WXK_UP); + } else { + if (key == WXK_UP || key == WXK_DOWN) + move_current_thumb(key == WXK_UP); } - else if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); } - else { - if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); - } - } - event.Skip(); // !Needed to have EVT_CHAR generated as well + event.Skip(); // !Needed to have EVT_CHAR generated as well + } + this->fire_update_if_needed(); } void Control::OnKeyUp(wxKeyEvent &event) { - if (event.GetKeyCode() == WXK_CONTROL) - m_is_one_layer = false; - else if (event.GetKeyCode() == WXK_SHIFT) - UseDefaultColors(true); + { + std::lock_guard lock(m_lock_data); + if (event.GetKeyCode() == WXK_CONTROL) + m_is_one_layer = false; + else if (event.GetKeyCode() == WXK_SHIFT) + UseDefaultColors(true); - Refresh(); - Update(); + m_need_refresh_and_update = true; + } event.Skip(); + this->fire_update_if_needed(); } void Control::OnChar(wxKeyEvent& event) { - const int key = event.GetKeyCode(); - if (m_draw_mode != dmSequentialGCodeView) { - if (key == '+' && !m_ticks.suppressed_plus()) { - add_current_tick(true); - m_ticks.suppress_plus(false); - } - else if (key == '-' && !m_ticks.suppressed_minus()) { - delete_current_tick(); - m_ticks.suppress_minus(false); + { + std::lock_guard lock(m_lock_data); + const int key = event.GetKeyCode(); + if (m_draw_mode != dmSequentialGCodeView) { + if (key == '+' && !m_ticks.suppressed_plus()) { + add_current_tick(true); + m_ticks.suppress_plus(false); + } else if (key == '-' && !m_ticks.suppressed_minus()) { + delete_current_tick(); + m_ticks.suppress_minus(false); + } } + if (key == 'G') + jump_to_value(); } - if (key == 'G') - jump_to_value(); + this->fire_update_if_needed(); } void Control::OnRightDown(wxMouseEvent& event) { - if (HasCapture() || m_is_left_down) - return; - this->CaptureMouse(); - - const wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); - - m_mouse = maNone; - if (m_draw_mode == dmRegular) { - if (is_point_in_rect(pos, m_rect_tick_action)) { - const int tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; - m_mouse = m_ticks.ticks.find(TickCode{ tick }) == m_ticks.ticks.end() ? - maAddMenu : maEditMenu; + { + std::lock_guard lock(m_lock_data); + if (HasCapture() || m_is_left_down) + return; + this->CaptureMouse(); + + const wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); + + m_mouse = maNone; + if (m_draw_mode == dmRegular) { + if (is_point_in_rect(pos, m_rect_tick_action)) { + const int tick = m_selection == ssLower ? m_lower_tick : m_higher_tick; + m_mouse = m_ticks.ticks.find(TickCode{tick}) == m_ticks.ticks.end() ? maAddMenu : maEditMenu; + } else if (m_mode == SingleExtruder && !detect_selected_slider(pos) && + is_point_in_rect(pos, get_colored_band_rect())) + m_mouse = maForceColorEdit; + else if (m_mode == MultiAsSingle && is_point_in_rect(pos, m_rect_cog_icon)) + m_mouse = maCogIconMenu; } - else if (m_mode == SingleExtruder && !detect_selected_slider(pos) && is_point_in_rect(pos, get_colored_band_rect())) - m_mouse = maForceColorEdit; - else if (m_mode == MultiAsSingle && is_point_in_rect(pos, m_rect_cog_icon)) - m_mouse = maCogIconMenu; - } - if (m_mouse != maNone || !detect_selected_slider(pos)) - return; + if (m_mouse != maNone || !detect_selected_slider(pos)) + return; - if (m_selection == ssLower) - m_higher_tick = m_lower_tick; - else - m_lower_tick = m_higher_tick; + if (m_selection == ssLower) + m_higher_tick = m_lower_tick; + else + m_lower_tick = m_higher_tick; - // set slider to "one layer" mode - m_is_right_down = m_is_one_layer = true; + // set slider to "one layer" mode + m_is_right_down = m_is_one_layer = true; - Refresh(); - Update(); - event.Skip(); + m_need_refresh_and_update = true; + event.Skip(); + this->fire_update_if_needed(); + } } // Get active extruders for tick. @@ -2254,29 +2278,45 @@ void Control::auto_color_change() post_ticks_changed_event(); } +void Control::fire_update_if_needed() { + if (m_need_refresh_and_update) { + m_need_refresh_and_update = false; + Refresh(); + Update(); + } + if (m_need_fire_scroll_change) { + m_need_fire_scroll_change = false; + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); + } +} + void Control::OnRightUp(wxMouseEvent& event) { - if (!HasCapture() || m_is_left_down) - return; - this->ReleaseMouse(); - m_is_right_down = m_is_one_layer = false; - - if (m_mouse == maForceColorEdit) { - wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); - int edited_tick = get_edited_tick_for_position(pos); - if (edited_tick >= 0) - edit_tick(edited_tick); + { + std::lock_guard lock(m_lock_data); + if (!HasCapture() || m_is_left_down) + return; + this->ReleaseMouse(); + m_is_right_down = m_is_one_layer = false; + + if (m_mouse == maForceColorEdit) { + wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); + int edited_tick = get_edited_tick_for_position(pos); + if (edited_tick >= 0) + edit_tick(edited_tick); + } else if (m_mouse == maAddMenu) + show_add_context_menu(); + else if (m_mouse == maEditMenu) + show_edit_context_menu(); + else if (m_mouse == maCogIconMenu) + show_cog_icon_context_menu(); + + m_need_refresh_and_update = true; } - else if (m_mouse == maAddMenu) - show_add_context_menu(); - else if (m_mouse == maEditMenu) - show_edit_context_menu(); - else if (m_mouse == maCogIconMenu) - show_cog_icon_context_menu(); - - Refresh(); - Update(); event.Skip(); + this->fire_update_if_needed(); } static std::string get_new_color(const std::string& color) @@ -2406,7 +2446,8 @@ void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/) if ( it == m_ticks.ticks.end() ) { // try to add tick - if (!m_ticks.add_tick(tick, type, extruder, m_values[tick])) + assert(tick < m_values.size() && !m_values.empty()); + if (!m_ticks.add_tick(tick, type, extruder, tick < m_values.size() ? m_values[tick] : m_values.back())) return; } else if (type == ToolChange || type == ColorChange) { @@ -2457,7 +2498,7 @@ void Control::add_current_tick(bool call_from_keyboard /*= false*/) wxPoint(get_position_from_tick(tick), height + coord) : wxPoint(width + coord, get_position_from_tick(tick)); } - + //fire many event, including an OnLeaveWindows that will refresh us GUI::wxGetApp().plater()->PopupMenu(&menu, pos); } } diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 616f86221cd..d66d23343e3 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -10,8 +10,9 @@ #include #include -#include +#include #include +#include class wxMenu; @@ -301,6 +302,12 @@ class Control : public wxControl void show_cog_icon_context_menu(); void auto_color_change(); + // mutex to lock the rendering & callbacks while updating the data. + std::recursive_mutex &lock_render() { return m_lock_data; } + // emit refresh, update, and event. Do it only outside of lock_render() + void fire_update_if_needed(); + bool ensure_correctly_filled() const; + ExtrudersSequence m_extruders_sequence; protected: @@ -347,7 +354,7 @@ class Control : public wxControl wxSize get_size() const; void get_size(int* w, int* h) const; double get_double_value(const SelectedSlider& selection); - int get_tick_from_value(double value, bool force_lower_bound = false); + int get_tick_from_value(double value, bool force_lower_bound = false) const; wxString get_tooltip(int tick = -1); int get_edited_tick_for_position(wxPoint pos, Type type = ColorChange); @@ -422,6 +429,13 @@ class Control : public wxControl long m_extra_style; float m_label_koef{ 1.0 }; + //for updating + bool m_need_refresh_and_update = false; + bool m_need_fire_scroll_change = false; + + // lock for avoiding render & callbacks while data is updating + std::recursive_mutex m_lock_data; + std::vector m_values; TickCodeInfo m_ticks; std::vector m_layers_times; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a831834d481..7b67380cdda 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5922,11 +5922,20 @@ Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) Vec4i32 viewport(camera.get_viewport().data()); GLint y = viewport[3] - (GLint)mouse_pos(1); - GLfloat mouse_z; - if (z == nullptr) - glsafe(::glReadPixels((GLint)mouse_pos(0), y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z)); - else + double mouse_z; + if (z == nullptr) { + GLuint render_z; + if(sizeof(render_z)==2) + glsafe(::glReadPixels((GLint) mouse_pos(0), y, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, (void *) &render_z)); + else if(sizeof(render_z)==4) + glsafe(::glReadPixels((GLint) mouse_pos(0), y, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, (void *) &render_z)); + else { + BOOST_LOG_TRIVIAL(error) << "error, opengl depth buffer of odd size."; + } + mouse_z = (double(render_z)/std::numeric_limits::max()); + } else { mouse_z = *z; + } Vec3d out; igl::unproject(Vec3d(mouse_pos(0), y, mouse_z), modelview, projection, viewport, out); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 4d97aba7a6e..c6e8a4539e5 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -503,7 +503,7 @@ void ObjectManipulation::update_ui_from_settings() if (m_use_colors) { editor->SetBackgroundColour(wxColour(axes_color_back[axis_id])); if (wxGetApp().dark_mode()) - editor->SetForegroundColour(*wxBLACK); + editor->SetForegroundColour(wxGetApp().get_label_clr_default()); } else { #ifdef _WIN32 @@ -1086,7 +1086,7 @@ ManipulationEditor::ManipulationEditor(ObjectManipulation* parent, #endif // __WXOSX__ if (parent->use_colors()) { this->SetBackgroundColour(wxColour(axes_color_back[axis])); - this->SetForegroundColour(*wxBLACK); + this->SetForegroundColour(wxGetApp().get_label_clr_default()); } else { wxGetApp().UpdateDarkUI(this); } @@ -1140,7 +1140,7 @@ void ManipulationEditor::msw_rescale() void ManipulationEditor::sys_color_changed(ObjectManipulation* parent) { if (parent->use_colors()) - SetForegroundColour(*wxBLACK); + SetForegroundColour(wxGetApp().get_label_clr_default()); else #ifdef _WIN32 wxGetApp().UpdateDarkUI(this); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 9d54ac891e5..11664a45046 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -609,6 +609,7 @@ wxBoxSizer* Preview::create_layers_slider_sizer() { wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); m_layers_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); + std::lock_guard lock(m_layers_slider->lock_render()); m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA, wxGetApp().preset_bundle->fff_prints.get_edited_preset().config.opt_bool("complete_objects")); @@ -685,6 +686,10 @@ void Preview::check_layers_slider_values(std::vector& ticks_f void Preview::update_layers_slider(const std::vector& layers_z, bool keep_z_range) { + //lock rendering while updating + { + std::lock_guard lock(m_layers_slider->lock_render()); + // Save the initial slider span. double z_low = m_layers_slider->GetLowerValueD(); double z_high = m_layers_slider->GetHigherValueD(); @@ -742,18 +747,27 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee if (sla_print_technology) m_layers_slider->SetLayersTimes(plater->sla_print().print_statistics().layers_times); else { - auto print_mode_stat = m_gcode_result->print_statistics.modes.front(); - m_layers_slider->SetLayersTimes(print_mode_stat.layers_times, print_mode_stat.time); + if (plater->fff_print().print_statistics().is_computing_gcode || !plater->fff_print().finished()) { + //do not fetch uncomplete data + m_layers_slider->SetLayersTimes({}, 0); + } else { + auto print_mode_stat = m_gcode_result->print_statistics.modes.front(); + m_layers_slider->SetLayersTimes(print_mode_stat.layers_times, print_mode_stat.time); + } } - { // create area array //area not computed for sla_print_technology //TODO - if (!sla_print_technology){ + if (!sla_print_technology){ + if (plater->fff_print().print_statistics().is_computing_gcode || !plater->fff_print().finished()) { + //do not fetch uncomplete data + m_layers_slider->SetLayersAreas({}); + } else { const std::vector> &layerz_to_area = plater->fff_print().print_statistics().layer_area_stats; std::vector areas; for(auto [z, area] : layerz_to_area) areas.push_back(area); m_layers_slider->SetLayersAreas(areas); + assert(areas.size() == m_gcode_result->print_statistics.modes.front().layers_times.size()); //auto objects = plater->fff_print().objects(); //for (auto object : objects) { // for (auto layer : object->layers()) { @@ -825,9 +839,11 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee break; } } - - m_layers_slider_sizer->Show((size_t)0); - Layout(); + m_layers_slider->ensure_correctly_filled(); + } + m_layers_slider_sizer->Show((size_t)0); + m_layers_slider->fire_update_if_needed(); + Layout(); } void Preview::update_layers_slider_mode() @@ -881,12 +897,14 @@ void Preview::update_layers_slider_mode() } m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder); + m_layers_slider->fire_update_if_needed(); } void Preview::reset_layers_slider() { m_layers_slider->SetHigherValue(0); m_layers_slider->SetLowerValue(0); + m_layers_slider->fire_update_if_needed(); } void Preview::update_layers_slider_from_canvas(wxKeyEvent& event) @@ -901,19 +919,20 @@ void Preview::update_layers_slider_from_canvas(wxKeyEvent& event) if (key == 'S' || key == 'W') { const int new_pos = key == 'W' ? m_layers_slider->GetHigherValue() + 1 : m_layers_slider->GetHigherValue() - 1; m_layers_slider->SetHigherValue(new_pos); - if (event.ShiftDown() || m_layers_slider->is_one_layer()) m_layers_slider->SetLowerValue(m_layers_slider->GetHigherValue()); - } - else if (key == 'A' || key == 'D') { + if (event.ShiftDown() || m_layers_slider->is_one_layer()) + m_layers_slider->SetLowerValue(m_layers_slider->GetHigherValue()); + } else if (key == 'A' || key == 'D') { const int new_pos = key == 'D' ? m_moves_slider->GetHigherValue() + 1 : m_moves_slider->GetHigherValue() - 1; m_moves_slider->SetHigherValue(new_pos); - if (event.ShiftDown() || m_moves_slider->is_one_layer()) m_moves_slider->SetLowerValue(m_moves_slider->GetHigherValue()); - } - else if (key == 'X') + if (event.ShiftDown() || m_moves_slider->is_one_layer()) + m_moves_slider->SetLowerValue(m_moves_slider->GetHigherValue()); + } else if (key == 'X') { m_layers_slider->ChangeOneLayerLock(); - else if (key == WXK_SHIFT) + } else if (key == WXK_SHIFT) m_layers_slider->UseDefaultColors(false); else event.Skip(); + m_layers_slider->fire_update_if_needed(); } void Preview::update_moves_slider() @@ -937,6 +956,8 @@ void Preview::update_moves_slider() m_moves_slider->SetSliderAlternateValues(alternate_values); m_moves_slider->SetMaxValue(view.endpoints.last - view.endpoints.first); m_moves_slider->SetSelectionSpan(view.current.first - view.endpoints.first, view.current.last - view.endpoints.first); + m_moves_slider->Refresh(); + m_moves_slider->Update(); } void Preview::enable_moves_slider(bool enable) @@ -1080,7 +1101,6 @@ void Preview::load_print_as_fff(bool keep_z_range) m_canvas->set_gcode_view_preview_type(static_cast(type)); if (wxGetApp().is_gcode_viewer()) m_keep_current_preview_type = true; - refresh_print(); } } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ee30a76694a..21553c2e2aa 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1340,28 +1340,36 @@ void Sidebar::update_sliced_info_sizer() //if multiple filament/extruderss, then print them all if (ps.filament_stats.size() > 1 || ps.color_extruderid_to_used_filament.size() > 0) { new_label += ":"; + const std::vector& filament_presets = wxGetApp().preset_bundle->filament_presets; + const PresetCollection& filaments = wxGetApp().preset_bundle->filaments; //for each extruder - for (auto filament : ps.filament_stats) { + for (auto [filament_id, filament_volume] : ps.filament_stats) { int items_printed = 0; double total_length = 0; - // print each color change for this extruder - for (auto entry : ps.color_extruderid_to_used_filament) { - if (filament.first == entry.first) { - items_printed++; - new_label += "\n - " + format_wxstr(_L("Color %1% at extruder %2%"), items_printed , (filament.first + 1)); - total_length += entry.second; - info_text += wxString::Format("\n%.2f (%.2f)", entry.second / 1000, total_length / 1000); + const Preset* filament_preset = filaments.find_preset(filament_presets[filament_id], false); + if (filament_preset) { + double crosssection = 0.5 * filament_preset->config.opt_float("filament_diameter", filament_id); + crosssection *= crosssection * PI; + double mm3_to_m = 0.001 / crosssection; + // print each color change for this extruder + for (auto entry : ps.color_extruderid_to_used_filament) { + if (filament_id == entry.first) { + items_printed++; + new_label += "\n - " + format_wxstr(_L("Color %1% at extruder %2%"), items_printed , (filament_id + 1)); + total_length += entry.second; + info_text += wxString::Format("\n%.2f (%.2f)", entry.second / 1000, total_length / 1000); + } + } + //print total for this extruder + if (items_printed == 0) { + new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament_id + 1); + //new_label += from_u8((boost::format("\n - %1% %2%") % _utf8(L("Color")) % ps.color_extruderid_to_used_filament.size()).str()); + info_text += wxString::Format("\n%.2f", filament_volume * mm3_to_m); + } + else { + new_label += "\n - " + format_wxstr(_L("Color %1% at extruder %2%"), (items_printed+1), (filament_id + 1)); + info_text += wxString::Format("\n%.2f (%.2f)", (filament_volume - total_length) * mm3_to_m, filament_volume * mm3_to_m); } - } - //print total for this extruder - if (items_printed == 0) { - new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament.first + 1); - //new_label += from_u8((boost::format("\n - %1% %2%") % _utf8(L("Color")) % ps.color_extruderid_to_used_filament.size()).str()); - info_text += wxString::Format("\n%.2f", filament.second / 1000); - } - else { - new_label += "\n - " + format_wxstr(_L("Color %1% at extruder %2%"), (items_printed+1), (filament.first + 1)); - info_text += wxString::Format("\n%.2f (%.2f)", (filament.second - total_length) / 1000, filament.second / 1000); } } } @@ -1386,15 +1394,15 @@ void Sidebar::update_sliced_info_sizer() bool has_spool = false; new_label += ":"; //for each extruder - for (auto filament : ps.filament_stats) { - const Preset* filament_preset = filaments.find_preset(filament_presets[filament.first], false); + for (auto [filament_id, filament_volume] : ps.filament_stats) { + const Preset* filament_preset = filaments.find_preset(filament_presets[filament_id], false); if (filament_preset) { double spool_weight = filament_preset->config.opt_float("filament_spool_weight", 0); - double filament_density = filament_preset->config.opt_float("filament_density", filament.first); - double crosssection = filament_preset->config.opt_float("filament_diameter", filament.first); - crosssection *= crosssection; - crosssection *= 0.25 * PI; + double filament_density = filament_preset->config.opt_float("filament_density", filament_id); + double crosssection = 0.5 * filament_preset->config.opt_float("filament_diameter", filament_id); + crosssection *= crosssection * PI; double m_to_g = filament_density / (crosssection * 1000); + double mm3_to_g = filament_density *0.001; int items_printed = 0; double total_length = 0; //for (int i = 0; i < ps.color_extruderid_to_used_filament.size(); i++) { @@ -1406,9 +1414,9 @@ void Sidebar::update_sliced_info_sizer() if (spool_weight != 0.0) has_spool = true; for (auto entry : ps.color_extruderid_to_used_filament) { - if (filament.first == entry.first) { + if (filament_id == entry.first) { items_printed++; - new_label += "\n - " + format_wxstr(_L("Color %1% at extruder %2%"), items_printed, (filament.first + 1)); + new_label += "\n - " + format_wxstr(_L("Color %1% at extruder %2%"), items_printed, (filament_id + 1)); total_length += entry.second; info_text += wxString::Format("\n%.2f", entry.second * m_to_g); if (spool_weight != 0.0) @@ -1417,16 +1425,16 @@ void Sidebar::update_sliced_info_sizer() } //print total for this extruder if (items_printed == 0) { - new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament.first + 1); + new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament_id + 1); //new_label += from_u8((boost::format("\n - %1% %2%") % _utf8(L("Color")) % ps.color_extruderid_to_used_filament.size()).str()); - info_text += wxString::Format("\n%.2f", filament.second * m_to_g); + info_text += wxString::Format("\n%.2f", filament_volume * mm3_to_g); if (spool_weight != 0.0) - info_text += wxString::Format(" (%.2f)", filament.second * m_to_g + spool_weight); + info_text += wxString::Format(" (%.2f)", filament_volume * mm3_to_g + spool_weight); } else { - new_label += "\n - " + format_wxstr(_L("Color %1% at extruder %2%"), (items_printed + 1), (filament.first + 1)); - info_text += wxString::Format("\n%.2f", (filament.second - total_length) * m_to_g); + new_label += "\n - " + format_wxstr(_L("Color %1% at extruder %2%"), (items_printed + 1), (filament_id + 1)); + info_text += wxString::Format("\n%.2f", (filament_volume - total_length) * mm3_to_g); if (spool_weight != 0.0) - info_text += wxString::Format(" (%.2f)", (filament.second - total_length) * m_to_g + spool_weight); + info_text += wxString::Format(" (%.2f)", (filament_volume - total_length) * mm3_to_g + spool_weight); } } } @@ -4680,6 +4688,7 @@ void Plater::priv::enable_preview_moves_slider(bool enable) void Plater::priv::reset_gcode_toolpaths() { + gcode_result.reset(); preview->reset_gcode_toolpaths(); } diff --git a/src/slic3r/GUI/RammingChart.hpp b/src/slic3r/GUI/RammingChart.hpp index 5193db98185..0d3bfec96c8 100644 --- a/src/slic3r/GUI/RammingChart.hpp +++ b/src/slic3r/GUI/RammingChart.hpp @@ -38,9 +38,9 @@ class Chart : public wxWindow { recalculate_line(); } void set_manual_points_manipulation(bool manip) { m_manual_points_manipulation = manip; } - void set_x_label(wxString &label, float incr = 0.1f) { m_x_legend = label; m_x_legend_incr = incr; } - void set_y_label(wxString &label, float incr = 0.1f) { m_y_legend = label; m_y_legend_incr = incr; } - void set_no_point_label(wxString &label) { m_no_point_legend = label; } + void set_x_label(const wxString &label, float incr = 0.1f) { m_x_legend = label; m_x_legend_incr = incr; } + void set_y_label(const wxString &label, float incr = 0.1f) { m_y_legend = label; m_y_legend_incr = incr; } + void set_no_point_label(const wxString &label) { m_no_point_legend = label; } float get_volume() const { return m_total_volume; } float get_max_x() const { return visible_area.m_width; } diff --git a/src/slic3r/GUI/ScriptExecutor.cpp b/src/slic3r/GUI/ScriptExecutor.cpp index c276b77126e..5fce15732d1 100644 --- a/src/slic3r/GUI/ScriptExecutor.cpp +++ b/src/slic3r/GUI/ScriptExecutor.cpp @@ -183,14 +183,14 @@ float as_get_float_idx(std::string& key, int idx) float val = 1; // if precent, divide by 100 if (opt->type() == ConfigOptionType::coPercent || opt->type() == ConfigOptionType::coPercents) { - val *= 0.01; + val *= 0.01f; } if (opt->type() == ConfigOptionType::coFloatOrPercent && static_cast(opt)->percent) - val *= 0.01; + val *= 0.01f; if (opt->is_vector()) { const ConfigOptionVectorBase* vector = static_cast(opt); if (opt->type() == ConfigOptionType::coFloatsOrPercents && static_cast(vector)->get_at(idx).percent) - val *= 0.01; + val *= 0.01f; val *= (float)vector->get_float(idx); } else { val *= (float)(opt->get_float());