diff options
25 files changed, 373 insertions, 127 deletions
diff --git a/doc/How to build - Mac OS.md b/doc/How to build - Mac OS.md index 25e14cf4b..964599fdb 100644 --- a/doc/How to build - Mac OS.md +++ b/doc/How to build - Mac OS.md @@ -14,6 +14,8 @@ XCode is available through Apple's App Store, the other three tools are availabl ### How to get the source code You have to gitclone the repository + +Open 'terminal' and enter each line, one at a time. ``` git clone https://github.com/supermerill/SuperSlicer.git ``` @@ -21,6 +23,7 @@ git clone https://github.com/supermerill/SuperSlicer.git and then you have to clone the profiles submodules ``` +cd SuperSlicer cd resources/profiles git submodule update ``` diff --git a/resources/profiles b/resources/profiles -Subproject 0b8261ae8bbd7c31d736f371323476d6aff3ef3 +Subproject ab609331353a14890c8d65fcb55f07dd0ca5b1c diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 04afd9ea5..3c4064d8d 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -67,8 +67,37 @@ void AppConfig::set_defaults() if (get("drop_project_action").empty()) set("drop_project_action", "1"); - if (get("freecad_path").empty()) + if (get("freecad_path").empty() || get("freecad_path") == ".") { set("freecad_path", "."); + //try to find it +#ifdef _WIN32 + //windows + boost::filesystem::path prg_files = "C:/Program Files"; + boost::filesystem::path freecad_path; + if (boost::filesystem::exists(prg_files)) { + for (boost::filesystem::directory_entry& prg_dir : boost::filesystem::directory_iterator(prg_files)) { + if (prg_dir.status().type() == boost::filesystem::file_type::directory_file + && boost::starts_with(prg_dir.path().filename().string(), "FreeCAD") + && (freecad_path.empty() || freecad_path.filename().string() < prg_dir.path().filename().string())) { + freecad_path = prg_dir.path(); + } + } + } + if (!freecad_path.empty()) + set("freecad_path", freecad_path.string()); +#else +#ifdef __APPLE__ + //apple + if (boost::filesystem::exists("/Applications/FreeCAD.app/Contents/Frameworks/FreeCAD")) + set("freecad_path", "/Applications/FreeCAD.app/Contents/Frameworks/FreeCAD"); + +#else + // linux + if (boost::filesystem::exists("/usr/local/bin/FreeCAD")) + set("freecad_path", "/usr/local/bin/FreeCAD"); +#endif +#endif + } if (get("show_overwrite_dialog").empty()) set("show_overwrite_dialog", "1"); diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index f05d35d2d..bc8041c75 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -820,7 +820,7 @@ ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCo return this->load(tree, compatibility_rule); } catch (const ConfigurationError &e) { throw ConfigurationError(format("Failed loading configuration file \"%1%\": %2%", file, e.what())); -} + } } ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule) @@ -933,6 +933,14 @@ size_t ConfigBase::load_from_gcode_string(const char* str, ConfigSubstitutionCon } catch (UnknownOptionException & /* e */) { // ignore + } catch (BadOptionValueException & e) { + if (substitutions.rule == ForwardCompatibilitySubstitutionRule::Disable) + throw e; + // log the error + const ConfigDef* def = this->def(); + if (def == nullptr) throw e; + const ConfigOptionDef* optdef = def->get(std::string(key, key_end)); + substitutions.substitutions.emplace_back(optdef, std::string(value, end), ConfigOptionUniquePtr(optdef->default_value->clone())); } end = start; } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 43c770d3f..83d52c847 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1326,6 +1326,9 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<uint16_t>(-1)) break; } + if (initial_extruder_id == static_cast<unsigned int>(-1)) + // No object to print was found, cancel the G-code export. + throw Slic3r::SlicingError(_(L("No extrusions were generated for objects."))); // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode. // Use the extruder IDs collected from Regions. std::set<uint16_t> extruder_set = print.extruders(); @@ -1337,6 +1340,9 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it. tool_ordering = print.tool_ordering(); tool_ordering.assign_custom_gcodes(print); + if (tool_ordering.all_extruders().empty()) + // No object to print was found, cancel the G-code export. + throw Slic3r::SlicingError(_(L("No extrusions were generated for objects."))); has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ? // The priming towers will be skipped. diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 44e713d18..a974b5a7a 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -340,6 +340,7 @@ Point SeamPlacer::get_seam(const Layer& layer, SeamPosition seam_position, // Look for all lambda-seam-modifiers below current z, choose the highest one ModelVolume* v_lambda_seam = nullptr; Vec3d lambda_pos; + double lambda_z; double lambda_dist; double lambda_radius; //get model_instance (like from po->model_object()->instances, but we don't have the index for that array) @@ -347,28 +348,34 @@ Point SeamPlacer::get_seam(const Layer& layer, SeamPosition seam_position, for (ModelVolume* v : po->model_object()->volumes) { if (v->is_seam_position()) { //xy in object coordinates, z in plater coordinates - Vec3d test_lambda_pos = model_instance->transform_vector(v->get_offset(), true); + // created/moved shpere have offset in their transformation, and loaded ones have their loaded transformation in the source transformation. + Vec3d test_lambda_pos = model_instance->transform_vector((v->get_transformation() * v->source.transform).get_offset(), false); + // remove shift, as we used the transform_vector(.., FALSE). that way, we have a correct z vs the layer height, and same for the x and y vs polygon. + test_lambda_pos.x() -= unscaled(po->instances()[print_object_instance_idx].shift.x()); + test_lambda_pos.y() -= unscaled(po->instances()[print_object_instance_idx].shift.y()); + double test_lambda_z = std::abs(layer.print_z - test_lambda_pos.z()); Point xy_lambda(scale_(test_lambda_pos.x()), scale_(test_lambda_pos.y())); Point nearest = polygon.point_projection(xy_lambda); Vec3d polygon_3dpoint{ unscaled(nearest.x()), unscaled(nearest.y()), (double)layer.print_z }; double test_lambda_dist = (polygon_3dpoint - test_lambda_pos).norm(); double sphere_radius = po->model_object()->instance_bounding_box(0, true).size().x() / 2; - //if (test_lambda_dist > sphere_radius) - // continue; - //use this one if the first or nearer (in z) - if (v_lambda_seam == nullptr || lambda_dist > test_lambda_dist) { + + //use this one if the first or nearer (in z, or in xy if same z) + if (v_lambda_seam == nullptr + || ( lambda_z > test_lambda_z ) + || ( lambda_z == test_lambda_z && lambda_dist > test_lambda_dist ) ){ v_lambda_seam = v; lambda_pos = test_lambda_pos; lambda_radius = sphere_radius; lambda_dist = test_lambda_dist; + lambda_z = test_lambda_z; } } } if (v_lambda_seam != nullptr) { - lambda_pos = model_instance->transform_vector(v_lambda_seam->get_offset(), true); // Found, get the center point and apply rotation and scaling of Model instance. Continues to spAligned if not found or Weight set to Zero. last_pos = Point::new_scale(lambda_pos.x(), lambda_pos.y()); // Weight is set by user and stored in the radius of the sphere diff --git a/src/libslic3r/MedialAxis.cpp b/src/libslic3r/MedialAxis.cpp index 1e31c714f..c6e1128ab 100644 --- a/src/libslic3r/MedialAxis.cpp +++ b/src/libslic3r/MedialAxis.cpp @@ -16,9 +16,6 @@ #include <list> namespace Slic3r { - int count_error = 0; - - //int Slic3r::MedialAxis::staticid = 0; void MedialAxis::build(Polylines &polylines) @@ -667,6 +664,7 @@ MedialAxis::fusion_corners(ThickPolylines &pp) //if (polyline.points.size() != 2) continue; // maybe we should have something to merge X-point to 2-point if it's near enough. if (polyline.endpoints.first) polyline.reverse(); else if (!polyline.endpoints.second) continue; + if (polyline.width.back() > min_width) continue; //check my length is small coord_t length = (coord_t)polyline.length(); @@ -1210,6 +1208,7 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) bool changes = false; for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline& polyline = pp[i]; + bool polyline_changes = false; // remove bits with too small extrusion while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) { //try to split if possible @@ -1226,11 +1225,13 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) polyline.width.erase(polyline.width.begin()); } changes = true; + polyline_changes = true; break; } polyline.points.erase(polyline.points.begin()); polyline.width.erase(polyline.width.begin()); changes = true; + polyline_changes = true; } while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) { //try to split if possible @@ -1247,14 +1248,16 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) polyline.width.erase(polyline.width.end() - 1); } changes = true; + polyline_changes = true; break; } polyline.points.erase(polyline.points.end() - 1); polyline.width.erase(polyline.width.end() - 1); changes = true; + polyline_changes = true; } //remove points and bits that comes from a "main line" - if (polyline.points.size() < 2 || (changes && polyline.length() < this->max_width && polyline.points.size() ==2)) { + if (polyline.points.size() < 2 || (polyline_changes && polyline.length() < this->max_width && polyline.points.size() ==2)) { //remove self if too small pp.erase(pp.begin() + i); --i; @@ -1264,6 +1267,110 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp) } void +MedialAxis::concatenate_small_polylines(ThickPolylines& pp) +{ + + /* + new goal: ensure that if there is a too short segment, it will be connected with a sufficiently long one, to save it + */ + coordf_t shortest_size = (coordf_t)this->min_length; + std::set<size_t> deleted; + std::vector<size_t> idx_per_size; + //TODO: cache the length + for (size_t i = 0; i < pp.size(); ++i) { + ThickPolyline& polyline = pp[i]; + if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization + if (polyline.length() <= shortest_size) + idx_per_size.push_back(i); + } + std::sort(idx_per_size.begin(), idx_per_size.end(), [&pp](size_t a, size_t b) -> bool {return pp[a].length() > pp[b].length(); }); + for (size_t idx_sorted = 0; idx_sorted < idx_per_size.size(); ++idx_sorted) { + if (deleted.find(idx_per_size[idx_sorted]) != deleted.end()) continue; + //all these polylines need to be saved + ThickPolyline& polyline = pp[idx_per_size[idx_sorted]]; + + ThickPolyline* best_candidate = nullptr; + float best_dot = -1; + coordf_t best_length = -1; + size_t best_idx = 0; + + // find another polyline starting here + for (size_t j = 0; j < pp.size(); ++j) { + if (deleted.find(j) != deleted.end()) continue; + if (j == idx_per_size[idx_sorted]) continue; + ThickPolyline& other = pp[j]; + if (other.endpoints.first && other.endpoints.second) continue; + coordf_t other_length = other.length(); + if (other_length + polyline.length() <= shortest_size) continue; // need to be long enough to save it + bool me_reverse = false; + bool other_reverse = false; + if (polyline.last_point().coincides_with_epsilon(other.last_point())) { + other_reverse = true; + } else if (polyline.first_point().coincides_with_epsilon(other.last_point())) { + me_reverse = true; + other_reverse = true; + } else if (polyline.first_point().coincides_with_epsilon(other.first_point())) { + me_reverse = true; + } else if (!polyline.last_point().coincides_with_epsilon(other.first_point())) { + continue; + } + + //find the straitest + Vec2d v_poly(me_reverse ? polyline.lines().front().vector().x() : polyline.lines().back().vector().x(), + me_reverse ? polyline.lines().front().vector().y() : polyline.lines().back().vector().y()); + v_poly *= (1 / std::sqrt(v_poly.x() * v_poly.x() + v_poly.y() * v_poly.y())); + Vec2d v_other(other_reverse ? other.lines().back().vector().x() : other.lines().front().vector().x(), + other_reverse ? other.lines().back().vector().y() : other.lines().front().vector().y()); + v_other *= (1 / std::sqrt(v_other.x() * v_other.x() + v_other.y() * v_other.y())); + float other_dot = std::abs(float(v_poly.x() * v_other.x() + v_poly.y() * v_other.y())); + // use the straitest one + // but if almost equal, use the shortest one + if (std::abs(other_dot - best_dot) < 0.01) { + if (best_length < 0 || best_length > other_length) { + best_candidate = &other; + best_idx = j; + best_dot = other_dot; + best_length = other_length; + } + } else if (other_dot > best_dot) { + best_candidate = &other; + best_idx = j; + best_dot = other_dot; + best_length = other_length; + } + } + if (best_candidate != nullptr && best_candidate->points.size() > 1) { + if (polyline.last_point().coincides_with_epsilon(best_candidate->last_point())) { + best_candidate->reverse(); + } else if (polyline.first_point().coincides_with_epsilon(best_candidate->last_point())) { + polyline.reverse(); + best_candidate->reverse(); + } else if (polyline.first_point().coincides_with_epsilon(best_candidate->first_point())) { + polyline.reverse(); + } + //intersections may create over-extrusion because the included circle can be a bit larger. We have to make it short again if needed. + if (polyline.points.size() > 1 && best_candidate->points.size() > 1 + && polyline.width.back() > polyline.width[polyline.width.size() - 2] + && polyline.width.back() > best_candidate->width[1]) { + polyline.width.back() = std::min(polyline.width[polyline.width.size() - 2], best_candidate->width[1]); + } + //be far enough + int far_idx = 1; + while (far_idx < best_candidate->points.size() && polyline.last_point().coincides_with_epsilon(best_candidate->points[far_idx])) + far_idx++; + polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + far_idx, best_candidate->points.end()); + polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + far_idx, best_candidate->width.end()); + polyline.endpoints.second = best_candidate->endpoints.second; + assert(polyline.width.size() == polyline.points.size()); + deleted.insert(best_idx); + } + } + //delete items to delete (iterate in reverse order to not invalidate the idxs + for (auto rit = deleted.rbegin(); rit != deleted.rend(); rit++) + pp.erase(pp.begin() + (*rit)); +} + +void MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp) { @@ -1402,7 +1509,7 @@ MedialAxis::remove_too_thin_points(ThickPolylines& pp) } void -MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size) +MedialAxis::remove_too_short_polylines(ThickPolylines& pp) { // reduce the flow at the intersection ( + ) points //FIXME: TODO: note that crossings are unnafected right now. they may need a different codepath directly in their method @@ -1468,7 +1575,7 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz while (changes) { changes = false; - coordf_t shortest_size = (coordf_t) min_size; + coordf_t shortest_size = (coordf_t)this->min_length; size_t shortest_idx = -1; for (size_t i = 0; i < pp.size(); ++i) { ThickPolyline& polyline = pp[i]; @@ -1499,8 +1606,6 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz changes = true; while (changes) { changes = false; - - coordf_t shortest_size = (coordf_t)min_size; size_t shortest_idx = -1; for (size_t polyidx = 0; polyidx < pp.size(); ++polyidx) { ThickPolyline& tp = pp[polyidx]; @@ -2000,10 +2105,20 @@ MedialAxis::build(ThickPolylines &polylines_out) // svg.Close(); //} //TODO: reduce the flow at the intersection ( + ) points on crossing? + concatenate_small_polylines(pp); + //{ + // std::stringstream stri; + // stri << "medial_axis_6_concat_small_" << id << ".svg"; + // SVG svg(stri.str()); + // svg.draw(*bounds, "grey"); + // svg.draw(this->expolygon, "green"); + // svg.draw(pp, "red"); + // svg.Close(); + //} concatenate_polylines_with_crossing(pp); //{ // std::stringstream stri; - // stri << "medial_axis_6_concat_" << id << ".svg"; + // stri << "medial_axis_7_concat_" << id << ".svg"; // SVG svg(stri.str()); // svg.draw(*bounds, "grey"); // svg.draw(this->expolygon, "green"); @@ -2011,7 +2126,7 @@ MedialAxis::build(ThickPolylines &polylines_out) // svg.Close(); //} - remove_too_short_polylines(pp, max_w * 2); + remove_too_short_polylines(pp); //{ // std::stringstream stri; // stri << "medial_axis_8_tooshort_" << id << ".svg"; diff --git a/src/libslic3r/MedialAxis.hpp b/src/libslic3r/MedialAxis.hpp index e8614c5ba..f16741302 100644 --- a/src/libslic3r/MedialAxis.hpp +++ b/src/libslic3r/MedialAxis.hpp @@ -30,7 +30,7 @@ class MedialAxis { /// _height: height of the extrusion, used to compute the difference between width and spacing. MedialAxis(const ExPolygon &_expolygon, const coord_t _max_width, const coord_t _min_width, const coord_t _height) : surface(_expolygon), max_width(_max_width), min_width(_min_width), height(_height), - bounds(&_expolygon), nozzle_diameter(_min_width), taper_size(0), stop_at_min_width(true){/*id= staticid;staticid++;*/}; + bounds(&_expolygon), nozzle_diameter(_min_width), taper_size(0), stop_at_min_width(true), min_length(_max_width) {/*id= staticid;staticid++;*/}; /// create the polylines_out collection of variable-width polyline to extrude. void build(ThickPolylines &polylines_out); @@ -45,6 +45,7 @@ class MedialAxis { MedialAxis& use_tapers(const coord_t taper_size) { this->taper_size = taper_size; return *this; } /// optional parameter: if true, the entension inside the bounds can be cut if the width is too small. Default : true MedialAxis& set_stop_at_min_width(const bool stop_at_min_width) { this->stop_at_min_width = stop_at_min_width; return *this; } + MedialAxis& set_min_length(const coord_t min_length) { this->min_length = min_length; return *this; } private: @@ -57,6 +58,8 @@ class MedialAxis { const coord_t max_width; /// minimum width of the extrusion, every spot where a circle diameter is lower than that will be ignored (unless it's the tip of the extrusion) const coord_t min_width; + /// minimum length of continuous segments (may cross a crossing) + coord_t min_length; /// height of the extrusion, used to compute the diufference between width and spacing. const coord_t height; /// Used to compute the real minimum width we can extrude. if != min_width, it activate grow_to_nozzle_diameter(). @@ -96,12 +99,14 @@ class MedialAxis { void extends_line(ThickPolyline& polyline, const ExPolygons& anchors, const coord_t join_width); /// remove too thin bits at start & end of polylines void remove_too_thin_extrusion(ThickPolylines& pp); - /// instead of keeping polyline split at each corssing, we try to create long strait polylines that can cross each other. + /// when we have a too small polyline, try to see if we can't concatenate it at a crossing to keep it. + void concatenate_small_polylines(ThickPolylines& pp); + /// instead of keeping polyline split at each crossing, we try to create long strait polylines that can cross each other. void concatenate_polylines_with_crossing(ThickPolylines& pp); /// remove bits around points that are too thin (can be inside the polyline) void remove_too_thin_points(ThickPolylines& pp); - /// delete polylines that are too short - void remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size); + /// delete polylines that are too short (below the this->min_length) + void remove_too_short_polylines(ThickPolylines& pp); /// be sure we didn't try to push more plastic than the volume defined by surface * height can receive. If overextruded, reduce all widths by the correct %. void ensure_not_overextrude(ThickPolylines& pp); /// if nozzle_diameter > min_width, grow bits that are < width(nozzle_diameter) to width(nozzle_diameter) (don't activate that for gapfill) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 283eeb9c6..0b372b451 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -390,9 +390,10 @@ void PerimeterGenerator::process() ExPolygons top_fills; ExPolygons fill_clip; // simplification already done at slicing - //ExPolygons last = union_ex(surface.expolygon.simplify_p(SCALED_RESOLUTION)); - ExPolygons last = union_ex(surface.expolygon); - double last_area = -1; + //simplify the loop to avoid artifacts when shrinking almost-0 segments + coord_t resolution = scale_t(this->print_config->resolution.value); + ExPolygons last = union_ex(surface.expolygon.simplify_p((resolution < SCALED_EPSILON ? SCALED_EPSILON : resolution))); + double last_area = -1; if (loop_number >= 0) { @@ -562,6 +563,7 @@ void PerimeterGenerator::process() ma.use_bounds(bound) .use_min_real_width(scale_t(this->ext_perimeter_flow.nozzle_diameter)) .use_tapers(thin_walls_overlap) + .set_min_length(ext_perimeter_width + ext_perimeter_spacing) .build(thin_walls_thickpolys); } break; @@ -702,7 +704,12 @@ void PerimeterGenerator::process() holes[i].emplace_back(hole, i, false, has_steep_overhang); } } - last = std::move(next_onion); + + //simplify the loop to avoid artifacts when shrinking almost-0 segments + resolution = scale_t(this->print_config->resolution.value); + last.clear(); + for(ExPolygon& exp : next_onion) + exp.simplify((resolution < SCALED_EPSILON ? SCALED_EPSILON : resolution), &last); //store surface for top infill if only_one_perimeter_top if (i == 0 && (config->only_one_perimeter_top && this->upper_slices != NULL)) { diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index d4683b351..002b3aa11 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1471,7 +1471,7 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi && (ignore_phony || !(this_opt->is_phony() && other_opt->is_phony())) && ((*this_opt != *other_opt) || (this_opt->is_phony() != other_opt->is_phony()))) { - if (opt_key == "bed_shape" || opt_key == "compatible_prints" || opt_key == "compatible_printers") { + if (opt_key == "bed_shape" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "filament_ramming_parameters" || opt_key == "gcode_substitutions") { // Scalar variable, or a vector variable, which is independent from number of extruders, // thus the vector is presented to the user as a single input. // note that thumbnails are not here becasue it has individual # entries diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 98b7e2c8a..a2a870de5 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2141,7 +2141,7 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio out_first_layer->append(eloop); if (m_config.min_skirt_length.value > 0 && !first_layer_only) { // The skirt length is limited. Sum the total amount of filament length extruded, in mm. - extruded_length[extruder_idx] += unscale<double>(loop.length());// *extruders_e_per_mm[extruder_idx]; + extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx]; if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) { // Not extruded enough yet with the current extruder. Add another loop. if (i == 1 && extruded_length[extruder_idx] > 0) @@ -2374,7 +2374,11 @@ void Print::_make_brim(const Flow &flow, const PrintObjectPtrs &objects, ExPolyg if (!object->support_layers().empty()) { Polygons polys = object->support_layers().front()->support_fills.polygons_covered_by_spacing(flow.spacing_ratio, float(SCALED_EPSILON)); for (Polygon poly : polys) { - object_islands.emplace_back(brim_offset == 0 ? ExPolygon{ poly } : offset_ex(poly, brim_offset)[0]); + if (brim_offset == 0) { + object_islands.emplace_back(poly); + } else { + append(object_islands, offset_ex(Polygons{ poly }, brim_offset)); + } } } islands.reserve(islands.size() + object_islands.size() * object->m_instances.size()); diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index beabfb76b..4a348022f 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -64,9 +64,10 @@ std::string PrintBase::output_filename(const std::string &format, const std::str cfg.set_key_value("input_filename_base", new ConfigOptionString(filename_base)); } try { + uint16_t extruder_initial = config_override->option("initial_extruder") != nullptr ? config_override->option("initial_extruder")->getInt() : 0; boost::filesystem::path filepath = format.empty() ? - cfg.opt_string("input_filename_base") + default_ext : - this->placeholder_parser().process(format, 0, &cfg); + cfg.opt_string("input_filename_base") + default_ext : + this->placeholder_parser().process(format, extruder_initial, &cfg); //remove unwanted characters std::string forbidden_base; if (const ConfigOptionString* opt = this->placeholder_parser().external_config()->option<ConfigOptionString>("gcode_filename_illegal_char")) { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 68b4d3475..a8e1c49f3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -489,7 +489,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Max angle"); def->full_label = L("Brim ear max angle"); def->category = OptionCategory::skirtBrim; - def->tooltip = L("Maximum angle to let a brim ear appear. \nIf set to 0, no brim will be created. \nIf set to ~178, brim will be created on everything but strait sections."); + def->tooltip = L("Maximum angle to let a brim ear appear. \nIf set to 0, no brim will be created. \nIf set to ~178, brim will be created on everything but straight sections."); def->sidetext = L("°"); def->min = 0; def->max = 180; @@ -5932,6 +5932,12 @@ std::map<std::string,std::string> PrintConfigDef::from_prusa(t_config_option_key output["first_layer_min_speed"] = value; output["first_layer_infill_speed"] = value; } + if ("resolution" == opt_key) { + value = "0.0125"; + } + if ("gcode_resolution" == opt_key) { + output["min_length"] = value; + } return output; } @@ -6166,8 +6172,14 @@ void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, } } else if ("elephant_foot_min_width" == opt_key) { opt_key = "elefant_foot_min_width"; - } else if("first_layer_acceleration" == opt_key || "infill_acceleration" == opt_key || "bridge_acceleration" == opt_key || "default_acceleration" == opt_key || "perimeter_acceleration" == opt_key - || "overhangs_speed" == opt_key || "ironing_speed" == opt_key){ + } else if("first_layer_acceleration" == opt_key) { + if (value.find("%") != std::string::npos) { + // can't support %, so we uese the default accel a baseline for half-assed conversion + value = std::to_string(all_conf.get_abs_value(opt_key, all_conf.get_computed_value("default_acceleration"))); + } + } else if ("infill_acceleration" == opt_key || "bridge_acceleration" == opt_key || "default_acceleration" == opt_key || "perimeter_acceleration" == opt_key + || "overhangs_speed" == opt_key || "ironing_speed" == opt_key + ) { // remove '%' if (value.find("%") != std::string::npos) { value = std::to_string(all_conf.get_computed_value(opt_key)); diff --git a/src/slic3r/GUI/CalibrationBedDialog.cpp b/src/slic3r/GUI/CalibrationBedDialog.cpp index f24f86888..e0ec46615 100644 --- a/src/slic3r/GUI/CalibrationBedDialog.cpp +++ b/src/slic3r/GUI/CalibrationBedDialog.cpp @@ -103,11 +103,11 @@ void CalibrationBedDialog::create_geometry(wxCommandEvent& event_args) { plat->arrange(); //TODO add message } else { - model.objects[objs_idx[0]]->translate({ bed_min.x() + offsetx, bed_min.y() + bed_size.y() - offsety,0 }); - model.objects[objs_idx[1]]->translate({ bed_min.x() + bed_size.x() - offsetx,bed_min.y() + offsety , 0 }); - model.objects[objs_idx[2]]->translate({ bed_min.x() + bed_size.x()/2, bed_min.y() + bed_size.y() / 2, 0 }); - model.objects[objs_idx[3]]->translate({ bed_min.x() + offsetx, bed_min.y() + offsety, 0 }); - model.objects[objs_idx[4]]->translate({ bed_min.x() + bed_size.x() - offsetx,bed_min.y() + bed_size.y() - offsety,0 }); + model.objects[objs_idx[0]]->translate({ bed_min.x() + offsetx, bed_min.y() + bed_size.y() - offsety, 1 * zscale }); + model.objects[objs_idx[1]]->translate({ bed_min.x() + bed_size.x() - offsetx,bed_min.y() + offsety , 1 * zscale }); + model.objects[objs_idx[2]]->translate({ bed_min.x() + bed_size.x()/2, bed_min.y() + bed_size.y() / 2, 1 * zscale }); + model.objects[objs_idx[3]]->translate({ bed_min.x() + offsetx, bed_min.y() + offsety, 1 * zscale }); + model.objects[objs_idx[4]]->translate({ bed_min.x() + bed_size.x() - offsetx,bed_min.y() + bed_size.y() - offsety, 1 * zscale }); } /// --- main config, please modify object config when possible --- diff --git a/src/slic3r/GUI/CalibrationBridgeDialog.cpp b/src/slic3r/GUI/CalibrationBridgeDialog.cpp index 302c0c856..3755c43cf 100644 --- a/src/slic3r/GUI/CalibrationBridgeDialog.cpp +++ b/src/slic3r/GUI/CalibrationBridgeDialog.cpp @@ -96,7 +96,7 @@ void CalibrationBridgeDialog::create_geometry(std::string setting_to_test, bool float z_scale = nozzle_diameter / 0.4; //do scaling if (z_scale < 0.9 || 1.2 < z_scale) { - for (size_t i = 0; i < 5; i++) + for (size_t i = 0; i < nb_items; i++) model.objects[objs_idx[i]]->scale(1, 1, z_scale); } else { z_scale = 1; @@ -120,9 +120,9 @@ void CalibrationBridgeDialog::create_geometry(std::string setting_to_test, bool Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); Vec2d bed_min = BoundingBoxf(bed_shape->values).min; float offsety = 2 + 10 * 1 + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); - model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 0 }); + model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 2.5 * z_scale }); for (int i = 1; i < nb_items; i++) { - model.objects[objs_idx[i]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2 + (i % 2 == 0 ? -1 : 1) * offsety * ((i + 1) / 2), 0 }); + model.objects[objs_idx[i]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2 + (i % 2 == 0 ? -1 : 1) * offsety * ((i + 1) / 2), 2.5 * z_scale }); } // if not enough space, forget about complete_objects if (bed_size.y() < offsety * (nb_items + 1)) diff --git a/src/slic3r/GUI/CalibrationFlowDialog.cpp b/src/slic3r/GUI/CalibrationFlowDialog.cpp index edaafcaeb..5cabc666a 100644 --- a/src/slic3r/GUI/CalibrationFlowDialog.cpp +++ b/src/slic3r/GUI/CalibrationFlowDialog.cpp @@ -78,7 +78,7 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { //do scaling if (xyScale < 0.9 || 1.2 < xyScale) { for (size_t i = 0; i < 5; i++) - model.objects[objs_idx[i]]->scale(xyScale, xyScale, zscale); + model.objects[objs_idx[i]]->scale(xyScale, xyScale, zscale); // base: 10 10 1 } else { for (size_t i = 0; i < 5; i++) model.objects[objs_idx[i]]->scale(1, 1, zscale); @@ -106,7 +106,7 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { add_part(model.objects[objs_idx[4]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "_0.amf").string(), Vec3d{ 10 * xyScale,0,zshift }, Vec3d{ xyScale , xyScale, zscale_number}); } for (size_t i = 0; i < 5; i++) { - add_part(model.objects[objs_idx[i]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "O.amf").string(), Vec3d{ 0,0,zscale/2.f + 0.5 }, Vec3d{xyScale , xyScale, layer_height / 0.2}); + add_part(model.objects[objs_idx[i]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_flow" / "O.amf").string(), Vec3d{ 0,0,zscale/2.f + 0.5 }, Vec3d{xyScale , xyScale, layer_height / 0.2}); // base: 0.2mm height } /// --- translate ---; @@ -118,11 +118,11 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { Vec2d bed_min = BoundingBoxf(bed_shape->values).min; float offsetx = 3 + 20 * xyScale + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); float offsety = 3 + 20 * xyScale + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); - model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, 0 }); - model.objects[objs_idx[1]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 , 0 }); - model.objects[objs_idx[2]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 + offsety, 0 }); - model.objects[objs_idx[3]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, 0 }); - model.objects[objs_idx[4]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 + offsety, 0 }); + model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, zscale / 2 }); + model.objects[objs_idx[1]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 , zscale / 2 }); + model.objects[objs_idx[2]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 + offsety, zscale / 2 }); + model.objects[objs_idx[3]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, zscale / 2 }); + model.objects[objs_idx[4]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 + offsety, zscale / 2 }); // if not enough space, forget about complete_objects if (bed_size.y() < offsety * 2 + 25 * xyScale + brim_width || bed_size.x() < offsetx + 25 * xyScale + brim_width) diff --git a/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp b/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp index 829193326..323eb331b 100644 --- a/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp +++ b/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp @@ -99,12 +99,12 @@ void CalibrationOverBridgeDialog::create_geometry(bool over_bridge) { Vec2d bed_min = BoundingBoxf(bed_shape->values).min; float offsetx = 3 + 30 * xyz_scale + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); float offsety = 3 + 25 * xyz_scale + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); - model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, 0 }); - model.objects[objs_idx[1]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 , 0 }); - model.objects[objs_idx[2]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 + offsety, 0 }); - model.objects[objs_idx[3]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, 0 }); - model.objects[objs_idx[4]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 , 0 }); - model.objects[objs_idx[5]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 + offsety, 0 }); + model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, (nozzle_diameter / 0.4) }); + model.objects[objs_idx[1]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 , (nozzle_diameter / 0.4) }); + model.objects[objs_idx[2]]->translate({ bed_min.x() + bed_size.x() / 2 - offsetx / 2, bed_min.y() + bed_size.y() / 2 + offsety, (nozzle_diameter / 0.4) }); + model.objects[objs_idx[3]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 - offsety, (nozzle_diameter / 0.4) }); + model.objects[objs_idx[4]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 , (nozzle_diameter / 0.4) }); + model.objects[objs_idx[5]]->translate({ bed_min.x() + bed_size.x() / 2 + offsetx / 2, bed_min.y() + bed_size.y() / 2 + offsety, (nozzle_diameter / 0.4) }); // if not enough space, forget about complete_objects if (bed_size.y() < offsety * 2 + 30 * xyz_scale + brim_width * 2 + skirt_width * 2 + 5 || bed_size.x() < offsetx + 35 * xyz_scale + brim_width * 2 + skirt_width * 2 + 5) diff --git a/src/slic3r/GUI/CalibrationRetractionDialog.cpp b/src/slic3r/GUI/CalibrationRetractionDialog.cpp index 1de3f179c..f69c44219 100644 --- a/src/slic3r/GUI/CalibrationRetractionDialog.cpp +++ b/src/slic3r/GUI/CalibrationRetractionDialog.cpp @@ -179,7 +179,7 @@ void CalibrationRetractionDialog::create_geometry(wxCommandEvent& event_args) { int mytemp = temp - temp_decr * id_item; if (mytemp <= 285 && mytemp >= 180 && mytemp % 5 == 0) { add_part(model.objects[objs_idx[id_item]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_temp" / ("t" + std::to_string(mytemp) + ".amf")).string(), - Vec3d{ 0,0, scale * 0.2 - 4.8 }, Vec3d{ scale,scale,scale }); + Vec3d{ 0,0, scale * 0.0 - 4.8 }, Vec3d{ scale,scale,scale }); model.objects[objs_idx[id_item]]->volumes[1]->rotate(PI / 2, Vec3d(0, 0, 1)); model.objects[objs_idx[id_item]]->volumes[1]->rotate(-PI / 2, Vec3d(1, 0, 0)); } @@ -199,7 +199,7 @@ void CalibrationRetractionDialog::create_geometry(wxCommandEvent& event_args) { Vec2d bed_min = BoundingBoxf(bed_shape->values).min; float offset = 4 + 26 * scale * 1 + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); if (nb_items == 1) { - model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 0 }); + model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, zscale_number }); } else { has_to_arrange = true; } diff --git a/src/slic3r/GUI/CalibrationTempDialog.cpp b/src/slic3r/GUI/CalibrationTempDialog.cpp index 440975887..70c4b199b 100644 --- a/src/slic3r/GUI/CalibrationTempDialog.cpp +++ b/src/slic3r/GUI/CalibrationTempDialog.cpp @@ -109,7 +109,8 @@ void CalibrationTempDialog::create_geometry(wxCommandEvent& event_args) { float zshift = (1 - xyzScale) / 2; if (temperature > 175 && temperature < 290 && temperature%5==0) { tower.push_back(add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_temp" / ("t"+std::to_string(temperature)+".amf")).string(), - Vec3d{ xyzScale * 5, - xyzScale * 2.5, zshift - xyzScale * 2.5}, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 })); + //Vec3d{ xyzScale * 5, - xyzScale * 2.5, zshift - xyzScale * 2.5}, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 })); + Vec3d{ 8 - xyzScale * 5, -xyzScale * 2.3, xyzScale * (0 * 10 - 2.45) }, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 })); } for (int16_t i = 1; i < nb_items; i++) { tower.push_back(add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_temp" / ("Smart_compact_temperature_calibration_item.amf")).string(), @@ -117,7 +118,7 @@ void CalibrationTempDialog::create_geometry(wxCommandEvent& event_args) { int sub_temp = temperature - i * step_temp; if (sub_temp > 175 && sub_temp < 290 && sub_temp % 5 == 0) { tower.push_back(add_part(model.objects[objs_idx[0]], (boost::filesystem::path(Slic3r::resources_dir()) / "calibration" / "filament_temp" / ("t" + std::to_string(sub_temp) + ".amf")).string(), - Vec3d{ xyzScale * 5, -xyzScale * 2.5, xyzScale * (i * 10 - 2.5) }, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 })); + Vec3d{ 8 - xyzScale * 5, -xyzScale * 2.3, xyzScale * (i * 10 - 2.5) }, Vec3d{ xyzScale, xyzScale, xyzScale * 0.43 })); } } @@ -128,7 +129,7 @@ void CalibrationTempDialog::create_geometry(wxCommandEvent& event_args) { const ConfigOptionPoints* bed_shape = printer_config->option<ConfigOptionPoints>("bed_shape"); Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); Vec2d bed_min = BoundingBoxf(bed_shape->values).min; - model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 0 }); + model.objects[objs_idx[0]]->translate({ bed_min.x() + bed_size.x() / 2, bed_min.y() + bed_size.y() / 2, 5 * xyzScale - 5 }); } /// --- main config, please modify object config when possible --- diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 0bc236e43..8e1e929c9 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -15,6 +15,9 @@ #include <wx/tooltip.h> #include <wx/notebook.h> #include <wx/richtooltip.h> +#ifdef __WXGTK2__ +#include <wx/tglbtn.h> +#endif #include <wx/tokenzr.h> #include <boost/algorithm/string/predicate.hpp> #include "OG_CustomCtrl.hpp" @@ -875,24 +878,64 @@ void CheckBox::BUILD() { m_last_meaningful_value = static_cast<unsigned char>(check_value); - // Set Label as a string of at least one space simbol to correct system scaling of a CheckBox - auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(" "), wxDefaultPosition, size); - temp->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT); - temp->SetValue(check_value); - if (m_opt.readonly) temp->Disable(); - - temp->Bind(wxEVT_CHECKBOX, ([this](wxCommandEvent e) { - m_is_na_val = false; - on_change_field(); - }), temp->GetId()); - - // recast as a wxWindow to fit the calling convention - window = dynamic_cast<wxWindow*>(temp); +#ifdef __WXGTK2__ + //gtk2 can't resize checkboxes, so we are using togglable buttons instead + if (m_em_unit > 14) { + size = wxSize(def_width_thinner() * m_em_unit / 2, def_width_thinner() * m_em_unit / 2); + auto temp = new wxToggleButton(m_parent, wxID_ANY, wxString(" "), wxDefaultPosition, size, wxCHK_2STATE); + temp->Bind(wxEVT_TOGGLEBUTTON, ([this, temp](wxCommandEvent e) { + m_is_na_val = false; + if (temp->GetValue()) + temp->SetLabel("X"); + else + temp->SetLabel(""); + on_change_field(); + }), temp->GetId()); + // recast as a wxWindow to fit the calling convention + window = dynamic_cast<wxWindow*>(temp); + } + else +#endif + { + // Set Label as a string of at least one space simbol to correct system scaling of a CheckBox + auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(" "), wxDefaultPosition, size, wxCHK_2STATE); + temp->Bind(wxEVT_CHECKBOX, ([this](wxCommandEvent e) { + m_is_na_val = false; + on_change_field(); + }), temp->GetId()); + // recast as a wxWindow to fit the calling convention + window = dynamic_cast<wxWindow*>(temp); + temp->SetValue(check_value); + } + window->SetFont(Slic3r::GUI::wxGetApp().normal_font()); + if (!wxOSX) window->SetBackgroundStyle(wxBG_STYLE_PAINT); + if (m_opt.readonly) window->Enable(false); + this->set_tooltip(check_value ? "true" : "false"); } +void CheckBox::set_widget_value(bool new_val) +{ + wxCheckBox* chk = dynamic_cast<wxCheckBox*>(window); + if (chk != nullptr) { + chk->SetValue(new_val); + } +#ifdef __WXGTK2__ + else + { + wxToggleButton* tgl = dynamic_cast<wxToggleButton*>(window); + if (tgl) { + tgl->SetValue(new_val); + if (new_val) + tgl->SetLabel("X"); + else + tgl->SetLabel(""); + } + } +#endif +} + void CheckBox::set_value(const boost::any& value, bool change_event) { m_disable_change_event = !change_event; @@ -900,10 +943,10 @@ void CheckBox::set_value(const boost::any& value, bool change_event) m_is_na_val = boost::any_cast<unsigned char>(value) == ConfigOptionBoolsNullable::nil_value(); if (!m_is_na_val) m_last_meaningful_value = value; - dynamic_cast<wxCheckBox*>(window)->SetValue(m_is_na_val ? false : boost::any_cast<unsigned char>(value) != 0); + set_widget_value(m_is_na_val ? false : boost::any_cast<unsigned char>(value) != 0); } else - dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value)); + set_widget_value(boost::any_cast<bool>(value)); m_disable_change_event = false; } @@ -911,7 +954,7 @@ void CheckBox::set_last_meaningful_value() { if (m_opt.nullable) { m_is_na_val = false; - dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<unsigned char>(m_last_meaningful_value) != 0); + set_widget_value(boost::any_cast<unsigned char>(m_last_meaningful_value) != 0); on_change_field(); } } @@ -920,7 +963,7 @@ void CheckBox::set_na_value() { if (m_opt.nullable) { m_is_na_val = true; - dynamic_cast<wxCheckBox*>(window)->SetValue(false); + set_widget_value(false); on_change_field(); } } @@ -928,7 +971,18 @@ void CheckBox::set_na_value() boost::any& CheckBox::get_value() { // boost::any m_value; - bool value = dynamic_cast<wxCheckBox*>(window)->GetValue(); + bool value = false; + wxCheckBox* chk = dynamic_cast<wxCheckBox*>(window); + if (chk != nullptr) { + value = chk->GetValue(); + } +#ifdef __WXGTK2__ + else + { + wxToggleButton* tgl = dynamic_cast<wxToggleButton*>(window); + if (tgl) value = tgl->GetValue(); + } +#endif if (m_opt.type == coBool) m_value = static_cast<bool>(value); else @@ -940,8 +994,17 @@ void CheckBox::msw_rescale() { Field::msw_rescale(); - wxCheckBox* field = dynamic_cast<wxCheckBox*>(window); - field->SetMinSize(wxSize(-1, int(1.5f*field->GetFont().GetPixelSize().y +0.5f))); + wxCheckBox* chk = dynamic_cast<wxCheckBox*>(window); + if (chk != nullptr) { + chk->SetMinSize(wxSize(-1, int(1.5f * chk->GetFont().GetPixelSize().y + 0.5f))); + } +#ifdef __WXGTK2__ + else + { //a bit useless as it's a windows-only func. To have a correct thing, you have to del the previous window and create a new one anyway. + wxToggleButton* tgl = dynamic_cast<wxToggleButton*>(window); + if (tgl) tgl->SetMinSize(wxSize(def_width_thinner() * m_em_unit / 2, def_width_thinner() * m_em_unit / 2)); + } +#endif } diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index e48473346..5095015ef 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -295,6 +295,8 @@ public: class CheckBox : public Field { using Field::Field; bool m_is_na_val {false}; + + void set_widget_value(bool new_val); public: CheckBox(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} CheckBox(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} @@ -315,8 +317,8 @@ public: void msw_rescale() override; - void enable() override { dynamic_cast<wxCheckBox*>(window)->Enable(); } - void disable() override { dynamic_cast<wxCheckBox*>(window)->Disable(); } + void enable() override { window->Enable(true); } + void disable() override { window->Enable(false); } wxWindow* getWindow() override { return window; } }; diff --git a/src/slic3r/GUI/FreeCADDialog.cpp b/src/slic3r/GUI/FreeCADDialog.cpp index c02788ff6..1d2bc1b8a 100644 --- a/src/slic3r/GUI/FreeCADDialog.cpp +++ b/src/slic3r/GUI/FreeCADDialog.cpp @@ -452,7 +452,7 @@ void FreeCADDialog::on_autocomp_complete(wxStyledTextEvent& event) { } else if (((command->type & PyCommandType::pctMODIFIER) != 0) && !has_already_parenthese) { int nb_add_pos = 0; //check if there's not a forgotten '.' before - std::cout << "char before the word : " << stc->GetCharAt(currentPos - command->name.length() - 1) << "\n"; + BOOST_LOG_TRIVIAL(warning) << "char before the word : " << stc->GetCharAt(currentPos - command->name.length() - 1) << "\n"; if (stc->GetCharAt(currentPos - command->name.length() - 1) == ')') { stc->InsertText(currentPos - command->name.length(), "."); nb_add_pos++; @@ -481,19 +481,9 @@ void FreeCADDialog::on_word_change_for_autocomplete(wxStyledTextEvent& event) { if ((event.GetModificationType() & (wxSTC_MOD_INSERTTEXT | wxSTC_PERFORMED_USER)) != (wxSTC_MOD_INSERTTEXT | wxSTC_PERFORMED_USER)) { return; // not our event } - //std::cout << "word_change "<<event.GetModificationType()<<" typed: " << (int)stc->GetCharAt(current_pos) << " with len_entered " << len_entered - //<< ", event_string='"<< event_string << "' (last='"<< (event_string.empty()?-1:event_string.Last().GetValue()) <<"') ; Str is '" << str << "' with length " << str.length() - // << "' chars are (c-1)='" << stc->GetCharAt(current_pos - 1) << "' : (c)='" << stc->GetCharAt(current_pos) - //<< "', text length=" << stc->GetTextLength() << ", currentPos=" << current_pos << " , " << int('\n') << "\n"; - //std::cout << "test: " << (!(stc->GetCharAt(current_pos - 1) <= '\n')) << ", 2=" << (len_entered >= 0) << ", 3=" << (!str.empty()) - // << ", 4=" << (std::regex_match(str.ToStdString(), word_regex)) - // <<", Mod5="<<((event.GetModificationType() & wxSTC_STARTACTION) != 0) - // <<", 6="<< (current_pos <= 1 || str != ".")<<", 6b="<< (str == ".") - // << "\n"; if ((event.GetModificationType() & wxSTC_STARTACTION) != 0 && (str.empty() || str.Last() != '.')) return; - //if (!event_string.empty() && !str.empty() && int(str[str.length() - 1]) != event_string.Last().GetValue()) std::cout << "removecall?\n"; if (len_entered >= 0 && !str.empty() && std::regex_match(str.ToStdString(), word_regex)) { //check for possible words //todo: check for '.' to filter for modifiers @@ -509,33 +499,24 @@ void FreeCADDialog::on_word_change_for_autocomplete(wxStyledTextEvent& event) { if (nb_words >= 1) stc->AutoCompShow(len_entered, possible); } else if (!str.empty() && str.Last() == '.') { - //wxString possible; - //for(const wxString &str : modif_words) - // possible += possible.empty() ? str : (" " + str); wxString possible; for (const PyCommand &cmd : commands) { if (((cmd.type & PyCommandType::pctMODIFIER) != 0) && ((cmd.type & PyCommandType::pctDO_NOT_SHOW) == 0)) { possible += possible.empty() ? cmd.name : (" " + cmd.name); } } - //std::cout << "autocomplete: modifier: '"<< possible.ToStdString() <<"'\n"; if (possible.length() >= 1) stc->AutoCompShow(0, possible); } } void FreeCADDialog::on_char_add(wxStyledTextEvent& event) { - //if (event.GetUpdated() != wxSTC_UPDATE_CONTENT) return; wxStyledTextCtrl* stc = (wxStyledTextCtrl*)event.GetEventObject(); // Find the word start int current_pos = stc->GetCurrentPos(); int word_start_pos = stc->WordStartPosition(current_pos, true); //int len_entered = current_pos - word_start_pos; const wxString str = stc->GetTextRange(word_start_pos, current_pos + 1); - //if(current_pos>1) - // std::cout << "char typed: " << (char)stc->GetCharAt(current_pos)<<" with length "<< len_entered - // << ", str is "<< str << "chars are '"<< stc->GetCharAt(current_pos - 1) << "' : '" << stc->GetCharAt(current_pos) - // <<"', text length="<< stc->GetTextLength()<<", currentPos="<< current_pos<<" , "<<int('\n')<<"\n"; if(current_pos > 2 && stc->GetCharAt(current_pos-1) == '\n'){ //TODO: check that we are really in a freepyscad section. int lastpos = current_pos - 2; @@ -615,7 +596,6 @@ void FreeCADDialog::comment(bool is_switch) { } void FreeCADDialog::on_char_type(wxKeyEvent &event) { - //std::cout << "on_char_type " << event.GetUnicodeKey() <<", " << event.GetModifiers() << "\n"; if (event.GetUnicodeKey() == 'Q' && event.GetModifiers() == wxMOD_CONTROL) { comment(true); } else if (event.GetUnicodeKey() == 'K' && event.GetModifiers() == wxMOD_CONTROL) { @@ -629,14 +609,12 @@ void FreeCADDialog::on_char_type(wxKeyEvent &event) { // space, back, del are ok but no ascii char void FreeCADDialog::on_key_type(wxKeyEvent& event) { - //std::cout << "on_key_type " << event.GetUnicodeKey() << " ? "<< int('Q') <<", "<< event.GetKeyCode() << ", " << event.GetModifiers() << "\n"; if (event.GetKeyCode() == WXK_SPACE && event.GetModifiers() == wxMOD_CONTROL) { //get word, if any int current_pos = m_text->GetCurrentPos(); int word_start_pos = m_text->WordStartPosition(current_pos, true); const wxString str = m_text->GetTextRange(word_start_pos, current_pos); - //std::cout << "ctrl-space! " << event.GetEventType() << " '" << str.ToStdString() << "' " << int(m_text->GetCharAt(current_pos - 1)) << "\n"; if (current_pos > 0 && m_text->GetCharAt(current_pos - 1) == '.') { //only modifiers wxString possible; @@ -656,7 +634,6 @@ void FreeCADDialog::on_key_type(wxKeyEvent& event) nb_words++; possible += possible.empty() ? cmd.name : (" " + cmd.name); } } - //std::cout << "space autocomplete: find " << nb_words << " forstring '" << str << "'\n"; // Display the autocompletion list if (nb_words >= 1) m_text->AutoCompShow(str.length(), possible); @@ -714,10 +691,8 @@ void FreeCADDialog::createSTC() m_text = new wxStyledTextCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); - //m_text->SetMarginWidth(MARGIN_LINE_NUMBERS, 50); m_text->StyleSetForeground(wxSTC_STYLE_LINENUMBER, wxColour(75, 75, 75)); m_text->StyleSetBackground(wxSTC_STYLE_LINENUMBER, wxColour(220, 220, 220)); - //m_text->SetMarginType(MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER); m_text->SetTabWidth(4); m_text->SetIndent(4); @@ -818,22 +793,20 @@ void FreeCADDialog::test_update_script_file(std::string &json) { boost::locale::generator gen; std::locale loc = gen.generate(""); // or "C", "en_US.UTF-8" etc. std::locale::global(loc); - std::cout.imbue(loc); - std::cout << "root.commit.committer.date=" << str_date << "\n"; + BOOST_LOG_TRIVIAL(debug) << "root.commit.committer.date=" << str_date; std::time_t commit_time = parse_iso_time(str_date); - std::cout << "github time_t = "<<commit_time<<"\n"; + BOOST_LOG_TRIVIAL(debug) << "github time_t = " << commit_time; std::time_t last_modif = boost::filesystem::last_write_time(pyscad_path / "freepyscad.py"); - std::cout << "pyscad_path time_t = " << commit_time << "\n"; + BOOST_LOG_TRIVIAL(debug) << "pyscad_path time_t = " << commit_time; if (commit_time > last_modif) { - std::cout << "have to update!!\n"; get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/__init__.py", pyscad_path / "__init__.py"); get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/Init.py", pyscad_path / "Init.py"); get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/freepyscad.py", pyscad_path / "freepyscad.py"); } } catch (std::exception ex) { - std::cerr << "Error, cannot parse https://api.github.com/repos/supermerill/FreePySCAD/commits/master: " << ex.what() << "\n"; + BOOST_LOG_TRIVIAL(error) << "Error, cannot parse https://api.github.com/repos/supermerill/FreePySCAD/commits/master: " << ex.what(); } } @@ -872,7 +845,7 @@ bool FreeCADDialog::init_start_python() { get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/__init__.py", scripts_path / "FreePySCAD" / "__init__.py"); get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/Init.py", scripts_path / "FreePySCAD" / "Init.py"); get_file_from_web("https://raw.githubusercontent.com/supermerill/FreePySCAD/master/freepyscad.py", scripts_path / "FreePySCAD" / "freepyscad.py"); - }else if (!update_done){ + } else if (!update_done) { update_done = true; //try to check last version on website //it's async so maybe you won't update it in time, but it's not the end of the world. @@ -881,7 +854,7 @@ bool FreeCADDialog::init_start_python() { get_string_from_web_async("https://api.github.com/repos/supermerill/FreePySCAD/commits/master", this, &FreeCADDialog::test_update_script_file); } - exec_var->process.reset(new boost::process::child(pythonpath.string() + " -u -i", boost::process::std_in < exec_var->pyin, + exec_var->process.reset(new boost::process::child(pythonpath.string() + " -u -i", boost::process::std_in < exec_var->pyin, boost::process::std_out > exec_var->data_out, boost::process::std_err > exec_var->data_err, exec_var->ios)); exec_var->pyin << "import sys" << std::endl; #ifndef __WINDOWS__ @@ -892,9 +865,7 @@ bool FreeCADDialog::init_start_python() { exec_var->pyin << "import Part" << std::endl; exec_var->pyin << "import Draft" << std::endl; exec_var->pyin << "sys.path.append('" << scripts_path.generic_string() << "')" << std::endl; - //std::cout << "sys.path.append('" << pyscad_path.generic_string() << "')" << std::endl; exec_var->pyin << "from FreePySCAD.freepyscad import *" << std::endl; - //std::cout << "from FreePySCAD.freepyscad import *" << std::endl; exec_var->pyin << "App.newDocument(\"document\")" << std::endl; #ifdef __WINDOWS__ exec_var->pyin << "set_font_dir(\"C:/Windows/Fonts/\")" << std::endl; @@ -997,7 +968,6 @@ void FreeCADDialog::create_geometry(wxCommandEvent& event_args) { } - //std::cout<< "scene().redraw(" << boost::replace_all_copy(boost::replace_all_copy(m_text->GetText(), "\r", ""), "\n", "") << ")" << std::endl; //exec_var->pyin << "scene().redraw("<< boost::replace_all_copy(boost::replace_all_copy(m_text->GetText(), "\r", ""), "\n", "") <<")" << std::endl; exec_var->pyin << ("exec(open('" + temp_file.generic_string() + "').read())\n"); //filter to avoid importing "intermediate" object like ones from importStl @@ -1008,11 +978,9 @@ void FreeCADDialog::create_geometry(wxCommandEvent& event_args) { end_python(); std::string pyout_str_hello; - std::cout << "==cout==\n"; - std::cout << exec_var->data_out.get(); - std::cout << "==cerr==\n"; + BOOST_LOG_TRIVIAL(trace) << "==cout==\n" << exec_var->data_out.get()<<"\n"; std::string errStr = exec_var->data_err.get(); - std::cout << errStr << "\n"; + BOOST_LOG_TRIVIAL(trace) << "==cerr==\n" << errStr <<"\n"; std::string cleaned = boost::replace_all_copy(boost::replace_all_copy(errStr, ">>> ", ""),"\r",""); boost::replace_all(cleaned, "QWaitCondition: Destroyed while threads are still waiting\n", ""); boost::replace_all(cleaned, "Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n", ""); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index dfca32b7a..3bcd38ab8 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1204,11 +1204,19 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan if (this->activate_existing(notification.get())) { m_pop_notifications.back()->update(notification->get_data()); - canvas.request_extra_frame_delayed(33); + //request_extra_frame_delayed has to run from main thread + if (wxThread::IsMain()) + canvas.request_extra_frame_delayed(33); + else + wxGetApp().CallAfter([&canvas]() { canvas.request_extra_frame_delayed(33); }); return false; } else { m_pop_notifications.emplace_back(std::move(notification)); - canvas.request_extra_frame_delayed(33); + //request_extra_frame_delayed has to run from main thread + if (wxThread::IsMain()) + canvas.request_extra_frame_delayed(33); + else + wxGetApp().CallAfter([&canvas]() { canvas.request_extra_frame_delayed(33); }); return true; } } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0739bbd56..95766b1a5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1899,6 +1899,9 @@ bool Tab::create_pages(std::string setting_type_name, int idx_page) } else if (boost::starts_with(params[i], "width$")) { option.opt.width = atoi(params[i].substr(6, params[i].size() - 6).c_str()); +#ifdef __WXGTK3__ + option.opt.width += 4; // add width for the big [-][+] buttons +#endif } else if (boost::starts_with(params[i], "height$")) { option.opt.height = atoi(params[i].substr(7, params[i].size() - 7).c_str()); diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 970cd8528..3053073c4 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -9,7 +9,11 @@ #include <wx/sizer.h> int scale(const int val) { return val * Slic3r::GUI::wxGetApp().em_unit(); } -int ITEM_WIDTH() { return scale(6); } +#ifdef __WXGTK3__ +int ITEM_WIDTH() { return scale(10); } +#else +int ITEM_WIDTH() { return scale(6); } +#endif RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters) : wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/) |