diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp index 2b2893c80..c34aca8f2 100644 --- a/src/libslic3r/EdgeGrid.cpp +++ b/src/libslic3r/EdgeGrid.cpp @@ -9,7 +9,9 @@ #endif /* SLIC3R_GUI */ #include "libslic3r.h" +#include "ClipperUtils.hpp" #include "EdgeGrid.hpp" +#include "SVG.hpp" #if 0 // Enable debugging and assert in this file. @@ -756,8 +758,8 @@ void EdgeGrid::Grid::calculate_sdf() float search_radius = float(m_resolution<<1); m_signed_distance_field.assign(nrows * ncols, search_radius); // For each cell: - for (size_t r = 0; r < m_rows; ++ r) { - for (size_t c = 0; c < m_cols; ++ c) { + for (int r = 0; r < (int)m_rows; ++ r) { + for (int c = 0; c < (int)m_cols; ++ c) { const Cell &cell = m_cells[r * m_cols + c]; // For each segment in the cell: for (size_t i = cell.begin; i != cell.end; ++ i) { @@ -842,6 +844,8 @@ void EdgeGrid::Grid::calculate_sdf() #if 0 static int iRun = 0; ++ iRun; + if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) + wxImage::AddHandler(new wxPNGHandler); //#ifdef SLIC3R_GUI { wxImage img(ncols, nrows); @@ -1356,9 +1360,101 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co return out; } +inline int segments_could_intersect( + const Slic3r::Point &ip1, const Slic3r::Point &ip2, + const Slic3r::Point &jp1, const Slic3r::Point &jp2) +{ + Vec2i64 iv = (ip2 - ip1).cast<int64_t>(); + Vec2i64 vij1 = (jp1 - ip1).cast<int64_t>(); + Vec2i64 vij2 = (jp2 - ip1).cast<int64_t>(); + int64_t tij1 = cross2(iv, vij1); + int64_t tij2 = cross2(iv, vij2); + int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum + int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0); + return sij1 * sij2; +} + +inline bool segments_intersect( + const Slic3r::Point &ip1, const Slic3r::Point &ip2, + const Slic3r::Point &jp1, const Slic3r::Point &jp2) +{ + return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 && + segments_could_intersect(jp1, jp2, ip1, ip2) <= 0; +} + +std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> EdgeGrid::Grid::intersecting_edges() const +{ + std::vector<std::pair<ContourEdge, ContourEdge>> out; + // For each cell: + for (int r = 0; r < (int)m_rows; ++ r) { + for (int c = 0; c < (int)m_cols; ++ c) { + const Cell &cell = m_cells[r * m_cols + c]; + // For each pair of segments in the cell: + for (size_t i = cell.begin; i != cell.end; ++ i) { + const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first]; + size_t ipt = m_cell_data[i].second; + // End points of the line segment and their vector. + const Slic3r::Point &ip1 = ipts[ipt]; + const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]; + for (size_t j = i + 1; j != cell.end; ++ j) { + const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first]; + size_t jpt = m_cell_data[j].second; + // End points of the line segment and their vector. + const Slic3r::Point &jp1 = jpts[jpt]; + const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1]; + if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) + // Segments of the same contour share a common vertex. + continue; + if (segments_intersect(ip1, ip2, jp1, jp2)) { + // The two segments intersect. Add them to the output. + int jfirst = (&jpts < &ipts) || (&jpts == &ipts && jpt < ipt); + out.emplace_back(jfirst ? + std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)) : + std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt))); + } + } + } + } + } + Slic3r::sort_remove_duplicates(out); + return out; +} + +bool EdgeGrid::Grid::has_intersecting_edges() const +{ + // For each cell: + for (int r = 0; r < (int)m_rows; ++ r) { + for (int c = 0; c < (int)m_cols; ++ c) { + const Cell &cell = m_cells[r * m_cols + c]; + // For each pair of segments in the cell: + for (size_t i = cell.begin; i != cell.end; ++ i) { + const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first]; + size_t ipt = m_cell_data[i].second; + // End points of the line segment and their vector. + const Slic3r::Point &ip1 = ipts[ipt]; + const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]; + for (size_t j = i + 1; j != cell.end; ++ j) { + const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first]; + size_t jpt = m_cell_data[j].second; + // End points of the line segment and their vector. + const Slic3r::Point &jp1 = jpts[jpt]; + const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1]; + if (! (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) && + segments_intersect(ip1, ip2, jp1, jp2)) + return true; + } + } + } + } + return false; +} + #if 0 void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path) { + if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) + wxImage::AddHandler(new wxPNGHandler); + unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution; unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution; wxImage img(w, h); @@ -1450,4 +1546,59 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo } #endif /* SLIC3R_GUI */ +// Find all pairs of intersectiong edges from the set of polygons. +std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersecting_edges(const Polygons &polygons) +{ + double len = 0; + size_t cnt = 0; + BoundingBox bbox; + for (const Polygon &poly : polygons) { + if (poly.points.size() < 2) + continue; + for (size_t i = 0; i < poly.points.size(); ++ i) { + bbox.merge(poly.points[i]); + size_t j = (i == 0) ? (poly.points.size() - 1) : i - 1; + len += (poly.points[j] - poly.points[i]).cast<double>().norm(); + ++ cnt; + } + } + len /= double(cnt); + bbox.offset(20); + EdgeGrid::Grid grid; + grid.set_bbox(bbox); + grid.create(polygons, len); + return grid.intersecting_edges(); +} + +// Find all pairs of intersectiong edges from the set of polygons, highlight them in an SVG. +void export_intersections_to_svg(const std::string &filename, const Polygons &polygons) +{ + std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersections = intersecting_edges(polygons); + BoundingBox bbox = get_extents(polygons); + SVG svg(filename.c_str(), bbox); + svg.draw(union_ex(polygons), "gray", 0.25f); + svg.draw_outline(polygons, "black"); + std::set<const Points*> intersecting_contours; + for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &ie : intersections) { + intersecting_contours.insert(ie.first.first); + intersecting_contours.insert(ie.second.first); + } + // Highlight the contours with intersections. + coord_t line_width = coord_t(scale_(0.01)); + for (const Points *ic : intersecting_contours) { + svg.draw_outline(Polygon(*ic), "green"); + svg.draw_outline(Polygon(*ic), "black", line_width); + } + // Paint the intersections. + for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &intersecting_edges : intersections) { + auto edge = [](const EdgeGrid::Grid::ContourEdge &e) { + return Line(e.first->at(e.second), + e.first->at((e.second + 1 == e.first->size()) ? 0 : e.second + 1)); + }; + svg.draw(edge(intersecting_edges.first), "red", line_width); + svg.draw(edge(intersecting_edges.second), "red", line_width); + } + svg.Close(); +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index d6bf03ce6..04319bb26 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -133,7 +133,7 @@ void FillGyroid::_fill_surface_single( // no rotation is supported for this infill pattern (yet) BoundingBox bb = expolygon.contour.bounding_box(); // Density adjusted to have a good %of weight. - double density_adjusted = std::max(0., params.density * 2.); + double density_adjusted = std::max(0., params.density * 2.44); // Distance between the gyroid waves in scaled coordinates. coord_t distance = coord_t(scale_(this->spacing) / density_adjusted); diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index f8f5fddd6..5c24cd71c 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -545,7 +545,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( m_print_brim = true; // Ask our writer about how much material was consumed: - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + if (m_current_tool < m_used_filament_length.size()) + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); ToolChangeResult result; result.priming = true; @@ -698,7 +699,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo m_print_brim = false; // Mark the brim as extruded // Ask our writer about how much material was consumed: - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + if (m_current_tool < m_used_filament_length.size()) + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); ToolChangeResult result; result.priming = false; @@ -868,7 +870,8 @@ void WipeTowerPrusaMM::toolchange_Change( material_type new_material) { // Ask the writer about how much of the old filament we consumed: - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + if (m_current_tool < m_used_filament_length.size()) + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); // Speed override for the material. Go slow for flex and soluble materials. int speed_override; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 84c34e232..b02153128 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -521,10 +521,14 @@ void Model::adjust_min_z() unsigned int Model::get_auto_extruder_id(unsigned int max_extruders) { unsigned int id = s_auto_extruder_id; - - if (++s_auto_extruder_id > max_extruders) + if (id > max_extruders) { + // The current counter is invalid, likely due to switching the printer profiles + // to a profile with a lower number of extruders. reset_auto_extruder_id(); - + id = s_auto_extruder_id; + } else if (++ s_auto_extruder_id > max_extruders) { + reset_auto_extruder_id(); + } return id; } diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 521b4dc4f..38020d6e8 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -107,6 +107,23 @@ extern BoundingBox get_extents(const MultiPoint &mp); extern BoundingBox get_extents_rotated(const std::vector<Point> &points, double angle); extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle); +inline double length(const Points &pts) { + double total = 0; + if (! pts.empty()) { + auto it = pts.begin(); + for (auto it_prev = it ++; it != pts.end(); ++ it, ++ it_prev) + total += (*it - *it_prev).cast<double>().norm(); + } + return total; +} + +inline double area(const Points &polygon) { + double area = 0.; + for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j = i ++) + area += double(polygon[i](0) + polygon[j](0)) * double(polygon[i](1) - polygon[j](1)); + return area; +} + } // namespace Slic3r #endif diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 277fc5877..82df93cc1 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -187,6 +187,24 @@ public: m_map.emplace(std::make_pair(Vec2crd(pt->x()>>m_grid_log2, pt->y()>>m_grid_log2), std::move(value))); } + // Erase a data point equal to value. (ValueType has to declare the operator==). + // Returns true if the data point equal to value was found and removed. + bool erase(const ValueType &value) { + const Point *pt = m_point_accessor(value); + if (pt != nullptr) { + // Range of fragment starts around grid_corner, close to pt. + auto range = m_map.equal_range(Point(pt->x>>m_grid_log2, pt->y>>m_grid_log2)); + // Remove the first item. + for (auto it = range.first; it != range.second; ++ it) { + if (it->second == value) { + m_map.erase(it); + return true; + } + } + } + return false; + } + // Return a pair of <ValueType*, distance_squared> std::pair<const ValueType*, double> find(const Vec2crd &pt) { // Iterate over 4 closest grid cells around pt, @@ -214,7 +232,7 @@ public: } } } - return (value_min != nullptr && dist_min < coordf_t(m_search_radius * m_search_radius)) ? + return (value_min != nullptr && dist_min < coordf_t(m_search_radius) * coordf_t(m_search_radius)) ? std::make_pair(value_min, dist_min) : std::make_pair(nullptr, std::numeric_limits<double>::max()); } diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 925b88aca..e17fafd62 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -81,8 +81,8 @@ extern BoundingBox get_extents(const Polylines &polylines); inline double total_length(const Polylines &polylines) { double total = 0; - for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) - total += it->length(); + for (const Polyline &pl : polylines) + total += pl.length(); return total; } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index fb6d90d60..e382c7dbc 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -281,16 +281,17 @@ bool Print::is_step_done(PrintObjectStep step) const std::vector<unsigned int> Print::object_extruders() const { std::vector<unsigned int> extruders; + extruders.reserve(m_regions.size() * 3); - for (PrintRegion* region : m_regions) { + for (const PrintRegion *region : m_regions) { // these checks reflect the same logic used in the GUI for enabling/disabling // extruder selection fields if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0) - extruders.push_back(region->config().perimeter_extruder - 1); + extruders.emplace_back(region->config().perimeter_extruder - 1); if (region->config().fill_density.value > 0) - extruders.push_back(region->config().infill_extruder - 1); + extruders.emplace_back(region->config().infill_extruder - 1); if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0) - extruders.push_back(region->config().solid_infill_extruder - 1); + extruders.emplace_back(region->config().solid_infill_extruder - 1); } sort_remove_duplicates(extruders); @@ -480,14 +481,6 @@ bool Print::apply_config(DynamicPrintConfig config) PrintObjectConfig new_config = this->default_object_config(); // we override the new config with object-specific options normalize_and_apply_config(new_config, object->model_object()->config); - // Force a refresh of a variable layer height profile at the PrintObject if it is not valid. - if (! object->layer_height_profile_valid) { - // The layer_height_profile is not valid for some reason (updated by the user or invalidated due to some option change). - // Invalidate the slicing step, which in turn invalidates everything. - object->invalidate_step(posSlice); - // Trigger recalculation. - invalidated = true; - } // check whether the new config is different from the current one t_config_option_keys diff = object->config().diff(new_config); object->config_apply_only(new_config, diff, true); @@ -567,8 +560,7 @@ exit_for_rearrange_regions: // Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads. for (PrintObject *object : m_objects) - if (! object->layer_height_profile_valid) - object->update_layer_height_profile(); + object->update_layer_height_profile(); return invalidated; } @@ -1141,6 +1133,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co // Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads. for (PrintObject *object : m_objects) if (! object->layer_height_profile_valid) + // No need to call the next line as the step should already be invalidated above. + // update_apply_status(object->invalidate_step(posSlice)); object->update_layer_height_profile(); //FIXME there may be a race condition with the G-code export running at the background thread. @@ -1165,6 +1159,7 @@ bool Print::has_skirt() const || this->has_infinite_skirt(); } +// Precondition: Print::validate() requires the Print::apply() to be called its invocation. std::string Print::validate() const { if (m_objects.empty()) @@ -1253,12 +1248,10 @@ std::string Print::validate() const return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance"); if (! equal_layering(slicing_params, slicing_params0)) return L("The Wipe Tower is only supported for multiple objects if they are sliced equally."); - bool was_layer_height_profile_valid = object->layer_height_profile_valid; - object->update_layer_height_profile(); - object->layer_height_profile_valid = was_layer_height_profile_valid; if ( m_config.variable_layer_height ) { // comparing layer height profiles bool failed = false; + // layer_height_profile should be set by Print::apply(). if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) { int i = 0; while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 872ce31db..529608445 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -78,7 +78,6 @@ private: // Prevents erroneous use by other classes. public: // vector of (vectors of volume ids), indexed by region_id std::vector<std::vector<int>> region_volumes; - t_layer_height_ranges layer_height_ranges; // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. // The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 10f5a1ac2..4b7a8906f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -65,7 +65,6 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta this->set_copies(copies); } - this->layer_height_ranges = model_object->layer_height_ranges; this->layer_height_profile = model_object->layer_height_profile; } @@ -1109,7 +1108,7 @@ void PrintObject::discover_vertical_shells() #if 1 // Intentionally inflate a bit more than how much the region has been shrunk, // so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill). - shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); + shell = offset(offset_ex(union_ex(shell), - 0.5f * min_perimeter_infill_spacing), 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); if (shell.empty()) continue; #else @@ -1330,7 +1329,7 @@ bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_heigh bool updated = false; // If the layer height profile is not set, try to use the one stored at the ModelObject. - if (layer_height_profile.empty() && layer_height_profile.data() != this->model_object()->layer_height_profile.data()) { + if (layer_height_profile.empty()) { layer_height_profile = this->model_object()->layer_height_profile; updated = true; } @@ -1347,10 +1346,9 @@ bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_heigh if (layer_height_profile.empty()) { if (0) // if (this->layer_height_profile.empty()) - layer_height_profile = layer_height_profile_adaptive(slicing_params, this->layer_height_ranges, - this->model_object()->volumes); + layer_height_profile = layer_height_profile_adaptive(slicing_params, this->model_object()->layer_height_ranges, this->model_object()->volumes); else - layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->layer_height_ranges); + layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->model_object()->layer_height_ranges); updated = true; } return updated; diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index d85cf334b..28e6e1018 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -16,6 +16,7 @@ #include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ptree.hpp> #include <boost/algorithm/string/predicate.hpp> +#include <boost/format.hpp> namespace Slic3r { @@ -125,8 +126,13 @@ void AppConfig::load() void AppConfig::save() { + // The config is first written to a file with a PID suffix and then moved + // to avoid race conditions with multiple instances of Slic3r + const auto path = config_path(); + std::string path_pid = (boost::format("%1%.%2%") % path % get_current_pid()).str(); + boost::nowide::ofstream c; - c.open(AppConfig::config_path(), std::ios::out | std::ios::trunc); + c.open(path_pid, std::ios::out | std::ios::trunc); c << "# " << Slic3r::header_slic3r_generated() << std::endl; // Make sure the "no" category is written first. for (const std::pair<std::string, std::string> &kvp : m_storage[""]) @@ -155,6 +161,8 @@ void AppConfig::save() } } c.close(); + + rename_file(path_pid, path); m_dirty = false; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5dc685111..ae5743f67 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -323,17 +323,17 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : double brim_width = config->opt_float("brim_width"); if (boost::any_cast<bool>(value) == true) { - new_val = m_brim_width == 0.0 ? 10 : + new_val = m_brim_width == 0.0 ? 5 : m_brim_width < 0.0 ? m_brim_width * (-1) : m_brim_width; } - else{ + else { m_brim_width = brim_width * (-1); new_val = 0; } new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val)); } - else{ //(opt_key == "support") + else { //(opt_key == "support") const wxString& selection = boost::any_cast<wxString>(value); auto support_material = selection == _("None") ? false : true;