From d8be4de6cf8be3fba78570e51b0db276bc1669c2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 23 Nov 2016 15:51:47 +0100 Subject: [PATCH 01/82] One step further to the C++ Supports. --- xs/src/libslic3r/ClipperUtils.cpp | 21 + xs/src/libslic3r/ClipperUtils.hpp | 2 + xs/src/libslic3r/ExPolygon.hpp | 12 + xs/src/libslic3r/ExtrusionEntity.hpp | 28 +- xs/src/libslic3r/Fill/Fill.cpp | 29 +- xs/src/libslic3r/Flow.cpp | 55 +- xs/src/libslic3r/Flow.hpp | 22 +- xs/src/libslic3r/SupportMaterial.cpp | 726 ++++++++++++++------------- 8 files changed, 500 insertions(+), 395 deletions(-) diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index f5e98b724..f2cc7eb38 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -1004,4 +1004,25 @@ void safety_offset(ClipperLib::Paths* paths) scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE); } +Polygons top_level_islands(const Slic3r::Polygons &polygons) +{ + ClipperLib::Paths input; + Slic3rMultiPoints_to_ClipperPaths(polygons, &input); + // init Clipper + ClipperLib::Clipper clipper; + clipper.Clear(); + // perform union + clipper.AddPaths(input, ClipperLib::ptSubject, true); + ClipperLib::PolyTree polytree; + clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); + // Convert only the top level islands to the output. + Polygons out; + out.reserve(polytree.ChildCount()); + for (int i = 0; i < polytree.ChildCount(); ++i) { + out.push_back(Polygon()); + ClipperPath_to_Slic3rMultiPoint(polytree.Childs[i]->Contour, &out.back()); + } + return out; } + +} // namespace Slic3r \ No newline at end of file diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 2bb58bdf6..188867fe5 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -162,6 +162,8 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retv void safety_offset(ClipperLib::Paths* paths); +Polygons top_level_islands(const Slic3r::Polygons &polygons); + } #endif diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 0a212668f..7f76cfe4e 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -253,6 +253,18 @@ inline void polygons_append(Polygons &dst, ExPolygons &&src) } #endif +inline void expolygons_append(ExPolygons &dst, const ExPolygons &src) +{ + dst.insert(dst.end(), src.begin(), src.end()); +} + +#if SLIC3R_CPPVER >= 11 +inline void expolygons_append(ExPolygons &dst, ExPolygons &&src) +{ + std::move(std::begin(src), std::end(src), std::back_inserter(dst)); +} +#endif + inline void expolygons_rotate(ExPolygons &expolys, double angle) { for (ExPolygons::iterator p = expolys.begin(); p != expolys.end(); ++p) diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 524ab8c54..7ce053a10 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -69,11 +69,11 @@ class ExtrusionPath : public ExtrusionEntity public: Polyline polyline; ExtrusionRole role; - // Volumetric velocity. mm^3 of plastic per mm of linear head motion + // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator. double mm3_per_mm; - // Width of the extrusion. + // Width of the extrusion, used for visualization purposes. float width; - // Height of the extrusion. + // Height of the extrusion, used for visualization purposed. float height; ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {}; @@ -194,6 +194,28 @@ class ExtrusionLoop : public ExtrusionEntity Polyline as_polyline() const { return this->polygon().split_at_first_point(); } }; +inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +{ + dst.reserve(dst.size() + polylines.size()); + for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { + ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); + dst.push_back(extrusion_path); + extrusion_path->polyline = *it_polyline; + } +} + +#if SLIC3R_CPPVER >= 11 +inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +{ + dst.reserve(dst.size() + polylines.size()); + for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { + ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); + dst.push_back(extrusion_path); + extrusion_path->polyline = std::move(*it_polyline); + } +} +#endif // SLIC3R_CPPVER >= 11 + } #endif diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index d840d44c0..b4c14bb6c 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -246,22 +246,21 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); } - // save into layer - { - ExtrusionRole role = is_bridge ? erBridgeInfill : - (surface.is_solid() ? ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : erInternalInfill); - ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection()); - out.entities.push_back(&collection); - // Only concentric fills are not sorted. - collection.no_sort = f->no_sort(); - for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) { - ExtrusionPath *path = new ExtrusionPath(role, flow.mm3_per_mm(), flow.width, flow.height); - collection.entities.push_back(path); - path->polyline.points.swap(it->points); - } - } + // Save into layer. + auto *eec = new ExtrusionEntityCollection(); + out.entities.push_back(eec); + // Only concentric fills are not sorted. + eec->no_sort = f->no_sort(); + extrusion_entities_append_paths( + eec->entities, STDMOVE(polylines), + is_bridge ? + erBridgeInfill : + (surface.is_solid() ? + ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : + erInternalInfill), + flow.mm3_per_mm(), flow.width, flow.height); } - + // add thin fill regions // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection // Unpacks the collection, creates multiple collections per path. diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index 49a55a50e..c7e5f8495 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -40,14 +40,17 @@ Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool /* This method returns the centerline spacing between two adjacent extrusions having the same extrusion width (and other properties). */ float -Flow::spacing() const { - if (this->bridge) { +Flow::spacing() const +{ +#ifdef HAS_PERIMETER_LINE_OVERLAP + if (this->bridge) return this->width + BRIDGE_EXTRA_SPACING; - } - // rectangle with semicircles at the ends float min_flow_spacing = this->width - this->height * (1 - PI/4.0); - return this->width - OVERLAP_FACTOR * (this->width - min_flow_spacing); + return this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing); +#else + return this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1 - PI/4.0)); +#endif } /* This method returns the centerline spacing between an extrusion using this @@ -57,23 +60,17 @@ float Flow::spacing(const Flow &other) const { assert(this->height == other.height); assert(this->bridge == other.bridge); - - if (this->bridge) { - return this->width/2 + other.width/2 + BRIDGE_EXTRA_SPACING; - } - - return this->spacing()/2 + other.spacing()/2; + return this->bridge ? + 0.5f * this->width + 0.5f * other.width + BRIDGE_EXTRA_SPACING : + 0.5f * this->spacing() + 0.5f * other.spacing(); } /* This method returns extrusion volume per head move unit. */ -double -Flow::mm3_per_mm() const { - if (this->bridge) { - return (this->width * this->width) * PI/4.0; - } - - // rectangle with semicircles at the ends - return this->width * this->height + (this->height*this->height) / 4.0 * (PI-4.0); +double Flow::mm3_per_mm() const +{ + return this->bridge ? + (this->width * this->width) * PI/4.0 : + this->width * this->height + (this->height * this->height) / 4.0 * (PI-4.0); } /* This static method returns bridge width for a given nozzle diameter. */ @@ -85,8 +82,7 @@ float Flow::_bridge_width(float nozzle_diameter, float bridge_flow_ratio) { } /* This static method returns a sane extrusion width default. */ -float -Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) { +float Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) { // here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate // shape: rectangle with semicircles at the ends float width = ((nozzle_diameter*nozzle_diameter) * PI + (height*height) * (4.0 - PI)) / (4.0 * height); @@ -106,14 +102,15 @@ Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) { } /* This static method returns the extrusion width value corresponding to the supplied centerline spacing. */ -float -Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) { - if (bridge) { - return spacing - BRIDGE_EXTRA_SPACING; - } - - // rectangle with semicircles at the ends - return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0); +float Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) +{ + return bridge ? + (spacing - BRIDGE_EXTRA_SPACING) : +#ifdef HAS_PERIMETER_LINE_OVERLAP + (spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1 - PI/4.0)); +#else + (spacing + height * (1 - PI/4.0)); +#endif } } diff --git a/xs/src/libslic3r/Flow.hpp b/xs/src/libslic3r/Flow.hpp index 2f041d03c..a3ff4b6b6 100644 --- a/xs/src/libslic3r/Flow.hpp +++ b/xs/src/libslic3r/Flow.hpp @@ -7,8 +7,14 @@ namespace Slic3r { +// Extra spacing of bridge threads, in mm. #define BRIDGE_EXTRA_SPACING 0.05 -#define OVERLAP_FACTOR 1.0 + +// Overlap factor of perimeter lines. Currently no overlap. +// #define HAS_OVERLAP +#ifdef HAS_PERIMETER_LINE_OVERLAP + #define PERIMETER_LINE_OVERLAP_FACTOR 1.0 +#endif enum FlowRole { frExternalPerimeter, @@ -22,9 +28,17 @@ enum FlowRole { class Flow { - public: - float width, height, nozzle_diameter; - bool bridge; +public: + // Non bridging flow: Maximum width of an extrusion with semicircles at the ends. + // Bridging flow: Bridge thread diameter. + float width; + // Non bridging flow: Layer height. + // Bridging flow: Bridge thread diameter = layer height. + float height; + // Nozzle diameter is + float nozzle_diameter; + // Is it a bridge? + bool bridge; Flow(float _w, float _h, float _nd, bool _bridge = false) : width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}; diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 701e11799..afb725f98 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -1,17 +1,22 @@ #include "ClipperUtils.hpp" #include "ExtrusionEntityCollection.hpp" #include "PerimeterGenerator.hpp" -#include "Print.hpp" #include "Layer.hpp" +#include "Print.hpp" #include "SupportMaterial.hpp" #include "Fill/FillBase.hpp" -#include "SVG.hpp" #include #include #include -#define SLIC3R_DEBUG + #define SLIC3R_DEBUG + +// Make assert active if SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG + #undef NDEBUG + #include "SVG.hpp" +#endif namespace Slic3r { @@ -650,11 +655,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // now apply the contact areas to the layer were they need to be made if (! contact_polygons.empty()) { // get the average nozzle diameter used on this layer - MyLayer &new_layer = layer_allocate(layer_storage, sltTopContact); + MyLayer &new_layer = layer_allocate(layer_storage, sltTopContact); + const Layer *layer_below = (layer_id > 0) ? object.get_layer(layer_id - 1) : NULL; new_layer.idx_object_layer_above = layer_id; if (m_soluble_interface) { // Align the contact surface height with a layer immediately below the supported layer. - new_layer.height = (layer_id > 0) ? + new_layer.height = layer_below ? // Interface layer will be synchronized with the object. object.get_layer(layer_id - 1)->height : // Don't know the thickness of the raft layer yet. @@ -678,10 +684,23 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } nozzle_dmr /= coordf_t(n_nozzle_dmrs); new_layer.print_z = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance; - // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and - // its height will be set adaptively later on. - new_layer.height = 0.; - new_layer.bottom_z = new_layer.print_z; + if (m_synchronize_support_layers_with_object && layer_below) { + int layer_id_below = layer_id - 1; + const Layer *layer_above = layer_below; + for (;;) { + if (layer_below->print_z - layer_below->height < new_layer.print_z - m_support_layer_height_max) { + // layer_below is too low. + break; + } + } + new_layer.height = 0.; + new_layer.bottom_z = new_layer.print_z - new_layer.height; + } else if (layer_below) { + // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and + // its height will be set adaptively later on. + new_layer.height = 0.; + new_layer.bottom_z = new_layer.print_z; + } } // Ignore this contact area if it's too low. @@ -729,12 +748,16 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta if (top.empty()) continue; // Collect projections of all contact areas above or at the same level as this top surface. - for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) + for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) { + // Contact surfaces are expanded away from the object, trimmed by the object. polygons_append(projection, top_contacts[contact_idx]->polygons); + // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. + polygons_append(projection, *top_contacts[contact_idx]->aux_polygons); + } // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any // top surfaces above layer.print_z falls onto this top surface. // touching are the contact surfaces supported exclusively by this top surfaaces. - Polygons touching = intersection(projection, top); + Polygons touching = intersection(top, projection, true); // Do safety offset on the projection surfaces, so they overlap. if (touching.empty()) continue; // Allocate a new bottom contact layer. @@ -760,6 +783,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta projection = diff(projection, touching); } } + + std::reverse(bottom_contacts.begin(), bottom_contacts.end()); return bottom_contacts; } @@ -829,8 +854,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int assert(dist > 0.); // Insert intermediate layers. size_t n_layers_extra = size_t(ceil(dist / m_support_layer_height_max)); + assert(n_layers_extra > 0); coordf_t step = dist / coordf_t(n_layers_extra); - if (! m_soluble_interface && extr2.layer->layer_type == sltTopContact) { + if (! m_soluble_interface && ! m_synchronize_support_layers_with_object && extr2.layer->layer_type == sltTopContact) { assert(extr2.layer->height == 0.); // This is a top interface layer, which does not have a height assigned yet. Do it now. if (m_synchronize_support_layers_with_object) { @@ -838,7 +864,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // Find the } extr2.layer->height = step; - extr2.layer->bottom_z = extr2.layer->print_z - step; + extr2.layer->bottom_z = extr2z = extr2.layer->print_z - step; -- n_layers_extra; if (extr2.layer->bottom_z < this->first_layer_height()) { // Split the span into two layers: the top layer up to the first layer height, @@ -855,8 +881,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int intermediate_layers.push_back(&layer_new); continue; } - } - if (n_layers_extra > 0 && extr1z + step < this->first_layer_height()) { + } else if (extr1z + step < this->first_layer_height()) { MyLayer &layer_new = layer_allocate(layer_storage, stlIntermediate); layer_new.bottom_z = extr1z; layer_new.print_z = extr1z = this->first_layer_height(); @@ -865,13 +890,22 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int dist = extr2z - extr1z; assert(dist >= 0.); n_layers_extra = size_t(ceil(dist / m_support_layer_height_max)); - coordf_t step = dist / coordf_t(n_layers_extra); + step = dist / coordf_t(n_layers_extra); } for (size_t i = 0; i < n_layers_extra; ++ i) { MyLayer &layer_new = layer_allocate(layer_storage, stlIntermediate); - layer_new.height = step; - layer_new.bottom_z = (i + 1 == n_layers_extra) ? extr2z : extr1z + i * step; - layer_new.print_z = layer_new.bottom_z + step; + if (i + 1 == n_layers_extra) { + // Last intermediate layer added. Align the last entered layer with extr2z exactly. + layer_new.bottom_z = (i == 0) ? extr1z : intermediate_layers.back()->print_z; + layer_new.print_z = extr2z; + layer_new.height = layer_new.print_z - layer_new.bottom_z; + } + else { + // Intermediate layer, not the last added. + layer_new.height = step; + layer_new.bottom_z = (i + 1 == n_layers_extra) ? extr2z : extr1z + i * step; + layer_new.print_z = layer_new.bottom_z + step; + } intermediate_layers.push_back(&layer_new); } } @@ -909,9 +943,9 @@ void PrintObjectSupportMaterial::generate_base_layers( // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new. while (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->bottom_z > layer_intermediate.print_z + EPSILON) -- idx_top_contact_above; - if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z) - polygons_append(polygons_new, top_contacts[idx_top_contact_above]->polygons); - + if (idx_top_contact_above >= 0 && idx_top_contact_above + 1 < top_contacts.size() && top_contacts[idx_top_contact_above + 1]->print_z > layer_intermediate.print_z) + polygons_append(polygons_new, top_contacts[idx_top_contact_above + 1]->polygons); + // Add polygons from the intermediate layer above. if (idx_intermediate + 1 < int(intermediate_layers.size())) polygons_append(polygons_new, intermediate_layers[idx_intermediate+1]->polygons); @@ -1073,7 +1107,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int MyLayersPtr interface_layers; // Contact layer is considered an interface layer, therefore run the following block only if support_material_interface_layers > 1. - if (! intermediate_layers.empty() && m_object_config->support_material_interface_layers > 1) { + if (! intermediate_layers.empty() && m_object_config->support_material_interface_layers.value > 1) { // Index of the first top contact layer intersecting the current intermediate layer. size_t idx_top_contact_first = 0; // Index of the first bottom contact layer intersecting the current intermediate layer. @@ -1132,6 +1166,182 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int return interface_layers; } +static inline void fill_expolygons_generate_paths( + ExtrusionEntitiesPtr &dst, + const ExPolygons &expolygons, + Fill *filler, + float density, + ExtrusionRole role, + const Flow &flow) +{ + FillParams fill_params; + fill_params.density = density; + fill_params.complete = true; + for (ExPolygons::const_iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) { + Surface surface(stInternal, *it_expolygon); + extrusion_entities_append_paths( + dst, + filler->fill_surface(&surface, fill_params), + role, + flow.mm3_per_mm(), flow.width, flow.height); + } +} + +static inline void fill_expolygons_generate_paths( + ExtrusionEntitiesPtr &dst, + ExPolygons &&expolygons, + Fill *filler, + float density, + ExtrusionRole role, + const Flow &flow) +{ + FillParams fill_params; + fill_params.density = density; + fill_params.complete = true; + for (ExPolygons::iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) { + Surface surface(stInternal, std::move(*it_expolygon)); + extrusion_entities_append_paths( + dst, + filler->fill_surface(&surface, fill_params), + role, + flow.mm3_per_mm(), flow.width, flow.height); + } +} + +// Support layers, partially processed. +struct MyLayerExtruded +{ + MyLayerExtruded() : layer(nullptr) {} + + bool empty() const { + return layer == nullptr || layer->polygons.empty(); + } + + bool could_merge(const MyLayerExtruded &other) const { + return ! this->empty() && ! other.empty() && + this->layer->height == other.layer->height && + this->layer->bridging == other.layer->bridging; + } + + void merge(MyLayerExtruded &&other) { + assert(could_merge(other)); + Slic3r::polygons_append(layer->polygons, std::move(other.layer->polygons)); + other.layer->polygons.clear(); + } + + void polygons_append(Polygons &dst) const { + if (layer != NULL && ! layer->polygons.empty()) + Slic3r::polygons_append(dst, layer->polygons); + } + + // The source layer. It carries the height and extrusion type (bridging / non bridging, extrusion height). + PrintObjectSupportMaterial::MyLayer *layer; + // Collect extrusions. They will be exported sorted by the bottom height. + ExtrusionEntitiesPtr extrusions; +}; + +typedef std::vector MyLayerExtrudedPtrs; + +struct LoopInterfaceProcessor +{ + LoopInterfaceProcessor(coordf_t circle_r) : + n_contact_loops(1), + circle_radius(circle_r), + circle_distance(circle_r * 3.) + { + // Shape of the top contact area. + circle.points.reserve(6); + for (size_t i = 0; i < 6; ++ i) { + double angle = double(i) * M_PI / 3.; + circle.points.push_back(Point(circle_radius * cos(angle), circle_radius * sin(angle))); + } + } + + // Generate loop contacts at the top_contact_layer, + // trim the top_contact_layer->polygons with the areas covered by the loops. + void generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src); + + int n_contact_loops; + coordf_t circle_radius; + coordf_t circle_distance; + Polygon circle; +}; + +void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src) +{ + if (n_contact_loops == 0 || top_contact_layer.empty()) + return; + + Flow flow = interface_flow_src; + flow.height = float(top_contact_layer.layer->height); + + Polygons overhang_polygons; + if (top_contact_layer.layer->aux_polygons != nullptr) + overhang_polygons = std::move(*top_contact_layer.layer->aux_polygons); + + // Generate the outermost loop. + // Find centerline of the external loop (or any other kind of extrusions should the loop be skipped) + Polygons top_contact_polygons = offset(top_contact_layer.layer->polygons, - 0.5f * flow.scaled_width()); + + Polygons loops0; + { + // find centerline of the external loop of the contours + // only consider the loops facing the overhang + Polygons external_loops; + // Positions of the loop centers. + Polygons circles; + Polygons overhang_with_margin = offset(overhang_polygons, 0.5f * flow.scaled_width()); + for (Polygons::const_iterator it_contact = top_contact_polygons.begin(); it_contact != top_contact_polygons.end(); ++ it_contact) { + Polylines tmp; + tmp.push_back(it_contact->split_at_first_point()); + if (! intersection(tmp, overhang_with_margin).empty()) { + external_loops.push_back(*it_contact); + Points positions_new = it_contact->equally_spaced_points(circle_distance); + for (Points::const_iterator it_center = positions_new.begin(); it_center != positions_new.end(); ++ it_center) { + circles.push_back(circle); + Polygon &circle_new = circles.back(); + for (size_t i = 0; i < circle_new.points.size(); ++ i) + circle_new.points[i].translate(*it_center); + } + } + } + // Apply a pattern to the loop. + loops0 = diff(external_loops, circles); + } + + Polylines loop_lines; + { + // make more loops + Polygons loop_polygons = loops0; + for (size_t i = 1; i < n_contact_loops; ++ i) + polygons_append(loop_polygons, + offset2( + loops0, + - int(i) * flow.scaled_spacing() - 0.5f * flow.scaled_spacing(), + 0.5f * flow.scaled_spacing())); + // clip such loops to the side oriented towards the object + loop_lines.reserve(loop_polygons.size()); + for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it) + loop_lines.push_back(it->split_at_first_point()); + loop_lines = intersection(loop_lines, offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN))); + } + + // add the contact infill area to the interface area + // note that growing loops by $circle_radius ensures no tiny + // extrusions are left inside the circles; however it creates + // a very large gap between loops and contact_infill_polygons, so maybe another + // solution should be found to achieve both goals + Polygons thick_loop_lines; + offset(loop_lines, &thick_loop_lines, float(circle_radius * 1.1)); + top_contact_layer.layer->polygons = diff(top_contact_layer.layer->polygons, std::move(thick_loop_lines)); + + // Transform loops into ExtrusionPath objects. + extrusion_entities_append_paths( + top_contact_layer.extrusions, + STDMOVE(loop_lines), + erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height); +} + void PrintObjectSupportMaterial::generate_toolpaths( const PrintObject &object, const Polygons &raft, @@ -1140,18 +1350,9 @@ void PrintObjectSupportMaterial::generate_toolpaths( const MyLayersPtr &intermediate_layers, const MyLayersPtr &interface_layers) const { - // Shape of the top contact area. - int n_contact_loops = 1; - coordf_t circle_radius = 1.5 * m_support_material_interface_flow.scaled_width(); - coordf_t circle_distance = 3. * circle_radius; - Polygon circle; - circle.points.reserve(6); - for (size_t i = 0; i < 6; ++ i) { - double angle = double(i) * M_PI / 3.; - circle.points.push_back(Point(circle_radius * cos(angle), circle_radius * sin(angle))); - } - // Slic3r::debugf "Generating patterns\n"; + // loop_interface_processor with a given circle radius. + LoopInterfaceProcessor loop_interface_processor(1.5 * m_support_material_interface_flow.scaled_width()); // Prepare fillers. SupportMaterialPattern support_pattern = m_object_config->support_material_pattern; @@ -1171,13 +1372,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( infill_pattern = ipHoneycomb; break; } -#if SLIC3R_CPPVER >= 11 std::unique_ptr filler_interface = std::unique_ptr(Fill::new_from_type(ipRectilinear)); std::unique_ptr filler_support = std::unique_ptr(Fill::new_from_type(infill_pattern)); -#else - std::auto_ptr filler_interface = std::auto_ptr(Fill::new_from_type(ipRectilinear)); - std::auto_ptr filler_support = std::auto_ptr(Fill::new_from_type(infill_pattern)); -#endif { BoundingBox bbox_object = object.bounding_box(); filler_interface->set_bounding_box(bbox_object); @@ -1186,9 +1382,9 @@ void PrintObjectSupportMaterial::generate_toolpaths( coordf_t interface_angle = m_object_config->support_material_angle + 90.; coordf_t interface_spacing = m_object_config->support_material_interface_spacing.value + m_support_material_interface_flow.spacing(); - coordf_t interface_density = (interface_spacing == 0.) ? 1. : (m_support_material_interface_flow.spacing() / interface_spacing); + coordf_t interface_density = std::min(1., m_support_material_interface_flow.spacing() / interface_spacing); coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing(); - coordf_t support_density = (support_spacing == 0.) ? 1. : (m_support_material_flow.spacing() / support_spacing); + coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing); //FIXME Parallelize the support generator: /* @@ -1216,10 +1412,11 @@ void PrintObjectSupportMaterial::generate_toolpaths( SupportLayer &support_layer = *object.support_layers[support_layer_id]; // Find polygons with the same print_z. - Polygons bottom_contact_polygons; - Polygons top_contact_polygons; - Polygons base_polygons; - Polygons interface_polygons; + MyLayerExtruded bottom_contact_layer; + MyLayerExtruded top_contact_layer; + MyLayerExtruded base_layer; + MyLayerExtruded interface_layer; + MyLayerExtrudedPtrs mylayers; // Increment the layer indices to find a layer at support_layer.print_z. for (; idx_layer_bottom_contact < bottom_contacts .size() && bottom_contacts [idx_layer_bottom_contact]->print_z < support_layer.print_z - EPSILON; ++ idx_layer_bottom_contact) ; @@ -1227,321 +1424,163 @@ void PrintObjectSupportMaterial::generate_toolpaths( for (; idx_layer_intermediate < intermediate_layers.size() && intermediate_layers[idx_layer_intermediate ]->print_z < support_layer.print_z - EPSILON; ++ idx_layer_intermediate ) ; for (; idx_layer_inteface < interface_layers .size() && interface_layers [idx_layer_inteface ]->print_z < support_layer.print_z - EPSILON; ++ idx_layer_inteface ) ; // Copy polygons from the layers. - if (idx_layer_bottom_contact < bottom_contacts.size() && bottom_contacts[idx_layer_bottom_contact]->print_z < support_layer.print_z + EPSILON) - bottom_contact_polygons = bottom_contacts[idx_layer_bottom_contact]->polygons; - if (idx_layer_top_contact < top_contacts.size() && top_contacts[idx_layer_top_contact]->print_z < support_layer.print_z + EPSILON) - top_contact_polygons = top_contacts[idx_layer_top_contact]->polygons; - if (idx_layer_inteface < interface_layers.size() && interface_layers[idx_layer_inteface]->print_z < support_layer.print_z + EPSILON) - interface_polygons = interface_layers[idx_layer_inteface]->polygons; - if (idx_layer_intermediate < intermediate_layers.size() && intermediate_layers[idx_layer_intermediate]->print_z < support_layer.print_z + EPSILON) - base_polygons = intermediate_layers[idx_layer_intermediate]->polygons; - - // We redefine flows locally by applying this layer's height. - Flow flow = m_support_material_flow; - Flow interface_flow = m_support_material_interface_flow; - flow.height = support_layer.height; - interface_flow.height = support_layer.height; - - /* - if (1) { + mylayers.reserve(4); + if (idx_layer_bottom_contact < bottom_contacts.size() && bottom_contacts[idx_layer_bottom_contact]->print_z < support_layer.print_z + EPSILON) { + bottom_contact_layer.layer = bottom_contacts[idx_layer_bottom_contact]; + mylayers.push_back(&bottom_contact_layer); + } + if (idx_layer_top_contact < top_contacts.size() && top_contacts[idx_layer_top_contact]->print_z < support_layer.print_z + EPSILON) { + top_contact_layer.layer = top_contacts[idx_layer_top_contact]; + mylayers.push_back(&top_contact_layer); + } + if (idx_layer_inteface < interface_layers.size() && interface_layers[idx_layer_inteface]->print_z < support_layer.print_z + EPSILON) { + interface_layer.layer = interface_layers[idx_layer_inteface]; + mylayers.push_back(&interface_layer); + } + if (idx_layer_intermediate < intermediate_layers.size() && intermediate_layers[idx_layer_intermediate]->print_z < support_layer.print_z + EPSILON) { + base_layer.layer = intermediate_layers[idx_layer_intermediate]; + mylayers.push_back(&base_layer); + } + // Sort the layers with the same print_z coordinate by their heights, thickest first. + std::sort(mylayers.begin(), mylayers.end(), [](const MyLayerExtruded *p1, const MyLayerExtruded *p2) { return p1->layer->height > p2->layer->height; }); + + /* { require "Slic3r/SVG.pm"; Slic3r::SVG::output("out\\layer_" . $z . ".svg", blue_expolygons => union_ex($base), red_expolygons => union_ex($contact), green_expolygons => union_ex($interface), ); - } - */ + } */ - // Store inslands, over which the retract will be disabled. - { - Polygons polys(bottom_contact_polygons); - polygons_append(polys, interface_polygons); - polygons_append(polys, base_polygons); - polygons_append(polys, top_contact_polygons); - ExPolygons islands = union_ex(polys); - support_layer.support_islands.expolygons.insert(support_layer.support_islands.expolygons.end(), islands.begin(), islands.end()); + if (m_object_config->support_material_interface_layers == 0) { + // If no interface layers were requested, we treat the contact layer exactly as a generic base layer. + if (base_layer.could_merge(top_contact_layer)) + base_layer.merge(std::move(top_contact_layer)); + } else { + loop_interface_processor.generate(top_contact_layer, m_support_material_interface_flow); + // If no loops are allowed, we treat the contact layer exactly as a generic interface layer. + if (interface_layer.could_merge(top_contact_layer)) + interface_layer.merge(std::move(top_contact_layer)); } - - Polygons contact_infill_polygons; - if (! top_contact_polygons.empty()) - { - // Having a top interface layer. - if (m_object_config->support_material_interface_layers == 0) - // If no interface layers were requested, we treat the contact layer exactly as a generic base layer. - polygons_append(base_polygons, top_contact_polygons); - else if (n_contact_loops == 0) - // If no loops are allowed, we treat the contact layer exactly as a generic interface layer. - polygons_append(interface_polygons, top_contact_polygons); - else if (! top_contact_polygons.empty()) - { - // Create loop paths and - Polygons overhang_polygons = (top_contacts[idx_layer_top_contact]->aux_polygons == NULL) ? - Polygons() : - *top_contacts[idx_layer_top_contact]->aux_polygons; - // Generate the outermost loop. - // Find centerline of the external loop (or any other kind of extrusions should the loop be skipped) - top_contact_polygons = offset(top_contact_polygons, - 0.5 * interface_flow.scaled_width()); - - Polygons loops0; - { - // find centerline of the external loop of the contours - // only consider the loops facing the overhang - Polygons external_loops; - // Positions of the loop centers. - Polygons circles; - { - Polygons overhang_with_margin = offset(overhang_polygons, 0.5 * interface_flow.scaled_width()); - for (Polygons::const_iterator it_contact = top_contact_polygons.begin(); it_contact != top_contact_polygons.end(); ++ it_contact) { - Polylines tmp; - tmp.push_back(it_contact->split_at_first_point()); - if (! intersection(tmp, overhang_with_margin).empty()) { - external_loops.push_back(*it_contact); - Points positions_new = it_contact->equally_spaced_points(circle_distance); - for (Points::const_iterator it_center = positions_new.begin(); it_center != positions_new.end(); ++ it_center) { - circles.push_back(circle); - Polygon &circle_new = circles.back(); - for (size_t i = 0; i < circle_new.points.size(); ++ i) - circle_new.points[i].translate(*it_center); - } - } - } - } - // Apply a pattern to the loop. - loops0 = diff(external_loops, circles); - } - - Polylines loop_lines; - { - // make more loops - Polygons loop_polygons = loops0; - for (size_t i = 1; i < n_contact_loops; ++ i) - polygons_append(loop_polygons, - offset2( - loops0, - - int(i) * interface_flow.scaled_spacing() - 0.5 * interface_flow.scaled_spacing(), - 0.5 * interface_flow.scaled_spacing())); - // clip such loops to the side oriented towards the object - loop_lines.reserve(loop_polygons.size()); - for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it) - loop_lines.push_back(it->split_at_first_point()); - loop_lines = intersection(loop_lines, offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN))); - } - - // add the contact infill area to the interface area - // note that growing loops by $circle_radius ensures no tiny - // extrusions are left inside the circles; however it creates - // a very large gap between loops and contact_infill_polygons, so maybe another - // solution should be found to achieve both goals - { - Polygons loop_polygons; - offset(loop_lines, &loop_polygons, circle_radius * 1.1); - contact_infill_polygons = diff(top_contact_polygons, loop_polygons); - } - - // Transform loops into ExtrusionPath objects. - for (Polylines::const_iterator it_polyline = loop_lines.begin(); it_polyline != loop_lines.end(); ++ it_polyline) { - ExtrusionPath *extrusion_path = new ExtrusionPath(erSupportMaterialInterface); - support_layer.support_interface_fills.entities.push_back(extrusion_path); - extrusion_path->polyline = *it_polyline; - extrusion_path->mm3_per_mm = interface_flow.mm3_per_mm(); - extrusion_path->width = interface_flow.width; - extrusion_path->height = support_layer.height; - } - } + if (! interface_layer.empty() && ! base_layer.empty()) { + // turn base support into interface when it's contained in our holes + // (this way we get wider interface anchoring) + Polygons islands = top_level_islands(interface_layer.layer->polygons); + base_layer.layer->polygons = diff(base_layer.layer->polygons, islands); + polygons_append(interface_layer.layer->polygons, intersection(base_layer.layer->polygons, islands)); } // interface and contact infill - if (! interface_polygons.empty() || ! contact_infill_polygons.empty()) { + if (! interface_layer.empty()) { //FIXME When paralellizing, each thread shall have its own copy of the fillers. + Flow interface_flow( + interface_layer.layer->bridging ? interface_layer.layer->height : m_support_material_interface_flow.width, + interface_layer.layer->height, + m_support_material_interface_flow.nozzle_diameter, + interface_layer.layer->bridging); + filler_interface->angle = interface_angle; + filler_interface->spacing = m_support_material_interface_flow.spacing(); + fill_expolygons_generate_paths( + // Destination + support_layer.support_fills.entities, + // Regions to fill + union_ex(interface_layer.layer->polygons, true), + // Filler and its parameters + filler_interface.get(), interface_density, + // Extrusion parameters + erSupportMaterialInterface, interface_flow); + } + + // support or flange + if (! base_layer.empty()) { + //FIXME When paralellizing, each thread shall have its own copy of the fillers. + Fill *filler = filler_support.get(); + filler->angle = angles[support_layer_id % angles.size()]; + // We don't use $base_flow->spacing because we need a constant spacing + // value that guarantees that all layers are correctly aligned. + Flow flow(m_support_material_flow.width, base_layer.layer->height, m_support_material_flow.nozzle_diameter, base_layer.layer->bridging); + filler->spacing = flow.spacing(); + float density = support_density; + // find centerline of the external loop/extrusions + ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ? + // union_ex(base_polygons, true) : + offset2_ex(base_layer.layer->polygons, SCALED_EPSILON, - SCALED_EPSILON) : + offset2_ex(base_layer.layer->polygons, SCALED_EPSILON, - SCALED_EPSILON - 0.5*flow.scaled_width()); + /* { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output("out\\to_infill_base" . $z . ".svg", + red_expolygons => union_ex($contact), + green_expolygons => union_ex($interface), + blue_expolygons => $to_infill, + ); + } */ + if (support_layer_id == 0) { + // Base flange. + filler = filler_interface.get(); + filler->angle = m_object_config->support_material_angle + 90.; + density = 0.5f; + flow = m_first_layer_flow; + // use the proper spacing for first layer as we don't need to align + // its pattern to the other layers + //FIXME When paralellizing, each thread shall have its own copy of the fillers. + filler->spacing = flow.spacing(); + } else if (with_sheath) { + // Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove. + // TODO: use brim ordering algorithm + Polygons to_infill_polygons = to_polygons(to_infill); + // TODO: use offset2_ex() + to_infill = offset_ex(to_infill_polygons, - flow.scaled_spacing()); + extrusion_entities_append_paths( + support_layer.support_fills.entities, + to_polylines(STDMOVE(to_infill_polygons)), + erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); + } + fill_expolygons_generate_paths( + // Destination + support_layer.support_fills.entities, + // Regions to fill + STDMOVE(to_infill), + // Filler and its parameters + filler, density, + // Extrusion parameters + erSupportMaterial, flow); + } + + // support or flange + if (! bottom_contact_layer.empty()) { + //FIXME When paralellizing, each thread shall have its own copy of the fillers. + Flow interface_flow( + bottom_contact_layer.layer->bridging ? bottom_contact_layer.layer->height : m_support_material_interface_flow.width, + bottom_contact_layer.layer->height, + m_support_material_interface_flow.nozzle_diameter, + bottom_contact_layer.layer->bridging); filler_interface->angle = interface_angle; filler_interface->spacing = interface_flow.spacing(); - - // find centerline of the external loop - interface_polygons = offset2(interface_polygons, SCALED_EPSILON, - SCALED_EPSILON - 0.5 * interface_flow.scaled_width()); - // join regions by offsetting them to ensure they're merged - polygons_append(interface_polygons, contact_infill_polygons); - interface_polygons = offset(interface_polygons, SCALED_EPSILON); - - // turn base support into interface when it's contained in our holes - // (this way we get wider interface anchoring) - { - Polygons interface_polygons_new; - interface_polygons_new.reserve(interface_polygons.size()); - for (Polygons::iterator it_polygon = interface_polygons.begin(); it_polygon != interface_polygons.end(); ++ it_polygon) { - if (it_polygon->is_clockwise()) { - Polygons hole; - hole.push_back(*it_polygon); - hole.back().make_counter_clockwise(); - if (diff(hole, base_polygons, true).empty()) - continue; - } - interface_polygons_new.push_back(Polygon()); - interface_polygons_new.back().points.swap(it_polygon->points); - } - interface_polygons.swap(interface_polygons_new); - } - base_polygons = diff(base_polygons, interface_polygons); - - ExPolygons to_fill = union_ex(interface_polygons); - for (ExPolygons::const_iterator it_expolygon = to_fill.begin(); it_expolygon != to_fill.end(); ++ it_expolygon) { - Surface surface(stInternal, *it_expolygon); - FillParams fill_params; - fill_params.density = interface_density; - fill_params.complete = true; - Polylines polylines = filler_interface->fill_surface(&surface, fill_params); - for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { - ExtrusionPath *extrusion_path = new ExtrusionPath(erSupportMaterialInterface); - support_layer.support_interface_fills.entities.push_back(extrusion_path); - extrusion_path->polyline = *it_polyline; - extrusion_path->mm3_per_mm = interface_flow.mm3_per_mm(); - extrusion_path->width = interface_flow.width; - extrusion_path->height = support_layer.height; - } - } - } - - // support or flange - if (! base_polygons.empty()) { - //FIXME When paralellizing, each thread shall have its own copy of the fillers. - Fill *filler = filler_support.get(); - filler->angle = angles[support_layer_id % angles.size()]; - // We don't use $base_flow->spacing because we need a constant spacing - // value that guarantees that all layers are correctly aligned. - filler->spacing = flow.spacing(); - - coordf_t density = support_density; - Flow base_flow = flow; - - // find centerline of the external loop/extrusions - ExPolygons to_infill = offset2_ex(base_polygons, SCALED_EPSILON, - SCALED_EPSILON - 0.5*flow.scaled_width()); - - /* - if (1) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output("out\\to_infill_base" . $z . ".svg", - red_expolygons => union_ex($contact), - green_expolygons => union_ex($interface), - blue_expolygons => $to_infill, - ); - } - */ - - if (support_layer_id == 0) { - // Base flange. - filler = filler_interface.get(); - filler->angle = m_object_config->support_material_angle + 90.; - density = 0.5; - base_flow = m_first_layer_flow; - // use the proper spacing for first layer as we don't need to align - // its pattern to the other layers - //FIXME When paralellizing, each thread shall have its own copy of the fillers. - filler->spacing = base_flow.spacing(); - } else if (with_sheath) { - // Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove. - // TODO: use brim ordering algorithm - Polygons to_infill_polygons = to_polygons(to_infill); - for (Polygons::const_iterator it_polyline = to_infill_polygons.begin(); it_polyline != to_infill_polygons.end(); ++ it_polyline) { - ExtrusionPath *extrusion_path = new ExtrusionPath(erSupportMaterial); - support_layer.support_fills.entities.push_back(extrusion_path); - extrusion_path->polyline = *it_polyline; - extrusion_path->mm3_per_mm = flow.mm3_per_mm(); - extrusion_path->width = flow.width; - extrusion_path->height = support_layer.height; - } - // TODO: use offset2_ex() - to_infill = offset_ex(to_infill_polygons, - flow.scaled_spacing()); - } - - for (ExPolygons::const_iterator it_expolygon = to_infill.begin(); it_expolygon != to_infill.end(); ++ it_expolygon) { - Surface surface(stInternal, *it_expolygon); - FillParams fill_params; - fill_params.density = density; - fill_params.complete = true; - Polylines polylines = filler->fill_surface(&surface, fill_params); - for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { - ExtrusionPath *extrusion_path = new ExtrusionPath(erSupportMaterial); - support_layer.support_fills.entities.push_back(extrusion_path); - extrusion_path->polyline = *it_polyline; - extrusion_path->mm3_per_mm = base_flow.mm3_per_mm(); - extrusion_path->width = base_flow.width; - extrusion_path->height = support_layer.height; - } - } + fill_expolygons_generate_paths( + // Destination + support_layer.support_fills.entities, + // Regions to fill + union_ex(bottom_contact_layer.layer->polygons, true), + // Filler and its parameters + filler_interface.get(), interface_density, + // Extrusion parameters + erSupportMaterial, interface_flow); } - // support or flange - if (! bottom_contact_polygons.empty()) { - //FIXME When paralellizing, each thread shall have its own copy of the fillers. - Fill *filler = filler_support.get(); - filler->angle = angles[support_layer_id % angles.size()]; - // We don't use $base_flow->spacing because we need a constant spacing - // value that guarantees that all layers are correctly aligned. - filler->spacing = flow.spacing(); - - coordf_t density = support_density; - Flow base_flow = flow; - - // find centerline of the external loop/extrusions - ExPolygons to_infill = offset2_ex(base_polygons, SCALED_EPSILON, - SCALED_EPSILON - 0.5*flow.scaled_width()); - - /* - if (1) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output("out\\to_infill_base" . $z . ".svg", - red_expolygons => union_ex($contact), - green_expolygons => union_ex($interface), - blue_expolygons => $to_infill, - ); - } - */ - - if (support_layer_id == 0) { - // Base flange. - filler = filler_interface.get(); - filler->angle = m_object_config->support_material_angle + 90.; - density = 0.5; - base_flow = m_first_layer_flow; - // use the proper spacing for first layer as we don't need to align - // its pattern to the other layers - //FIXME When paralellizing, each thread shall have its own copy of the fillers. - filler->spacing = base_flow.spacing(); - } else if (with_sheath) { - // Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove. - // TODO: use brim ordering algorithm - Polygons to_infill_polygons = to_polygons(to_infill); - for (Polygons::const_iterator it_polyline = to_infill_polygons.begin(); it_polyline != to_infill_polygons.end(); ++ it_polyline) { - ExtrusionPath *extrusion_path = new ExtrusionPath(erSupportMaterial); - support_layer.support_fills.entities.push_back(extrusion_path); - extrusion_path->polyline = *it_polyline; - extrusion_path->mm3_per_mm = flow.mm3_per_mm(); - extrusion_path->width = flow.width; - extrusion_path->height = support_layer.height; - } - // TODO: use offset2_ex() - to_infill = offset_ex(to_infill_polygons, - flow.scaled_spacing()); - } - - for (ExPolygons::const_iterator it_expolygon = to_infill.begin(); it_expolygon != to_infill.end(); ++ it_expolygon) { - Surface surface(stInternal, *it_expolygon); - FillParams fill_params; - fill_params.density = density; - fill_params.complete = true; - Polylines polylines = filler->fill_surface(&surface, fill_params); - for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { - ExtrusionPath *extrusion_path = new ExtrusionPath(erSupportMaterial); - support_layer.support_fills.entities.push_back(extrusion_path); - extrusion_path->polyline = *it_polyline; - extrusion_path->mm3_per_mm = base_flow.mm3_per_mm(); - extrusion_path->width = base_flow.width; - extrusion_path->height = support_layer.height; - } - } + // Collect the support areas with this print_z into islands, as there is no need + // for retraction over these islands. + Polygons polys; + // Collect the extrusions, sorted by the bottom extrusion height. + for (MyLayerExtrudedPtrs::iterator it = mylayers.begin(); it != mylayers.end(); ++ it) { + (*it)->polygons_append(polys); + std::move(std::begin((*it)->extrusions), std::end((*it)->extrusions), + std::back_inserter(support_layer.support_fills.entities)); } - - /* - if (0) { + if (! polys.empty()) + expolygons_append(support_layer.support_islands.expolygons, union_ex(polys)); + /* { require "Slic3r/SVG.pm"; Slic3r::SVG::output("islands_" . $z . ".svg", red_expolygons => union_ex($contact), @@ -1549,8 +1588,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( green_polylines => [ map $_->unpack->polyline, @{$layer->support_contact_fills} ], polylines => [ map $_->unpack->polyline, @{$layer->support_fills} ], ); - } - */ + } */ } // for each support_layer_id } From 14df0717ca67e1a8a935eec91254e57e8964a79a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 24 Nov 2016 10:43:47 +0100 Subject: [PATCH 02/82] Fix of new suports. --- xs/src/libslic3r/SupportMaterial.cpp | 80 ++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index afb725f98..bbc2abaee 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -921,17 +921,16 @@ void PrintObjectSupportMaterial::generate_base_layers( const MyLayersPtr &top_contacts, MyLayersPtr &intermediate_layers) const { +#ifdef SLIC3R_DEBUG + static int iRun = 0; +#endif /* SLIC3R_DEBUG */ + if (top_contacts.empty()) // No top contacts -> no intermediate layers will be produced. return; // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing); - //FIXME make configurable: - coordf_t overlap_extra_above = 0.01; - coordf_t overlap_extra_below = 0.01; - int idx_top_contact_above = int(top_contacts.size()) - 1; - int idx_top_contact_overlapping = int(top_contacts.size()) - 1; int idx_bottom_contact_overlapping = int(bottom_contacts.size()) - 1; for (int idx_intermediate = int(intermediate_layers.size()) - 1; idx_intermediate >= 0; -- idx_intermediate) { @@ -943,10 +942,10 @@ void PrintObjectSupportMaterial::generate_base_layers( // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new. while (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->bottom_z > layer_intermediate.print_z + EPSILON) -- idx_top_contact_above; - if (idx_top_contact_above >= 0 && idx_top_contact_above + 1 < top_contacts.size() && top_contacts[idx_top_contact_above + 1]->print_z > layer_intermediate.print_z) - polygons_append(polygons_new, top_contacts[idx_top_contact_above + 1]->polygons); + if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z) + polygons_append(polygons_new, top_contacts[idx_top_contact_above]->polygons); - // Add polygons from the intermediate layer above. + // Add polygons projected from the intermediate layer above. if (idx_intermediate + 1 < int(intermediate_layers.size())) polygons_append(polygons_new, intermediate_layers[idx_intermediate+1]->polygons); @@ -954,34 +953,45 @@ void PrintObjectSupportMaterial::generate_base_layers( Polygons polygons_trimming; // Find the first top_contact layer intersecting with this layer. + int idx_top_contact_overlapping = idx_top_contact_above; while (idx_top_contact_overlapping >= 0 && - top_contacts[idx_top_contact_overlapping]->bottom_z > layer_intermediate.print_z + overlap_extra_above - EPSILON) + top_contacts[idx_top_contact_overlapping]->bottom_z > layer_intermediate.print_z - EPSILON) -- idx_top_contact_overlapping; // Collect all the top_contact layer intersecting with this layer. - for (int i = idx_top_contact_overlapping; i >= 0; -- i) { - MyLayer &layer_top_overlapping = *top_contacts[i]; - if (layer_top_overlapping.print_z < layer_intermediate.bottom_z - overlap_extra_below) + for (; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) { + MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping]; + if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON) break; polygons_append(polygons_trimming, layer_top_overlapping.polygons); } // Find the first bottom_contact layer intersecting with this layer. while (idx_bottom_contact_overlapping >= 0 && - bottom_contacts[idx_bottom_contact_overlapping]->bottom_z > layer_intermediate.print_z + overlap_extra_above - EPSILON) + bottom_contacts[idx_bottom_contact_overlapping]->bottom_z > layer_intermediate.print_z - EPSILON) -- idx_bottom_contact_overlapping; // Collect all the top_contact layer intersecting with this layer. for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) { MyLayer &layer_bottom_overlapping = *bottom_contacts[idx_bottom_contact_overlapping]; - if (layer_bottom_overlapping.print_z < layer_intermediate.print_z - layer_intermediate.height - overlap_extra_below) - break; + if (layer_bottom_overlapping.print_z < layer_intermediate.print_z - layer_intermediate.height - EPSILON) + break; polygons_append(polygons_trimming, layer_bottom_overlapping.polygons); } +#ifdef SLIC3R_DEBUG + { + BoundingBox bbox = get_extents(polygons_new); + bbox.merge(get_extents(polygons_trimming)); + ::Slic3r::SVG svg(debug_out_path("support-intermediate-layers-raw-%d-%lf.svg", iRun, layer_intermediate.print_z), bbox); + svg.draw(union_ex(polygons_new, false), "blue", 0.5f); + svg.draw(union_ex(polygons_trimming, true), "red", 0.5f); + } +#endif /* SLIC3R_DEBUG */ + // Trim the polygons, store them. if (polygons_trimming.empty()) - layer_intermediate.polygons.swap(polygons_new); + layer_intermediate.polygons = std::move(polygons_new); else - layer_intermediate.polygons = diff( + layer_intermediate.polygons = diff( polygons_new, polygons_trimming, true); // safety offset to merge the touching source polygons @@ -1005,9 +1015,7 @@ void PrintObjectSupportMaterial::generate_base_layers( } #ifdef SLIC3R_DEBUG - static int iRun = 0; - iRun ++; - for (MyLayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) { + for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++it) { const MyLayer &layer = *(*it); ::Slic3r::SVG svg(debug_out_path("support-intermediate-layers-untrimmed-%d-%lf.svg", iRun, layer.print_z), get_extents(layer.polygons)); Slic3r::ExPolygons expolys = union_ex(layer.polygons, false); @@ -1015,15 +1023,16 @@ void PrintObjectSupportMaterial::generate_base_layers( } #endif /* SLIC3R_DEBUG */ - //FIXME This could be parallelized. + //FIXME This could be trivially parallelized. const coordf_t gap_extra_above = 0.1f; const coordf_t gap_extra_below = 0.1f; const coord_t gap_xy_scaled = m_support_material_flow.scaled_width(); size_t idx_object_layer_overlapping = 0; - // For all intermediate layers: + // For all intermediate support layers: for (MyLayersPtr::iterator it_layer = intermediate_layers.begin(); it_layer != intermediate_layers.end(); ++ it_layer) { MyLayer &layer_intermediate = *(*it_layer); if (layer_intermediate.polygons.empty()) + // Empty support layer, nothing to trim. continue; // Find the overlapping object layers including the extra above / below gap. while (idx_object_layer_overlapping < object.layer_count() && @@ -1033,7 +1042,7 @@ void PrintObjectSupportMaterial::generate_base_layers( Polygons polygons_trimming; for (int i = idx_object_layer_overlapping; i < object.layer_count(); ++ i) { const Layer &object_layer = *object.get_layer(i); - if (object_layer.print_z > layer_intermediate.print_z + gap_extra_above - EPSILON) + if (object_layer.print_z - object_layer.height > layer_intermediate.print_z + gap_extra_above - EPSILON) break; polygons_append(polygons_trimming, (Polygons)object_layer.slices); } @@ -1046,6 +1055,10 @@ void PrintObjectSupportMaterial::generate_base_layers( layer_intermediate.polygons, offset(polygons_trimming, gap_xy_scaled)); } + +#ifdef SLIC3R_DEBUG + ++ iRun; +#endif /* SLIC3R_DEBUG */ } Polygons PrintObjectSupportMaterial::generate_raft_base( @@ -1472,6 +1485,27 @@ void PrintObjectSupportMaterial::generate_toolpaths( polygons_append(interface_layer.layer->polygons, intersection(base_layer.layer->polygons, islands)); } + // interface and contact infill + if (! top_contact_layer.empty()) { + //FIXME When paralellizing, each thread shall have its own copy of the fillers. + Flow interface_flow( + top_contact_layer.layer->bridging ? top_contact_layer.layer->height : m_support_material_interface_flow.width, + top_contact_layer.layer->height, + m_support_material_interface_flow.nozzle_diameter, + top_contact_layer.layer->bridging); + filler_interface->angle = interface_angle; + filler_interface->spacing = m_support_material_interface_flow.spacing(); + fill_expolygons_generate_paths( + // Destination + support_layer.support_fills.entities, + // Regions to fill + union_ex(top_contact_layer.layer->polygons, true), + // Filler and its parameters + filler_interface.get(), interface_density, + // Extrusion parameters + erSupportMaterialInterface, interface_flow); + } + // interface and contact infill if (! interface_layer.empty()) { //FIXME When paralellizing, each thread shall have its own copy of the fillers. From e67e37c77269681d24a622aca4b4ef18f0103206 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 24 Nov 2016 11:29:31 +0100 Subject: [PATCH 03/82] Supports: Disabled debugging,fix of bottom surface over print. --- xs/src/libslic3r/SupportMaterial.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index bbc2abaee..02b2610e0 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -10,7 +10,7 @@ #include #include - #define SLIC3R_DEBUG +// #define SLIC3R_DEBUG // Make assert active if SLIC3R_DEBUG #ifdef SLIC3R_DEBUG @@ -972,7 +972,7 @@ void PrintObjectSupportMaterial::generate_base_layers( // Collect all the top_contact layer intersecting with this layer. for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) { MyLayer &layer_bottom_overlapping = *bottom_contacts[idx_bottom_contact_overlapping]; - if (layer_bottom_overlapping.print_z < layer_intermediate.print_z - layer_intermediate.height - EPSILON) + if (layer_bottom_overlapping.print_z < layer_intermediate.print_z - layer_intermediate.height + EPSILON) break; polygons_append(polygons_trimming, layer_bottom_overlapping.polygons); } From 0d20a81354ee8ae6a73643519d718beeaeda72f4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 24 Nov 2016 13:44:51 +0100 Subject: [PATCH 04/82] Log support through boost::log --- lib/Slic3r.pm | 6 ++++++ xs/Build.PL | 2 +- xs/src/libslic3r/SupportMaterial.cpp | 23 +++++++++++++++++++++++ xs/src/libslic3r/utils.cpp | 27 +++++++++++++++++++++++++++ xs/xsp/XS.xsp | 7 +++++++ 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index a4e1d07d8..0d7d350d6 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -18,6 +18,8 @@ sub debugf { printf @_ if $debug; } +our $loglevel = 0; + # load threads before Moo as required by it our $have_threads; BEGIN { @@ -104,6 +106,10 @@ my $pause_sema = Thread::Semaphore->new; my $parallel_sema; my $paused = 0; +# Set the logging level at the Slic3r XS module. +$Slic3r::loglevel = (defined($ENV{'SLIC3R_LOGLEVEL'}) && $ENV{'SLIC3R_LOGLEVEL'} =~ /^[1-9]/) ? $ENV{'SLIC3R_LOGLEVEL'} : 0; +set_logging_level($Slic3r::loglevel); + sub spawn_thread { my ($cb) = @_; diff --git a/xs/Build.PL b/xs/Build.PL index 98af40ead..e014ddbcf 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -117,7 +117,7 @@ if (defined $ENV{BOOST_LIBRARYDIR}) { # In order to generate the -l switches we need to know how Boost libraries are named my $have_boost = 0; -my @boost_libraries = qw(system thread); # we need these +my @boost_libraries = qw(system thread log); # we need these # check without explicit lib path (works on Linux) if (! $mswin) { diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 02b2610e0..d9fe8084c 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -9,6 +9,7 @@ #include #include #include +#include // #define SLIC3R_DEBUG @@ -197,6 +198,8 @@ struct MyLayersPtrCompare void PrintObjectSupportMaterial::generate(PrintObject &object) { + BOOST_LOG_TRIVIAL(info) << "Support generator - Start"; + coordf_t max_object_layer_height = 0.; for (size_t i = 0; i < object.layer_count(); ++ i) max_object_layer_height = std::max(max_object_layer_height, object.get_layer(i)->height); @@ -210,6 +213,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // The layers will be referenced by various LayersPtr (of type std::vector) MyLayerStorage layer_storage; + BOOST_LOG_TRIVIAL(info) << "Support generator - Creating top contacts"; + // Determine the top contact surfaces of the support, defined as: // contact = overhangs - clearance + margin // This method is responsible for identifying what contact surfaces @@ -232,6 +237,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) } #endif /* SLIC3R_DEBUG */ + BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts"; + // Determine the bottom contact surfaces of the supports over the top surfaces of the object. // Depending on whether the support is soluble or not, the contact layer thickness is decided. MyLayersPtr bottom_contacts = this->bottom_contact_layers(object, top_contacts, layer_storage); @@ -245,11 +252,15 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) } #endif /* SLIC3R_DEBUG */ + BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts"; + // Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion // and unwanted strong bonds to the object. // Rather trim the top contacts by their overlapping bottom contacts to leave a gap instead of over extruding. this->trim_top_contacts_by_bottom_contacts(object, bottom_contacts, top_contacts); + BOOST_LOG_TRIVIAL(info) << "Support generator - Creating intermediate layers - indices"; + // Generate empty intermediate layers between the top / bottom support contact layers, // The layers may or may not be synchronized with the object layers, depending on the configuration. // For example, a single nozzle multi material printing will need to generate a waste tower, which in turn @@ -257,6 +268,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers( object, bottom_contacts, top_contacts, layer_storage, max_object_layer_height); + BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers"; + // Fill in intermediate layers between the top / bottom support contact layers, trimmed by the object. this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers); @@ -269,6 +282,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) } #endif /* SLIC3R_DEBUG */ + BOOST_LOG_TRIVIAL(info) << "Support generator - Creating raft"; + // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette without holes. // Add the bottom contacts to the raft, inflate the support bases. // There is a contact layer below the 1st object layer in the bottom contacts. @@ -284,6 +299,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) shape = this->generate_pillars_shape(contact, support_z); */ + BOOST_LOG_TRIVIAL(info) << "Support generator - Creating interfaces"; + // Propagate top / bottom contact layers to generate interface layers. MyLayersPtr interface_layers = this->generate_interface_layers( object, bottom_contacts, top_contacts, intermediate_layers, layer_storage); @@ -305,6 +322,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) } */ + BOOST_LOG_TRIVIAL(info) << "Support generator - Creating layers"; + // Install support layers into the object. MyLayersPtr layers_sorted; layers_sorted.reserve(bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size()); @@ -332,8 +351,12 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) ++ layer_id; } + BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths"; + // Generate the actual toolpaths and save them into each layer. this->generate_toolpaths(object, raft, bottom_contacts, top_contacts, intermediate_layers, interface_layers); + + BOOST_LOG_TRIVIAL(info) << "Support generator - End"; } void collect_region_slices_by_type(const Layer &layer, SurfaceType surface_type, Polygons &out) diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 3f340e04a..0f298e954 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -66,3 +66,30 @@ confess_at(const char *file, int line, const char *func, } #endif + +#include +#include +#include + +namespace Slic3r { + +static boost::log::trivial::severity_level logSeverity = boost::log::trivial::fatal; + +void set_logging_level(unsigned int level) +{ + switch (level) { + case 0: logSeverity = boost::log::trivial::fatal; break; + case 1: logSeverity = boost::log::trivial::error; break; + case 2: logSeverity = boost::log::trivial::warning; break; + case 3: logSeverity = boost::log::trivial::info; break; + case 4: logSeverity = boost::log::trivial::debug; break; + default: logSeverity = boost::log::trivial::trace; break; + } + + boost::log::core::get()->set_filter + ( + boost::log::trivial::severity >= logSeverity + ); +} + +} // namespace Slic3r diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index c324396b2..20c9d4154 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -2,6 +2,7 @@ %package{Slic3r::XS}; #include +#include "Utils.hpp" %{ @@ -28,6 +29,12 @@ FORK_NAME() RETVAL = newSVpv(SLIC3R_FORK_NAME, 0); OUTPUT: RETVAL +void +set_logging_level(level) + unsigned int level; + CODE: + Slic3r::set_logging_level(level); + void xspp_test_croak_hangs_on_strawberry() CODE: From e02755632ec6999a48ae620b90c9433c2cef23b0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 24 Nov 2016 14:05:06 +0100 Subject: [PATCH 05/82] new Utils.hpp --- xs/src/libslic3r/Utils.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 xs/src/libslic3r/Utils.hpp diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp new file mode 100644 index 000000000..ca2125b06 --- /dev/null +++ b/xs/src/libslic3r/Utils.hpp @@ -0,0 +1,10 @@ +#ifndef slic3r_Utils_hpp_ +#define slic3r_Utils_hpp_ + +namespace Slic3r { + +extern void set_logging_level(unsigned int level); + +} // namespace Slic3r + +#endif // slic3r_Utils_hpp_ From a5b04d5cf30dbb04f72db81fa1c4c93fcc946902 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 24 Nov 2016 15:05:05 +0100 Subject: [PATCH 06/82] Support calculation optimized. --- xs/src/libslic3r/SupportMaterial.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index d9fe8084c..45b768357 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -770,17 +770,27 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta Polygons top = collect_region_slices_by_type(layer, stTop); if (top.empty()) continue; + size_t projection_size_old = projection.size(); // Collect projections of all contact areas above or at the same level as this top surface. for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) { // Contact surfaces are expanded away from the object, trimmed by the object. - polygons_append(projection, top_contacts[contact_idx]->polygons); + // Use a slight positive offset to overlap the touching regions. + polygons_append(projection, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON)); // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. - polygons_append(projection, *top_contacts[contact_idx]->aux_polygons); + // Use a slight positive offset to overlap the touching regions. + polygons_append(projection, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON)); + } + if (projection.empty()) + continue; + if (projection_size_old < projection.size()) { + // Merge the newly added regions. Don't use the safety offset, the offset has been added already. + projection = union_(projection, false); } // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any // top surfaces above layer.print_z falls onto this top surface. // touching are the contact surfaces supported exclusively by this top surfaaces. - Polygons touching = intersection(top, projection, true); // Do safety offset on the projection surfaces, so they overlap. + // Don't use a safety offset as it has been applied during insertion of polygons. + Polygons touching = intersection(top, projection, false); if (touching.empty()) continue; // Allocate a new bottom contact layer. @@ -800,8 +810,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta layer_new.idx_object_layer_below = layer_id; layer_new.bridging = ! m_soluble_interface; //FIXME how much to inflate the top surface? - Polygons poly_new = offset(touching, float(m_support_material_flow.scaled_width())); - layer_new.polygons.swap(poly_new); + layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width())); // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. projection = diff(projection, touching); } From e93253e2704a23e0d743fbc0b6ac60e5b94ecfd2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 24 Nov 2016 15:38:19 +0100 Subject: [PATCH 07/82] Extended tracing of Support generator --- xs/src/libslic3r/SupportMaterial.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 45b768357..29b016568 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -766,6 +766,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // Last top contact layer visited when collecting the projection of contact areas. int contact_idx = int(top_contacts.size()) - 1; for (int layer_id = int(object.total_layer_count()) - 2; layer_id >= 0; -- layer_id) { + BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id; const Layer &layer = *object.get_layer(layer_id); Polygons top = collect_region_slices_by_type(layer, stTop); if (top.empty()) From 695c92fb00a1e2a47c8fc2e43d099c660c00b23f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 28 Nov 2016 17:33:17 +0100 Subject: [PATCH 08/82] CLIPPER_OFFSET_SCALE was made a power of two, the scaling functions inside ClipperUtils are now using bit shifts instead of multiplication by doubles, which makes the scaling precise. Removed the scale parameter from all offset functions. Modified the safety offset to calculate offset per polygon instead of over all polygons at once. The old way was not safe and very slow, sometimes this meant a kiss of death for supports for example. --- lib/Slic3r/GUI/Plater/2D.pm | 4 +- lib/Slic3r/Geometry/Clipper.pm | 2 +- lib/Slic3r/Print.pm | 4 +- lib/Slic3r/Print/Object.pm | 6 +- lib/Slic3r/Print/SupportMaterial.pm | 8 +- xs/src/libslic3r/ClipperUtils.cpp | 253 ++++++++++++++------- xs/src/libslic3r/ClipperUtils.hpp | 41 ++-- xs/src/libslic3r/Fill/FillRectilinear2.cpp | 2 - xs/src/libslic3r/LayerRegion.cpp | 8 +- xs/src/libslic3r/Print.cpp | 2 +- xs/src/libslic3r/PrintObject.cpp | 5 +- xs/xsp/Clipper.xsp | 21 +- xs/xsp/Polyline.xsp | 5 +- xs/xsp/Surface.xsp | 5 +- 14 files changed, 214 insertions(+), 152 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm index 8fb8908e1..a24a5a6ff 100644 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ b/lib/Slic3r/GUI/Plater/2D.pm @@ -173,7 +173,7 @@ sub repaint { # if sequential printing is enabled and we have more than one object, draw clearance area if ($self->{config}->complete_objects && (map @{$_->instances}, @{$self->{model}->objects}) > 1) { - my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), 1, JT_ROUND, scale(0.1))}; + my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), JT_ROUND, scale(0.1))}; $dc->SetPen($self->{clearance_pen}); $dc->SetBrush($self->{transparent_brush}); $dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0); @@ -185,7 +185,7 @@ sub repaint { if (@{$self->{objects}} && $self->{config}->skirts) { my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}}; if (@points >= 3) { - my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), 1, JT_ROUND, scale(0.1))}; + my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), JT_ROUND, scale(0.1))}; $dc->SetPen($self->{skirt_pen}); $dc->SetBrush($self->{transparent_brush}); $dc->DrawPolygon($self->scaled_points_to_pixel($convex_hull, 1), 0, 0); diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 652abb8db..16dfa6f6b 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -7,7 +7,7 @@ our @ISA = qw(Exporter); our @EXPORT_OK = qw(offset offset_ex diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex - intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE + intersection intersection_pl diff_pl union union_pt_chained diff_ppl intersection_ppl); 1; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 187ee84e5..303058c3f 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -276,7 +276,7 @@ sub make_skirt { my $distance = scale max($self->config->skirt_distance, $self->config->brim_width); for (my $i = $skirts; $i > 0; $i--) { $distance += scale $spacing; - my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0]; + my $loop = offset([$convex_hull], $distance, JT_ROUND, scale(0.1))->[0]; my $eloop = Slic3r::ExtrusionLoop->new_from_paths( Slic3r::ExtrusionPath->new( polyline => Slic3r::Polygon->new(@$loop)->split_at_first_point, @@ -369,7 +369,7 @@ sub make_brim { # -0.5 because islands are not represented by their centerlines # (first offset more, then step back - reverse order than the one used for # perimeters because here we're offsetting outwards) - push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, 100000, JT_SQUARE)}; + push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, JT_SQUARE)}; } $self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths( diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 8fd2bf4cf..00b3ab669 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -7,7 +7,7 @@ use List::Util qw(min max sum first); use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path epsilon); use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex - offset offset_ex offset2 offset2_ex intersection_ppl CLIPPER_OFFSET_SCALE JT_MITER); + offset offset_ex offset2 offset2_ex intersection_ppl JT_MITER); use Slic3r::Print::State ':steps'; use Slic3r::Surface ':types'; @@ -863,7 +863,7 @@ sub discover_horizontal_shells { # and it's not wanted in a hollow print even if it would make sense when # obeying the solid shell count option strictly (DWIM!) my $margin = $neighbor_layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width; - my $regularized = offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5); + my $regularized = offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5); my $too_narrow = diff( $new_internal_solid, $regularized, @@ -893,7 +893,7 @@ sub discover_horizontal_shells { # have the same angle, so the next shell would be grown even more and so on. my $too_narrow = diff( $new_internal_solid, - offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5), + offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5), 1, ); diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 9dbfe4003..b4caffc7d 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -8,7 +8,7 @@ use Slic3r::ExtrusionPath ':roles'; use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(epsilon scale scaled_epsilon PI rad2deg deg2rad convex_hull); use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2 - intersection_pl offset2_ex diff_pl CLIPPER_OFFSET_SCALE JT_MITER JT_ROUND); + intersection_pl offset2_ex diff_pl JT_MITER JT_ROUND); use Slic3r::Surface ':types'; has 'print_config' => (is => 'rw', required => 1); @@ -299,9 +299,8 @@ sub contact_area { offset( $diff, $_, - CLIPPER_OFFSET_SCALE, JT_ROUND, - scale(0.05)*CLIPPER_OFFSET_SCALE), + scale(0.05)), $slices_margin ); } @@ -584,9 +583,8 @@ sub generate_base_layers { $fillet_radius_scaled, -$fillet_radius_scaled, # Use a geometric offsetting for filleting. - CLIPPER_OFFSET_SCALE, JT_ROUND, - 0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE), + 0.2*$fillet_radius_scaled), $trim_polygons, 0); # don't apply the safety offset. } diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index f2cc7eb38..ebb80cd51 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -9,8 +9,51 @@ #include +// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library +// for general offsetting (the offset(), offset2(), offset_ex() functions) and for the safety offset, +// which is optionally executed by other functions (union, intersection, diff). +// By the way, is the scalling for offset needed at all? +#define CLIPPER_OFFSET_POWER_OF_2 17 +// 2^17=131072 +#define CLIPPER_OFFSET_SCALE (1 << CLIPPER_OFFSET_POWER_OF_2) +#define CLIPPER_OFFSET_SCALE_ROUNDING_DELTA ((1 << (CLIPPER_OFFSET_POWER_OF_2 - 1)) - 1) + namespace Slic3r { +#ifdef CLIPPER_UTILS_DEBUG +bool clipper_export_enabled = false; +// For debugging the Clipper library, for providing bug reports to the Clipper author. +bool export_clipper_input_polygons_bin(const char *path, const ClipperLib::Paths &input_subject, const ClipperLib::Paths &input_clip) +{ + FILE *pfile = fopen(path, "wb"); + if (pfile == NULL) + return false; + + uint32_t sz = uint32_t(input_subject.size()); + fwrite(&sz, 1, sizeof(sz), pfile); + for (size_t i = 0; i < input_subject.size(); ++i) { + const ClipperLib::Path &path = input_subject[i]; + sz = uint32_t(path.size()); + ::fwrite(&sz, 1, sizeof(sz), pfile); + ::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile); + } + sz = uint32_t(input_clip.size()); + ::fwrite(&sz, 1, sizeof(sz), pfile); + for (size_t i = 0; i < input_clip.size(); ++i) { + const ClipperLib::Path &path = input_clip[i]; + sz = uint32_t(path.size()); + ::fwrite(&sz, 1, sizeof(sz), pfile); + ::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile); + } + ::fclose(pfile); + return true; + +err: + ::fclose(pfile); + return false; +} +#endif /* CLIPPER_UTILS_DEBUG */ + //----------------------------------------------------------- // legacy code from Clipper documentation void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons* expolygons) @@ -120,37 +163,51 @@ Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output) } } -void -scaleClipperPolygon(ClipperLib::Path &polygon, const double scale) +void scaleClipperPolygon(ClipperLib::Path &polygon) { PROFILE_FUNC(); - for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) { - //FIXME multiplication of int64_t by double! - // Replace by bit shifts? - (*pit).X *= scale; - (*pit).Y *= scale; + pit->X <<= CLIPPER_OFFSET_POWER_OF_2; + pit->Y <<= CLIPPER_OFFSET_POWER_OF_2; } } -void -scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale) +void scaleClipperPolygons(ClipperLib::Paths &polygons) { PROFILE_FUNC(); - - for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) { + for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { - //FIXME multiplication of int64_t by double! - // Replace by bit shifts? - (*pit).X *= scale; - (*pit).Y *= scale; + pit->X <<= CLIPPER_OFFSET_POWER_OF_2; + pit->Y <<= CLIPPER_OFFSET_POWER_OF_2; } +} + +void unscaleClipperPolygon(ClipperLib::Path &polygon) +{ + PROFILE_FUNC(); + for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) { + pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; + pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; + pit->X >>= CLIPPER_OFFSET_POWER_OF_2; + pit->Y >>= CLIPPER_OFFSET_POWER_OF_2; } } +void unscaleClipperPolygons(ClipperLib::Paths &polygons) +{ + PROFILE_FUNC(); + for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) + for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { + pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; + pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; + pit->X >>= CLIPPER_OFFSET_POWER_OF_2; + pit->Y >>= CLIPPER_OFFSET_POWER_OF_2; + } +} + void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { PROFILE_FUNC(); // read input @@ -158,12 +215,12 @@ offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float Slic3rMultiPoints_to_ClipperPaths(polygons, &input); // scale input - scaleClipperPolygons(input, scale); + scaleClipperPolygons(input); // perform offset ClipperLib::ClipperOffset co; if (joinType == jtRound) { - co.ArcTolerance = miterLimit; + co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); } else { co.MiterLimit = miterLimit; } @@ -173,20 +230,20 @@ offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float } { PROFILE_BLOCK(offset_Execute); - co.Execute(*retval, (delta*scale)); + co.Execute(*retval, delta * float(CLIPPER_OFFSET_SCALE)); } // unscale output - scaleClipperPolygons(*retval, 1/scale); + unscaleClipperPolygons(*retval); } void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // perform offset ClipperLib::Paths output; - offset(polygons, &output, delta, scale, joinType, miterLimit); + offset(polygons, &output, delta, joinType, miterLimit); // convert into ExPolygons ClipperPaths_to_Slic3rMultiPoints(output, retval); @@ -194,45 +251,45 @@ offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float d Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { Slic3r::Polygons pp; - offset(polygons, &pp, delta, scale, joinType, miterLimit); + offset(polygons, &pp, delta, joinType, miterLimit); return pp; } void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // read input ClipperLib::Paths input; Slic3rMultiPoints_to_ClipperPaths(polylines, &input); // scale input - scaleClipperPolygons(input, scale); + scaleClipperPolygons(input); // perform offset ClipperLib::ClipperOffset co; if (joinType == jtRound) { - co.ArcTolerance = miterLimit; + co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); } else { co.MiterLimit = miterLimit; } co.AddPaths(input, joinType, ClipperLib::etOpenButt); - co.Execute(*retval, (delta*scale)); + co.Execute(*retval, delta * float(CLIPPER_OFFSET_SCALE)); // unscale output - scaleClipperPolygons(*retval, 1/scale); + unscaleClipperPolygons(*retval); } void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // perform offset ClipperLib::Paths output; - offset(polylines, &output, delta, scale, joinType, miterLimit); + offset(polylines, &output, delta, joinType, miterLimit); // convert into ExPolygons ClipperPaths_to_Slic3rMultiPoints(output, retval); @@ -240,11 +297,11 @@ offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // perform offset Slic3r::ExPolygons expp; - offset(surface.expolygon, &expp, delta, scale, joinType, miterLimit); + offset(surface.expolygon, &expp, delta, joinType, miterLimit); // clone the input surface for each expolygon we got retval->clear(); @@ -258,11 +315,11 @@ offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float del void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // perform offset ClipperLib::Paths output; - offset(polygons, &output, delta, scale, joinType, miterLimit); + offset(polygons, &output, delta, joinType, miterLimit); // convert into ExPolygons ClipperPaths_to_Slic3rExPolygons(output, retval); @@ -272,19 +329,19 @@ offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float // a single polygon with multiple non-overlapping holes. // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours. void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // printf("new ExPolygon offset\n"); // 1) Offset the outer contour. - const float delta_scaled = float(delta * scale); + const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); ClipperLib::Paths contours; { ClipperLib::Path input; Slic3rMultiPoint_to_ClipperPath(expolygon.contour, &input); - scaleClipperPolygon(input, scale); + scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) - co.ArcTolerance = miterLimit; + co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.AddPath(input, joinType, ClipperLib::etClosedPolygon); @@ -298,10 +355,10 @@ void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const for (Polygons::const_iterator it_hole = expolygon.holes.begin(); it_hole != expolygon.holes.end(); ++ it_hole) { ClipperLib::Path input; Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole, &input); - scaleClipperPolygon(input, scale); + scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) - co.ArcTolerance = miterLimit; + co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.AddPath(input, joinType, ClipperLib::etClosedPolygon); @@ -322,15 +379,15 @@ void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const } // 4) Unscale the output. - scaleClipperPolygons(*retval, 1/scale); + unscaleClipperPolygons(*retval); } Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // perform offset ClipperLib::Paths output; - offset(expolygon, &output, delta, scale, joinType, miterLimit); + offset(expolygon, &output, delta, joinType, miterLimit); // convert into ExPolygons Slic3r::Polygons retval; @@ -339,11 +396,11 @@ Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, } Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // perform offset ClipperLib::Paths output; - offset(expolygon, &output, delta, scale, joinType, miterLimit); + offset(expolygon, &output, delta, joinType, miterLimit); // convert into ExPolygons Slic3r::ExPolygons retval; @@ -355,10 +412,10 @@ Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float del // a single polygon with multiple non-overlapping holes. // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours. void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // printf("new ExPolygon offset\n"); - const float delta_scaled = float(delta * scale); + const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); ClipperLib::Paths contours; ClipperLib::Paths holes; contours.reserve(expolygons.size()); @@ -374,10 +431,10 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con { ClipperLib::Path input; Slic3rMultiPoint_to_ClipperPath(it_expoly->contour, &input); - scaleClipperPolygon(input, scale); + scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) - co.ArcTolerance = miterLimit; + co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.AddPath(input, joinType, ClipperLib::etClosedPolygon); @@ -391,10 +448,10 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) { ClipperLib::Path input; Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole, &input); - scaleClipperPolygon(input, scale); + scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) - co.ArcTolerance = miterLimit; + co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.AddPath(input, joinType, ClipperLib::etClosedPolygon); @@ -416,15 +473,15 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con } // 4) Unscale the output. - scaleClipperPolygons(*retval, 1/scale); + unscaleClipperPolygons(*retval); } Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // perform offset ClipperLib::Paths output; - offset(expolygons, &output, delta, scale, joinType, miterLimit); + offset(expolygons, &output, delta, joinType, miterLimit); // convert into ExPolygons Slic3r::Polygons retval; @@ -433,11 +490,11 @@ Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, } Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { // perform offset ClipperLib::Paths output; - offset(expolygons, &output, delta, scale, joinType, miterLimit); + offset(expolygons, &output, delta, joinType, miterLimit); // convert into ExPolygons Slic3r::ExPolygons retval; @@ -447,20 +504,20 @@ Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float d Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) + ClipperLib::JoinType joinType, double miterLimit) { Slic3r::ExPolygons expp; - offset(polygons, &expp, delta, scale, joinType, miterLimit); + offset(polygons, &expp, delta, joinType, miterLimit); return expp; } void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) + const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) { if (delta1 * delta2 >= 0) { // Both deltas are the same signum - offset(polygons, retval, delta1 + delta2, scale, joinType, miterLimit); + offset(polygons, retval, delta1 + delta2, joinType, miterLimit); return; } #ifdef CLIPPER_UTILS_DEBUG @@ -479,12 +536,12 @@ offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float Slic3rMultiPoints_to_ClipperPaths(polygons, &input); // scale input - scaleClipperPolygons(input, scale); + scaleClipperPolygons(input); // prepare ClipperOffset object ClipperLib::ClipperOffset co; if (joinType == jtRound) { - co.ArcTolerance = miterLimit; + co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); } else { co.MiterLimit = miterLimit; } @@ -492,30 +549,30 @@ offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float // perform first offset ClipperLib::Paths output1; co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); - co.Execute(output1, (delta1*scale)); + co.Execute(output1, delta1 * float(CLIPPER_OFFSET_SCALE)); #ifdef CLIPPER_UTILS_DEBUG - svg.draw(output1, 1./CLIPPER_OFFSET_SCALE, "red", stroke_width); + svg.draw(output1, 1. / double(CLIPPER_OFFSET_SCALE), "red", stroke_width); #endif /* CLIPPER_UTILS_DEBUG */ // perform second offset co.Clear(); co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon); - co.Execute(*retval, (delta2*scale)); + co.Execute(*retval, delta2 * float(CLIPPER_OFFSET_SCALE)); #ifdef CLIPPER_UTILS_DEBUG - svg.draw(*retval, 1./CLIPPER_OFFSET_SCALE, "green", stroke_width); + svg.draw(*retval, 1. / double(CLIPPER_OFFSET_SCALE), "green", stroke_width); #endif /* CLIPPER_UTILS_DEBUG */ // unscale output - scaleClipperPolygons(*retval, 1/scale); + unscaleClipperPolygons(*retval); } void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) + const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) { // perform offset ClipperLib::Paths output; - offset2(polygons, &output, delta1, delta2, scale, joinType, miterLimit); + offset2(polygons, &output, delta1, delta2, joinType, miterLimit); // convert into ExPolygons ClipperPaths_to_Slic3rMultiPoints(output, retval); @@ -523,20 +580,20 @@ offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) + const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) { Slic3r::Polygons pp; - offset2(polygons, &pp, delta1, delta2, scale, joinType, miterLimit); + offset2(polygons, &pp, delta1, delta2, joinType, miterLimit); return pp; } void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) + const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) { // perform offset ClipperLib::Paths output; - offset2(polygons, &output, delta1, delta2, scale, joinType, miterLimit); + offset2(polygons, &output, delta1, delta2, joinType, miterLimit); // convert into ExPolygons ClipperPaths_to_Slic3rExPolygons(output, retval); @@ -544,10 +601,10 @@ offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const floa Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) + const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) { Slic3r::ExPolygons expp; - offset2(polygons, &expp, delta1, delta2, scale, joinType, miterLimit); + offset2(polygons, &expp, delta1, delta2, joinType, miterLimit); return expp; } @@ -580,6 +637,13 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &su PROFILE_BLOCK(_clipper_do_polygons_AddPaths); clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); clipper.AddPaths(input_clip, ClipperLib::ptClip, true); + +#ifdef CLIPPER_UTILS_DEBUG + if (clipper_export_enabled) { + static int iRun = 0; + export_clipper_input_polygons_bin(debug_out_path("_clipper_do_polygons_AddPaths-polygons-%d", ++iRun).c_str(), input_subject, input_clip); + } +#endif /* CLIPPER_UTILS_DEBUG */ } // perform operation @@ -986,22 +1050,39 @@ void safety_offset(ClipperLib::Paths* paths) PROFILE_FUNC(); // scale input - scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE); + scaleClipperPolygons(*paths); // perform offset (delta = scale 1e-05) ClipperLib::ClipperOffset co; - co.MiterLimit = 2; - { - PROFILE_BLOCK(safety_offset_AddPaths); - co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); - } - { - PROFILE_BLOCK(safety_offset_Execute); - co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE); +#ifdef CLIPPER_UTILS_DEBUG + if (clipper_export_enabled) { + static int iRun = 0; + export_clipper_input_polygons_bin(debug_out_path("safety_offset-polygons-%d", ++iRun).c_str(), *paths, ClipperLib::Paths()); + } +#endif /* CLIPPER_UTILS_DEBUG */ + ClipperLib::Paths out; + for (size_t i = 0; i < paths->size(); ++ i) { + co.Clear(); + co.MiterLimit = 2; + { + PROFILE_BLOCK(safety_offset_AddPaths); + co.AddPath((*paths)[i], ClipperLib::jtMiter, ClipperLib::etClosedPolygon); + } + { + PROFILE_BLOCK(safety_offset_Execute); + // offset outside by 10um + ClipperLib::Paths out_this; + co.Execute(out_this, 10.f * float(CLIPPER_OFFSET_SCALE)); + if (out.empty()) + out = std::move(out_this); + else + std::move(std::begin(out_this), std::end(out_this), std::back_inserter(out)); + } } + *paths = std::move(out); // unscale output - scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE); + unscaleClipperPolygons(*paths); } Polygons top_level_islands(const Slic3r::Polygons &polygons) diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 188867fe5..783453807 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -14,13 +14,6 @@ using ClipperLib::jtSquare; namespace Slic3r { -// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library. -//FIXME Vojtech: Better to use a power of 2 coefficient and to use bit shifts for scaling. -// How about 2^17=131072? -// By the way, is the scalling needed at all? Cura runs all the computation with a fixed point precision of 1um, while Slic3r scales to 1nm, -// further scaling by 10e5 brings us to -#define CLIPPER_OFFSET_SCALE 100000.0 - //----------------------------------------------------------- // legacy code from Clipper documentation void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons); @@ -40,29 +33,29 @@ void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale); // offset Polygons void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); // This is a safe variant of the polygon offset, tailored for a single ExPolygon: // a single polygon with multiple non-overlapping holes. // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours. void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, double scale, ClipperLib::JoinType joinType, double miterLimit); @@ -71,36 +64,36 @@ Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float d // offset Polylines void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare, + ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare, + ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare, + ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, - double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, - const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1, - const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1, - const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); template diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp index 766f389b0..3f7232e06 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp @@ -372,11 +372,9 @@ public: bool sticks_removed = remove_sticks(polygons_src); // if (sticks_removed) printf("Sticks removed!\n"); polygons_outer = offset(polygons_src, aoffset1, - CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, mitterLimit); polygons_inner = offset(polygons_outer, aoffset2 - aoffset1, - CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, mitterLimit); // Filter out contours with zero area or small area, contours with 2 points only. diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 7c84629e2..52e384adf 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -91,9 +91,9 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* g.process(); } -//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 3. -//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 1.5 -#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare, 0. +//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3. +//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 +#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. void LayerRegion::process_external_surfaces(const Layer* lower_layer) @@ -194,7 +194,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) break; } // Grown by 3mm. - Polygons polys = offset(bridges[i].expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS); + Polygons polys = offset(to_polygons(bridges[i].expolygon), float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS); if (idx_island == -1) { printf("Bridge did not fall into the source region!\r\n"); } else { diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 3ef964339..f396409e7 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -610,7 +610,7 @@ Print::validate() const // grow convex hull with the clearance margin { Polygons grown_hull; - offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, 1, jtRound, scale_(0.1)); + offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)); convex_hull = grown_hull.front(); } diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 3808a545e..84891d2df 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -692,8 +692,7 @@ 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, - CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare); + shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); if (shell.empty()) continue; #else @@ -705,7 +704,7 @@ PrintObject::discover_vertical_shells() // get a triangle in $too_narrow; if we grow it below then the shell // would have a different shape from the external surface and we'd still // have the same angle, so the next shell would be grown even more and so on. - Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 5.), true); + Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, ClipperLib::jtMiter, 5.), true); if (! too_narrow.empty()) { // grow the collapsing parts and add the extra area to the neighbor layer // as well as to our original surfaces so that we support this diff --git a/xs/xsp/Clipper.xsp b/xs/xsp/Clipper.xsp index 7a33ea0c4..6a28ea3ca 100644 --- a/xs/xsp/Clipper.xsp +++ b/xs/xsp/Clipper.xsp @@ -16,58 +16,53 @@ _constant() JT_MITER = jtMiter JT_ROUND = jtRound JT_SQUARE = jtSquare - CLIPPER_OFFSET_SCALE = CLIPPER_OFFSET_SCALE CODE: RETVAL = ix; OUTPUT: RETVAL Polygons -offset(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) +offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3) Polygons polygons const float delta - double scale ClipperLib::JoinType joinType double miterLimit CODE: - offset(polygons, &RETVAL, delta, scale, joinType, miterLimit); + offset(polygons, &RETVAL, delta, joinType, miterLimit); OUTPUT: RETVAL ExPolygons -offset_ex(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) +offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3) Polygons polygons const float delta - double scale ClipperLib::JoinType joinType double miterLimit CODE: - offset(polygons, &RETVAL, delta, scale, joinType, miterLimit); + offset(polygons, &RETVAL, delta, joinType, miterLimit); OUTPUT: RETVAL Polygons -offset2(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) +offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3) Polygons polygons const float delta1 const float delta2 - double scale ClipperLib::JoinType joinType double miterLimit CODE: - offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit); + offset2(polygons, &RETVAL, delta1, delta2, joinType, miterLimit); OUTPUT: RETVAL ExPolygons -offset2_ex(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) +offset2_ex(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3) Polygons polygons const float delta1 const float delta2 - double scale ClipperLib::JoinType joinType double miterLimit CODE: - offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit); + offset2(polygons, &RETVAL, delta1, delta2, joinType, miterLimit); OUTPUT: RETVAL diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index 31bd4045e..176826290 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -80,13 +80,12 @@ Polyline::rotate(angle, center_sv) THIS->rotate(angle, center); Polygons -Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSquare, miterLimit = 3) +Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3) const float delta - double scale ClipperLib::JoinType joinType double miterLimit CODE: - offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit); + offset(*THIS, &RETVAL, delta, joinType, miterLimit); OUTPUT: RETVAL diff --git a/xs/xsp/Surface.xsp b/xs/xsp/Surface.xsp index 597ed1b5d..4e3bbbb69 100644 --- a/xs/xsp/Surface.xsp +++ b/xs/xsp/Surface.xsp @@ -83,13 +83,12 @@ Surface::polygons() RETVAL Surfaces -Surface::offset(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) +Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3) const float delta - double scale ClipperLib::JoinType joinType double miterLimit CODE: - offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit); + offset(*THIS, &RETVAL, delta, joinType, miterLimit); OUTPUT: RETVAL From 8b0784f26c363ed1a6fc592035288cbbbd2d9002 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 28 Nov 2016 17:34:52 +0100 Subject: [PATCH 09/82] Added a free "cross product" function to Pointf (thinking the Pointf is really a vector in this case). Made the == operator inline. --- xs/src/libslic3r/Point.cpp | 6 ------ xs/src/libslic3r/Point.hpp | 4 +++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp index 8b71746e0..2ee5dabdd 100644 --- a/xs/src/libslic3r/Point.cpp +++ b/xs/src/libslic3r/Point.cpp @@ -12,12 +12,6 @@ Point::Point(double x, double y) this->y = lrint(y); } -bool -Point::operator==(const Point& rhs) const -{ - return this->coincides_with(rhs); -} - std::string Point::wkt() const { diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 2cf046133..a69f8d9f8 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -36,7 +36,7 @@ class Point static Point new_scale(coordf_t x, coordf_t y) { return Point(scale_(x), scale_(y)); }; - bool operator==(const Point& rhs) const; + bool operator==(const Point& rhs) const { return this->x == rhs.x && this->y == rhs.y; } std::string wkt() const; std::string dump_perl() const; void scale(double factor); @@ -105,6 +105,8 @@ class Pointf inline Pointf operator+(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x + point2.x, point1.y + point2.y); } inline Pointf operator-(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x - point2.x, point1.y - point2.y); } inline Pointf operator*(double scalar, const Pointf& point2) { return Pointf(scalar * point2.x, scalar * point2.y); } +inline Pointf operator*(const Pointf& point2, double scalar) { return Pointf(scalar * point2.x, scalar * point2.y); } +inline coordf_t cross(const Pointf &v1, const Pointf &v2) { return v1.x * v2.y - v1.y * v2.x; } class Pointf3 : public Pointf { From bde2ee6a7ea33513a5ba405348ac65e82c72b31e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 28 Nov 2016 17:36:50 +0100 Subject: [PATCH 10/82] Step forward in the C++ supports. --- xs/src/libslic3r/SupportMaterial.cpp | 176 ++++++++++++++++++++++++--- 1 file changed, 158 insertions(+), 18 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 29b016568..cc9dd8f7b 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -10,6 +10,7 @@ #include #include #include +#include // #define SLIC3R_DEBUG @@ -243,15 +244,6 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Depending on whether the support is soluble or not, the contact layer thickness is decided. MyLayersPtr bottom_contacts = this->bottom_contact_layers(object, top_contacts, layer_storage); -#ifdef SLIC3R_DEBUG - for (MyLayersPtr::const_iterator it = bottom_contacts.begin(); it != bottom_contacts.end(); ++ it) { - const MyLayer &layer = *(*it); - ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer.print_z), get_extents(layer.polygons)); - Slic3r::ExPolygons expolys = union_ex(layer.polygons, false); - svg.draw(expolys); - } -#endif /* SLIC3R_DEBUG */ - BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts"; // Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion @@ -664,10 +656,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ offset( diff_polygons, SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS, - CLIPPER_OFFSET_SCALE, ClipperLib::jtRound, // round mitter limit - scale_(0.05) * CLIPPER_OFFSET_SCALE), + scale_(0.05)), slices_margin); } } @@ -753,9 +744,128 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ return contact_out; } +struct PointHash { + size_t operator()(const Point &pt) const { + return std::hash()(pt.x) ^ std::hash()(pt.y); + } +}; + +typedef std::unordered_set PointHashMap; + +void fillet(Polygon &poly, PointHashMap &new_points_hash_map) +{ + if (poly.points.size() < 3) + // an invalid contour will not be modified. + return; + + // Flag describing a contour point. + std::vector point_flag(std::vector(poly.points.size(), 0)); + + // Does a point belong to new points? + for (size_t i = 0; i < poly.points.size(); ++ i) + if (new_points_hash_map.find(poly.points[i]) != new_points_hash_map.end()) + // Mark the point as from the new contour. + point_flag[i] = 1; + + // Mark the intersection points between the old and new contours. + size_t j = poly.points.size() - 1; + bool has_some = false; + for (size_t i = 0; i < poly.points.size(); j = i, ++ i) + if ((point_flag[i] ^ point_flag[j]) & 1) { + point_flag[(point_flag[i] & 1) ? j : i] |= 2; + has_some = true; + } + if (! has_some) + return; + + // Mark a range of points around the intersection points. + const double rounding_range = scale_(1.5); + std::vector pts; + pts.reserve(poly.points.size()); + for (int i = 0; i < int(poly.points.size()); ++ i) { + if (point_flag[i] & 2) { + point_flag[i] |= 4; + // Extend a filetting span left / right from i by an Euclidian distance of rounding_range. + double d = 0.f; + const Point *pt = &poly.points[i]; + for (int j = 1; j < int(poly.points.size()); ++ j) { + int idx = (i + j) % poly.points.size(); + const Point *pt2 = &poly.points[idx]; + d += pt->distance_to(*pt2); + if (d > rounding_range) + break; + point_flag[idx] |= 4; + pt = pt2; + } + for (int j = 1; j < int(poly.points.size()); ++ j) { + int idx = (i + int(poly.points.size()) - j) % poly.points.size(); + const Point *pt2 = &poly.points[idx]; + d += pt->distance_to(*pt2); + if (d > rounding_range) + break; + point_flag[idx] |= 4; + pt = pt2; + } + } + pts.push_back(Pointf(poly.points[i].x, poly.points[i].y)); + } + + //FIXME avoid filetting over long edges. Insert new points into long edges at the ends of the filetting interval. + + // Perform the filetting over the marked vertices. + std::vector pts2(pts); + double laplacian_weight = 0.5; + for (size_t i_round = 0; i_round < 5; ++ i_round) { + for (size_t i = 0; i < int(pts.size()); ++ i) { + if (point_flag[i] & 4) { + size_t prev = (i == 0) ? pts.size() - 1 : i - 1; + size_t next = (i + 1 == pts.size()) ? 0 : i + 1; + Pointf &p0 = pts[prev]; + Pointf &p1 = pts[i]; + Pointf &p2 = pts[next]; + // Is the point reflex? + coordf_t c = cross(p1 - p0, p2 - p1); + if (c < 0) + // The point is reflex, perform Laplacian smoothing. + pts2[i] = (1. - laplacian_weight) * pts[i] + (0.5 * laplacian_weight) * (pts[prev] + pts[next]); + } + } + pts.swap(pts2); + } + + // Mark vertices representing short edges for removal. + + // Convert the filetted points back, remove points marked for removal. + j = 0; + for (size_t i = 0; i < poly.points.size(); ++ i) { + if (point_flag[i] & 8) + // Remove this point. + continue; + if (point_flag[i] & 4) + // Update the point coordinates. + poly.points[i] = Point(pts[i].x, pts[i].y); + if (j < i) + poly.points[j] = poly.points[i]; + ++ j; + } + if (j < poly.points.size()) + poly.points.erase(poly.points.begin() + j, poly.points.end()); +} + +void fillet(Polygons &polygons, PointHashMap &new_points_hash_map) +{ + for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it) + fillet(*it, new_points_hash_map); +} + PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers( const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const { +#ifdef SLIC3R_DEBUG + static int iRun = 0; + ++ iRun; +#endif /* SLIC3R_DEBUG */ + // find object top surfaces // we'll use them to clip our support and detect where does it stick MyLayersPtr bottom_contacts; @@ -784,9 +894,30 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta if (projection.empty()) continue; if (projection_size_old < projection.size()) { + // Fill in the new_points hash table with points of new contours. + PointHashMap new_points; + for (size_t i = projection_size_old; i < projection.size(); ++ i) { + const Polygon &poly = projection[i]; + for (size_t j = 0; j < poly.points.size(); ++ j) + new_points.insert(poly.points[j]); + } // Merge the newly added regions. Don't use the safety offset, the offset has been added already. projection = union_(projection, false); + // Fillet transition between the old and new points. + fillet(projection, new_points); } + +#ifdef SLIC3R_DEBUG + { + BoundingBox bbox = get_extents(projection); + bbox.merge(get_extents(top)); + ::Slic3r::SVG svg(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), bbox); + svg.draw(union_ex(top, false), "blue", 0.5f); + svg.draw(union_ex(projection, true), "red", 0.5f); + svg.draw(layer.slices.expolygons, "green", 0.5f); + } +#endif /* SLIC3R_DEBUG */ + // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any // top surfaces above layer.print_z falls onto this top surface. // touching are the contact surfaces supported exclusively by this top surfaaces. @@ -812,12 +943,22 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta layer_new.bridging = ! m_soluble_interface; //FIXME how much to inflate the top surface? layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width())); - // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. - projection = diff(projection, touching); - } - } + // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. + // projection = diff(projection, touching); + projection = diff(projection, to_polygons(layer.slices.expolygons), true); + +#ifdef SLIC3R_DEBUG + { + ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), get_extents(layer_new.polygons)); + Slic3r::ExPolygons expolys = union_ex(layer_new.polygons, false); + svg.draw(expolys); + } +#endif /* SLIC3R_DEBUG */ + } + + std::reverse(bottom_contacts.begin(), bottom_contacts.end()); + } // if (! m_object_config->support_material_buildplate_only.value && ! top_contacts.empty()) - std::reverse(bottom_contacts.begin(), bottom_contacts.end()); return bottom_contacts; } @@ -1038,9 +1179,8 @@ void PrintObjectSupportMaterial::generate_base_layers( $fillet_radius_scaled, -$fillet_radius_scaled, # Use a geometric offsetting for filleting. - CLIPPER_OFFSET_SCALE, JT_ROUND, - 0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE), + 0.2*$fillet_radius_scaled), $trim_polygons, false); // don't apply the safety offset. } From 50fe691604e767162f333c76b83023e66dd9205c Mon Sep 17 00:00:00 2001 From: Bill Waggoner Date: Mon, 28 Nov 2016 14:28:50 -0500 Subject: [PATCH 11/82] Squashed commit of the following: commit 7fc66514bf3fbcc2c2709a4eb94856fa9b3ba523 Author: Bill Waggoner Date: Mon Nov 28 14:09:09 2016 -0500 Build.pl fails to find boost libraries commit 3fa66c4970eb2235aa57041a5a318ddcb1910219 Author: Bill Waggoner Date: Mon Nov 28 14:00:07 2016 -0500 Build.pl fails to find boost correctly Signed-off-by: Bill Waggoner --- xs/Build.PL | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/Build.PL b/xs/Build.PL index e014ddbcf..0a700c37a 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -138,8 +138,8 @@ if (!$ENV{SLIC3R_STATIC} && $have_boost) { # Try to find the boost system library. my @files = glob "$path/${lib_prefix}system*$lib_ext"; next if !@files; - - if ($files[0] =~ /${lib_prefix}system([^.]+)$lib_ext$/) { + + if ($files[0] =~ /\Q${lib_prefix}system\E([^.]*)\Q$lib_ext\E$/) { # Suffix contains the version number, the build type etc. my $suffix = $1; # Verify existence of all required boost libraries at $path. From 792856a50501d13e41877512eeb8c39b762595f8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 29 Nov 2016 19:25:10 +0100 Subject: [PATCH 12/82] Bounding Box - new method align_to_grid --- xs/src/libslic3r/BoundingBox.cpp | 22 ++++++++++++++++++++++ xs/src/libslic3r/BoundingBox.hpp | 3 +++ 2 files changed, 25 insertions(+) diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp index bffe1dbfb..75ece90bf 100644 --- a/xs/src/libslic3r/BoundingBox.cpp +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -277,4 +277,26 @@ BoundingBoxBase::overlap(const BoundingBoxBase &other) c template bool BoundingBoxBase::overlap(const BoundingBoxBase &point) const; template bool BoundingBoxBase::overlap(const BoundingBoxBase &point) const; + +// Align a coordinate to a grid. The coordinate may be negative, +// the aligned value will never be bigger than the original one. +static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) { + // Current C++ standard defines the result of integer division to be rounded to zero, + // for both positive and negative numbers. Here we want to round down for negative + // numbers as well. + coord_t aligned = (coord < 0) ? + ((coord - spacing + 1) / spacing) * spacing : + (coord / spacing) * spacing; + assert(aligned <= coord); + return aligned; +} + +void BoundingBox::align_to_grid(const coord_t cell_size) +{ + if (this->defined) { + min.x = _align_to_grid(min.x, cell_size); + min.y = _align_to_grid(min.y, cell_size); + } +} + } diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index 5f676151c..232dada80 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -65,6 +65,9 @@ class BoundingBox : public BoundingBoxBase BoundingBox rotated(double angle, const Point ¢er) const; void rotate(double angle) { (*this) = this->rotated(angle); } void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); } + // Align the min corner to a grid of cell_size x cell_size cells, + // to encompass the original bounding box. + void align_to_grid(const coord_t cell_size); BoundingBox() : BoundingBoxBase() {}; BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {}; From 85aa802d4bcd6207b0d5dacf0f3f740fcf82b987 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 29 Nov 2016 19:26:26 +0100 Subject: [PATCH 13/82] Fix of the last clipperutil safety_offset modification. Handle safety offset of holes separately, don't forget to reverse them before / after. --- xs/src/libslic3r/ClipperUtils.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index ebb80cd51..6b5991acd 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -1062,8 +1062,12 @@ void safety_offset(ClipperLib::Paths* paths) #endif /* CLIPPER_UTILS_DEBUG */ ClipperLib::Paths out; for (size_t i = 0; i < paths->size(); ++ i) { + ClipperLib::Path &path = (*paths)[i]; co.Clear(); co.MiterLimit = 2; + bool ccw = ClipperLib::Orientation(path); + if (! ccw) + std::reverse(path.begin(), path.end()); { PROFILE_BLOCK(safety_offset_AddPaths); co.AddPath((*paths)[i], ClipperLib::jtMiter, ClipperLib::etClosedPolygon); @@ -1072,7 +1076,12 @@ void safety_offset(ClipperLib::Paths* paths) PROFILE_BLOCK(safety_offset_Execute); // offset outside by 10um ClipperLib::Paths out_this; - co.Execute(out_this, 10.f * float(CLIPPER_OFFSET_SCALE)); + co.Execute(out_this, ccw ? 10.f * float(CLIPPER_OFFSET_SCALE) : -10.f * float(CLIPPER_OFFSET_SCALE)); + if (! ccw) { + // Reverse the resulting contours once again. + for (ClipperLib::Paths::iterator it = out_this.begin(); it != out_this.end(); ++ it) + std::reverse(it->begin(), it->end()); + } if (out.empty()) out = std::move(out_this); else From ca5ad58ad2acbc9d9011b76775c0293f3486012c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 29 Nov 2016 19:27:23 +0100 Subject: [PATCH 14/82] logging of slicing process --- xs/src/libslic3r/PrintObject.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 84891d2df..d870c7fc3 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -4,6 +4,8 @@ #include "Geometry.hpp" #include "SVG.hpp" +#include + #include namespace Slic3r { @@ -323,7 +325,8 @@ PrintObject::has_support_material() const // If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins. void PrintObject::detect_surfaces_type() { -// Slic3r::debugf "Detecting solid surfaces...\n"; + BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..."; + for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) { @@ -469,6 +472,8 @@ void PrintObject::detect_surfaces_type() void PrintObject::process_external_surfaces() { + BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; + FOREACH_REGION(this->_print, region) { size_t region_id = region - this->_print->regions.begin(); @@ -497,6 +502,8 @@ PrintObject::discover_vertical_shells() { PROFILE_FUNC(); + BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..."; + const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { @@ -774,6 +781,8 @@ PrintObject::discover_vertical_shells() void PrintObject::bridge_over_infill() { + BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; + FOREACH_REGION(this->_print, region) { size_t region_id = region - this->_print->regions.begin(); From 5c23ee504c82b2c4c2f97cb98c42e257311589cb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 29 Nov 2016 19:28:33 +0100 Subject: [PATCH 15/82] EdgeGrid::contours_simplified for supports --- xs/src/libslic3r/EdgeGrid.cpp | 112 +++++++++++++++++++++++++++++++++- xs/src/libslic3r/EdgeGrid.hpp | 17 ++++++ 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp index 67c438f8e..4d1432722 100644 --- a/xs/src/libslic3r/EdgeGrid.cpp +++ b/xs/src/libslic3r/EdgeGrid.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #ifdef SLIC3R_GUI #include @@ -109,7 +110,6 @@ void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resol void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) { // 1) Measure the bounding box. - m_bbox.defined = false; for (size_t i = 0; i < m_contours.size(); ++ i) { const Slic3r::Points &pts = *m_contours[i]; for (size_t j = 0; j < pts.size(); ++ j) @@ -1118,7 +1118,7 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const return f; } - + bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const { BoundingBox bbox; bbox.min = bbox.max = Point(pt.x - m_bbox.min.x, pt.y - m_bbox.min.y); @@ -1222,6 +1222,114 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo return true; } +Polygons EdgeGrid::Grid::contours_simplified() const +{ + typedef std::unordered_multimap EndPointMapType; + // 1) Collect the lines. + std::vector lines; + EndPointMapType start_point_to_line_idx; + for (int r = 0; r <= int(m_rows); ++ r) { + for (int c = 0; c <= int(m_cols); ++ c) { + bool left = cell_inside_or_crossing(r , c-1); + bool top = cell_inside_or_crossing(r-1, c ); + bool current = cell_inside_or_crossing(r , c ); + if (left != current) { + lines.push_back( + left ? + Line(Point(c, r+1), Point(c, r )) : + Line(Point(c, r ), Point(c, r+1))); + start_point_to_line_idx.insert(std::pair(lines.back().a, int(lines.size()) - 1)); + } + if (top != current) { + lines.push_back( + top ? + Line(Point(c , r), Point(c+1, r)) : + Line(Point(c+1, r), Point(c , r))); + start_point_to_line_idx.insert(std::pair(lines.back().a, int(lines.size()) - 1)); + } + } + } + + // 2) Chain the lines. + std::vector line_processed(lines.size(), false); + Polygons out; + for (int i_candidate = 0; i_candidate < int(lines.size()); ++ i_candidate) { + if (line_processed[i_candidate]) + continue; + Polygon poly; + line_processed[i_candidate] = true; + poly.points.push_back(lines[i_candidate].b); + int i_line_current = i_candidate; + for (;;) { + std::pair line_range = + start_point_to_line_idx.equal_range(lines[i_line_current].b); + // The interval has to be non empty, there shall be at least one line continuing the current one. + assert(line_range.first != line_range.second); + int i_next = -1; + for (EndPointMapType::iterator it = line_range.first; it != line_range.second; ++ it) { + if (it->second == i_candidate) { + // closing the loop. + goto end_of_poly; + } + if (line_processed[it->second]) + continue; + if (i_next == -1) { + i_next = it->second; + } else { + // This is a corner, where two lines meet exactly. Pick the line, which encloses a smallest angle with + // the current edge. + const Line &line_current = lines[i_line_current]; + const Line &line_next = lines[it->second]; + const Vector v1 = line_current.vector(); + const Vector v2 = line_next.vector(); + int64_t cross = int64_t(v1.x) * int64_t(v2.y) - int64_t(v2.x) * int64_t(v1.y); + if (cross > 0) { + // This has to be a convex right angle. There is no better next line. + i_next = it->second; + break; + } + } + } + line_processed[i_next] = true; + i_line_current = i_next; + poly.points.push_back(lines[i_line_current].b); + } + end_of_poly: + out.push_back(std::move(poly)); + } + + // 3) Scale the polygons back into world, shrink slightly and remove collinear points. + for (size_t i = 0; i < out.size(); ++ i) { + Polygon &poly = out[i]; + for (size_t j = 0; j < poly.points.size(); ++ j) { + Point &p = poly.points[j]; + p.x *= m_resolution; + p.y *= m_resolution; + p.x += m_bbox.min.x; + p.y += m_bbox.min.y; + } + // Shrink the contour slightly, so if the same contour gets discretized and simplified again, one will get the same result. + // Remove collineaer points. + Points pts; + pts.reserve(poly.points.size()); + coord_t downscale = 5; + for (size_t j = 0; j < poly.points.size(); ++ j) { + size_t j0 = (j == 0) ? poly.points.size() - 1 : j - 1; + size_t j2 = (j + 1 == poly.points.size()) ? 0 : j + 1; + Point v = poly.points[j2] - poly.points[j0]; + if (v.x != 0 && v.y != 0) { + // This is a corner point. Copy it to the output contour. + Point p = poly.points[j]; + p.y += (v.x < 0) ? downscale : -downscale; + p.x += (v.y > 0) ? downscale : -downscale; + pts.push_back(p); + } + } + poly.points = std::move(pts); + } + return out; +} + #ifdef SLIC3R_GUI void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path) { diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp index 626a430cd..f194b4644 100644 --- a/xs/src/libslic3r/EdgeGrid.hpp +++ b/xs/src/libslic3r/EdgeGrid.hpp @@ -17,6 +17,8 @@ struct Grid Grid(); ~Grid(); + void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; } + void create(const Polygons &polygons, coord_t resolution); void create(const ExPolygon &expoly, coord_t resolution); void create(const ExPolygons &expolygons, coord_t resolution); @@ -54,6 +56,9 @@ struct Grid const size_t rows() const { return m_rows; } const size_t cols() const { return m_cols; } + // For supports: Contours enclosing the rasterized edges. + Polygons contours_simplified() const; + protected: struct Cell { Cell() : begin(0), end(0) {} @@ -65,6 +70,18 @@ protected: #if 0 bool line_cell_intersect(const Point &p1, const Point &p2, const Cell &cell); #endif + bool cell_inside_or_crossing(int r, int c) const + { + if (r < 0 || r >= m_rows || + c < 0 || c >= m_cols) + // The cell is outside the domain. Hoping that the contours were correctly oriented, so + // there is a CCW outmost contour so the out of domain cells are outside. + return false; + const Cell &cell = m_cells[r * m_cols + c]; + return + (cell.begin < cell.end) || + (! m_signed_distance_field.empty() && m_signed_distance_field[r * (m_cols + 1) + c] <= 0.f); + } // Bounding box around the contours. BoundingBox m_bbox; From 1d44e92a6e621524496a960d9c6ff88ddc2479ea Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 29 Nov 2016 19:29:24 +0100 Subject: [PATCH 16/82] Point dot operator and PointHash object for std unique_xxx functions. --- xs/src/libslic3r/Point.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index a69f8d9f8..66e78ae46 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -70,6 +70,12 @@ inline Point operator+(const Point& point1, const Point& point2) { return Point( inline Point operator-(const Point& point1, const Point& point2) { return Point(point1.x - point2.x, point1.y - point2.y); } inline Point operator*(double scalar, const Point& point2) { return Point(scalar * point2.x, scalar * point2.y); } +struct PointHash { + size_t operator()(const Point &pt) const { + return std::hash()(pt.x) ^ std::hash()(pt.y); + } +}; + class Point3 : public Point { public: @@ -107,6 +113,7 @@ inline Pointf operator-(const Pointf& point1, const Pointf& point2) { return Poi inline Pointf operator*(double scalar, const Pointf& point2) { return Pointf(scalar * point2.x, scalar * point2.y); } inline Pointf operator*(const Pointf& point2, double scalar) { return Pointf(scalar * point2.x, scalar * point2.y); } inline coordf_t cross(const Pointf &v1, const Pointf &v2) { return v1.x * v2.y - v1.y * v2.x; } +inline coordf_t dot(const Pointf &v1, const Pointf &v2) { return v1.x * v1.y + v2.x * v2.y; } class Pointf3 : public Pointf { From 946b36bb4de80313fffd3a37f37406164c67d2d2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 29 Nov 2016 19:30:59 +0100 Subject: [PATCH 17/82] Supports using the EdgeGrid simplify_contour --- xs/src/libslic3r/SupportMaterial.cpp | 268 +++++++++++++++++++-------- xs/src/libslic3r/SupportMaterial.hpp | 7 +- 2 files changed, 198 insertions(+), 77 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index cc9dd8f7b..835dea79d 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -5,6 +5,7 @@ #include "Print.hpp" #include "SupportMaterial.hpp" #include "Fill/FillBase.hpp" +#include "EdgeGrid.hpp" #include #include @@ -242,7 +243,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Determine the bottom contact surfaces of the supports over the top surfaces of the object. // Depending on whether the support is soluble or not, the contact layer thickness is decided. - MyLayersPtr bottom_contacts = this->bottom_contact_layers(object, top_contacts, layer_storage); + std::vector layer_support_areas; + MyLayersPtr bottom_contacts = this->bottom_contact_layers_and_layer_support_areas( + object, top_contacts, layer_storage, + layer_support_areas); BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts"; @@ -263,7 +267,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers"; // Fill in intermediate layers between the top / bottom support contact layers, trimmed by the object. - this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers); + this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers, layer_support_areas); #ifdef SLIC3R_DEBUG for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) { @@ -429,7 +433,8 @@ Polygons collect_slices_outer(const Layer &layer) } // Find the top contact surfaces of the support or the raft. -PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers(const PrintObject &object, MyLayerStorage &layer_storage) const +PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers( + const PrintObject &object, MyLayerStorage &layer_storage) const { #ifdef SLIC3R_DEBUG static int iRun = 0; @@ -744,12 +749,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ return contact_out; } -struct PointHash { - size_t operator()(const Point &pt) const { - return std::hash()(pt.x) ^ std::hash()(pt.y); - } -}; - +#if 0 typedef std::unordered_set PointHashMap; void fillet(Polygon &poly, PointHashMap &new_points_hash_map) @@ -778,6 +778,35 @@ void fillet(Polygon &poly, PointHashMap &new_points_hash_map) if (! has_some) return; +#ifdef SLIC3R_DEBUG + static int iRun = 0; + ++ iRun; + { + FILE *pfile = ::fopen(debug_out_path("fillet-in-%d.bin", iRun).c_str(), "wb"); + size_t cnt = poly.points.size(); + ::fwrite(&cnt, 1, sizeof(cnt), pfile); + ::fwrite(poly.points.data(), cnt, sizeof(Point), pfile); + cnt = new_points_hash_map.size(); + ::fwrite(&cnt, 1, sizeof(cnt), pfile); + for (PointHashMap::iterator it = new_points_hash_map.begin(); it != new_points_hash_map.end(); ++ it) { + const Point &pt = *it; + ::fwrite(&pt, 1, sizeof(Point), pfile); + } + ::fclose(pfile); + } + ::Slic3r::SVG svg(debug_out_path("fillet-%d.svg", iRun), get_extents(poly)); + svg.draw(poly, "black", scale_(0.05)); + for (size_t i = 0; i < poly.points.size(); ++ i) { + const Point &pt1 = poly.points[i]; + const Point &pt2 = poly.points[(i+1)%poly.points.size()]; + if (new_points_hash_map.find(pt1) != new_points_hash_map.end()) + svg.draw(Line(pt1, pt2), "red", scale_(0.035)); + if (new_points_hash_map.find(pt1) != new_points_hash_map.end() && + new_points_hash_map.find(pt2) != new_points_hash_map.end()) + svg.draw(Line(pt1, pt2), "red", scale_(0.05)); + } +#endif + // Mark a range of points around the intersection points. const double rounding_range = scale_(1.5); std::vector pts; @@ -795,7 +824,7 @@ void fillet(Polygon &poly, PointHashMap &new_points_hash_map) if (d > rounding_range) break; point_flag[idx] |= 4; - pt = pt2; + //pt = pt2; } for (int j = 1; j < int(poly.points.size()); ++ j) { int idx = (i + int(poly.points.size()) - j) % poly.points.size(); @@ -804,7 +833,7 @@ void fillet(Polygon &poly, PointHashMap &new_points_hash_map) if (d > rounding_range) break; point_flag[idx] |= 4; - pt = pt2; + //pt = pt2; } } pts.push_back(Pointf(poly.points[i].x, poly.points[i].y)); @@ -850,6 +879,10 @@ void fillet(Polygon &poly, PointHashMap &new_points_hash_map) } if (j < poly.points.size()) poly.points.erase(poly.points.begin() + j, poly.points.end()); + +#ifdef SLIC3R_DEBUG + svg.draw_outline(poly, "blue", scale_(0.025)); +#endif /* SLIC3R_DEBUG */ } void fillet(Polygons &polygons, PointHashMap &new_points_hash_map) @@ -858,19 +891,46 @@ void fillet(Polygons &polygons, PointHashMap &new_points_hash_map) fillet(*it, new_points_hash_map); } -PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers( - const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const +void union_and_fillet(Polygons &polygons, size_t n_polygons_old) +{ + if (n_polygons_old == polygons.size()) + // No new polygons. + return; + + // Fill in the new_points hash table with points of new contours. + PointHashMap new_points; + for (size_t i = n_polygons_old; i < polygons.size(); ++ i) { + const Polygon &poly = polygons[i]; + for (size_t j = 0; j < poly.points.size(); ++ j) + new_points.insert(poly.points[j]); + } + // Merge the newly added regions. Don't use the safety offset, the offset has been added already. + polygons = union_(polygons, false); + // Fillet transition between the old and new points. + fillet(polygons, new_points); +} +#endif + +// Collect +PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( + const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage, + std::vector &layer_support_areas) const { #ifdef SLIC3R_DEBUG static int iRun = 0; ++ iRun; #endif /* SLIC3R_DEBUG */ + // Allocate empty surface areas, one per object layer. + layer_support_areas.assign(object.total_layer_count(), Polygons()); + // find object top surfaces // we'll use them to clip our support and detect where does it stick MyLayersPtr bottom_contacts; - if (! m_object_config->support_material_buildplate_only.value && ! top_contacts.empty()) + + if (! top_contacts.empty()) { + // There is some support to be built, if there are non-empty top surfaces detected. // Sum of unsupported contact areas above the current layer.print_z. Polygons projection; // Last top contact layer visited when collecting the projection of contact areas. @@ -878,36 +938,36 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta for (int layer_id = int(object.total_layer_count()) - 2; layer_id >= 0; -- layer_id) { BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id; const Layer &layer = *object.get_layer(layer_id); - Polygons top = collect_region_slices_by_type(layer, stTop); - if (top.empty()) - continue; + // Top surfaces of this layer, to be used to stop the surface volume from growing down. + Polygons top; + if (! m_object_config->support_material_buildplate_only) + top = collect_region_slices_by_type(layer, stTop); size_t projection_size_old = projection.size(); // Collect projections of all contact areas above or at the same level as this top surface. for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) { + Polygons polygons_new; // Contact surfaces are expanded away from the object, trimmed by the object. // Use a slight positive offset to overlap the touching regions. - polygons_append(projection, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON)); + polygons_append(polygons_new, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON)); + size_t size1 = polygons_new.size(); // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. // Use a slight positive offset to overlap the touching regions. - polygons_append(projection, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON)); + polygons_append(polygons_new, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON)); +#if 0 + union_and_fillet(polygons_new, size1); +#else + union_(polygons_new); +#endif + polygons_append(projection, std::move(polygons_new)); } if (projection.empty()) continue; - if (projection_size_old < projection.size()) { - // Fill in the new_points hash table with points of new contours. - PointHashMap new_points; - for (size_t i = projection_size_old; i < projection.size(); ++ i) { - const Polygon &poly = projection[i]; - for (size_t j = 0; j < poly.points.size(); ++ j) - new_points.insert(poly.points[j]); - } - // Merge the newly added regions. Don't use the safety offset, the offset has been added already. - projection = union_(projection, false); - // Fillet transition between the old and new points. - fillet(projection, new_points); - } - -#ifdef SLIC3R_DEBUG +#if 0 + union_and_fillet(projection, projection_size_old); +#else + union_(projection); +#endif + #ifdef SLIC3R_DEBUG { BoundingBox bbox = get_extents(projection); bbox.merge(get_extents(top)); @@ -916,48 +976,77 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta svg.draw(union_ex(projection, true), "red", 0.5f); svg.draw(layer.slices.expolygons, "green", 0.5f); } -#endif /* SLIC3R_DEBUG */ + #endif /* SLIC3R_DEBUG */ // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any // top surfaces above layer.print_z falls onto this top surface. // touching are the contact surfaces supported exclusively by this top surfaaces. // Don't use a safety offset as it has been applied during insertion of polygons. - Polygons touching = intersection(top, projection, false); - if (touching.empty()) - continue; - // Allocate a new bottom contact layer. - MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact); - bottom_contacts.push_back(&layer_new); - // Grow top surfaces so that interface and support generation are generated - // with some spacing from object - it looks we don't need the actual - // top shapes so this can be done here - layer_new.height = m_soluble_interface ? - // Align the interface layer with the object's layer height. - object.get_layer(layer_id + 1)->height : - // Place a bridge flow interface layer over the top surface. - m_support_material_interface_flow.nozzle_diameter; - layer_new.print_z = layer.print_z + layer_new.height + - (m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value); - layer_new.bottom_z = layer.print_z; - layer_new.idx_object_layer_below = layer_id; - layer_new.bridging = ! m_soluble_interface; - //FIXME how much to inflate the top surface? - layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width())); + if (! top.empty()) { + Polygons touching = intersection(top, projection, false); + if (! touching.empty()) { + // Allocate a new bottom contact layer. + MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact); + bottom_contacts.push_back(&layer_new); + // Grow top surfaces so that interface and support generation are generated + // with some spacing from object - it looks we don't need the actual + // top shapes so this can be done here + layer_new.height = m_soluble_interface ? + // Align the interface layer with the object's layer height. + object.get_layer(layer_id + 1)->height : + // Place a bridge flow interface layer over the top surface. + m_support_material_interface_flow.nozzle_diameter; + layer_new.print_z = layer.print_z + layer_new.height + + (m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value); + layer_new.bottom_z = layer.print_z; + layer_new.idx_object_layer_below = layer_id; + layer_new.bridging = ! m_soluble_interface; + //FIXME how much to inflate the top surface? + layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width())); + #ifdef SLIC3R_DEBUG + { + ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), get_extents(layer_new.polygons)); + Slic3r::ExPolygons expolys = union_ex(layer_new.polygons, false); + svg.draw(expolys); + } + #endif /* SLIC3R_DEBUG */ + } + } // ! top.empty() + + remove_sticks(projection); + remove_degenerate(projection); + + // Create an EdgeGrid, initialize it with projection, initialize signed distance field. + Slic3r::EdgeGrid::Grid grid; + coord_t grid_resolution = scale_(1.5f); + BoundingBox bbox = get_extents(projection); + bbox.offset(20); + bbox.align_to_grid(grid_resolution); + grid.set_bbox(bbox); + grid.create(projection, grid_resolution); + grid.calculate_sdf(); + + // Extract a bounding contour from the grid. + Polygons projection_simplified = grid.contours_simplified(); +#ifdef SLIC3R_DEBUG + { + BoundingBox bbox = get_extents(projection); + bbox.merge(get_extents(projection_simplified)); + + ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-simplified-%d-%d.svg", iRun, layer_id), bbox); + svg.draw(union_ex(projection, false), "blue", 0.5); + svg.draw(union_ex(projection_simplified, false), "red", 0.5); + } +#endif /* SLIC3R_DEBUG */ + projection = std::move(projection_simplified); + // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. // projection = diff(projection, touching); projection = diff(projection, to_polygons(layer.slices.expolygons), true); - -#ifdef SLIC3R_DEBUG - { - ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), get_extents(layer_new.polygons)); - Slic3r::ExPolygons expolys = union_ex(layer_new.polygons, false); - svg.draw(expolys); - } -#endif /* SLIC3R_DEBUG */ + layer_support_areas[layer_id] = projection; } - std::reverse(bottom_contacts.begin(), bottom_contacts.end()); - } // if (! m_object_config->support_material_buildplate_only.value && ! top_contacts.empty()) + } // ! top_contacts.empty() return bottom_contacts; } @@ -1093,7 +1182,8 @@ void PrintObjectSupportMaterial::generate_base_layers( const PrintObject &object, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, - MyLayersPtr &intermediate_layers) const + MyLayersPtr &intermediate_layers, + std::vector &layer_support_areas) const { #ifdef SLIC3R_DEBUG static int iRun = 0; @@ -1106,25 +1196,50 @@ void PrintObjectSupportMaterial::generate_base_layers( // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing); int idx_top_contact_above = int(top_contacts.size()) - 1; int idx_bottom_contact_overlapping = int(bottom_contacts.size()) - 1; + int idx_object_layer_above = int(object.total_layer_count()) - 1; for (int idx_intermediate = int(intermediate_layers.size()) - 1; idx_intermediate >= 0; -- idx_intermediate) { + BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " << + idx_intermediate << " of " << intermediate_layers.size(); MyLayer &layer_intermediate = *intermediate_layers[idx_intermediate]; - // New polygons for layer_intermediate. - Polygons polygons_new; - // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new. while (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->bottom_z > layer_intermediate.print_z + EPSILON) -- idx_top_contact_above; - if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z) - polygons_append(polygons_new, top_contacts[idx_top_contact_above]->polygons); - + + // New polygons for layer_intermediate. + Polygons polygons_new; + +#if 0 // Add polygons projected from the intermediate layer above. if (idx_intermediate + 1 < int(intermediate_layers.size())) polygons_append(polygons_new, intermediate_layers[idx_intermediate+1]->polygons); + if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z) { + // Contact surfaces are expanded away from the object, trimmed by the object. + // Use a slight positive offset to overlap the touching regions. + Polygons polygons_new2; + polygons_append(polygons_new2, offset(top_contacts[idx_top_contact_above]->polygons, SCALED_EPSILON)); + size_t size2 = polygons_new2.size(); + // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. + // Use a slight positive offset to overlap the touching regions. + polygons_append(polygons_new2, offset(*top_contacts[idx_top_contact_above]->aux_polygons, SCALED_EPSILON)); + union_and_fillet(polygons_new2, size2); + if (! polygons_new2.empty()) { + size_t polygons_size_old = polygons_new.size(); + polygons_append(polygons_new, std::move(polygons_new2)); + union_and_fillet(polygons_new, polygons_size_old); + } + } +#else + // Use the precomputed layer_support_areas. + while (idx_object_layer_above > 0 && object.get_layer(idx_object_layer_above - 1)->print_z > layer_intermediate.print_z - EPSILON) + -- idx_object_layer_above; + polygons_new = layer_support_areas[idx_object_layer_above]; +#endif + // Polygons to trim polygons_new. - Polygons polygons_trimming; + Polygons polygons_trimming; // Find the first top_contact layer intersecting with this layer. int idx_top_contact_overlapping = idx_top_contact_above; @@ -1145,7 +1260,7 @@ void PrintObjectSupportMaterial::generate_base_layers( -- idx_bottom_contact_overlapping; // Collect all the top_contact layer intersecting with this layer. for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) { - MyLayer &layer_bottom_overlapping = *bottom_contacts[idx_bottom_contact_overlapping]; + MyLayer &layer_bottom_overlapping = *bottom_contacts[i]; if (layer_bottom_overlapping.print_z < layer_intermediate.print_z - layer_intermediate.height + EPSILON) break; polygons_append(polygons_trimming, layer_bottom_overlapping.polygons); @@ -1203,6 +1318,9 @@ void PrintObjectSupportMaterial::generate_base_layers( size_t idx_object_layer_overlapping = 0; // For all intermediate support layers: for (MyLayersPtr::iterator it_layer = intermediate_layers.begin(); it_layer != intermediate_layers.end(); ++ it_layer) { + BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - trimmming layer " << + (it_layer - intermediate_layers.begin()) << " of " << intermediate_layers.size(); + MyLayer &layer_intermediate = *(*it_layer); if (layer_intermediate.polygons.empty()) // Empty support layer, nothing to trim. diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 31a7899ce..abcaf676a 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -163,7 +163,9 @@ private: // Generate bottom contact layers supporting the top contact layers. // For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. - MyLayersPtr bottom_contact_layers(const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const; + MyLayersPtr bottom_contact_layers_and_layer_support_areas( + const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage, + std::vector &layer_support_areas) const; // Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them. void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const; @@ -180,7 +182,8 @@ private: const PrintObject &object, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, - MyLayersPtr &intermediate_layers) const; + MyLayersPtr &intermediate_layers, + std::vector &layer_support_areas) const; Polygons generate_raft_base( const PrintObject &object, From 556204fddc55f787a1539828deacc53fe5a3dc47 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 30 Nov 2016 16:04:15 +0100 Subject: [PATCH 18/82] support_material_synchronize_layers configuration value for synchronization of object layers with print layers. --- lib/Slic3r/GUI/Tab.pm | 5 +++-- xs/src/libslic3r/PrintConfig.cpp | 7 +++++++ xs/src/libslic3r/PrintConfig.hpp | 2 ++ xs/src/libslic3r/PrintObject.cpp | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index f91f273f9..b87dfa852 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -532,7 +532,7 @@ sub build { brim_width support_material support_material_threshold support_material_enforce_layers raft_layers - support_material_pattern support_material_with_sheath support_material_spacing support_material_angle + support_material_pattern support_material_with_sheath support_material_spacing support_material_synchronize_layers support_material_angle support_material_interface_layers support_material_interface_spacing support_material_contact_distance support_material_buildplate_only dont_support_bridges notes @@ -648,6 +648,7 @@ sub build { $optgroup->append_single_option_line('support_material_interface_spacing'); $optgroup->append_single_option_line('support_material_buildplate_only'); $optgroup->append_single_option_line('dont_support_bridges'); + $optgroup->append_single_option_line('support_material_synchronize_layers'); } } @@ -910,7 +911,7 @@ sub _update { my $have_support_interface = $config->support_material_interface_layers > 0; $self->get_field($_)->toggle($have_support_material) for qw(support_material_threshold support_material_pattern support_material_with_sheath - support_material_spacing support_material_angle + support_material_spacing support_material_synchronize_layers support_material_angle support_material_interface_layers dont_support_bridges support_material_extrusion_width support_material_contact_distance); $self->get_field($_)->toggle($have_support_material && $have_support_interface) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 59dcea879..a56bdcdce 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1247,6 +1247,13 @@ PrintConfigDef::PrintConfigDef() def->min = 0; def->default_value = new ConfigOptionFloat(60); + def = this->add("support_material_synchronize_layers", coBool); + def->label = "Synchronize with object layers"; + def->category = "Support material"; + def->tooltip = "Synchronize support layers with the object print layers. This is useful with multi-material printers, where the extruder switch is expensive."; + def->cli = "support-material-synchronize-layers!"; + def->default_value = new ConfigOptionBool(true); + def = this->add("support_material_threshold", coInt); def->label = "Overhang threshold"; def->category = "Support material"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index f4e6cb8c4..f908812fc 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -160,6 +160,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig ConfigOptionEnum support_material_pattern; ConfigOptionFloat support_material_spacing; ConfigOptionFloat support_material_speed; + ConfigOptionBool support_material_synchronize_layers; ConfigOptionInt support_material_threshold; ConfigOptionBool support_material_with_sheath; ConfigOptionFloat xy_size_compensation; @@ -194,6 +195,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig OPT_PTR(support_material_pattern); OPT_PTR(support_material_spacing); OPT_PTR(support_material_speed); + OPT_PTR(support_material_synchronize_layers); OPT_PTR(support_material_threshold); OPT_PTR(support_material_with_sheath); OPT_PTR(xy_size_compensation); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 3808a545e..9dcd73d39 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -203,6 +203,7 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Wed, 30 Nov 2016 16:06:12 +0100 Subject: [PATCH 19/82] support_material_synchronize_layers implementation --- lib/Slic3r/Print/SupportMaterial.pm | 62 +++++++++++++++++------------ t/support.t | 2 +- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 9dbfe4003..5aad19cad 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -45,6 +45,7 @@ sub generate { # We now know the upper and lower boundaries for our support material object # (@$contact_z and @$top_z), so we can generate intermediate layers. my $support_z = $self->support_layers_z( + $object, [ sort keys %$contact ], [ sort keys %$top ], max(map $_->height, @{$object->layers}) @@ -384,7 +385,7 @@ sub object_top { } sub support_layers_z { - my ($self, $contact_z, $top_z, $max_object_layer_height) = @_; + my ($self, $object, $contact_z, $top_z, $max_object_layer_height) = @_; # quick table to check whether a given Z is a top surface my %top = map { $_ => 1 } @$top_z; @@ -397,13 +398,18 @@ sub support_layers_z { my $contact_distance = $self->contact_distance($support_material_height, $nozzle_diameter); # initialize known, fixed, support layers - my @z = sort { $a <=> $b } - @$contact_z, - # TODO: why we have this? - # Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z. - @$top_z, - # Top surfaces of the bottom interface layers. - (map $_ + $contact_distance, @$top_z); + my @z = @$contact_z; + my $synchronize = $self->object_config->support_material_synchronize_layers; + if (! $synchronize) { + push @z, + # TODO: why we have this? + # Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z. + @$top_z; + push @z, + # Top surfaces of the bottom interface layers. + (map $_ + $contact_distance, @$top_z); + } + @z = sort { $a <=> $b } @z; # enforce first layer height my $first_layer_height = $self->object_config->get_value('first_layer_height'); @@ -423,23 +429,29 @@ sub support_layers_z { 1..($self->object_config->raft_layers - 2); } - # create other layers (skip raft layers as they're already done and use thicker layers) - for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) { - my $target_height = $support_material_height; - if ($i > 0 && $top{ $z[$i-1] }) { - # Bridge flow? - #FIXME We want to enforce not only the bridge flow height, but also the interface gap! - # This will introduce an additional layer if the gap is set to an extreme value! - $target_height = $nozzle_diameter; - } - - # enforce first layer height - #FIXME better to split the layers regularly, than to bite a constant height one at a time, - # and then be left with a very thin layer at the end. - if (($i == 0 && $z[$i] > $target_height + $first_layer_height) - || ($z[$i] - $z[$i-1] > $target_height + Slic3r::Geometry::epsilon)) { - splice @z, $i, 0, ($z[$i] - $target_height); - $i++; + if ($synchronize) { + @z = splice @z, $self->object_config->raft_layers; +# if ($self->object_config->raft_layers > scalar(@z)); + push @z, map $_->print_z, @{$object->layers}; + } else { + # create other layers (skip raft layers as they're already done and use thicker layers) + for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) { + my $target_height = $support_material_height; + if ($i > 0 && $top{ $z[$i-1] }) { + # Bridge flow? + #FIXME We want to enforce not only the bridge flow height, but also the interface gap! + # This will introduce an additional layer if the gap is set to an extreme value! + $target_height = $nozzle_diameter; + } + + # enforce first layer height + #FIXME better to split the layers regularly, than to bite a constant height one at a time, + # and then be left with a very thin layer at the end. + if (($i == 0 && $z[$i] > $target_height + $first_layer_height) + || ($z[$i] - $z[$i-1] > $target_height + Slic3r::Geometry::epsilon)) { + splice @z, $i, 0, ($z[$i] - $target_height); + $i++; + } } } diff --git a/t/support.t b/t/support.t index 3eba6e64b..303f77088 100644 --- a/t/support.t +++ b/t/support.t @@ -28,7 +28,7 @@ use Slic3r::Test; interface_flow => $flow, first_layer_flow => $flow, ); - my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height); + my $support_z = $support->support_layers_z($print->print->objects->[0], \@contact_z, \@top_z, $config->layer_height); my $expected_top_spacing = $support->contact_distance($config->layer_height, $config->nozzle_diameter->[0]); is $support_z->[0], $config->first_layer_height, From 26a8017e99c75d1d334a2a20e545696ce7a2d8e2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 30 Nov 2016 17:33:55 +0100 Subject: [PATCH 20/82] Made the support interface contact loops configurable. --- lib/Slic3r/GUI/Tab.pm | 5 +++-- lib/Slic3r/Print/SupportMaterial.pm | 7 +++++-- xs/src/libslic3r/PrintConfig.cpp | 9 ++++++++- xs/src/libslic3r/PrintConfig.hpp | 2 ++ xs/src/libslic3r/PrintObject.cpp | 1 + 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index b87dfa852..a744b4c47 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -533,7 +533,7 @@ sub build { support_material support_material_threshold support_material_enforce_layers raft_layers support_material_pattern support_material_with_sheath support_material_spacing support_material_synchronize_layers support_material_angle - support_material_interface_layers support_material_interface_spacing + support_material_interface_layers support_material_interface_spacing support_material_interface_contact_loops support_material_contact_distance support_material_buildplate_only dont_support_bridges notes complete_objects extruder_clearance_radius extruder_clearance_height @@ -646,6 +646,7 @@ sub build { $optgroup->append_single_option_line('support_material_angle'); $optgroup->append_single_option_line('support_material_interface_layers'); $optgroup->append_single_option_line('support_material_interface_spacing'); + $optgroup->append_single_option_line('support_material_interface_contact_loops'); $optgroup->append_single_option_line('support_material_buildplate_only'); $optgroup->append_single_option_line('dont_support_bridges'); $optgroup->append_single_option_line('support_material_synchronize_layers'); @@ -916,7 +917,7 @@ sub _update { support_material_extrusion_width support_material_contact_distance); $self->get_field($_)->toggle($have_support_material && $have_support_interface) for qw(support_material_interface_spacing support_material_interface_extruder - support_material_interface_speed); + support_material_interface_speed support_material_interface_contact_loops); $self->get_field('perimeter_extrusion_width')->toggle($have_perimeters || $have_skirt || $have_brim); $self->get_field('support_material_extruder')->toggle($have_support_material || $have_skirt); diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 5aad19cad..522b4fd54 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -640,7 +640,7 @@ sub generate_toolpaths { my $interface_flow = $self->interface_flow; # shape of contact area - my $contact_loops = 1; + my $contact_loops = $self->object_config->support_material_interface_contact_loops ? 1 : 0; my $circle_radius = 1.5 * $interface_flow->scaled_width; my $circle_distance = 3 * $circle_radius; my $circle = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ], @@ -704,7 +704,10 @@ sub generate_toolpaths { # if no interface layers were requested we treat the contact layer # exactly as a generic base layer push @$base, @$contact; - } elsif (@$contact && $contact_loops > 0) { + } elsif ($contact_loops == 0) { + # No contact loops, but some interface layers. Print the contact layer as a normal interface layer. + push @$interface, @$contact; + } elsif (@$contact) { # generate the outermost loop # find centerline of the external loop (or any other kind of extrusions should the loop be skipped) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index a56bdcdce..a2a9973de 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1177,6 +1177,13 @@ PrintConfigDef::PrintConfigDef() def->cli = "support-material-extrusion-width=s"; def->default_value = new ConfigOptionFloatOrPercent(0, false); + def = this->add("support_material_interface_contact_loops", coBool); + def->label = "Interface circles"; + def->category = "Support material"; + def->tooltip = "Cover the top most interface layer with contact loops"; + def->cli = "support-material-interface-contact-loops!"; + def->default_value = new ConfigOptionBool(true); + def = this->add("support_material_interface_extruder", coInt); def->label = "Support material/raft interface extruder"; def->category = "Extruders"; @@ -1252,7 +1259,7 @@ PrintConfigDef::PrintConfigDef() def->category = "Support material"; def->tooltip = "Synchronize support layers with the object print layers. This is useful with multi-material printers, where the extruder switch is expensive."; def->cli = "support-material-synchronize-layers!"; - def->default_value = new ConfigOptionBool(true); + def->default_value = new ConfigOptionBool(false); def = this->add("support_material_threshold", coInt); def->label = "Overhang threshold"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index f908812fc..8e66d1482 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -153,6 +153,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig ConfigOptionInt support_material_enforce_layers; ConfigOptionInt support_material_extruder; ConfigOptionFloatOrPercent support_material_extrusion_width; + ConfigOptionBool support_material_interface_contact_loops; ConfigOptionInt support_material_interface_extruder; ConfigOptionInt support_material_interface_layers; ConfigOptionFloat support_material_interface_spacing; @@ -186,6 +187,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig OPT_PTR(support_material_buildplate_only); OPT_PTR(support_material_contact_distance); OPT_PTR(support_material_enforce_layers); + OPT_PTR(support_material_interface_contact_loops); OPT_PTR(support_material_extruder); OPT_PTR(support_material_extrusion_width); OPT_PTR(support_material_interface_extruder); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 9dcd73d39..e0a287f75 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -197,6 +197,7 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Wed, 30 Nov 2016 18:54:19 +0100 Subject: [PATCH 21/82] Print the supports before the object layer at the same height. --- lib/Slic3r/Print/GCode.pm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index d1b0009ad..f7fdf1a4e 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -262,7 +262,10 @@ sub export { $gcodegen->avoid_crossing_perimeters->set_disable_once(1); } - my @layers = sort { $a->print_z <=> $b->print_z } @{$object->layers}, @{$object->support_layers}; + # Order layers by print_z, support layers preceding the object layers. + my @layers = sort + { ($a->print_z == $b->print_z) ? ($a->isa('Slic3r::Layer::Support') ? -1 : 0) : $a->print_z <=> $b->print_z } + @{$object->layers}, @{$object->support_layers}; for my $layer (@layers) { # if we are printing the bottom layer of an object, and we have already finished # another one, set first layer temperatures. this happens before the Z move @@ -289,7 +292,8 @@ sub export { my %layers = (); # print_z => [ [layers], [layers], [layers] ] by obj_idx foreach my $obj_idx (0 .. ($self->print->object_count - 1)) { my $object = $self->objects->[$obj_idx]; - foreach my $layer (@{$object->layers}, @{$object->support_layers}) { + # Collect the object layers by z, support layers first, object layers second. + foreach my $layer (@{$object->support_layers}, @{$object->layers}) { $layers{ $layer->print_z } ||= []; $layers{ $layer->print_z }[$obj_idx] ||= []; push @{$layers{ $layer->print_z }[$obj_idx]}, $layer; From 7e1af658b6bffdfab0b843fd5bc0c3a3290f9bfe Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 2 Dec 2016 11:14:24 +0100 Subject: [PATCH 22/82] Initial experiments with vertex / fragment shaders, prototype to visualize layer thickness. --- lib/Slic3r/GUI.pm | 1 + lib/Slic3r/GUI/3DScene.pm | 116 ++++++++++++++++++++++++- lib/Slic3r/GUI/GLShader.pm | 174 +++++++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 lib/Slic3r/GUI/GLShader.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 087f13cc2..e655bb1b3 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -14,6 +14,7 @@ use Slic3r::GUI::ConfigWizard; use Slic3r::GUI::Controller; use Slic3r::GUI::Controller::ManualControlDialog; use Slic3r::GUI::Controller::PrinterPanel; +use Slic3r::GUI::GLShader; use Slic3r::GUI::MainFrame; use Slic3r::GUI::Notifier; use Slic3r::GUI::Plater; diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 374469a0e..ed4b82fbd 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -720,6 +720,13 @@ sub InitGL { return if $self->init; return unless $self->GetContext; $self->init(1); + + my $shader; +# $shader = $self->{shader} = new Slic3r::GUI::GLShader; + if ($self->{shader}) { + my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader); + print $info if $info; + } glClearColor(0, 0, 0, 1); glColor3f(1, 0, 0); @@ -957,6 +964,8 @@ sub draw_volumes { # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. my ($self, $fakecolor) = @_; + $self->{shader}->Enable if (! $fakecolor && $self->{shader}); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1017,15 +1026,33 @@ sub draw_volumes { if ($qverts_begin < $qverts_end) { glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr); glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr); - glDrawArrays(GL_QUADS, $qverts_begin / 3, ($qverts_end-$qverts_begin) / 3); + $qverts_begin /= 3; + $qverts_end /= 3; + my $nvertices = $qverts_end-$qverts_begin; + while ($nvertices > 0) { + my $nvertices_this = ($nvertices > 4096) ? 4096 : $nvertices; + glDrawArrays(GL_QUADS, $qverts_begin, $nvertices_this); + $qverts_begin += $nvertices_this; + $nvertices -= $nvertices_this; + } } if ($tverts_begin < $tverts_end) { glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr); glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr); - glDrawArrays(GL_TRIANGLES, $tverts_begin / 3, ($tverts_end-$tverts_begin) / 3); + $tverts_begin /= 3; + $tverts_end /= 3; + my $nvertices = $tverts_end-$tverts_begin; + while ($nvertices > 0) { + my $nvertices_this = ($nvertices > 4095) ? 4095 : $nvertices; + glDrawArrays(GL_TRIANGLES, $tverts_begin, $nvertices_this); + $tverts_begin += $nvertices_this; + $nvertices -= $nvertices_this; + } } - + + glVertexPointer_c(3, GL_FLOAT, 0, 0); + glNormalPointer_c(GL_FLOAT, 0, 0); glPopMatrix(); } glDisableClientState(GL_NORMAL_ARRAY); @@ -1036,8 +1063,11 @@ sub draw_volumes { glColor3f(0, 0, 0); glVertexPointer_p(3, $self->cut_lines_vertices); glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3); + glVertexPointer_c(3, GL_FLOAT, 0, 0); } glDisableClientState(GL_VERTEX_ARRAY); + + $self->{shader}->Disable if (! $fakecolor && $self->{shader}); } sub _report_opengl_state @@ -1069,6 +1099,86 @@ sub _report_opengl_state } } +sub _vertex_shader { + return <<'VERTEX'; +#version 110 + +varying float object_z; + +void main() +{ + vec3 normal, lightDir, viewVector, halfVector; + vec4 diffuse, ambient, globalAmbient, specular = vec4(0.0); + float NdotL,NdotHV; + + // First transform the normal into eye space and normalize the result. + normal = normalize(gl_NormalMatrix * gl_Normal); + + // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space. + // Also since we're talking about a directional light, the position field is actually direction. + lightDir = normalize(vec3(gl_LightSource[0].position)); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + NdotL = max(dot(normal, lightDir), 0.0); + + // Compute the diffuse, ambient and globalAmbient terms. +// diffuse = NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse); +// ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient; + diffuse = NdotL * (gl_Color * gl_LightSource[0].diffuse); + ambient = gl_Color * gl_LightSource[0].ambient; + globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient; + + // compute the specular term if NdotL is larger than zero + if (NdotL > 0.0) { + NdotHV = max(dot(normal, normalize(gl_LightSource[0].halfVector.xyz)),0.0); + specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess); + } + + // Perform the same lighting calculation for the 2nd light source. + lightDir = normalize(vec3(gl_LightSource[1].position)); + NdotL = max(dot(normal, lightDir), 0.0); +// diffuse += NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[1].diffuse); +// ambient += gl_FrontMaterial.ambient * gl_LightSource[1].ambient; + diffuse += NdotL * (gl_Color * gl_LightSource[1].diffuse); + ambient += gl_Color * gl_LightSource[1].ambient; + + // compute the specular term if NdotL is larger than zero + if (NdotL > 0.0) { + NdotHV = max(dot(normal, normalize(gl_LightSource[1].halfVector.xyz)),0.0); + specular += gl_FrontMaterial.specular * gl_LightSource[1].specular * pow(NdotHV,gl_FrontMaterial.shininess); + } + + gl_FrontColor = globalAmbient + diffuse + ambient + specular; + gl_FrontColor.a = 1.; + + gl_Position = ftransform(); + object_z = gl_Vertex.z / gl_Vertex.w; +} + +VERTEX +} + +sub _fragment_shader { + return <<'FRAGMENT'; +#version 110 +#define M_PI 3.1415926535897932384626433832795 + +varying float object_z; + +void main() +{ + float layer_height = 0.25; + float layer_height2 = 0.5 * layer_height; + float layer_center = floor(object_z / layer_height) * layer_height + layer_height2; + float intensity = cos(M_PI * 0.7 * (layer_center - object_z) / layer_height); + gl_FragColor = gl_Color * intensity; + gl_FragColor.a = 1.; +} + +FRAGMENT +} + # Container for object geometry and selection status. package Slic3r::GUI::3DScene::Volume; use Moo; diff --git a/lib/Slic3r/GUI/GLShader.pm b/lib/Slic3r/GUI/GLShader.pm new file mode 100644 index 000000000..e5f9c4a62 --- /dev/null +++ b/lib/Slic3r/GUI/GLShader.pm @@ -0,0 +1,174 @@ +############################################################ +# +# OpenGL::Shader::Objects - Copyright 2007 Graphcomp - ALL RIGHTS RESERVED +# Author: Bob "grafman" Free - grafman@graphcomp.com +# +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +############################################################ + +package Slic3r::GUI::GLShader; +use OpenGL(':all'); + +# Shader constructor +sub new +{ + # Check for required OpenGL extensions + return undef if (OpenGL::glpCheckExtension('GL_ARB_shader_objects')); + return undef if (OpenGL::glpCheckExtension('GL_ARB_fragment_shader')); + return undef if (OpenGL::glpCheckExtension('GL_ARB_vertex_shader')); + return undef if (OpenGL::glpCheckExtension('GL_ARB_shading_language_100')); +# my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION); +# my $glsl_version_ARB = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB ); +# print "GLSL version: $glsl_version, ARB: $glsl_version_ARB\n"; + + my $this = shift; + my $class = ref($this) || $this; + my($type) = @_; + my $self = {type => uc($type)}; + bless($self,$class); + + # Get GL_SHADING_LANGUAGE_VERSION_ARB + my $shader_ver = glGetString(0x8B8C); + $shader_ver =~ m|([\d\.]+)|; + $self->{version} = $1 || '0'; + print + return $self; +} + +# Shader destructor +# Must be disabled first +sub DESTROY +{ + my($self) = @_; + + if ($self->{program}) + { + glDetachObjectARB($self->{program},$self->{fragment_id}) if ($self->{fragment_id}); + glDetachObjectARB($self->{program},$self->{vertex_id}) if ($self->{vertex_id}); + glDeleteProgramsARB_p($self->{program}); + } + + glDeleteProgramsARB_p($self->{fragment_id}) if ($self->{fragment_id}); + glDeleteProgramsARB_p($self->{vertex_id}) if ($self->{vertex_id}); +} + + +# Load shader strings +sub Load +{ + my($self,$fragment,$vertex) = @_; + + # Load fragment code + if ($fragment) + { + $self->{fragment_id} = glCreateShaderObjectARB(GL_FRAGMENT_SHADER); + return undef if (!$self->{fragment_id}); + + glShaderSourceARB_p($self->{fragment_id}, $fragment); + #my $frag = glGetShaderSourceARB_p($self->{fragment_id}); + #print STDERR "Loaded fragment:\n$frag\n"; + + glCompileShaderARB($self->{fragment_id}); + my $stat = glGetInfoLogARB_p($self->{fragment_id}); + return "Fragment shader: $stat" if ($stat); + } + + # Load vertex code + if ($vertex) + { + $self->{vertex_id} = glCreateShaderObjectARB(GL_VERTEX_SHADER); + return undef if (!$self->{vertex_id}); + + glShaderSourceARB_p($self->{vertex_id}, $vertex); + #my $vert = glGetShaderSourceARB_p($self->{vertex_id}); + #print STDERR "Loaded vertex:\n$vert\n"; + + glCompileShaderARB($self->{vertex_id}); + $stat = glGetInfoLogARB_p($self->{vertex_id}); + return "Vertex shader: $stat" if ($stat); + } + + + # Link shaders + my $sp = glCreateProgramObjectARB(); + glAttachObjectARB($sp, $self->{fragment_id}) if ($fragment); + glAttachObjectARB($sp, $self->{vertex_id}) if ($vertex); + glLinkProgramARB($sp); + my $linked = glGetObjectParameterivARB_p($sp, GL_OBJECT_LINK_STATUS_ARB); + if (!$linked) + { + $stat = glGetInfoLogARB_p($sp); + #print STDERR "Load shader: $stat\n"; + return "Link shader: $stat" if ($stat); + return 'Unable to link shader'; + } + + $self->{program} = $sp; + + return ''; +} + +# Enable shader +sub Enable +{ + my($self) = @_; + glUseProgramObjectARB($self->{program}) if ($self->{program}); +} + +# Disable shader +sub Disable +{ + my($self) = @_; + glUseProgramObjectARB(0) if ($self->{program}); +} + +# Return shader vertex attribute ID +sub MapAttr +{ + my($self,$attr) = @_; + return undef if (!$self->{program}); + my $id = glGetAttribLocationARB_p($self->{program},$attr); + return undef if ($id < 0); + return $id; +} + +# Return shader uniform variable ID +sub Map +{ + my($self,$var) = @_; + return undef if (!$self->{program}); + my $id = glGetUniformLocationARB_p($self->{program},$var); + return undef if ($id < 0); + return $id; +} + +# Set shader vector +sub SetVector +{ + my($self,$var,@values) = @_; + + my $id = $self->Map($var); + return 'Unable to map $var' if (!defined($id)); + + my $count = scalar(@values); + eval('glUniform'.$count.'fARB($id,@values)'); + + return ''; +} + +# Set shader 4x4 matrix +sub SetMatrix +{ + my($self,$var,$oga) = @_; + + my $id = $self->Map($var); + return 'Unable to map $var' if (!defined($id)); + + glUniformMatrix4fvARB_c($id,1,0,$oga->ptr()); + return ''; +} + +1; +__END__ From 330ffed87e27da13e8bc70b90b64bcacd53aec08 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 2 Dec 2016 11:19:27 +0100 Subject: [PATCH 23/82] Adjusted copyright header of the Perl GLShader module. --- lib/Slic3r/GUI/GLShader.pm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/GLShader.pm b/lib/Slic3r/GUI/GLShader.pm index e5f9c4a62..81298f44c 100644 --- a/lib/Slic3r/GUI/GLShader.pm +++ b/lib/Slic3r/GUI/GLShader.pm @@ -1,6 +1,13 @@ ############################################################ # -# OpenGL::Shader::Objects - Copyright 2007 Graphcomp - ALL RIGHTS RESERVED +# Stripped down from the Perl OpenGL::Shader package by Vojtech Bubnik +# to only support the GLSL shaders. The original source was not maintained +# and did not install properly through the CPAN archive, and it was unnecessary +# complex. +# +# Original copyright: +# +# Copyright 2007 Graphcomp - ALL RIGHTS RESERVED # Author: Bob "grafman" Free - grafman@graphcomp.com # # This program is free software; you can redistribute it and/or From 3e8cafa8578ac53e453df3aaa0e9217d46b883a9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 5 Dec 2016 11:39:17 +0100 Subject: [PATCH 24/82] Fix of ASCII STL parser. Accept a model even with invalid normals. --- xs/src/admesh/stlinit.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/xs/src/admesh/stlinit.c b/xs/src/admesh/stlinit.c index de1760bd5..33293cd47 100644 --- a/xs/src/admesh/stlinit.c +++ b/xs/src/admesh/stlinit.c @@ -43,23 +43,8 @@ stl_open(stl_file *stl, char *file) { void stl_initialize(stl_file *stl) { - stl->error = 0; - stl->stats.degenerate_facets = 0; - stl->stats.edges_fixed = 0; - stl->stats.facets_added = 0; - stl->stats.facets_removed = 0; - stl->stats.facets_reversed = 0; - stl->stats.normals_fixed = 0; - stl->stats.number_of_parts = 0; - stl->stats.original_num_facets = 0; - stl->stats.number_of_facets = 0; - stl->stats.facets_malloced = 0; + memset(stl, 0, sizeof(stl_file)); stl->stats.volume = -1.0; - - stl->neighbors_start = NULL; - stl->facet_start = NULL; - stl->v_indices = NULL; - stl->v_shared = NULL; } void @@ -270,6 +255,7 @@ stl_read(stl_file *stl, int first_facet, int first) { rewind(stl->fp); } + char normal_buf[3][32]; for(i = first_facet; i < stl->stats.number_of_facets; i++) { if(stl->stats.type == binary) /* Read a single facet from a binary .STL file */ @@ -287,17 +273,25 @@ stl_read(stl_file *stl, int first_facet, int first) { fscanf(stl->fp, "endsolid\n"); fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid") - if((fscanf(stl->fp, " facet normal %f %f %f\n", &facet.normal.x, &facet.normal.y, &facet.normal.z) + \ - fscanf(stl->fp, " outer loop\n") + \ - fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z) + \ - fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z) + \ - fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z) + \ - fscanf(stl->fp, " endloop\n") + \ + if((fscanf(stl->fp, " facet normal %31s %31s %31s\n", normal_buf[0], normal_buf[1], normal_buf[2]) + + fscanf(stl->fp, " outer loop\n") + + fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z) + + fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z) + + fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z) + + fscanf(stl->fp, " endloop\n") + fscanf(stl->fp, " endfacet\n")) != 12) { perror("Something is syntactically very wrong with this ASCII STL!"); stl->error = 1; return; } + // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. + if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 || + sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 || + sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) { + // Normal was mangled. Maybe denormals or "not a number" were stored? + // Just reset the normal and silently ignore it. + memset(&facet.normal, 0, sizeof(facet.normal)); + } } #if 0 From 86c8207d31d7f6c08a5f9aa956f6ebb300c4dd33 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 12:28:39 +0100 Subject: [PATCH 25/82] Ported make_perimeters() and infill() to C++/XS, use pure C++ threads, cherry picked from @alexrj 66591bcc556c01572ec7519b1f8cb4ee2d430685 --- lib/Slic3r/Print.pm | 4 + lib/Slic3r/Print/Object.pm | 134 +--------------------- xs/src/libslic3r/Print.hpp | 10 +- xs/src/libslic3r/PrintObject.cpp | 185 +++++++++++++++++++++++++++++++ xs/xsp/Print.xsp | 2 + 5 files changed, 202 insertions(+), 133 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 303058c3f..47a58c3a0 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -41,8 +41,12 @@ sub size { sub process { my ($self) = @_; + $self->status_cb->(20, "Generating perimeters"); $_->make_perimeters for @{$self->objects}; + + $self->status_cb->(70, "Infilling layers"); $_->infill for @{$self->objects}; + $_->generate_support_material for @{$self->objects}; $self->make_skirt; $self->make_brim; # must come after make_skirt diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 00b3ab669..709bc65e4 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -371,114 +371,11 @@ sub _slice_region { # 2) Increases an "extra perimeters" counter at region slices where needed. # 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). sub make_perimeters { - my $self = shift; + my ($self) = @_; # prerequisites $self->slice; - - return if $self->step_done(STEP_PERIMETERS); - $self->set_step_started(STEP_PERIMETERS); - $self->print->status_cb->(20, "Generating perimeters"); - - # Merge region slices if they were split into types. - # FIXME this is using a safety offset, so the region slices will be slightly bigger with each iteration. - if ($self->typed_slices) { - $_->merge_slices for @{$self->layers}; - $self->set_typed_slices(0); - $self->invalidate_step(STEP_PREPARE_INFILL); - } - - # compare each layer to the one below, and mark those slices needing - # one additional inner perimeter, like the top of domed objects- - - # this algorithm makes sure that at least one perimeter is overlapping - # but we don't generate any extra perimeter if fill density is zero, as they would be floating - # inside the object - infill_only_where_needed should be the method of choice for printing - # hollow objects - for my $region_id (0 .. ($self->print->region_count-1)) { - my $region = $self->print->regions->[$region_id]; - my $region_perimeters = $region->config->perimeters; - - next if !$region->config->extra_perimeters; - next if $region_perimeters == 0; - next if $region->config->fill_density == 0; - - for my $i (0 .. ($self->layer_count - 2)) { - my $layerm = $self->get_layer($i)->get_region($region_id); - my $upper_layerm = $self->get_layer($i+1)->get_region($region_id); - my $upper_layerm_polygons = [ map $_->p, @{$upper_layerm->slices} ]; - # Filter upper layer polygons in intersection_ppl by their bounding boxes? - # my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; - my $total_loop_length = sum(map $_->length, @$upper_layerm_polygons) // 0; - - my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing; - my $ext_perimeter_flow = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER); - my $ext_perimeter_width = $ext_perimeter_flow->scaled_width; - my $ext_perimeter_spacing = $ext_perimeter_flow->scaled_spacing; - - foreach my $slice (@{$layerm->slices}) { - while (1) { - # compute the total thickness of perimeters - my $perimeters_thickness = $ext_perimeter_width/2 + $ext_perimeter_spacing/2 - + ($region_perimeters-1 + $slice->extra_perimeters) * $perimeter_spacing; - - # define a critical area where we don't want the upper slice to fall into - # (it should either lay over our perimeters or outside this area) - my $critical_area_depth = $perimeter_spacing*1.5; - my $critical_area = diff( - offset($slice->expolygon->arrayref, -$perimeters_thickness), - offset($slice->expolygon->arrayref, -($perimeters_thickness + $critical_area_depth)), - ); - - # check whether a portion of the upper slices falls inside the critical area - my $intersection = intersection_ppl( - $upper_layerm_polygons, - $critical_area, - ); - - # only add an additional loop if at least 30% of the slice loop would benefit from it - my $total_intersection_length = sum(map $_->length, @$intersection) // 0; - last unless $total_intersection_length > $total_loop_length*0.3; - - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "extra.svg", - no_arrows => 1, - expolygons => union_ex($critical_area), - polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], - ); - } - - $slice->extra_perimeters($slice->extra_perimeters + 1); - } - Slic3r::debugf " adding %d more perimeter(s) at layer %d\n", - $slice->extra_perimeters, $layerm->layer->id - if $slice->extra_perimeters > 0; - } - } - } - - Slic3r::parallelize( - threads => $self->print->config->threads, - items => sub { 0 .. ($self->layer_count - 1) }, - thread_cb => sub { - my $q = shift; - while (defined (my $i = $q->dequeue)) { - $self->get_layer($i)->make_perimeters; - } - }, - no_threads_cb => sub { - $_->make_perimeters for @{$self->layers}; - }, - ); - - # simplify slices (both layer and region slices), - # we only need the max resolution for perimeters - ### This makes this method not-idempotent, so we keep it disabled for now. - ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); - - $self->set_step_done(STEP_PERIMETERS); + $self->_make_perimeters; } sub prepare_infill { @@ -598,32 +495,7 @@ sub infill { # prerequisites $self->prepare_infill; - - return if $self->step_done(STEP_INFILL); - $self->set_step_started(STEP_INFILL); - $self->print->status_cb->(70, "Infilling layers"); - - Slic3r::parallelize( - threads => $self->print->config->threads, - items => sub { 0..$#{$self->layers} }, - thread_cb => sub { - my $q = shift; - while (defined (my $i = $q->dequeue)) { - $self->get_layer($i)->make_fills; - } - }, - no_threads_cb => sub { - foreach my $layer (@{$self->layers}) { - $layer->make_fills; - } - }, - ); - - ### we could free memory now, but this would make this step not idempotent - ### Vojtech: Cannot release the fill_surfaces, they are used by the support generator. - ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; - - $self->set_step_done(STEP_INFILL); + $self->_infill; } sub generate_support_material { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 30b1f40cc..3b0020b08 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -2,9 +2,11 @@ #define slic3r_Print_hpp_ #include "libslic3r.h" +#include #include -#include #include +#include +#include #include "BoundingBox.hpp" #include "Flow.hpp" #include "PrintConfig.hpp" @@ -142,6 +144,8 @@ public: void process_external_surfaces(); void discover_vertical_shells(); void bridge_over_infill(); + void _make_perimeters(); + void _infill(); private: Print* _print; @@ -151,7 +155,9 @@ private: // TODO: call model_object->get_bounding_box() instead of accepting // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); - ~PrintObject() {} + ~PrintObject(); + void _make_perimeters_do(std::queue* queue, boost::mutex* queue_mutex); + void _infill_do(std::queue* queue, boost::mutex* queue_mutex); }; typedef std::vector PrintObjectPtrs; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index d870c7fc3..742f55da4 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -910,4 +910,189 @@ PrintObject::bridge_over_infill() } } +void +PrintObject::_make_perimeters() +{ + if (this->state.is_done(posPerimeters)) return; + this->state.set_started(posPerimeters); + + // merge slices if they were split into types + if (this->typed_slices) { + FOREACH_LAYER(this, layer_it) + (*layer_it)->merge_slices(); + this->typed_slices = false; + this->state.invalidate(posPrepareInfill); + } + + // compare each layer to the one below, and mark those slices needing + // one additional inner perimeter, like the top of domed objects- + + // this algorithm makes sure that at least one perimeter is overlapping + // but we don't generate any extra perimeter if fill density is zero, as they would be floating + // inside the object - infill_only_where_needed should be the method of choice for printing + // hollow objects + FOREACH_REGION(this->_print, region_it) { + size_t region_id = region_it - this->_print->regions.begin(); + const PrintRegion ®ion = **region_it; + + + if (!region.config.extra_perimeters + || region.config.perimeters == 0 + || region.config.fill_density == 0) continue; + + for (size_t i = 0; i <= (this->layer_count()-2); ++i) { + LayerRegion &layerm = *this->get_layer(i)->get_region(region_id); + const LayerRegion &upper_layerm = *this->get_layer(i+1)->get_region(region_id); + const Polygons upper_layerm_polygons = upper_layerm.slices; + + // Filter upper layer polygons in intersection_ppl by their bounding boxes? + // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; + double total_loop_length = 0; + for (Polygons::const_iterator it = upper_layerm_polygons.begin(); it != upper_layerm_polygons.end(); ++it) + total_loop_length += it->length(); + + const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing(); + const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter); + const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width(); + const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing(); + + for (Surfaces::iterator slice = layerm.slices.surfaces.begin(); + slice != layerm.slices.surfaces.end(); ++slice) { + while (true) { + // compute the total thickness of perimeters + const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 + + (region.config.perimeters-1 + region.config.extra_perimeters) * perimeter_spacing; + + // define a critical area where we don't want the upper slice to fall into + // (it should either lay over our perimeters or outside this area) + const coord_t critical_area_depth = perimeter_spacing * 1.5; + const Polygons critical_area = diff( + offset(slice->expolygon, -perimeters_thickness), + offset(slice->expolygon, -(perimeters_thickness + critical_area_depth)) + ); + + // check whether a portion of the upper slices falls inside the critical area + const Polylines intersection = intersection_pl( + upper_layerm_polygons, + critical_area + ); + + // only add an additional loop if at least 30% of the slice loop would benefit from it + { + double total_intersection_length = 0; + for (Polylines::const_iterator it = intersection.begin(); it != intersection.end(); ++it) + total_intersection_length += it->length(); + if (total_intersection_length <= total_loop_length*0.3) break; + } + + /* + if (0) { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "extra.svg", + no_arrows => 1, + expolygons => union_ex($critical_area), + polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], + ); + } + */ + + slice->extra_perimeters++; + } + + #ifdef DEBUG + if (slice->extra_perimeters > 0) + printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, layer->id(); + #endif + } + } + } + + { + // queue all the layer numbers + std::queue queue; + boost::mutex queue_mutex; + for (size_t i = 0; i < this->layer_count(); ++i) + queue.push(i); + + boost::thread_group workers; + for (int i = 0; i < this->_print->config.threads; i++) + workers.add_thread(new boost::thread(&Slic3r::PrintObject::_make_perimeters_do, this, &queue, &queue_mutex)); + workers.join_all(); + } + + /* + simplify slices (both layer and region slices), + we only need the max resolution for perimeters + ### This makes this method not-idempotent, so we keep it disabled for now. + ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); + */ + + this->state.set_done(posPerimeters); +} + +void +PrintObject::_make_perimeters_do(std::queue* queue, boost::mutex* queue_mutex) +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + + while (true) { + size_t layer_id; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + layer_id = queue->front(); + queue->pop(); + } + //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; + this->get_layer(layer_id)->make_perimeters(); + boost::this_thread::interruption_point(); + } +} + +void +PrintObject::_infill() +{ + if (this->state.is_done(posInfill)) return; + this->state.set_started(posInfill); + + { + // queue all the layer numbers + std::queue queue; + boost::mutex queue_mutex; + for (size_t i = 0; i < this->layer_count(); ++i) + queue.push(i); + + boost::thread_group workers; + for (int i = 0; i < this->_print->config.threads; i++) + workers.add_thread(new boost::thread(&Slic3r::PrintObject::_infill_do, this, &queue, &queue_mutex)); + workers.join_all(); + } + + /* we could free memory now, but this would make this step not idempotent + ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; + */ + + this->state.set_done(posInfill); +} + +void +PrintObject::_infill_do(std::queue* queue, boost::mutex* queue_mutex) +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + + while (true) { + size_t layer_id; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + layer_id = queue->front(); + queue->pop(); + } + //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; + this->get_layer(layer_id)->make_fills(); + boost::this_thread::interruption_point(); + } +} + } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index ad90eb32e..d36134839 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -111,6 +111,8 @@ _constant() void process_external_surfaces(); void discover_vertical_shells(); void bridge_over_infill(); + void _make_perimeters(); + void _infill(); int ptr() %code%{ RETVAL = (int)(intptr_t)THIS; %}; From e9290252d60edbe28c86e0bc80a6229d932f04a8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 12:47:11 +0100 Subject: [PATCH 26/82] Fixed regression in the _make_perimeters port --- xs/src/libslic3r/PrintObject.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 742f55da4..3f41179dc 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -938,7 +938,8 @@ PrintObject::_make_perimeters() if (!region.config.extra_perimeters || region.config.perimeters == 0 - || region.config.fill_density == 0) continue; + || region.config.fill_density == 0 + || this->layer_count() < 2) continue; for (size_t i = 0; i <= (this->layer_count()-2); ++i) { LayerRegion &layerm = *this->get_layer(i)->get_region(region_id); @@ -1002,7 +1003,7 @@ PrintObject::_make_perimeters() #ifdef DEBUG if (slice->extra_perimeters > 0) - printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, layer->id(); + printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, i); #endif } } From 70a9de085b1ad4519eeb142125b42da96e135222 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 13:45:58 +0100 Subject: [PATCH 27/82] Parallelize TriangleMeshSlicer::slice(), cherry picked from @alexrj 83ad123d951c6ee663d2f3b02e095c203ca794e7 --- xs/src/libslic3r/TriangleMesh.cpp | 133 ++++++++++++++++++++++-------- xs/src/libslic3r/TriangleMesh.hpp | 24 ++++-- 2 files changed, 116 insertions(+), 41 deletions(-) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 5737747a2..9ed0dfcd4 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -2,7 +2,6 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" #include -#include #include #include #include @@ -403,7 +402,7 @@ TriangleMesh::require_shared_vertices() } void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { /* This method gets called with a list of unscaled Z coordinates and outputs @@ -427,7 +426,6 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la At the end, we free the tables generated by analyze() as we don't need them anymore. - FUTURE: parallelize slice_facet() and make_loops() NOTE: this method accepts a vector of floats because the mesh coordinate type is float. @@ -435,18 +433,66 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la std::vector lines(z.size()); - for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { - stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; + { + // queue all the facet indices + std::queue queue; + boost::mutex queue_mutex, lines_mutex; + for (size_t i = 0; i < this->mesh->stl.stats.number_of_facets; ++i) queue.push(i); + + boost::thread_group workers; + for (int i = 0; i < boost::thread::hardware_concurrency(); i++) + workers.add_thread(new boost::thread(&TriangleMeshSlicer::_slice_do, this, + &queue, &queue_mutex, &lines, &lines_mutex, z)); + workers.join_all(); + } + + // v_scaled_shared could be freed here + + // build loops + layers->resize(z.size()); + { + // queue all the layer numbers + std::queue queue; + boost::mutex queue_mutex; + for (size_t i = 0; i < lines.size(); ++i) queue.push(i); + + // We don't use a mutex for lines because workers are only writing the skip property + // and no workers work on the same layer (i.e. item of 'lines'). + boost::thread_group workers; + for (int i = 0; i < boost::thread::hardware_concurrency(); i++) + workers.add_thread(new boost::thread(&TriangleMeshSlicer::_make_loops_do, this, + &queue, &queue_mutex, &lines, layers)); + workers.join_all(); + } +} + +void +TriangleMeshSlicer::_slice_do(std::queue* queue, boost::mutex* queue_mutex, + std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + + while (true) { + int facet_idx; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + facet_idx = queue->front(); + queue->pop(); + } + //std::cout << " Facet " << facet_idx << " (" << boost::this_thread::get_id() << ")" << std::endl; + + const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; // find facet extents - float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); - float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); + const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); + const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); #ifdef SLIC3R_TRIANGLEMESH_DEBUG printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, - facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z, - facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z, - facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z); + facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, + facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, + facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); printf("z: min = %.2f, max = %.2f\n", min_z, max_z); #endif @@ -460,25 +506,14 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { std::vector::size_type layer_idx = it - z.begin(); - this->slice_facet(*it / SCALING_FACTOR, *facet, facet_idx, min_z, max_z, &lines[layer_idx]); + this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex); } - } - - // v_scaled_shared could be freed here - - // build loops - layers->resize(z.size()); - for (std::vector::iterator it = lines.begin(); it != lines.end(); ++it) { - size_t layer_idx = it - lines.begin(); - #ifdef SLIC3R_TRIANGLEMESH_DEBUG - printf("Layer " PRINTF_ZU ":\n", layer_idx); - #endif - this->make_loops(*it, &(*layers)[layer_idx]); + boost::this_thread::interruption_point(); } } void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { std::vector layers_p; this->slice(z, &layers_p); @@ -495,7 +530,9 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* } void -TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines) const +TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, + const float &min_z, const float &max_z, std::vector* lines, + boost::mutex* lines_mutex) const { std::vector points; std::vector< std::vector::size_type > points_on_layer; @@ -547,7 +584,12 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int line.b.y = b->y; line.a_id = a_id; line.b_id = b_id; - lines->push_back(line); + if (lines_mutex != NULL) { + boost::lock_guard l(*lines_mutex); + lines->push_back(line); + } else { + lines->push_back(line); + } found_horizontal_edge = true; @@ -600,13 +642,38 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int line.b_id = points[0].point_id; line.edge_a_id = points[1].edge_id; line.edge_b_id = points[0].edge_id; - lines->push_back(line); + if (lines_mutex != NULL) { + boost::lock_guard l(*lines_mutex); + lines->push_back(line); + } else { + lines->push_back(line); + } return; } } void -TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) +TriangleMeshSlicer::_make_loops_do(std::queue* queue, boost::mutex* queue_mutex, + std::vector* lines, std::vector* layers) const +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + + while (true) { + size_t layer_id; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + layer_id = queue->front(); + queue->pop(); + } + //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; + this->make_loops((*lines)[layer_id], &(*layers)[layer_id]); + boost::this_thread::interruption_point(); + } +} + +void +TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) const { /* SVG svg("lines.svg"); @@ -707,6 +774,7 @@ TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* l for (IntersectionLinePtrs::const_iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) { p.points.push_back((*lineptr)->a); } + loops->push_back(p); #ifdef SLIC3R_TRIANGLEMESH_DEBUG @@ -746,7 +814,7 @@ class _area_comp { }; void -TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) +TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) const { Polygons loops; this->make_loops(lines, &loops); @@ -780,7 +848,7 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, } void -TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) +TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const { /* Input loops are not suitable for evenodd nor nonzero fill types, as we might get @@ -844,7 +912,7 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) } void -TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) +TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) const { Polygons pp; this->make_loops(lines, &pp); @@ -852,7 +920,7 @@ TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPoly } void -TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) +TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const { IntersectionLines upper_lines, lower_lines; @@ -1004,7 +1072,6 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) stl_get_size(&(upper->stl)); stl_get_size(&(lower->stl)); - } TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index cf129809a..ec08f0d7c 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -3,7 +3,9 @@ #include "libslic3r.h" #include +#include #include +#include #include "BoundingBox.hpp" #include "Line.hpp" #include "Point.hpp" @@ -88,19 +90,25 @@ class TriangleMeshSlicer TriangleMesh* mesh; TriangleMeshSlicer(TriangleMesh* _mesh); ~TriangleMeshSlicer(); - void slice(const std::vector &z, std::vector* layers); - void slice(const std::vector &z, std::vector* layers); - void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines) const; - void cut(float z, TriangleMesh* upper, TriangleMesh* lower); + void slice(const std::vector &z, std::vector* layers) const; + void slice(const std::vector &z, std::vector* layers) const; + void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, + const float &min_z, const float &max_z, std::vector* lines, + boost::mutex* lines_mutex = NULL) const; + void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; private: typedef std::vector< std::vector > t_facets_edges; t_facets_edges facets_edges; stl_vertex* v_scaled_shared; - void make_loops(std::vector &lines, Polygons* loops); - void make_expolygons(const Polygons &loops, ExPolygons* slices); - void make_expolygons_simple(std::vector &lines, ExPolygons* slices); - void make_expolygons(std::vector &lines, ExPolygons* slices); + void _slice_do(std::queue* queue, boost::mutex* queue_mutex, + std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const; + void _make_loops_do(std::queue* queue, boost::mutex* queue_mutex, + std::vector* lines, std::vector* layers) const; + void make_loops(std::vector &lines, Polygons* loops) const; + void make_expolygons(const Polygons &loops, ExPolygons* slices) const; + void make_expolygons_simple(std::vector &lines, ExPolygons* slices) const; + void make_expolygons(std::vector &lines, ExPolygons* slices) const; }; } From 9fbd135f14ce6769786226f96f99ec9c30cfd00d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 13:46:19 +0100 Subject: [PATCH 28/82] Automatically detect the number of cores and use that as a default for threads number --- xs/src/libslic3r/PrintConfig.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 59dcea879..5e1732a0f 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1,4 +1,5 @@ #include "PrintConfig.hpp" +#include namespace Slic3r { @@ -1291,7 +1292,7 @@ PrintConfigDef::PrintConfigDef() def->readonly = true; def->min = 1; def->max = 16; - def->default_value = new ConfigOptionInt(2); + def->default_value = new ConfigOptionInt(boost::thread::hardware_concurrency()); def = this->add("toolchange_gcode", coString); def->label = "Tool change G-code"; From 73ddd3b438cf4fa499e89705c30daa2fbfdf1c99 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 16:07:36 +0100 Subject: [PATCH 29/82] Refactored parallelization code, cherry picked from @alexrj 36789774471a0bf9a66bb33cc3ab6984f8ede8c6 --- xs/src/libslic3r/Print.hpp | 3 - xs/src/libslic3r/PrintConfig.cpp | 2 +- xs/src/libslic3r/PrintObject.cpp | 72 +++--------------- xs/src/libslic3r/TriangleMesh.cpp | 121 ++++++++++-------------------- xs/src/libslic3r/TriangleMesh.hpp | 7 +- xs/src/libslic3r/libslic3r.h | 60 +++++++++++++-- 6 files changed, 106 insertions(+), 159 deletions(-) diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 3b0020b08..132e7d0df 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -2,7 +2,6 @@ #define slic3r_Print_hpp_ #include "libslic3r.h" -#include #include #include #include @@ -156,8 +155,6 @@ private: // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject(); - void _make_perimeters_do(std::queue* queue, boost::mutex* queue_mutex); - void _infill_do(std::queue* queue, boost::mutex* queue_mutex); }; typedef std::vector PrintObjectPtrs; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 5e1732a0f..6aace7732 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1292,7 +1292,7 @@ PrintConfigDef::PrintConfigDef() def->readonly = true; def->min = 1; def->max = 16; - def->default_value = new ConfigOptionInt(boost::thread::hardware_concurrency()); + def->default_value = new ConfigOptionInt((boost::thread::hardware_concurrency() == 0) ? 2 : boost::thread::hardware_concurrency()); def = this->add("toolchange_gcode", coString); def->label = "Tool change G-code"; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 3f41179dc..b276744c4 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -1009,18 +1009,11 @@ PrintObject::_make_perimeters() } } - { - // queue all the layer numbers - std::queue queue; - boost::mutex queue_mutex; - for (size_t i = 0; i < this->layer_count(); ++i) - queue.push(i); - - boost::thread_group workers; - for (int i = 0; i < this->_print->config.threads; i++) - workers.add_thread(new boost::thread(&Slic3r::PrintObject::_make_perimeters_do, this, &queue, &queue_mutex)); - workers.join_all(); - } + parallelize( + std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue + boost::bind(&Slic3r::Layer::make_perimeters, _1), + this->_print->config.threads.value + ); /* simplify slices (both layer and region slices), @@ -1032,43 +1025,17 @@ PrintObject::_make_perimeters() this->state.set_done(posPerimeters); } -void -PrintObject::_make_perimeters_do(std::queue* queue, boost::mutex* queue_mutex) -{ - //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; - - while (true) { - size_t layer_id; - { - boost::lock_guard l(*queue_mutex); - if (queue->empty()) return; - layer_id = queue->front(); - queue->pop(); - } - //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; - this->get_layer(layer_id)->make_perimeters(); - boost::this_thread::interruption_point(); - } -} - void PrintObject::_infill() { if (this->state.is_done(posInfill)) return; this->state.set_started(posInfill); - { - // queue all the layer numbers - std::queue queue; - boost::mutex queue_mutex; - for (size_t i = 0; i < this->layer_count(); ++i) - queue.push(i); - - boost::thread_group workers; - for (int i = 0; i < this->_print->config.threads; i++) - workers.add_thread(new boost::thread(&Slic3r::PrintObject::_infill_do, this, &queue, &queue_mutex)); - workers.join_all(); - } + parallelize( + std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue + boost::bind(&Slic3r::Layer::make_fills, _1), + this->_print->config.threads.value + ); /* we could free memory now, but this would make this step not idempotent ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; @@ -1077,23 +1044,4 @@ PrintObject::_infill() this->state.set_done(posInfill); } -void -PrintObject::_infill_do(std::queue* queue, boost::mutex* queue_mutex) -{ - //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; - - while (true) { - size_t layer_id; - { - boost::lock_guard l(*queue_mutex); - if (queue->empty()) return; - layer_id = queue->front(); - queue->pop(); - } - //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; - this->get_layer(layer_id)->make_fills(); - boost::this_thread::interruption_point(); - } -} - } diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 9ed0dfcd4..ee09ac532 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -3,6 +3,7 @@ #include "Geometry.hpp" #include #include +#include #include #include #include @@ -432,83 +433,55 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la */ std::vector lines(z.size()); - { - // queue all the facet indices - std::queue queue; - boost::mutex queue_mutex, lines_mutex; - for (size_t i = 0; i < this->mesh->stl.stats.number_of_facets; ++i) queue.push(i); - - boost::thread_group workers; - for (int i = 0; i < boost::thread::hardware_concurrency(); i++) - workers.add_thread(new boost::thread(&TriangleMeshSlicer::_slice_do, this, - &queue, &queue_mutex, &lines, &lines_mutex, z)); - workers.join_all(); + boost::mutex lines_mutex; + parallelize( + 0, + this->mesh->stl.stats.number_of_facets-1, + boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z) + ); } // v_scaled_shared could be freed here // build loops layers->resize(z.size()); - { - // queue all the layer numbers - std::queue queue; - boost::mutex queue_mutex; - for (size_t i = 0; i < lines.size(); ++i) queue.push(i); - - // We don't use a mutex for lines because workers are only writing the skip property - // and no workers work on the same layer (i.e. item of 'lines'). - boost::thread_group workers; - for (int i = 0; i < boost::thread::hardware_concurrency(); i++) - workers.add_thread(new boost::thread(&TriangleMeshSlicer::_make_loops_do, this, - &queue, &queue_mutex, &lines, layers)); - workers.join_all(); - } + parallelize( + 0, + lines.size()-1, + boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers) + ); } void -TriangleMeshSlicer::_slice_do(std::queue* queue, boost::mutex* queue_mutex, - std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const +TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, + const std::vector &z) const { - //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; - while (true) { - int facet_idx; - { - boost::lock_guard l(*queue_mutex); - if (queue->empty()) return; - facet_idx = queue->front(); - queue->pop(); - } - //std::cout << " Facet " << facet_idx << " (" << boost::this_thread::get_id() << ")" << std::endl; - - const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; - - // find facet extents - const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); - const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); - - #ifdef SLIC3R_TRIANGLEMESH_DEBUG - printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, - facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, - facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, - facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); - printf("z: min = %.2f, max = %.2f\n", min_z, max_z); - #endif - - // find layer extents - std::vector::const_iterator min_layer, max_layer; - min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z - max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z - #ifdef SLIC3R_TRIANGLEMESH_DEBUG - printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); - #endif - - for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { - std::vector::size_type layer_idx = it - z.begin(); - this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex); - } - boost::this_thread::interruption_point(); + // find facet extents + const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); + const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); + + #ifdef SLIC3R_DEBUG + printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, + facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, + facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, + facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); + printf("z: min = %.2f, max = %.2f\n", min_z, max_z); + #endif + + // find layer extents + std::vector::const_iterator min_layer, max_layer; + min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z + max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z + #ifdef SLIC3R_DEBUG + printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); + #endif + + for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { + std::vector::size_type layer_idx = it - z.begin(); + this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex); } } @@ -653,23 +626,9 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int } void -TriangleMeshSlicer::_make_loops_do(std::queue* queue, boost::mutex* queue_mutex, - std::vector* lines, std::vector* layers) const +TriangleMeshSlicer::_make_loops_do(size_t i, std::vector* lines, std::vector* layers) const { - //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; - - while (true) { - size_t layer_id; - { - boost::lock_guard l(*queue_mutex); - if (queue->empty()) return; - layer_id = queue->front(); - queue->pop(); - } - //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; - this->make_loops((*lines)[layer_id], &(*layers)[layer_id]); - boost::this_thread::interruption_point(); - } + this->make_loops((*lines)[i], &(*layers)[i]); } void diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index ec08f0d7c..ff332ed66 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -3,7 +3,6 @@ #include "libslic3r.h" #include -#include #include #include #include "BoundingBox.hpp" @@ -101,10 +100,8 @@ class TriangleMeshSlicer typedef std::vector< std::vector > t_facets_edges; t_facets_edges facets_edges; stl_vertex* v_scaled_shared; - void _slice_do(std::queue* queue, boost::mutex* queue_mutex, - std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const; - void _make_loops_do(std::queue* queue, boost::mutex* queue_mutex, - std::vector* lines, std::vector* layers) const; + void _slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const; + void _make_loops_do(size_t i, std::vector* lines, std::vector* layers) const; void make_loops(std::vector &lines, Polygons* loops) const; void make_expolygons(const Polygons &loops, ExPolygons* slices) const; void make_expolygons_simple(std::vector &lines, ExPolygons* slices) const; diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 5aa7866b8..77d30489d 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -4,10 +4,13 @@ // this needs to be included early for MSVC (listing it in Build.PL is not enough) #include #include +#include #include #include #include #include +#include +#include #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" #define SLIC3R_VERSION "1.31.6" @@ -40,13 +43,6 @@ typedef long coord_t; typedef double coordf_t; -namespace Slic3r { - -enum Axis { X=0, Y, Z }; - -} -using namespace Slic3r; - /* Implementation of CONFESS("foo"): */ #ifdef _MSC_VER #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) @@ -91,4 +87,54 @@ inline std::string debug_out_path(const char *name, ...) // Write slices as SVG images into out directory during the 2D processing of the slices. // #define SLIC3R_DEBUG_SLICE_PROCESSING +namespace Slic3r { + +enum Axis { X=0, Y, Z }; + +template +inline void append_to(std::vector &dst, const std::vector &src) +{ + dst.insert(dst.end(), src.begin(), src.end()); +} + +template void +_parallelize_do(std::queue* queue, boost::mutex* queue_mutex, boost::function func) +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + while (true) { + T i; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + i = queue->front(); + queue->pop(); + } + //std::cout << " Thread " << boost::this_thread::get_id() << " processing item " << i << std::endl; + func(i); + boost::this_thread::interruption_point(); + } +} + +template void +parallelize(std::queue queue, boost::function func, + int threads_count = boost::thread::hardware_concurrency()) +{ + boost::mutex queue_mutex; + boost::thread_group workers; + for (int i = 0; i < threads_count; i++) + workers.add_thread(new boost::thread(&_parallelize_do, &queue, &queue_mutex, func)); + workers.join_all(); +} + +template void +parallelize(T start, T end, boost::function func, + int threads_count = boost::thread::hardware_concurrency()) +{ + std::queue queue; + for (T i = start; i <= end; ++i) queue.push(i); + parallelize(queue, func, threads_count); +} + +} // namespace Slic3r + #endif From d628764da60a518861dd9175e29d7343d69d4af4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 15:25:22 +0100 Subject: [PATCH 30/82] Minor fixes to parallelize code, cherry picked from @alexrj 5242b3e03ab2b195ba9c7c53fba705a8ed1c7abd --- xs/src/libslic3r/PrintConfig.cpp | 8 ++++++-- xs/src/libslic3r/libslic3r.h | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 6aace7732..6f3c459b2 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1292,8 +1292,12 @@ PrintConfigDef::PrintConfigDef() def->readonly = true; def->min = 1; def->max = 16; - def->default_value = new ConfigOptionInt((boost::thread::hardware_concurrency() == 0) ? 2 : boost::thread::hardware_concurrency()); - + { + int threads = boost::thread::hardware_concurrency(); + if (threads == 0) threads = 2; + def->default_value = new ConfigOptionInt(threads); + } + def = this->add("toolchange_gcode", coString); def->label = "Tool change G-code"; def->tooltip = "This custom code is inserted right before every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder]."; diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 77d30489d..89db6d81e 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -4,6 +4,7 @@ // this needs to be included early for MSVC (listing it in Build.PL is not enough) #include #include +#include #include #include #include @@ -119,9 +120,10 @@ template void parallelize(std::queue queue, boost::function func, int threads_count = boost::thread::hardware_concurrency()) { + if (threads_count == 0) threads_count = 2; boost::mutex queue_mutex; boost::thread_group workers; - for (int i = 0; i < threads_count; i++) + for (int i = 0; i < fminf(threads_count, queue.size()); i++) workers.add_thread(new boost::thread(&_parallelize_do, &queue, &queue_mutex, func)); workers.join_all(); } From 9e8dae817d6219a0f34e4b02afac106cce58caf7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 16:48:48 +0100 Subject: [PATCH 31/82] Fixes and improvements to the view selection menu, cherry picked from @alexrj a7693c4719c0e0e0129aea7461706d2ed53a52cc --- lib/Slic3r/GUI/3DScene.pm | 41 ++++++++++++++++++++----------------- lib/Slic3r/GUI/MainFrame.pm | 2 +- xs/xsp/BoundingBox.xsp | 7 +++++-- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index ed4b82fbd..edb5eb492 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -74,7 +74,7 @@ use constant SELECTED_COLOR => [0,1,0,1]; use constant HOVER_COLOR => [0.4,0.9,0,1]; # phi / theta angles to orient the camera. -use constant VIEW_DEFAULT => [45.0,45.0]; +use constant VIEW_ISO => [45.0,45.0]; use constant VIEW_LEFT => [90.0,90.0]; use constant VIEW_RIGHT => [-90.0,90.0]; use constant VIEW_TOP => [0.0,0.0]; @@ -82,8 +82,7 @@ use constant VIEW_BOTTOM => [0.0,180.0]; use constant VIEW_FRONT => [0.0,90.0]; use constant VIEW_REAR => [180.0,90.0]; -#use constant GIMBALL_LOCK_THETA_MAX => 150; -use constant GIMBALL_LOCK_THETA_MAX => 170; +use constant GIMBAL_LOCK_THETA_MAX => 170; # make OpenGL::Array thread-safe { @@ -257,7 +256,7 @@ sub mouse_event { if (TURNTABLE_MODE) { $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- - $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; + $self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX; $self->_stheta(0) if $self->_stheta < 0; } else { my $size = $self->GetClientSize; @@ -341,8 +340,8 @@ sub select_view { if (ref($direction)) { $dirvec = $direction; } else { - if ($direction eq 'default') { - $dirvec = VIEW_DEFAULT; + if ($direction eq 'iso') { + $dirvec = VIEW_ISO; } elsif ($direction eq 'left') { $dirvec = VIEW_LEFT; } elsif ($direction eq 'right') { @@ -357,18 +356,22 @@ sub select_view { $dirvec = VIEW_REAR; } } - my $bb = $self->volumes_bounding_box; - if (! $bb->empty) { - $self->_sphi($dirvec->[0]); - $self->_stheta($dirvec->[1]); - # Avoid gimball lock. - $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; - $self->_stheta(0) if $self->_stheta < 0; - # View everything. - $self->zoom_to_bounding_box($bb); - $self->on_viewport_changed->() if $self->on_viewport_changed; - $self->Refresh; - } + + $self->_sphi($dirvec->[0]); + $self->_stheta($dirvec->[1]); + + # Avoid gimbal lock. + $self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX; + $self->_stheta(0) if $self->_stheta < 0; + + # View everything. + $self->volumes_bounding_box->defined + ? $self->zoom_to_volumes + : $self->zoom_to_bed; + + $self->on_viewport_changed->() if $self->on_viewport_changed; + $self->_dirty(1); + $self->Refresh; } sub zoom_to_bounding_box { @@ -377,7 +380,7 @@ sub zoom_to_bounding_box { # calculate the zoom factor needed to adjust viewport to # bounding box - my $max_size = max(@{$bb->size}) * 2; + my $max_size = max(@{$bb->size}) * 1.05; my $min_viewport_size = min($self->GetSizeWH); $self->_zoom($min_viewport_size / $max_size); diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index ca7143e28..5c6524d93 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -292,7 +292,7 @@ sub _init_menubar { # View menu if (!$self->{no_plater}) { $self->{viewMenu} = Wx::Menu->new; - $self->_append_menu_item($self->{viewMenu}, "Default", 'Default View', sub { $self->select_view('default'); }); + $self->_append_menu_item($self->{viewMenu}, "Iso" , 'Iso View' , sub { $self->select_view('iso' ); }); $self->_append_menu_item($self->{viewMenu}, "Top" , 'Top View' , sub { $self->select_view('top' ); }); $self->_append_menu_item($self->{viewMenu}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); }); $self->_append_menu_item($self->{viewMenu}, "Front" , 'Front View' , sub { $self->select_view('front' ); }); diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp index f6e35e0df..aec5be564 100644 --- a/xs/xsp/BoundingBox.xsp +++ b/xs/xsp/BoundingBox.xsp @@ -30,7 +30,8 @@ long y_min() %code{% RETVAL = THIS->min.y; %}; long y_max() %code{% RETVAL = THIS->max.y; %}; std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; - + bool defined() %code{% RETVAL = THIS->defined; %}; + %{ BoundingBox* @@ -69,7 +70,8 @@ new_from_points(CLASS, points) void set_y_min(double val) %code{% THIS->min.y = val; %}; void set_y_max(double val) %code{% THIS->max.y = val; %}; std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; - + bool defined() %code{% RETVAL = THIS->defined; %}; + %{ BoundingBoxf* @@ -106,4 +108,5 @@ new_from_points(CLASS, points) double z_min() %code{% RETVAL = THIS->min.z; %}; double z_max() %code{% RETVAL = THIS->max.z; %}; std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min.x, THIS->min.y, THIS->min.z, THIS->max.x, THIS->max.y, THIS->max.z); RETVAL = buf; %}; + bool defined() %code{% RETVAL = THIS->defined; %}; }; From d8082b9db43012a88ae739e7c46b129da87fa1f9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 18:18:01 +0100 Subject: [PATCH 32/82] Try to fix compilation error about the new threads default --- lib/Slic3r/Config.pm | 2 +- xs/src/libslic3r/PrintConfig.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index c315f9b4e..fe766e5fd 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -218,7 +218,7 @@ sub validate { my $self = shift; # -j, --threads - die "Invalid value for --threads\n" + die "Invalid value for --threads: " . $self->threads . "\n" if $self->threads < 1; # --layer-height diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 6f3c459b2..745976d46 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1293,7 +1293,7 @@ PrintConfigDef::PrintConfigDef() def->min = 1; def->max = 16; { - int threads = boost::thread::hardware_concurrency(); + unsigned int threads = boost::thread::hardware_concurrency(); if (threads == 0) threads = 2; def->default_value = new ConfigOptionInt(threads); } From 5dc8a0808ef102e06254bdde8540d22ef0fff4b2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 18:31:21 +0100 Subject: [PATCH 33/82] Maybe the Travis CI server has more than 16 cores? --- lib/Slic3r/Config.pm | 2 +- xs/src/libslic3r/PrintConfig.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index fe766e5fd..c315f9b4e 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -218,7 +218,7 @@ sub validate { my $self = shift; # -j, --threads - die "Invalid value for --threads: " . $self->threads . "\n" + die "Invalid value for --threads\n" if $self->threads < 1; # --layer-height diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 745976d46..746cf7f1d 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1291,11 +1291,9 @@ PrintConfigDef::PrintConfigDef() def->cli = "threads|j=i"; def->readonly = true; def->min = 1; - def->max = 16; { unsigned int threads = boost::thread::hardware_concurrency(); - if (threads == 0) threads = 2; - def->default_value = new ConfigOptionInt(threads); + def->default_value = new ConfigOptionInt(threads > 0 ? threads : 2); } def = this->add("toolchange_gcode", coString); From a8930f12cd955469580bdebcbc98e235bdf0f62d Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Tue, 29 Nov 2016 20:46:44 -0600 Subject: [PATCH 34/82] Fix perl redefining multiple functions also defined/used by boostlib through the include chain. (#3593) Resolved ordering dependency (thanks win32 perl!) --- xs/src/xsinit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 6b50bc2e4..b7c729406 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef SLIC3RXS extern "C" { @@ -50,7 +51,6 @@ extern "C" { } #endif -#include #include #include #include From 9c0c05631c435dfe5c05b5fe07841bbd45f65c1e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 11:45:31 +0100 Subject: [PATCH 35/82] Ignore failure to call SetAutoPop when not available. #3596 @alexrj 2e82fb01a65deb4e0872de9219f47e58b7000ebf --- lib/Slic3r/GUI/MainFrame.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 5c6524d93..f08adf672 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -42,6 +42,11 @@ sub new { $self->_init_tabpanel; $self->_init_menubar; + # set default tooltip timer in msec + # SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values + # (SetAutoPop is not available on GTK.) + eval { Wx::ToolTip::SetAutoPop(32767) }; + # initialize status bar $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1); $self->{statusbar}->SetStatusText("Version $Slic3r::VERSION - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"); From a26330a292cfdb643e2b0c3bf2042288e03e75fc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 11:50:02 +0100 Subject: [PATCH 36/82] Try to fix compilation problem with Boost 1.54. #3595 --- xs/src/admesh/stl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index d97760eb0..34e1907e7 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #ifndef BOOST_LITTLE_ENDIAN #error "admesh works correctly on little endian machines only!" From 24e0b568b5d721849f6a94523bc3a6c2ae31c580 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 2 Dec 2016 15:07:49 +0100 Subject: [PATCH 37/82] Fix compilation, cherry picked from @alexrj fde6e2e61deabd097a3bf565d4948077147b25ab --- xs/src/libslic3r/PerimeterGenerator.cpp | 27 ++++++++++++------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index acdff127b..299e4c787 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -54,8 +54,7 @@ PerimeterGenerator::process() for (Surfaces::const_iterator surface = this->slices->surfaces.begin(); surface != this->slices->surfaces.end(); ++surface) { // detect how many perimeters must be generated for this island - signed short loop_number = this->config->perimeters + surface->extra_perimeters; - loop_number--; // 0-indexed loops + const int loop_number = this->config->perimeters + surface->extra_perimeters -1; // 0-indexed loops Polygons gaps; @@ -67,7 +66,7 @@ PerimeterGenerator::process() ThickPolylines thin_walls; // we loop one time more than needed in order to find gaps after the last perimeter was applied - for (signed short i = 0; i <= loop_number+1; ++i) { // outer loop is 0 + for (int i = 0; i <= loop_number+1; ++i) { // outer loop is 0 Polygons offsets; if (i == 0) { // the minimum thickness of a single loop is: @@ -170,16 +169,16 @@ PerimeterGenerator::process() } // nest loops: holes first - for (signed short d = 0; d <= loop_number; ++d) { + for (int d = 0; d <= loop_number; ++d) { PerimeterGeneratorLoops &holes_d = holes[d]; // loop through all holes having depth == d - for (signed short i = 0; i < holes_d.size(); ++i) { + for (int i = 0; i < (int)holes_d.size(); ++i) { const PerimeterGeneratorLoop &loop = holes_d[i]; // find the hole loop that contains this one, if any - for (signed short t = d+1; t <= loop_number; ++t) { - for (signed short j = 0; j < holes[t].size(); ++j) { + for (int t = d+1; t <= loop_number; ++t) { + for (int j = 0; j < (int)holes[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = holes[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -191,8 +190,8 @@ PerimeterGenerator::process() } // if no hole contains this hole, find the contour loop that contains it - for (signed short t = loop_number; t >= 0; --t) { - for (signed short j = 0; j < contours[t].size(); ++j) { + for (int t = loop_number; t >= 0; --t) { + for (int j = 0; j < (int)contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -207,16 +206,16 @@ PerimeterGenerator::process() } // nest contour loops - for (signed short d = loop_number; d >= 1; --d) { + for (int d = loop_number; d >= 1; --d) { PerimeterGeneratorLoops &contours_d = contours[d]; // loop through all contours having depth == d - for (signed short i = 0; i < contours_d.size(); ++i) { + for (int i = 0; i < (int)contours_d.size(); ++i) { const PerimeterGeneratorLoop &loop = contours_d[i]; // find the contour loop that contains it - for (signed short t = d-1; t >= 0; --t) { - for (signed short j = 0; j < contours[t].size(); ++j) { + for (int t = d-1; t >= 0; --t) { + for (int j = 0; j < contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -459,7 +458,7 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo ExtrusionPath path(role); ThickLines lines = p->thicklines(); - for (size_t i = 0; i < lines.size(); ++i) { + for (int i = 0; i < (int)lines.size(); ++i) { const ThickLine& line = lines[i]; const coordf_t line_len = line.length(); From 5d18657ac5546022421da5fe559436a876ad9029 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 8 Dec 2016 15:16:09 +0100 Subject: [PATCH 38/82] Don't be lazy, use fully qualified Slic3r::Polygon/ExPolygon/Point/Line names --- xs/src/libslic3r/ExPolygon.hpp | 56 +++++++++++++++++----------------- xs/src/libslic3r/Line.hpp | 10 +++--- xs/src/libslic3r/Point.hpp | 16 +++++----- xs/src/libslic3r/Polygon.hpp | 40 ++++++++++++------------ 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 7f76cfe4e..7d1a609ea 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -293,37 +293,37 @@ extern bool remove_sticks(ExPolygon &poly); #include namespace boost { namespace polygon { template <> - struct polygon_traits { + struct polygon_traits { typedef coord_t coordinate_type; - typedef Points::const_iterator iterator_type; - typedef Point point_type; + typedef Slic3r::Points::const_iterator iterator_type; + typedef Slic3r::Point point_type; // Get the begin iterator - static inline iterator_type begin_points(const ExPolygon& t) { + static inline iterator_type begin_points(const Slic3r::ExPolygon& t) { return t.contour.points.begin(); } // Get the end iterator - static inline iterator_type end_points(const ExPolygon& t) { + static inline iterator_type end_points(const Slic3r::ExPolygon& t) { return t.contour.points.end(); } // Get the number of sides of the polygon - static inline std::size_t size(const ExPolygon& t) { + static inline std::size_t size(const Slic3r::ExPolygon& t) { return t.contour.points.size(); } // Get the winding direction of the polygon - static inline winding_direction winding(const ExPolygon& t) { + static inline winding_direction winding(const Slic3r::ExPolygon& t) { return unknown_winding; } }; template <> - struct polygon_mutable_traits { + struct polygon_mutable_traits { //expects stl style iterators template - static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) { + static inline Slic3r::ExPolygon& set_points(Slic3r::ExPolygon& expolygon, iT input_begin, iT input_end) { expolygon.contour.points.assign(input_begin, input_end); // skip last point since Boost will set last point = first point expolygon.contour.points.pop_back(); @@ -333,27 +333,27 @@ namespace boost { namespace polygon { template <> - struct geometry_concept { typedef polygon_with_holes_concept type; }; + struct geometry_concept { typedef polygon_with_holes_concept type; }; template <> - struct polygon_with_holes_traits { - typedef Polygons::const_iterator iterator_holes_type; - typedef Polygon hole_type; - static inline iterator_holes_type begin_holes(const ExPolygon& t) { + struct polygon_with_holes_traits { + typedef Slic3r::Polygons::const_iterator iterator_holes_type; + typedef Slic3r::Polygon hole_type; + static inline iterator_holes_type begin_holes(const Slic3r::ExPolygon& t) { return t.holes.begin(); } - static inline iterator_holes_type end_holes(const ExPolygon& t) { + static inline iterator_holes_type end_holes(const Slic3r::ExPolygon& t) { return t.holes.end(); } - static inline unsigned int size_holes(const ExPolygon& t) { + static inline unsigned int size_holes(const Slic3r::ExPolygon& t) { return (int)t.holes.size(); } }; template <> - struct polygon_with_holes_mutable_traits { + struct polygon_with_holes_mutable_traits { template - static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) { + static inline Slic3r::ExPolygon& set_holes(Slic3r::ExPolygon& t, iT inputBegin, iT inputEnd) { t.holes.assign(inputBegin, inputEnd); return t; } @@ -361,32 +361,32 @@ namespace boost { namespace polygon { //first we register CPolygonSet as a polygon set template <> - struct geometry_concept { typedef polygon_set_concept type; }; + struct geometry_concept { typedef polygon_set_concept type; }; //next we map to the concept through traits template <> - struct polygon_set_traits { + struct polygon_set_traits { typedef coord_t coordinate_type; - typedef ExPolygons::const_iterator iterator_type; - typedef ExPolygons operator_arg_type; + typedef Slic3r::ExPolygons::const_iterator iterator_type; + typedef Slic3r::ExPolygons operator_arg_type; - static inline iterator_type begin(const ExPolygons& polygon_set) { + static inline iterator_type begin(const Slic3r::ExPolygons& polygon_set) { return polygon_set.begin(); } - static inline iterator_type end(const ExPolygons& polygon_set) { + static inline iterator_type end(const Slic3r::ExPolygons& polygon_set) { return polygon_set.end(); } //don't worry about these, just return false from them - static inline bool clean(const ExPolygons& polygon_set) { return false; } - static inline bool sorted(const ExPolygons& polygon_set) { return false; } + static inline bool clean(const Slic3r::ExPolygons& polygon_set) { return false; } + static inline bool sorted(const Slic3r::ExPolygons& polygon_set) { return false; } }; template <> - struct polygon_set_mutable_traits { + struct polygon_set_mutable_traits { template - static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) { + static inline void set(Slic3r::ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) { expolygons.assign(input_begin, input_end); } }; diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp index 42e811449..6c40b062f 100644 --- a/xs/src/libslic3r/Line.hpp +++ b/xs/src/libslic3r/Line.hpp @@ -76,20 +76,20 @@ class Linef3 void scale(double factor); }; -} +} // namespace Slic3r // start Boost #include namespace boost { namespace polygon { template <> - struct geometry_concept { typedef segment_concept type; }; + struct geometry_concept { typedef segment_concept type; }; template <> - struct segment_traits { + struct segment_traits { typedef coord_t coordinate_type; - typedef Point point_type; + typedef Slic3r::Point point_type; - static inline point_type get(const Line& line, direction_1d dir) { + static inline point_type get(const Slic3r::Line& line, direction_1d dir) { return dir.to_int() ? line.b : line.a; } }; diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 66e78ae46..0405ec078 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -131,7 +131,7 @@ class Pointf3 : public Pointf Vectorf3 vector_to(const Pointf3 &point) const; }; -} +} // namespace Slic3r // start Boost #include @@ -155,28 +155,28 @@ namespace boost { namespace polygon { #endif template <> - struct geometry_concept { typedef point_concept type; }; + struct geometry_concept { typedef point_concept type; }; template <> - struct point_traits { + struct point_traits { typedef coord_t coordinate_type; - static inline coordinate_type get(const Point& point, orientation_2d orient) { + static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) { return (orient == HORIZONTAL) ? point.x : point.y; } }; template <> - struct point_mutable_traits { + struct point_mutable_traits { typedef coord_t coordinate_type; - static inline void set(Point& point, orientation_2d orient, coord_t value) { + static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) { if (orient == HORIZONTAL) point.x = value; else point.y = value; } - static inline Point construct(coord_t x_value, coord_t y_value) { - Point retval; + static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) { + Slic3r::Point retval; retval.x = x_value; retval.y = y_value; return retval; diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index 966264fb2..c2ffa959c 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -142,40 +142,40 @@ inline Polylines to_polylines(Polygons &&polys) #include namespace boost { namespace polygon { template <> - struct geometry_concept{ typedef polygon_concept type; }; + struct geometry_concept{ typedef polygon_concept type; }; template <> - struct polygon_traits { + struct polygon_traits { typedef coord_t coordinate_type; - typedef Points::const_iterator iterator_type; - typedef Point point_type; + typedef Slic3r::Points::const_iterator iterator_type; + typedef Slic3r::Point point_type; // Get the begin iterator - static inline iterator_type begin_points(const Polygon& t) { + static inline iterator_type begin_points(const Slic3r::Polygon& t) { return t.points.begin(); } // Get the end iterator - static inline iterator_type end_points(const Polygon& t) { + static inline iterator_type end_points(const Slic3r::Polygon& t) { return t.points.end(); } // Get the number of sides of the polygon - static inline std::size_t size(const Polygon& t) { + static inline std::size_t size(const Slic3r::Polygon& t) { return t.points.size(); } // Get the winding direction of the polygon - static inline winding_direction winding(const Polygon& t) { + static inline winding_direction winding(const Slic3r::Polygon& t) { return unknown_winding; } }; template <> - struct polygon_mutable_traits { + struct polygon_mutable_traits { // expects stl style iterators template - static inline Polygon& set_points(Polygon& polygon, iT input_begin, iT input_end) { + static inline Slic3r::Polygon& set_points(Slic3r::Polygon& polygon, iT input_begin, iT input_end) { polygon.points.clear(); while (input_begin != input_end) { polygon.points.push_back(Point()); @@ -189,32 +189,32 @@ namespace boost { namespace polygon { }; template <> - struct geometry_concept { typedef polygon_set_concept type; }; + struct geometry_concept { typedef polygon_set_concept type; }; //next we map to the concept through traits template <> - struct polygon_set_traits { + struct polygon_set_traits { typedef coord_t coordinate_type; - typedef Polygons::const_iterator iterator_type; - typedef Polygons operator_arg_type; + typedef Slic3r::Polygons::const_iterator iterator_type; + typedef Slic3r::Polygons operator_arg_type; - static inline iterator_type begin(const Polygons& polygon_set) { + static inline iterator_type begin(const Slic3r::Polygons& polygon_set) { return polygon_set.begin(); } - static inline iterator_type end(const Polygons& polygon_set) { + static inline iterator_type end(const Slic3r::Polygons& polygon_set) { return polygon_set.end(); } //don't worry about these, just return false from them - static inline bool clean(const Polygons& polygon_set) { return false; } - static inline bool sorted(const Polygons& polygon_set) { return false; } + static inline bool clean(const Slic3r::Polygons& polygon_set) { return false; } + static inline bool sorted(const Slic3r::Polygons& polygon_set) { return false; } }; template <> - struct polygon_set_mutable_traits { + struct polygon_set_mutable_traits { template - static inline void set(Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) { + static inline void set(Slic3r::Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) { polygons.assign(input_begin, input_end); } }; From 126126cc789d8c9b5c435a3b80cbb37287256077 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 8 Dec 2016 19:02:16 +0100 Subject: [PATCH 39/82] Fixes after the merge of changes by @alexrj. --- xs/src/libslic3r/ClipperUtils.cpp | 12 +- xs/src/libslic3r/ClipperUtils.hpp | 36 +++++- xs/src/libslic3r/ExtrusionSimulator.cpp | 2 +- xs/src/libslic3r/ExtrusionSimulator.hpp | 2 +- xs/src/libslic3r/Layer.hpp | 3 - xs/src/libslic3r/Model.hpp | 1 + xs/src/libslic3r/Polygon.hpp | 2 +- xs/src/libslic3r/Print.hpp | 2 +- xs/src/libslic3r/PrintObject.cpp | 2 +- xs/src/libslic3r/Slicing.hpp | 44 +++++++ xs/src/libslic3r/SlicingAdaptive.cpp | 149 ++++++++++++++++++++++++ xs/src/libslic3r/SlicingAdaptive.hpp | 38 ++++++ xs/src/libslic3r/SupportMaterial.cpp | 3 +- xs/src/xsinit.h | 2 + 14 files changed, 277 insertions(+), 21 deletions(-) create mode 100644 xs/src/libslic3r/Slicing.hpp create mode 100644 xs/src/libslic3r/SlicingAdaptive.cpp create mode 100644 xs/src/libslic3r/SlicingAdaptive.hpp diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 6b5991acd..5cc143540 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -709,7 +709,7 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, PolyTreeToExPolygons(polytree, retval); } -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, +void _clipper_pl(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_) { PROFILE_FUNC(); @@ -723,7 +723,7 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, ClipperPaths_to_Slic3rMultiPoints(output, retval); } -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, +void _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_) { // convert Lines to Polylines @@ -733,7 +733,7 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, polylines.push_back(*line); // perform operation - _clipper(clipType, polylines, clip, &polylines, safety_offset_); + _clipper_pl(clipType, polylines, clip, &polylines, safety_offset_); // convert Polylines to Lines for (Slic3r::Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) @@ -750,7 +750,7 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, polylines.push_back(*polygon); // implicit call to split_at_first_point() // perform clipping - _clipper(clipType, polylines, clip, retval, safety_offset_); + _clipper_pl(clipType, polylines, clip, retval, safety_offset_); /* If the split_at_first_point() call above happens to split the polygon inside the clipping area we would get two consecutive polylines instead of a single one, so we go through them in order @@ -796,8 +796,6 @@ void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); -template void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); -template void diff(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_); template void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_) @@ -850,8 +848,6 @@ void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, Resu template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); -template void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); -template void intersection(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_); template SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_) diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 783453807..8efebdfad 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -105,10 +105,10 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines* retval); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, - const Slic3r::Polygons &clip, Slic3r::Lines* retval); +void _clipper_pl(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, + const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); +void _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, + const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_); template void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); @@ -121,9 +121,29 @@ Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &c template Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false); +inline void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_ = false) +{ + _clipper_pl(ClipperLib::ctDifference, subject, clip, retval, safety_offset_); +} + +inline void diff(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_ = false) +{ + _clipper_ln(ClipperLib::ctDifference, subject, clip, retval, safety_offset_); +} + template void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); +inline void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_ = false) +{ + _clipper_pl(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_); +} + +inline void intersection(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_ = false) +{ + _clipper_ln(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_); +} + template SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); @@ -133,6 +153,14 @@ intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, b template bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +inline Slic3r::Polylines +intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + Slic3r::Polylines polylines_out; + _clipper_pl(ClipperLib::ctIntersection, subject, clip, &polylines_out, safety_offset_); + return polylines_out; +} + void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_ = false); diff --git a/xs/src/libslic3r/ExtrusionSimulator.cpp b/xs/src/libslic3r/ExtrusionSimulator.cpp index 0c86f073e..3752caed4 100644 --- a/xs/src/libslic3r/ExtrusionSimulator.cpp +++ b/xs/src/libslic3r/ExtrusionSimulator.cpp @@ -803,7 +803,7 @@ void gcode_spread_points( const Cell &cell = cells[i]; acc[cell.idx.y()][cell.idx.x()] = (1.f - cell.fraction_covered) * cell.volume + cell.fraction_covered * cell.area * height_avg; } - } else if (simulationType == ExtrusionSimulationSpreadExcess) { + } else if (simulationType == Slic3r::ExtrusionSimulationSpreadExcess) { // The volume under the circle does not fit. // 1) Fill the underfilled cells and remove them from the list. float volume_borrowed_total = 0.; diff --git a/xs/src/libslic3r/ExtrusionSimulator.hpp b/xs/src/libslic3r/ExtrusionSimulator.hpp index 8b956ec11..040406766 100644 --- a/xs/src/libslic3r/ExtrusionSimulator.hpp +++ b/xs/src/libslic3r/ExtrusionSimulator.hpp @@ -13,7 +13,7 @@ enum ExtrusionSimulationType ExtrusionSimulationDontSpread, ExtrisopmSimulationSpreadNotOverfilled, ExtrusionSimulationSpreadFull, - ExtrusionSimulationSpreadExcess, + ExtrusionSimulationSpreadExcess }; // An opaque class, to keep the boost stuff away from the header. diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index e59f0c9e1..a5ba3f804 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -11,9 +11,6 @@ namespace Slic3r { -typedef std::pair t_layer_height_range; -typedef std::map t_layer_height_ranges; - class Layer; class PrintRegion; class PrintObject; diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index b2a9d0b53..963bd7c85 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -6,6 +6,7 @@ #include "Layer.hpp" #include "Point.hpp" #include "TriangleMesh.hpp" +#include "Slicing.hpp" #include #include #include diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index c2ffa959c..b0a7ea40a 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -178,7 +178,7 @@ namespace boost { namespace polygon { static inline Slic3r::Polygon& set_points(Slic3r::Polygon& polygon, iT input_begin, iT input_end) { polygon.points.clear(); while (input_begin != input_end) { - polygon.points.push_back(Point()); + polygon.points.push_back(Slic3r::Point()); boost::polygon::assign(polygon.points.back(), *input_begin); ++input_begin; } diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 132e7d0df..3a5d38ec3 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -154,7 +154,7 @@ private: // TODO: call model_object->get_bounding_box() instead of accepting // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); - ~PrintObject(); + ~PrintObject() {} }; typedef std::vector PrintObjectPtrs; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index b276744c4..7b5014414 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -974,7 +974,7 @@ PrintObject::_make_perimeters() // check whether a portion of the upper slices falls inside the critical area const Polylines intersection = intersection_pl( - upper_layerm_polygons, + to_polylines(upper_layerm_polygons), critical_area ); diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp new file mode 100644 index 000000000..5e910c672 --- /dev/null +++ b/xs/src/libslic3r/Slicing.hpp @@ -0,0 +1,44 @@ +// Based on implementation by @platsch + +#ifndef slic3r_Slicing_hpp_ +#define slic3r_Slicing_hpp_ + +#include "libslic3r.h" + +namespace Slic3r +{ + +struct SlicingParameters +{ + SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); } + + // The regular layer height, applied for all but the first layer, if not overridden by layer ranges + // or by the variable layer thickness table. + coordf_t layer_height; + + // Thickness of the first layer. This is either the first print layer thickness if printed without a raft, + // or a bridging flow thickness if printed over a non-soluble raft, + // or a normal layer height if printed over a soluble raft. + coordf_t first_layer_height; + + // If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow. + bool first_layer_bridging; + + // Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm, + // or by an interactive layer height editor. + coordf_t min_layer_height; + coordf_t max_layer_height; + + // Bottom and top of the printed object. + // If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height. + // Otherwise object_print_z_min is equal to the raft height. + coordf_t object_print_z_min; + coordf_t object_print_z_max; +}; + +typedef std::pair t_layer_height_range; +typedef std::map t_layer_height_ranges; + +}; // namespace Slic3r + +#endif /* slic3r_Slicing_hpp_ */ diff --git a/xs/src/libslic3r/SlicingAdaptive.cpp b/xs/src/libslic3r/SlicingAdaptive.cpp new file mode 100644 index 000000000..b7f116770 --- /dev/null +++ b/xs/src/libslic3r/SlicingAdaptive.cpp @@ -0,0 +1,149 @@ +#include "libslic3r.h" +#include "TriangleMesh.hpp" +#include "SlicingAdaptive.hpp" + +namespace Slic3r +{ + +void SlicingAdaptive::clear() +{ + m_meshes.clear(); + m_faces.clear(); + m_face_normal_z.clear(); +} + +std::pair face_z_span(const stl_facet *f) +{ + return std::pair( + std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z), + std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z)); +} + +void SlicingAdaptive::prepare() +{ + // 1) Collect faces of all meshes. + { + int nfaces_total = 0; + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) + nfaces_total += (*it_mesh)->stl.stats.number_of_facets; + m_faces.reserve(nfaces_total); + } + m_max_z = 0; + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) { + const stl_facet *faces = (*it_mesh)->stl.facet_start; + int nfaces = (*it_mesh)->stl.stats.number_of_facets; + for (int i = 0; i < nfaces; ++ i) { + const stl_facet *facet = faces + i; + m_faces.push_back(facet); + m_max_z = std::max(std::max(m_max_z, facet->vertex[0].z), std::max(facet->vertex[1].z, facet->vertex[2].z)); + } + } + + // 2) Sort faces lexicographically by their Z span. + std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { + std::pair span1 = face_z_span(f1); + std::pair span2 = face_z_span(f2); + return span1 < span2; + }); + + // 3) Generate Z components of the facet normals. + m_face_normal_z.assign(m_faces.size(), 0.f); + for (size_t iface = 0; iface < m_faces.size(); ++ iface) + m_face_normal_z[iface] = m_faces[iface]->normal.z; +} + +float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) +{ + float height = m_layer_height_max; + bool first_hit = false; + + // find all facets intersecting the slice-layer + int ordered_id = current_facet; + for (; ordered_id < int(m_faces.size()); ++ ordered_id) { + std::pair zspan = face_z_span(m_faces[ordered_id]); + // facet's minimum is higher than slice_z -> end loop + if (zspan.first >= z) + break; + // facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point + if (zspan.second > z) { + // first event? + if (! first_hit) { + first_hit = true; + current_facet = ordered_id; + } + // skip touching facets which could otherwise cause small cusp values + if (zspan.second <= z + EPSILON) + continue; + // compute cusp-height for this facet and store minimum of all heights + float normal_z = m_face_normal_z[ordered_id]; + height = std::min(height, (normal_z == 0) ? 9999 : abs(cusp_value / normal_z)); + } + } + + // lower height limit due to printer capabilities + height = std::max(height, m_layer_height_min); + + // check for sloped facets inside the determined layer and correct height if necessary + if (height > m_layer_height_min) { + for (; ordered_id < int(m_faces.size()); ++ ordered_id) { + std::pair zspan = face_z_span(m_faces[ordered_id]); + // facet's minimum is higher than slice_z + height -> end loop + if (zspan.first >= z + height) + break; + + // skip touching facets which could otherwise cause small cusp values + if (zspan.second <= z + EPSILON) + continue; + + // Compute cusp-height for this facet and check against height. + float normal_z = m_face_normal_z[ordered_id]; + float cusp = (normal_z == 0) ? 9999 : abs(cusp_value / normal_z); + + float z_diff = zspan.first - z; + + // handle horizontal facets + if (m_face_normal_z[ordered_id] > 0.999) { + // Slic3r::debugf "cusp computation, height is reduced from %f", $height; + height = z_diff; + // Slic3r::debugf "to %f due to near horizontal facet\n", $height; + } else if (cusp > z_diff) { + if (cusp < height) { + // Slic3r::debugf "cusp computation, height is reduced from %f", $height; + height = cusp; + // Slic3r::debugf "to %f due to new cusp height\n", $height; + } + } else { + // Slic3r::debugf "cusp computation, height is reduced from %f", $height; + height = z_diff; + // Slic3r::debugf "to z-diff: %f\n", $height; + } + } + // lower height limit due to printer capabilities again + height = std::max(height, m_layer_height_min); + } + +// Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height; + return height; +} + +// Returns the distance to the next horizontal facet in Z-dir +// to consider horizontal object features in slice thickness +float SlicingAdaptive::horizontal_facet_distance(float z) +{ + for (size_t i = 0; i < m_faces.size(); ++ i) { + std::pair zspan = face_z_span(m_faces[i]); + // facet's minimum is higher than max forward distance -> end loop + if (zspan.first > z + m_layer_height_max) + break; + // min_z == max_z -> horizontal facet + if (zspan.first > z && zspan.first == zspan.second) + return zspan.first - z; + } + + // objects maximum? + return (z + m_layer_height_max > m_max_z) ? + std::max(m_max_z - z, 0.f) : + m_layer_height_max; +} + +}; // namespace Slic3r diff --git a/xs/src/libslic3r/SlicingAdaptive.hpp b/xs/src/libslic3r/SlicingAdaptive.hpp new file mode 100644 index 000000000..23e8f4b62 --- /dev/null +++ b/xs/src/libslic3r/SlicingAdaptive.hpp @@ -0,0 +1,38 @@ +// Based on implementation by @platsch + +#ifndef slic3r_SlicingAdaptive_hpp_ +#define slic3r_SlicingAdaptive_hpp_ + +#include "Slicing.hpp" +#include "admesh/stl.h" + +namespace Slic3r +{ + +class TriangleMesh; + +class SlicingAdaptive +{ +public: + void clear(); + void set_layer_height_range(float min, float max) { m_layer_height_min = min; m_layer_height_max = max; } + void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); } + void prepare(); + float cusp_height(float z, float cusp_value, int ¤t_facet); + float horizontal_facet_distance(float z); + +protected: + float m_layer_height_min; + float m_layer_height_max; + float m_max_z; + + std::vector m_meshes; + // Collected faces of all meshes, sorted by raising Z of the bottom most face. + std::vector m_faces; + // Z component of face normals, normalized. + std::vector m_face_normal_z; +}; + +}; // namespace Slic3r + +#endif /* slic3r_SlicingAdaptive_hpp_ */ diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 835dea79d..47ce5d6db 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -8,7 +8,6 @@ #include "EdgeGrid.hpp" #include -#include #include #include #include @@ -21,6 +20,8 @@ #include "SVG.hpp" #endif +#include + namespace Slic3r { // Increment used to reach MARGIN in steps to avoid trespassing thin objects diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index b7c729406..eafdf1522 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -163,4 +163,6 @@ SV* polynode2perl(const ClipperLib::PolyNode& node); #endif #endif +using namespace Slic3r; + #endif From 933a0db9ed558bacf93e7c1e9577a3753cae8d56 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 15:33:14 +0100 Subject: [PATCH 40/82] Minor fix of bridging flow, see @alexrj 3a3b24ec7c803c58919a5401e5804261999875b4 --- xs/src/libslic3r/LayerRegion.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 52e384adf..618396963 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -262,9 +262,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) BridgeDetector bd( initial, lower_layer->slices, - //FIXME parameters are not correct! - // flow(FlowRole role, bool bridge = false, double width = -1) const; - this->flow(frInfill, true, this->layer()->height).scaled_width() + this->flow(frInfill, true).scaled_width() ); #ifdef SLIC3R_DEBUG printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id()); From 333a0b1c0ef8da3df6d23bcdf4e830dc01bb03e1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 15:52:04 +0100 Subject: [PATCH 41/82] Shortcuts for zero mesh transformations. --- xs/src/libslic3r/TriangleMesh.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index ee09ac532..d372b33ea 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -193,12 +193,17 @@ void TriangleMesh::scale(const Pointf3 &versor) void TriangleMesh::translate(float x, float y, float z) { + if (x == 0.f && y == 0.f && z == 0.f) + return; stl_translate_relative(&(this->stl), x, y, z); stl_invalidate_shared_vertices(&this->stl); } void TriangleMesh::rotate(float angle, const Axis &axis) { + if (angle == 0.f) + return; + // admesh uses degrees angle = Slic3r::Geometry::rad2deg(angle); @@ -265,6 +270,8 @@ void TriangleMesh::align_to_origin() void TriangleMesh::rotate(double angle, Point* center) { + if (angle == 0.) + return; this->translate(-center->x, -center->y, 0); stl_rotate_z(&(this->stl), (float)angle); this->translate(+center->x, +center->y, 0); From d775c6c14c2ab2c746ef40cacc27ae4c707ef0b4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 15:54:37 +0100 Subject: [PATCH 42/82] Added methods set/add to SurfaceCollection. --- xs/src/libslic3r/SurfaceCollection.cpp | 13 ------------- xs/src/libslic3r/SurfaceCollection.hpp | 23 +++++++++++++++++++++-- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/xs/src/libslic3r/SurfaceCollection.cpp b/xs/src/libslic3r/SurfaceCollection.cpp index 804190548..cfeae4ccc 100644 --- a/xs/src/libslic3r/SurfaceCollection.cpp +++ b/xs/src/libslic3r/SurfaceCollection.cpp @@ -196,19 +196,6 @@ SurfaceCollection::remove_types(const SurfaceType *types, int ntypes) surfaces.erase(surfaces.begin() + j, surfaces.end()); } -void -SurfaceCollection::append(const SurfaceCollection &coll) -{ - this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end()); -} - -void -SurfaceCollection::append(const SurfaceType surfaceType, const Slic3r::ExPolygons &expoly) -{ - for (Slic3r::ExPolygons::const_iterator it = expoly.begin(); it != expoly.end(); ++ it) - this->surfaces.push_back(Slic3r::Surface(surfaceType, *it)); -} - void SurfaceCollection::export_to_svg(const char *path, bool show_labels) { BoundingBox bbox; diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp index e013bf891..22c26936e 100644 --- a/xs/src/libslic3r/SurfaceCollection.hpp +++ b/xs/src/libslic3r/SurfaceCollection.hpp @@ -28,8 +28,27 @@ class SurfaceCollection void remove_type(const SurfaceType type); void remove_types(const SurfaceType *types, int ntypes); void filter_by_type(SurfaceType type, Polygons* polygons); - void append(const SurfaceCollection &coll); - void append(const SurfaceType surfaceType, const ExPolygons &expoly); + + void clear() { surfaces.clear(); } + bool empty() const { return surfaces.empty(); } + + void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; } + void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); } + void set(const ExPolygons &src, SurfaceType surfaceType) { clear(); this->append(src, surfaceType); } + void set(const ExPolygons &src, const Surface &surfaceTempl) { clear(); this->append(src, surfaceTempl); } + void set(const Surfaces &src) { clear(); this->append(src); } + void set(ExPolygons &&src, SurfaceType surfaceType) { clear(); this->append(std::move(src), surfaceType); } + void set(ExPolygons &&src, const Surface &surfaceTempl) { clear(); this->append(std::move(src), surfaceTempl); } + void set(Surfaces &&src) { clear(); this->append(std::move(src)); } + + void append(const SurfaceCollection &coll) { this->append(coll.surfaces); } + void append(SurfaceCollection &&coll) { this->append(std::move(coll.surfaces)); } + void append(const ExPolygons &src, SurfaceType surfaceType) { surfaces_append(this->surfaces, src, surfaceType); } + void append(const ExPolygons &src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, src, surfaceTempl); } + void append(const Surfaces &src) { surfaces_append(this->surfaces, src); } + void append(ExPolygons &&src, SurfaceType surfaceType) { surfaces_append(this->surfaces, std::move(src), surfaceType); } + void append(ExPolygons &&src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, std::move(src), surfaceTempl); } + void append(Surfaces &&src) { surfaces_append(this->surfaces, std::move(src)); } // For debugging purposes: void export_to_svg(const char *path, bool show_labels); From 2ab86a4895e6bb219ee160be03e2b12c67a63b43 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 15:56:42 +0100 Subject: [PATCH 43/82] ConfigOptionVector::get_at(idx) Avoid using exceptons for normal work flow. Assert if the vector is empty. --- xs/src/libslic3r/Config.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 3fbd82060..6c8fa9f27 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_Config_hpp_ #define slic3r_Config_hpp_ +#include #include #include #include @@ -73,11 +74,8 @@ class ConfigOptionVector : public ConfigOptionVectorBase }; T get_at(size_t i) const { - try { - return this->values.at(i); - } catch (const std::out_of_range& oor) { - return this->values.front(); - } + assert(! this->values.empty()); + return (i < this->values.size()) ? this->values[i] : this->values.front(); }; }; From 1ea958158a9e0b7aee9a5ed006e81a7fa009d9ab Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 17:53:38 +0100 Subject: [PATCH 44/82] Support for user definable variable layer thickness, the C++ backend. --- xs/src/libslic3r/Print.hpp | 26 +- xs/src/libslic3r/PrintObject.cpp | 213 +++++++++- xs/src/libslic3r/Slicing.cpp | 585 +++++++++++++++++++++++++++ xs/src/libslic3r/Slicing.hpp | 74 +++- xs/src/libslic3r/SlicingAdaptive.cpp | 39 +- xs/src/libslic3r/SlicingAdaptive.hpp | 6 +- xs/xsp/Print.xsp | 25 +- 7 files changed, 924 insertions(+), 44 deletions(-) create mode 100644 xs/src/libslic3r/Slicing.cpp diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 3a5d38ec3..f97f888cd 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -3,9 +3,8 @@ #include "libslic3r.h" #include -#include #include -#include +#include #include "BoundingBox.hpp" #include "Flow.hpp" #include "PrintConfig.hpp" @@ -13,7 +12,7 @@ #include "Layer.hpp" #include "Model.hpp" #include "PlaceholderParser.hpp" - +#include "Slicing.hpp" namespace Slic3r { @@ -79,6 +78,10 @@ public: std::map< size_t,std::vector > region_volumes; PrintObjectConfig config; 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 are packed into a 1D array to simplify handling by the Perl XS. + std::vector layer_height_profile; // this is set to true when LayerRegion->slices is split in top/internal/bottom // so that next call to make_perimeters() performs a union() before computing loops @@ -137,7 +140,18 @@ public: bool invalidate_state_by_config_options(const std::vector &opt_keys); bool invalidate_step(PrintObjectStep step); bool invalidate_all_steps(); - + + // Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile. + // The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces. + void update_layer_height_profile(); + + // Collect the slicing parameters, to be used by variable layer thickness algorithm, + // by the interactive layer height editor and by the printing process itself. + // The slicing parameters are dependent on various configuration values + // (layer height, first layer height, raft settings, print nozzle diameter etc). + SlicingParameters slicing_parameters() const; + + void _slice(); bool has_support_material() const; void detect_surfaces_type(); void process_external_surfaces(); @@ -145,7 +159,7 @@ public: void bridge_over_infill(); void _make_perimeters(); void _infill(); - + private: Print* _print; ModelObject* _model_object; @@ -155,6 +169,8 @@ private: // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject() {} + + std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); }; typedef std::vector PrintObjectPtrs; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 7b5014414..90449d64f 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -2,12 +2,23 @@ #include "BoundingBox.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" -#include "SVG.hpp" #include #include +// #define SLIC3R_DEBUG + +// Make assert active if SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG + #undef NDEBUG + #define DEBUG + #define _DEBUG + #include "SVG.hpp" + #undef assert + #include +#endif + namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) @@ -760,9 +771,9 @@ PrintObject::discover_vertical_shells() // Assign resulting internal surfaces to layer. const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge }; layerm->fill_surfaces.keep_types(surfaceTypesKeep, sizeof(surfaceTypesKeep)/sizeof(SurfaceType)); - layerm->fill_surfaces.append(stInternal , new_internal); - layerm->fill_surfaces.append(stInternalVoid , new_internal_void); - layerm->fill_surfaces.append(stInternalSolid, new_internal_solid); + layerm->fill_surfaces.append(new_internal, stInternal); + layerm->fill_surfaces.append(new_internal_void, stInternalVoid); + layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells"); @@ -910,6 +921,194 @@ PrintObject::bridge_over_infill() } } +SlicingParameters PrintObject::slicing_parameters() const +{ + return SlicingParameters::create_from_config( + this->print()->config, this->config, + unscale(this->size.z), this->print()->object_extruders()); +} + +void PrintObject::update_layer_height_profile() +{ + if (this->layer_height_profile.empty()) { + if (0) +// if (this->layer_height_profile.empty()) + this->layer_height_profile = layer_height_profile_adaptive(this->slicing_parameters(), this->layer_height_ranges, + this->model_object()->volumes); + else + this->layer_height_profile = layer_height_profile_from_ranges(this->slicing_parameters(), this->layer_height_ranges); + } +} + +// 1) Decides Z positions of the layers, +// 2) Initializes layers and their regions +// 3) Slices the object meshes +// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes +// 5) Applies size compensation (offsets the slices in XY plane) +// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer +// Resulting expolygons of layer regions are marked as Internal. +// +// this should be idempotent +void PrintObject::_slice() +{ + SlicingParameters slicing_params = this->slicing_parameters(); + + // 1) Initialize layers and their slice heights. + std::vector slice_zs; + { + this->clear_layers(); + // Object layers (pairs of bottom/top Z coordinate), without the raft. + this->update_layer_height_profile(); + std::vector object_layers = generate_object_layers(slicing_params, this->layer_height_profile); + // Reserve object layers for the raft. Last layer of the raft is the contact layer. + int id = int(slicing_params.raft_layers()); + slice_zs.reserve(object_layers.size()); + Layer *prev = nullptr; + for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) { + coordf_t lo = object_layers[i_layer]; + coordf_t hi = object_layers[i_layer + 1]; + coordf_t slice_z = 0.5 * (lo + hi); + Layer *layer = this->add_layer(id ++, hi - lo, hi + slicing_params.object_print_z_min, slice_z); + slice_zs.push_back(float(slice_z)); + if (prev != nullptr) { + prev->upper_layer = layer; + layer->lower_layer = prev; + } + // Make sure all layers contain layer region objects for all regions. + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) + layer->add_region(this->print()->regions[region_id]); + prev = layer; + } + } + + if (this->print()->regions.size() == 1) { + // Optimized for a single region. Slice the single non-modifier mesh. + std::vector expolygons_by_layer = this->_slice_region(0, slice_zs, false); + for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) + this->layers[layer_id]->regions.front()->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); + } else { + // Slice all non-modifier volumes. + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, false); + for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) + this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); + } + // Slice all modifier volumes. + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, true); + // loop through the other regions and 'steal' the slices belonging to this one + for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) { + if (region_id == other_region_id) + continue; + for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) { + Layer *layer = layers[layer_id]; + LayerRegion *layerm = layer->regions[region_id]; + LayerRegion *other_layerm = layer->regions[other_region_id]; + if (layerm == nullptr || other_layerm == nullptr) + continue; + Polygons other_slices = to_polygons(other_layerm->slices); + ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id])); + if (my_parts.empty()) + continue; + // Remove such parts from original region. + other_layerm->slices.set(diff_ex(other_slices, my_parts), stInternal); + // Append new parts to our region. + layerm->slices.append(std::move(my_parts), stInternal); + } + } + } + } + + // remove last layer(s) if empty + while (! this->layers.empty()) { + const Layer *layer = this->layers.back(); + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) + if (layer->regions[region_id] != nullptr && ! layer->regions[region_id]->slices.empty()) + // Non empty layer. + goto end; + this->delete_layer(int(this->layers.size()) - 1); + } +end: + ; + + for (size_t layer_id = 0; layer_id < layers.size(); ++ layer_id) { + Layer *layer = this->layers[layer_id]; + // apply size compensation + if (this->config.xy_size_compensation.value != 0.) { + float delta = float(scale_(this->config.xy_size_compensation.value)); + if (layer->regions.size() == 1) { + // single region + LayerRegion *layerm = layer->regions.front(); + layerm->slices.set(offset_ex(to_polygons(std::move(layerm->slices.surfaces)), delta), stInternal); + } else { + if (delta < 0) { + // multiple regions, shrinking + // we apply the offset to the combined shape, then intersect it + // with the original slices for each region + Polygons region_slices; + for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) + polygons_append(region_slices, layer->regions[region_id]->slices.surfaces); + Polygons slices = offset(union_(region_slices), delta); + for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) { + LayerRegion *layerm = layer->regions[region_id]; + layerm->slices.set(std::move(intersection_ex(slices, to_polygons(std::move(layerm->slices.surfaces)))), stInternal); + } + } else { + // multiple regions, growing + // this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap + // so we give priority to the first one and so on + Polygons processed; + for (size_t region_id = 0;; ++ region_id) { + LayerRegion *layerm = layer->regions[region_id]; + ExPolygons slices = offset_ex(to_polygons(layerm->slices.surfaces), delta); + if (region_id > 0) + // Trim by the slices of already processed regions. + slices = diff_ex(to_polygons(std::move(slices)), processed); + if (region_id + 1 == layer->regions.size()) { + layerm->slices.set(std::move(slices), stInternal); + break; + } + polygons_append(processed, slices); + layerm->slices.set(std::move(slices), stInternal); + } + } + } + } + + // Merge all regions' slices to get islands, chain them by a shortest path. + layer->make_slices(); + } +} + +std::vector PrintObject::_slice_region(size_t region_id, const std::vector &z, bool modifier) +{ + std::vector layers; + assert(region_id < this->region_volumes.size()); + std::vector &volumes = this->region_volumes[region_id]; + if (! volumes.empty()) { + // Compose mesh. + //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. + TriangleMesh mesh; + for (std::vector::const_iterator it_volume = volumes.begin(); it_volume != volumes.end(); ++ it_volume) { + ModelVolume *volume = this->model_object()->volumes[*it_volume]; + if (volume->modifier == modifier) + mesh.merge(volume->mesh); + } + if (mesh.stl.stats.number_of_facets > 0) { + // transform mesh + // we ignore the per-instance transformations currently and only + // consider the first one + this->model_object()->instances.front()->transform_mesh(&mesh, true); + // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift + mesh.translate(- unscale(this->_copies_shift.x), - unscale(this->_copies_shift.y), -this->model_object()->bounding_box().min.z); + // perform actual slicing + TriangleMeshSlicer mslicer(&mesh); + mslicer.slice(z, &layers); + } + } + return layers; +} + void PrintObject::_make_perimeters() { @@ -930,7 +1129,7 @@ PrintObject::_make_perimeters() // this algorithm makes sure that at least one perimeter is overlapping // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing - // hollow objects + // hollow objects FOREACH_REGION(this->_print, region_it) { size_t region_id = region_it - this->_print->regions.begin(); const PrintRegion ®ion = **region_it; @@ -941,7 +1140,7 @@ PrintObject::_make_perimeters() || region.config.fill_density == 0 || this->layer_count() < 2) continue; - for (size_t i = 0; i <= (this->layer_count()-2); ++i) { + for (int i = 0; i < int(this->layer_count()) - 1; ++i) { LayerRegion &layerm = *this->get_layer(i)->get_region(region_id); const LayerRegion &upper_layerm = *this->get_layer(i+1)->get_region(region_id); const Polygons upper_layerm_polygons = upper_layerm.slices; @@ -1044,4 +1243,4 @@ PrintObject::_infill() this->state.set_done(posInfill); } -} +} // namespace Slic3r diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp new file mode 100644 index 000000000..82813770f --- /dev/null +++ b/xs/src/libslic3r/Slicing.cpp @@ -0,0 +1,585 @@ +#include "Slicing.hpp" +#include "SlicingAdaptive.hpp" +#include "PrintConfig.hpp" +#include "Model.hpp" + +// #define SLIC3R_DEBUG + +// Make assert active if SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG + #undef NDEBUG + #define DEBUG + #define _DEBUG + #include "SVG.hpp" + #undef assert + #include +#endif + +namespace Slic3r +{ + +SlicingParameters create_from_config( + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + coordf_t object_height, + const std::set &object_extruders) +{ + coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ? + object_config.layer_height.value : + object_config.first_layer_height.get_abs_value(object_config.layer_height.value); + coordf_t support_material_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_extruder.value - 1); + coordf_t support_material_interface_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_interface_extruder.value - 1); + bool soluble_interface = object_config.support_material_contact_distance.value == 0.; + + SlicingParameters params; + params.layer_height = object_config.layer_height.value; + params.first_object_layer_height = first_layer_height; + params.object_print_z_min = 0.; + params.object_print_z_max = object_height; + params.base_raft_layers = object_config.raft_layers.value; + + if (params.base_raft_layers > 0) { + params.interface_raft_layers = (params.base_raft_layers + 1) / 2; + params.base_raft_layers -= params.interface_raft_layers; + // Use as large as possible layer height for the intermediate raft layers. + params.base_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_extruder_dmr); + params.interface_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr); + params.contact_raft_layer_height_bridging = false; + params.first_object_layer_bridging = false; + #if 1 + params.contact_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr); + if (! soluble_interface) { + // Compute the average of all nozzles used for printing the object over a raft. + //FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa? + coordf_t average_object_extruder_dmr = 0.; + if (! object_extruders.empty()) { + for (std::set::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) + average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder); + average_object_extruder_dmr /= coordf_t(object_extruders.size()); + } + params.first_object_layer_height = average_object_extruder_dmr; + params.first_object_layer_bridging = true; + } + #else + params.contact_raft_layer_height = soluble_interface ? support_material_interface_extruder_dmr : 0.75 * support_material_interface_extruder_dmr; + params.contact_raft_layer_height_bridging = ! soluble_interface; + ... + #endif + } + + if (params.has_raft()) { + // Raise first object layer Z by the thickness of the raft itself plus the extra distance required by the support material logic. + //FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case. + coordf_t print_z = first_layer_height + object_config.support_material_contact_distance.value; + if (params.raft_layers() == 1) { + params.contact_raft_layer_height = first_layer_height; + } else { + print_z += + // Number of the base raft layers is decreased by the first layer, which has already been added to print_z. + coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height + + // Number of the interface raft layers is decreased by the contact layer. + coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height + + params.contact_raft_layer_height; + } + params.object_print_z_min = print_z; + params.object_print_z_max += print_z; + } + + params.min_layer_height = std::min(params.layer_height, first_layer_height); + params.max_layer_height = std::max(params.layer_height, first_layer_height); + + //FIXME add it to the print configuration + params.min_layer_height = 0.05; + + // Calculate the maximum layer height as 0.75 from the minimum nozzle diameter. + if (! object_extruders.empty()) { + coordf_t min_object_extruder_dmr = 1000000.; + for (std::set::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) + min_object_extruder_dmr = std::min(min_object_extruder_dmr, print_config.nozzle_diameter.get_at(*it_extruder)); + // Allow excessive maximum layer height higher than 0.75 * min_object_extruder_dmr + params.max_layer_height = std::max(std::max(params.layer_height, first_layer_height), 0.75 * min_object_extruder_dmr); + } + + return params; +} + +// Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for +// in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation. +std::vector layer_height_profile_from_ranges( + const SlicingParameters &slicing_params, + const t_layer_height_ranges &layer_height_ranges) +{ + // 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed. + std::vector> ranges_non_overlapping; + ranges_non_overlapping.reserve(layer_height_ranges.size() * 4); + if (slicing_params.first_object_layer_height_fixed()) + ranges_non_overlapping.push_back(std::pair( + t_layer_height_range(0., slicing_params.first_object_layer_height), + slicing_params.first_object_layer_height)); + // The height ranges are sorted lexicographically by low / high layer boundaries. + for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) { + coordf_t lo = it_range->first.first; + coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height()); + coordf_t height = it_range->second; + if (! ranges_non_overlapping.empty()) + // Trim current low with the last high. + lo = std::max(lo, ranges_non_overlapping.back().first.second); + if (lo + EPSILON < hi) + // Ignore too narrow ranges. + ranges_non_overlapping.push_back(std::pair(t_layer_height_range(lo, hi), height)); + } + + // 2) Convert the trimmed ranges to a height profile, fill in the undefined intervals between z=0 and z=slicing_params.object_print_z_max() + // with slicing_params.layer_height + std::vector layer_height_profile; + for (std::vector>::const_iterator it_range = ranges_non_overlapping.begin(); it_range != ranges_non_overlapping.end(); ++ it_range) { + coordf_t lo = it_range->first.first; + coordf_t hi = it_range->first.second; + coordf_t height = it_range->second; + coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; + coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1]; + if (lo > last_z + EPSILON) { + // Insert a step of normal layer height. + layer_height_profile.push_back(last_z); + layer_height_profile.push_back(slicing_params.layer_height); + layer_height_profile.push_back(lo); + layer_height_profile.push_back(slicing_params.layer_height); + } + // Insert a step of the overriden layer height. + layer_height_profile.push_back(lo); + layer_height_profile.push_back(height); + layer_height_profile.push_back(hi); + layer_height_profile.push_back(height); + } + + coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; + coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1]; + if (last_z < slicing_params.object_print_z_height()) { + // Insert a step of normal layer height up to the object top. + layer_height_profile.push_back(last_z); + layer_height_profile.push_back(slicing_params.layer_height); + layer_height_profile.push_back(slicing_params.object_print_z_height()); + layer_height_profile.push_back(slicing_params.layer_height); + } + + return layer_height_profile; +} + +// Based on the work of @platsch +// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height. +std::vector layer_height_profile_adaptive( + const SlicingParameters &slicing_params, + const t_layer_height_ranges &layer_height_ranges, + const ModelVolumePtrs &volumes) +{ + // 1) Initialize the SlicingAdaptive class with the object meshes. + SlicingAdaptive as; + as.set_slicing_parameters(slicing_params); + for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it) + if (! (*it)->modifier) + as.add_mesh(&(*it)->mesh); + as.prepare(); + + // 2) Generate layers using the algorithm of @platsch + // loop until we have at least one layer and the max slice_z reaches the object height + //FIXME make it configurable + // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm. + const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value'); + + std::vector layer_height_profile; + layer_height_profile.push_back(0.); + layer_height_profile.push_back(slicing_params.first_object_layer_height); + if (slicing_params.first_object_layer_height_fixed()) { + layer_height_profile.push_back(slicing_params.first_object_layer_height); + layer_height_profile.push_back(slicing_params.first_object_layer_height); + } + coordf_t slice_z = slicing_params.first_object_layer_height; + coordf_t height = slicing_params.first_object_layer_height; + coordf_t cusp_height = 0.; + int current_facet = 0; + while ((slice_z - height) <= slicing_params.object_print_z_height()) { + height = 999; + // Slic3r::debugf "\n Slice layer: %d\n", $id; + // determine next layer height + coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet); + // check for horizontal features and object size + /* + if($self->config->get_value('match_horizontal_surfaces')) { + my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height); + if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) { + Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist; + # can we shrink the current layer a bit? + if($cusp_height-($min_height-$horizontal_dist) > $min_height) { + # yes we can + $cusp_height = $cusp_height-($min_height-$horizontal_dist); + Slic3r::debugf "Shrink layer height to %f\n", $cusp_height; + }else{ + # no, current layer would become too thin + $cusp_height = $cusp_height+$horizontal_dist; + Slic3r::debugf "Widen layer height to %f\n", $cusp_height; + } + } + } + */ + height = std::min(cusp_height, height); + + // apply z-gradation + /* + my $gradation = $self->config->get_value('adaptive_slicing_z_gradation'); + if($gradation > 0) { + $height = $height - unscale((scale($height)) % (scale($gradation))); + } + */ + + // look for an applicable custom range + /* + if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) { + $height = $range->[2]; + + # if user set custom height to zero we should just skip the range and resume slicing over it + if ($height == 0) { + $slice_z += $range->[1] - $range->[0]; + next; + } + } + */ + + layer_height_profile.push_back(slice_z); + layer_height_profile.push_back(height); + slice_z += height; + layer_height_profile.push_back(slice_z); + layer_height_profile.push_back(height); + } + + coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); + layer_height_profile.push_back(last); + layer_height_profile.push_back(slicing_params.first_object_layer_height); + layer_height_profile.push_back(slicing_params.object_print_z_height()); + layer_height_profile.push_back(slicing_params.first_object_layer_height); + + return layer_height_profile; +} + +template +static inline T clamp(const T low, const T high, const T value) +{ + return std::max(low, std::min(high, value)); +} + +template +static inline T lerp(const T a, const T b, const T t) +{ + assert(t >= T(-EPSILON) && t <= T(1.+EPSILON)); + return (1. - t) * a + t * b; +} + +void adjust_layer_height_profile( + const SlicingParameters &slicing_params, + std::vector &layer_height_profile, + coordf_t z, + coordf_t layer_thickness_delta, + coordf_t band_width, + int action) +{ + // Constrain the profile variability by the 1st layer height. + std::pair z_span_variable = + std::pair( + slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0., + slicing_params.object_print_z_height()); + if (z < z_span_variable.first || z > z_span_variable.second) + return; + + assert(layer_height_profile.size() >= 2); + + // 1) Get the current layer thickness at z. + coordf_t current_layer_height = slicing_params.layer_height; + for (size_t i = 0; i < layer_height_profile.size(); i += 2) { + if (i + 2 == layer_height_profile.size()) { + current_layer_height = layer_height_profile[i + 1]; + break; + } else if (layer_height_profile[i + 2] > z) { + coordf_t z1 = layer_height_profile[i]; + coordf_t h1 = layer_height_profile[i + 1]; + coordf_t z2 = layer_height_profile[i + 2]; + coordf_t h2 = layer_height_profile[i + 3]; + current_layer_height = lerp(h1, h2, (z - z1) / (z2 - z1)); + break; + } + } + + // 2) Is it possible to apply the delta? + switch (action) { + case 0: + default: + if (layer_thickness_delta > 0) { + if (current_layer_height >= slicing_params.max_layer_height - EPSILON) + return; + layer_thickness_delta = std::min(layer_thickness_delta, slicing_params.max_layer_height - current_layer_height); + } else { + if (current_layer_height <= slicing_params.min_layer_height + EPSILON) + return; + layer_thickness_delta = std::max(layer_thickness_delta, slicing_params.min_layer_height - current_layer_height); + } + break; + case 1: + layer_thickness_delta = std::abs(layer_thickness_delta); + layer_thickness_delta = std::min(layer_thickness_delta, std::abs(slicing_params.layer_height - current_layer_height)); + if (layer_thickness_delta < EPSILON) + return; + break; + } + + // 3) Densify the profile inside z +- band_width/2, remove duplicate Zs from the height profile inside the band. + coordf_t lo = std::max(z_span_variable.first, z - 0.5 * band_width); + coordf_t hi = std::min(z_span_variable.second, z + 0.5 * band_width); + coordf_t z_step = 0.1; + size_t i = 0; + while (i < layer_height_profile.size() && layer_height_profile[i] < lo) + i += 2; + i -= 2; + + std::vector profile_new; + profile_new.reserve(layer_height_profile.size()); + assert(i >= 0 && i + 1 < layer_height_profile.size()); + profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + i + 2); + coordf_t zz = lo; + while (zz < hi) { + size_t next = i + 2; + coordf_t z1 = layer_height_profile[i]; + coordf_t h1 = layer_height_profile[i + 1]; + coordf_t height = h1; + if (next < layer_height_profile.size()) { + coordf_t z2 = layer_height_profile[next]; + coordf_t h2 = layer_height_profile[next + 1]; + height = lerp(h1, h2, (zz - z1) / (z2 - z1)); + } + // Adjust height by layer_thickness_delta. + coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.; + coordf_t height_new = height; + switch (action) { + case 0: + default: + height += weight * layer_thickness_delta; + break; + case 1: + { + coordf_t delta = height - slicing_params.layer_height; + coordf_t step = weight * layer_thickness_delta; + step = (std::abs(delta) > step) ? + (delta > 0) ? -step : step : + -delta; + height += step; + break; + } + } + // Avoid entering a too short segment. + if (profile_new[profile_new.size() - 2] + EPSILON < zz) { + profile_new.push_back(zz); + profile_new.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, height)); + } + zz += z_step; + i = next; + while (i < layer_height_profile.size() && layer_height_profile[i] < zz) + i += 2; + i -= 2; + } + + i += 2; + if (i < layer_height_profile.size()) { + if (profile_new[profile_new.size() - 2] + z_step < layer_height_profile[i]) { + profile_new.push_back(profile_new[profile_new.size() - 2] + z_step); + profile_new.push_back(layer_height_profile[i + 1]); + } + profile_new.insert(profile_new.end(), layer_height_profile.begin() + i, layer_height_profile.end()); + } + layer_height_profile = std::move(profile_new); + + assert(layer_height_profile.size() > 2); + assert(layer_height_profile.size() % 2 == 0); + assert(layer_height_profile[0] == 0.); +#ifdef _DEBUG + for (size_t i = 2; i < layer_height_profile.size(); i += 2) + assert(layer_height_profile[i - 2] <= layer_height_profile[i]); + for (size_t i = 1; i < layer_height_profile.size(); i += 2) { + assert(layer_height_profile[i] > slicing_params.min_layer_height - EPSILON); + assert(layer_height_profile[i] < slicing_params.max_layer_height + EPSILON); + } +#endif /* _DEBUG */ +} + +// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector. +std::vector generate_object_layers( + const SlicingParameters &slicing_params, + const std::vector &layer_height_profile) +{ + coordf_t print_z = 0; + coordf_t height = 0; + + std::vector out; + + if (slicing_params.first_object_layer_height_fixed()) { + out.push_back(0); + print_z = slicing_params.first_object_layer_height; + out.push_back(print_z); + } + + size_t idx_layer_height_profile = 0; + // loop until we have at least one layer and the max slice_z reaches the object height + coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height; + while (slice_z < slicing_params.object_print_z_height()) { + height = slicing_params.min_layer_height; + if (idx_layer_height_profile < layer_height_profile.size()) { + size_t next = idx_layer_height_profile + 2; + for (;;) { + if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next]) + break; + idx_layer_height_profile = next; + next += 2; + } + coordf_t z1 = layer_height_profile[idx_layer_height_profile]; + coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1]; + height = h1; + if (next < layer_height_profile.size()) { + coordf_t z2 = layer_height_profile[next]; + coordf_t h2 = layer_height_profile[next + 1]; + height = lerp(h1, h2, (slice_z - z1) / (z2 - z1)); + assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON); + } + } + slice_z = print_z + 0.5 * height; + if (slice_z >= slicing_params.object_print_z_height()) + break; + assert(height > slicing_params.min_layer_height - EPSILON); + assert(height < slicing_params.max_layer_height + EPSILON); + out.push_back(print_z); + print_z += height; + slice_z = print_z + 0.5 * slicing_params.min_layer_height; + out.push_back(print_z); + } + + //FIXME Adjust the last layer to align with the top object layer exactly? + return out; +} + +int generate_layer_height_texture( + const SlicingParameters &slicing_params, + const std::vector &layers, + void *data, int rows, int cols, bool level_of_detail_2nd_level) +{ +// https://github.com/aschn/gnuplot-colorbrewer + std::vector palette_raw; + palette_raw.push_back(Point3(0x0B2, 0x018, 0x02B)); + palette_raw.push_back(Point3(0x0D6, 0x060, 0x04D)); + palette_raw.push_back(Point3(0x0F4, 0x0A5, 0x082)); + palette_raw.push_back(Point3(0x0FD, 0x0DB, 0x0C7)); + palette_raw.push_back(Point3(0x0D1, 0x0E5, 0x0F0)); + palette_raw.push_back(Point3(0x092, 0x0C5, 0x0DE)); + palette_raw.push_back(Point3(0x043, 0x093, 0x0C3)); + palette_raw.push_back(Point3(0x021, 0x066, 0x0AC)); + + // Clear the main texture and the 2nd LOD level. + memset(data, 0, rows * cols * 5); + // 2nd LOD level data start + unsigned char *data1 = reinterpret_cast(data) + rows * cols * 4; + int ncells = std::min((cols-1) * rows, int(ceil(16. * (slicing_params.object_print_z_height() / slicing_params.min_layer_height)))); + int ncells1 = ncells / 2; + int cols1 = cols / 2; + coordf_t z_to_cell = coordf_t(ncells-1) / slicing_params.object_print_z_height(); + coordf_t cell_to_z = slicing_params.object_print_z_height() / coordf_t(ncells-1); + coordf_t z_to_cell1 = coordf_t(ncells1-1) / slicing_params.object_print_z_height(); + coordf_t cell_to_z1 = slicing_params.object_print_z_height() / coordf_t(ncells1-1); + // for color scaling + coordf_t hscale = 2.f * std::max(slicing_params.max_layer_height - slicing_params.layer_height, slicing_params.layer_height - slicing_params.min_layer_height); + if (hscale == 0) + // All layers have the same height. Provide some height scale to avoid division by zero. + hscale = slicing_params.layer_height; + for (size_t idx_layer = 0; idx_layer < layers.size(); idx_layer += 2) { + coordf_t lo = layers[idx_layer]; + coordf_t hi = layers[idx_layer + 1]; + coordf_t mid = 0.5f * (lo + hi); + assert(mid <= slicing_params.object_print_z_height()); + coordf_t h = hi - lo; + hi = std::min(hi, slicing_params.object_print_z_height()); + int cell_first = clamp(0, ncells-1, int(ceil(lo * z_to_cell))); + int cell_last = clamp(0, ncells-1, int(floor(hi * z_to_cell))); + for (int cell = cell_first; cell <= cell_last; ++ cell) { + coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale; + int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); + int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); + coordf_t t = idxf - coordf_t(idx1); + const Point3 &color1 = palette_raw[idx1]; + const Point3 &color2 = palette_raw[idx2]; + + coordf_t z = cell_to_z * coordf_t(cell); + assert(z >= lo && z <= hi); + // Intensity profile to visualize the layers. + coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); + + // Color mapping from layer height to RGB. + Pointf3 color( + intensity * lerp(coordf_t(color1.x), coordf_t(color2.x), t), + intensity * lerp(coordf_t(color1.y), coordf_t(color2.y), t), + intensity * lerp(coordf_t(color1.z), coordf_t(color2.z), t)); + + int row = cell / (cols - 1); + int col = cell - row * (cols - 1); + assert(row >= 0 && row < rows); + assert(col >= 0 && col < cols); + unsigned char *ptr = (unsigned char*)data + (row * cols + col) * 4; + ptr[0] = clamp(0, 255, int(floor(color.x + 0.5))); + ptr[1] = clamp(0, 255, int(floor(color.y + 0.5))); + ptr[2] = clamp(0, 255, int(floor(color.z + 0.5))); + ptr[3] = 255; + if (col == 0 && row > 0) { + // Duplicate the first value in a row as a last value of the preceding row. + ptr[-4] = ptr[0]; + ptr[-3] = ptr[1]; + ptr[-2] = ptr[2]; + ptr[-1] = ptr[3]; + } + } + if (level_of_detail_2nd_level) { + cell_first = clamp(0, ncells1-1, int(ceil(lo * z_to_cell1))); + cell_last = clamp(0, ncells1-1, int(floor(hi * z_to_cell1))); + for (int cell = cell_first; cell <= cell_last; ++ cell) { + coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale; + int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); + int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); + coordf_t t = idxf - coordf_t(idx1); + const Point3 &color1 = palette_raw[idx1]; + const Point3 &color2 = palette_raw[idx2]; + + coordf_t z = cell_to_z1 * coordf_t(cell); + assert(z >= lo && z <= hi); + + // Color mapping from layer height to RGB. + Pointf3 color( + lerp(coordf_t(color1.x), coordf_t(color2.x), t), + lerp(coordf_t(color1.y), coordf_t(color2.y), t), + lerp(coordf_t(color1.z), coordf_t(color2.z), t)); + + int row = cell / (cols1 - 1); + int col = cell - row * (cols1 - 1); + assert(row >= 0 && row < rows/2); + assert(col >= 0 && col < cols/2); + unsigned char *ptr = data1 + (row * cols1 + col) * 4; + ptr[0] = clamp(0, 255, int(floor(color.x + 0.5))); + ptr[1] = clamp(0, 255, int(floor(color.y + 0.5))); + ptr[2] = clamp(0, 255, int(floor(color.z + 0.5))); + ptr[3] = 255; + if (col == 0 && row > 0) { + // Duplicate the first value in a row as a last value of the preceding row. + ptr[-4] = ptr[0]; + ptr[-3] = ptr[1]; + ptr[-2] = ptr[2]; + ptr[-1] = ptr[3]; + } + } + } + } + + // Returns number of cells of the 0th LOD level. + return ncells; +} + +}; // namespace Slic3r diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp index 5e910c672..349be2e4a 100644 --- a/xs/src/libslic3r/Slicing.hpp +++ b/xs/src/libslic3r/Slicing.hpp @@ -3,15 +3,52 @@ #ifndef slic3r_Slicing_hpp_ #define slic3r_Slicing_hpp_ -#include "libslic3r.h" +#include +#include +#include "libslic3r.h" namespace Slic3r { +class PrintConfig; +class PrintObjectConfig; +class ModelVolume; +typedef std::vector ModelVolumePtrs; + +// Parameters to guide object slicing and support generation. +// The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow +// (using a normal flow over a soluble support, using a bridging flow over a non-soluble support). struct SlicingParameters { SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); } + static SlicingParameters create_from_config( + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + coordf_t object_height, + const std::set &object_extruders); + + // Has any raft layers? + bool has_raft() const { return raft_layers() > 0; } + size_t raft_layers() const { return base_raft_layers + interface_raft_layers; } + + // Is the 1st object layer height fixed, or could it be varied? + bool first_object_layer_height_fixed() const { return ! has_raft() || first_object_layer_bridging; } + + // Height of the object to be printed. This value does not contain the raft height. + coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; } + + // Number of raft layers. + size_t base_raft_layers; + // Number of interface layers including the contact layer. + size_t interface_raft_layers; + + // Layer heights of the raft (base, interface and a contact layer). + coordf_t base_raft_layer_height; + coordf_t interface_raft_layer_height; + coordf_t contact_raft_layer_height; + bool contact_raft_layer_height_bridging; + // The regular layer height, applied for all but the first layer, if not overridden by layer ranges // or by the variable layer thickness table. coordf_t layer_height; @@ -19,10 +56,10 @@ struct SlicingParameters // Thickness of the first layer. This is either the first print layer thickness if printed without a raft, // or a bridging flow thickness if printed over a non-soluble raft, // or a normal layer height if printed over a soluble raft. - coordf_t first_layer_height; + coordf_t first_object_layer_height; // If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow. - bool first_layer_bridging; + bool first_object_layer_bridging; // Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm, // or by an interactive layer height editor. @@ -39,6 +76,37 @@ struct SlicingParameters typedef std::pair t_layer_height_range; typedef std::map t_layer_height_ranges; +extern std::vector layer_height_profile_from_ranges( + const SlicingParameters &slicing_params, + const t_layer_height_ranges &layer_height_ranges); + +extern std::vector layer_height_profile_adaptive( + const SlicingParameters &slicing_params, + const t_layer_height_ranges &layer_height_ranges, + const ModelVolumePtrs &volumes); + +extern void adjust_layer_height_profile( + const SlicingParameters &slicing_params, + std::vector &layer_height_profile, + coordf_t z, + coordf_t layer_thickness_delta, + coordf_t band_width, + int action); + +// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector. +// The object layers are based at z=0, ignoring the raft layers. +extern std::vector generate_object_layers( + const SlicingParameters &slicing_params, + const std::vector &layer_height_profile); + +// Produce a 1D texture packed into a 2D texture describing in the RGBA format +// the planned object layers. +// Returns number of cells used by the texture of the 0th LOD level. +extern int generate_layer_height_texture( + const SlicingParameters &slicing_params, + const std::vector &layers, + void *data, int rows, int cols, bool level_of_detail_2nd_level); + }; // namespace Slic3r #endif /* slic3r_Slicing_hpp_ */ diff --git a/xs/src/libslic3r/SlicingAdaptive.cpp b/xs/src/libslic3r/SlicingAdaptive.cpp index b7f116770..d501d6f62 100644 --- a/xs/src/libslic3r/SlicingAdaptive.cpp +++ b/xs/src/libslic3r/SlicingAdaptive.cpp @@ -22,22 +22,13 @@ std::pair face_z_span(const stl_facet *f) void SlicingAdaptive::prepare() { // 1) Collect faces of all meshes. - { - int nfaces_total = 0; - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) - nfaces_total += (*it_mesh)->stl.stats.number_of_facets; - m_faces.reserve(nfaces_total); - } - m_max_z = 0; - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) { - const stl_facet *faces = (*it_mesh)->stl.facet_start; - int nfaces = (*it_mesh)->stl.stats.number_of_facets; - for (int i = 0; i < nfaces; ++ i) { - const stl_facet *facet = faces + i; - m_faces.push_back(facet); - m_max_z = std::max(std::max(m_max_z, facet->vertex[0].z), std::max(facet->vertex[1].z, facet->vertex[2].z)); - } - } + int nfaces_total = 0; + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) + nfaces_total += (*it_mesh)->stl.stats.number_of_facets; + m_faces.reserve(nfaces_total); + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) + for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i) + m_faces.push_back((*it_mesh)->stl.facet_start + i); // 2) Sort faces lexicographically by their Z span. std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { @@ -54,7 +45,7 @@ void SlicingAdaptive::prepare() float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) { - float height = m_layer_height_max; + float height = m_slicing_params.max_layer_height; bool first_hit = false; // find all facets intersecting the slice-layer @@ -81,10 +72,10 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet } // lower height limit due to printer capabilities - height = std::max(height, m_layer_height_min); + height = std::max(height, float(m_slicing_params.min_layer_height)); // check for sloped facets inside the determined layer and correct height if necessary - if (height > m_layer_height_min) { + if (height > m_slicing_params.min_layer_height) { for (; ordered_id < int(m_faces.size()); ++ ordered_id) { std::pair zspan = face_z_span(m_faces[ordered_id]); // facet's minimum is higher than slice_z + height -> end loop @@ -119,7 +110,7 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet } } // lower height limit due to printer capabilities again - height = std::max(height, m_layer_height_min); + height = std::max(height, float(m_slicing_params.min_layer_height)); } // Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height; @@ -133,7 +124,7 @@ float SlicingAdaptive::horizontal_facet_distance(float z) for (size_t i = 0; i < m_faces.size(); ++ i) { std::pair zspan = face_z_span(m_faces[i]); // facet's minimum is higher than max forward distance -> end loop - if (zspan.first > z + m_layer_height_max) + if (zspan.first > z + m_slicing_params.max_layer_height) break; // min_z == max_z -> horizontal facet if (zspan.first > z && zspan.first == zspan.second) @@ -141,9 +132,9 @@ float SlicingAdaptive::horizontal_facet_distance(float z) } // objects maximum? - return (z + m_layer_height_max > m_max_z) ? - std::max(m_max_z - z, 0.f) : - m_layer_height_max; + return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ? + std::max(m_slicing_params.object_print_z_height() - z, 0.f) : + m_slicing_params.max_layer_height; } }; // namespace Slic3r diff --git a/xs/src/libslic3r/SlicingAdaptive.hpp b/xs/src/libslic3r/SlicingAdaptive.hpp index 23e8f4b62..bfd081d81 100644 --- a/xs/src/libslic3r/SlicingAdaptive.hpp +++ b/xs/src/libslic3r/SlicingAdaptive.hpp @@ -15,16 +15,14 @@ class SlicingAdaptive { public: void clear(); - void set_layer_height_range(float min, float max) { m_layer_height_min = min; m_layer_height_max = max; } + void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); } void prepare(); float cusp_height(float z, float cusp_value, int ¤t_facet); float horizontal_facet_distance(float z); protected: - float m_layer_height_min; - float m_layer_height_max; - float m_max_z; + SlicingParameters m_slicing_params; std::vector m_meshes; // Collected faces of all meshes, sorted by raising Z of the bottom most face. diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index d36134839..7461b9d91 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -3,6 +3,7 @@ %{ #include #include "libslic3r/Print.hpp" +#include "libslic3r/Slicing.hpp" #include "libslic3r/PlaceholderParser.hpp" %} @@ -58,6 +59,8 @@ _constant() Points copies(); t_layer_height_ranges layer_height_ranges() %code%{ RETVAL = THIS->layer_height_ranges; %}; + std::vector layer_height_profile() + %code%{ RETVAL = THIS->layer_height_profile; %}; Ref size() %code%{ RETVAL = &THIS->size; %}; Clone bounding_box(); @@ -82,6 +85,8 @@ _constant() bool reload_model_instances(); void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges) %code%{ THIS->layer_height_ranges = layer_height_ranges; %}; + void set_layer_height_profile(std::vector profile) + %code%{ THIS->layer_height_profile = profile; %}; size_t total_layer_count(); size_t layer_count(); @@ -106,13 +111,31 @@ _constant() %code%{ THIS->state.set_done(step); %}; void set_step_started(PrintObjectStep step) %code%{ THIS->state.set_started(step); %}; - + + void _slice(); void detect_surfaces_type(); void process_external_surfaces(); void discover_vertical_shells(); void bridge_over_infill(); void _make_perimeters(); void _infill(); + + void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) + %code%{ + THIS->update_layer_height_profile(); + adjust_layer_height_profile( + THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, action); + %}; + + int generate_layer_height_texture(void *data, int rows, int cols, bool level_of_detail_2nd_level = true) + %code%{ + THIS->update_layer_height_profile(); + SlicingParameters slicing_params = THIS->slicing_parameters(); + RETVAL = generate_layer_height_texture( + slicing_params, + generate_object_layers(slicing_params, THIS->layer_height_profile), + data, rows, cols, level_of_detail_2nd_level); + %}; int ptr() %code%{ RETVAL = (int)(intptr_t)THIS; %}; From 2d030f3a3cd2ab1c0be189fcc7cd2cb37874f7c3 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 17:56:37 +0100 Subject: [PATCH 45/82] Most of the slicing code rewritten to C++. --- lib/Slic3r/Print/Object.pm | 251 +------------------------------------ 1 file changed, 1 insertion(+), 250 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 709bc65e4..18768b828 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -45,223 +45,7 @@ sub slice { $self->set_step_started(STEP_SLICE); $self->print->status_cb->(10, "Processing triangulated mesh"); - # init layers - { - $self->clear_layers; - - # make layers taking custom heights into account - my $id = 0; - my $print_z = 0; - my $first_object_layer_height = -1; - my $first_object_layer_distance = -1; - - # add raft layers - if ($self->config->raft_layers > 0) { - # Reserve object layers for the raft. Last layer of the raft is the contact layer. - $id += $self->config->raft_layers; - - # Raise first object layer Z by the thickness of the raft itself - # plus the extra distance required by the support material logic. - #FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case. - my $first_layer_height = $self->config->get_value('first_layer_height'); - $print_z += $first_layer_height; - - # Use as large as possible layer height for the intermediate raft layers. - my $support_material_layer_height; - { - my @nozzle_diameters = ( - map $self->print->config->get_at('nozzle_diameter', $_), - $self->config->support_material_extruder-1, - $self->config->support_material_interface_extruder-1, - ); - $support_material_layer_height = 0.75 * min(@nozzle_diameters); - } - $print_z += $support_material_layer_height * ($self->config->raft_layers - 1); - - # compute the average of all nozzles used for printing the object - #FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa? - my $nozzle_diameter; - { - my @nozzle_diameters = ( - map $self->print->config->get_at('nozzle_diameter', $_), @{$self->print->object_extruders} - ); - $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters; - } - $first_object_layer_distance = $self->_support_material->contact_distance($self->config->layer_height, $nozzle_diameter); - - # force first layer print_z according to the contact distance - # (the loop below will raise print_z by such height) - $first_object_layer_height = $first_object_layer_distance - $self->config->support_material_contact_distance; - } - - # loop until we have at least one layer and the max slice_z reaches the object height - my $slice_z = 0; - my $height = 0; - my $max_z = unscale($self->size->z); - while (($slice_z - $height) <= $max_z) { - # assign the default height to the layer according to the general settings - $height = ($id == 0) - ? $self->config->get_value('first_layer_height') - : $self->config->layer_height; - - # look for an applicable custom range - if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) { - $height = $range->[2]; - - # if user set custom height to zero we should just skip the range and resume slicing over it - if ($height == 0) { - $slice_z += $range->[1] - $range->[0]; - next; - } - } - - if ($first_object_layer_height != -1 && !@{$self->layers}) { - $height = $first_object_layer_height; - $print_z += ($first_object_layer_distance - $height); - } - - $print_z += $height; - $slice_z += $height/2; - - ### Slic3r::debugf "Layer %d: height = %s; slice_z = %s; print_z = %s\n", $id, $height, $slice_z, $print_z; - - $self->add_layer($id, $height, $print_z, $slice_z); - if ($self->layer_count >= 2) { - my $lc = $self->layer_count; - $self->get_layer($lc - 2)->set_upper_layer($self->get_layer($lc - 1)); - $self->get_layer($lc - 1)->set_lower_layer($self->get_layer($lc - 2)); - } - $id++; - - $slice_z += $height/2; # add the other half layer - } - } - - # make sure all layers contain layer region objects for all regions - my $regions_count = $self->print->region_count; - foreach my $layer (@{ $self->layers }) { - $layer->region($_) for 0 .. ($regions_count-1); - } - - # get array of Z coordinates for slicing - my @z = map $_->slice_z, @{$self->layers}; - - # slice all non-modifier volumes - for my $region_id (0..($self->region_count - 1)) { - my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 0); - for my $layer_id (0..$#$expolygons_by_layer) { - my $layerm = $self->get_layer($layer_id)->regions->[$region_id]; - $layerm->slices->clear; - foreach my $expolygon (@{ $expolygons_by_layer->[$layer_id] }) { - $layerm->slices->append(Slic3r::Surface->new( - expolygon => $expolygon, - surface_type => S_TYPE_INTERNAL, - )); - } - } - } - - # then slice all modifier volumes - if ($self->region_count > 1) { - for my $region_id (0..$self->region_count) { - my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 1); - - # loop through the other regions and 'steal' the slices belonging to this one - for my $other_region_id (0..$self->region_count) { - next if $other_region_id == $region_id; - - for my $layer_id (0..$#$expolygons_by_layer) { - my $layerm = $self->get_layer($layer_id)->regions->[$region_id]; - my $other_layerm = $self->get_layer($layer_id)->regions->[$other_region_id]; - next if !defined $other_layerm; - - my $other_slices = [ map $_->p, @{$other_layerm->slices} ]; # Polygons - my $my_parts = intersection_ex( - $other_slices, - [ map @$_, @{ $expolygons_by_layer->[$layer_id] } ], - ); - next if !@$my_parts; - - # append new parts to our region - foreach my $expolygon (@$my_parts) { - $layerm->slices->append(Slic3r::Surface->new( - expolygon => $expolygon, - surface_type => S_TYPE_INTERNAL, - )); - } - - # remove such parts from original region - $other_layerm->slices->clear; - $other_layerm->slices->append(Slic3r::Surface->new( - expolygon => $_, - surface_type => S_TYPE_INTERNAL, - )) for @{ diff_ex($other_slices, [ map @$_, @$my_parts ]) }; - } - } - } - } - - # remove last layer(s) if empty - $self->delete_layer($self->layer_count - 1) - while $self->layer_count && (!map @{$_->slices}, @{$self->get_layer($self->layer_count - 1)->regions}); - - foreach my $layer (@{ $self->layers }) { - # apply size compensation - if ($self->config->xy_size_compensation != 0) { - my $delta = scale($self->config->xy_size_compensation); - if (@{$layer->regions} == 1) { - # single region - my $layerm = $layer->regions->[0]; - my $slices = [ map $_->p, @{$layerm->slices} ]; - $layerm->slices->clear; - $layerm->slices->append(Slic3r::Surface->new( - expolygon => $_, - surface_type => S_TYPE_INTERNAL, - )) for @{offset_ex($slices, $delta)}; - } else { - if ($delta < 0) { - # multiple regions, shrinking - # we apply the offset to the combined shape, then intersect it - # with the original slices for each region - my $slices = union([ map $_->p, map @{$_->slices}, @{$layer->regions} ]); - $slices = offset($slices, $delta); - foreach my $layerm (@{$layer->regions}) { - my $this_slices = intersection_ex( - $slices, - [ map $_->p, @{$layerm->slices} ], - ); - $layerm->slices->clear; - $layerm->slices->append(Slic3r::Surface->new( - expolygon => $_, - surface_type => S_TYPE_INTERNAL, - )) for @$this_slices; - } - } else { - # multiple regions, growing - # this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap - # so we give priority to the first one and so on - for my $i (0..$#{$layer->regions}) { - my $layerm = $layer->regions->[$i]; - my $slices = offset_ex([ map $_->p, @{$layerm->slices} ], $delta); - if ($i > 0) { - $slices = diff_ex( - [ map @$_, @$slices ], - [ map $_->p, map @{$_->slices}, map $layer->regions->[$_], 0..($i-1) ], # slices of already processed regions - ); - } - $layerm->slices->clear; - $layerm->slices->append(Slic3r::Surface->new( - expolygon => $_, - surface_type => S_TYPE_INTERNAL, - )) for @$slices; - } - } - } - } - - # Merge all regions' slices to get islands, chain them by a shortest path. - $layer->make_slices; - } + $self->_slice; # detect slicing errors my $warning_thrown = 0; @@ -334,39 +118,6 @@ sub slice { $self->set_step_done(STEP_SLICE); } -# called from slice() -sub _slice_region { - my ($self, $region_id, $z, $modifier) = @_; - - return [] if !@{$self->get_region_volumes($region_id)}; - - # compose mesh - my $mesh; - foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) { - my $volume = $self->model_object->volumes->[$volume_id]; - next if $volume->modifier && !$modifier; - next if !$volume->modifier && $modifier; - - if (defined $mesh) { - $mesh->merge($volume->mesh); - } else { - $mesh = $volume->mesh->clone; - } - } - return if !defined $mesh; - - # transform mesh - # we ignore the per-instance transformations currently and only - # consider the first one - $self->model_object->instances->[0]->transform_mesh($mesh, 1); - - # align mesh to Z = 0 (it should be already aligned actually) and apply XY shift - $mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min); - - # perform actual slicing - return $mesh->slice($z); -} - # 1) Merges typed region slices into stInternal type. # 2) Increases an "extra perimeters" counter at region slices where needed. # 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). From 46b44fc141fb4d256955b2502d8d16c30f6343c1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 18:02:24 +0100 Subject: [PATCH 46/82] User interface of the variable layer thickness. Certainly not finished yet, but sufficient for evaluation of the prints. --- lib/Slic3r/GUI/3DScene.pm | 502 ++++++++++++++++------ lib/Slic3r/GUI/Plater.pm | 26 +- lib/Slic3r/GUI/Plater/3D.pm | 11 +- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 4 +- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 3 +- 5 files changed, 413 insertions(+), 133 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index edb5eb492..2be09e596 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -53,10 +53,15 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init origin _mouse_pos _hover_volume_idx + _drag_volume_idx _drag_start_pos _drag_start_xy _dragged + + layer_editing_enabled + _layer_height_edited + _camera_type _camera_target _camera_distance @@ -74,7 +79,7 @@ use constant SELECTED_COLOR => [0,1,0,1]; use constant HOVER_COLOR => [0.4,0.9,0,1]; # phi / theta angles to orient the camera. -use constant VIEW_ISO => [45.0,45.0]; +use constant VIEW_DEFAULT => [45.0,45.0]; use constant VIEW_LEFT => [90.0,90.0]; use constant VIEW_RIGHT => [-90.0,90.0]; use constant VIEW_TOP => [0.0,0.0]; @@ -82,7 +87,11 @@ use constant VIEW_BOTTOM => [0.0,180.0]; use constant VIEW_FRONT => [0.0,90.0]; use constant VIEW_REAR => [180.0,90.0]; -use constant GIMBAL_LOCK_THETA_MAX => 170; +use constant MANIPULATION_IDLE => 0; +use constant MANIPULATION_DRAGGING => 1; +use constant MANIPULATION_LAYER_HEIGHT => 2; + +use constant GIMBALL_LOCK_THETA_MAX => 170; # make OpenGL::Array thread-safe { @@ -129,6 +138,11 @@ sub new { # $self->_camera_type('perspective'); $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); $self->_camera_distance(0.); + + # Size of a layer height texture, used by a shader to color map the object print layers. + $self->{layer_preview_z_texture_width} = 512; + $self->{layer_preview_z_texture_height} = 512; + $self->{layer_height_edit_band_width} = 2.; $self->reset_objects; @@ -177,6 +191,16 @@ sub new { return $self; } +sub _first_selected_object_id { + my ($self) = @_; + for my $i (0..$#{$self->volumes}) { + if ($self->volumes->[$i]->selected) { + return int($self->volumes->[$i]->select_group_id / 1000000); + } + } + return -1; +} + sub mouse_event { my ($self, $e) = @_; @@ -191,40 +215,65 @@ sub mouse_event { # If user pressed left or right button we first check whether this happened # on a volume or not. my $volume_idx = $self->_hover_volume_idx // -1; - - # select volume in this 3D canvas - if ($self->enable_picking) { - $self->deselect_volumes; - $self->select_volume($volume_idx); - - if ($volume_idx != -1) { - my $group_id = $self->volumes->[$volume_idx]->select_group_id; - my @volumes; - if ($group_id != -1) { - $self->select_volume($_) - for grep $self->volumes->[$_]->select_group_id == $group_id, - 0..$#{$self->volumes}; + $self->_layer_height_edited(0); + if ($self->layer_editing_enabled && $self->{print}) { + my $object_idx_selected = $self->_first_selected_object_id; + if ($object_idx_selected != -1) { + # A volume is selected. Test, whether hovering over a layer thickness bar. + my ($cw, $ch) = $self->GetSizeWH; + my $bar_width = 70; + if ($e->GetX >= $cw - $bar_width) { + # Start editing the layer height. + $self->_layer_height_edited(1); + my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1); +# print "Modifying height profile at $z\n"; +# $self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightDown ? - 0.05 : 0.05, 2., 0); + $self->{print}->get_object($object_idx_selected)->generate_layer_height_texture( + $self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr, + $self->{layer_preview_z_texture_height}, + $self->{layer_preview_z_texture_width}); + $self->Refresh; } } - - $self->Refresh; } - - # propagate event through callback - $self->on_select->($volume_idx) - if $self->on_select; - - if ($volume_idx != -1) { - if ($e->LeftDown && $self->enable_moving) { - $self->_drag_volume_idx($volume_idx); - $self->_drag_start_pos($self->mouse_to_3d(@$pos)); - } elsif ($e->RightDown) { - # if right clicking on volume, propagate event through callback - $self->on_right_click->($e->GetPosition) - if $self->on_right_click; + + if (! $self->_layer_height_edited) { + # Select volume in this 3D canvas. + # Don't deselect a volume if layer editing is enabled. We want the object to stay selected + # during the scene manipulation. + if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) { + $self->deselect_volumes; + $self->select_volume($volume_idx); + + if ($volume_idx != -1) { + my $group_id = $self->volumes->[$volume_idx]->select_group_id; + my @volumes; + if ($group_id != -1) { + $self->select_volume($_) + for grep $self->volumes->[$_]->select_group_id == $group_id, + 0..$#{$self->volumes}; + } + } + + $self->Refresh; + } + + # propagate event through callback + $self->on_select->($volume_idx) + if $self->on_select; + + if ($volume_idx != -1) { + if ($e->LeftDown && $self->enable_moving) { + $self->_drag_volume_idx($volume_idx); + $self->_drag_start_pos($self->mouse_to_3d(@$pos)); + } elsif ($e->RightDown) { + # if right clicking on volume, propagate event through callback + $self->on_right_click->($e->GetPosition) + if $self->on_right_click; + } } } - } elsif ($e->Dragging && $e->LeftIsDown && defined($self->_drag_volume_idx)) { + } elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) { # get new position at the same Z of the initial click point my $mouse_ray = $self->mouse_ray($e->GetX, $e->GetY); my $cur_pos = $mouse_ray->intersect_plane($self->_drag_start_pos->z); @@ -249,14 +298,29 @@ sub mouse_event { $self->_dragged(1); $self->Refresh; } elsif ($e->Dragging) { - if ($e->LeftIsDown) { + if ($self->_layer_height_edited) { + my $object_idx_selected = $self->_first_selected_object_id; + if ($object_idx_selected != -1) { + # A volume is selected. Test, whether hovering over a layer thickness bar. + my ($cw, $ch) = $self->GetSizeWH; + my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1); +# print "Modifying height profile at $z\n"; + my $strength = 0.005; + $self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightIsDown ? - $strength : $strength, 2., $e->ShiftDown ? 1 : 0); + $self->{print}->get_object($object_idx_selected)->generate_layer_height_texture( + $self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr, + $self->{layer_preview_z_texture_height}, + $self->{layer_preview_z_texture_width}); + $self->Refresh; + } + } elsif ($e->LeftIsDown) { # if dragging over blank area with left button, rotate if (defined $self->_drag_start_pos) { my $orig = $self->_drag_start_pos; if (TURNTABLE_MODE) { $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- - $self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX; + $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; $self->_stheta(0) if $self->_stheta < 0; } else { my $size = $self->GetClientSize; @@ -304,6 +368,7 @@ sub mouse_event { $self->_drag_start_pos(undef); $self->_drag_start_xy(undef); $self->_dragged(undef); + $self->_layer_height_edited(undef); } elsif ($e->Moving) { $self->_mouse_pos($pos); $self->Refresh; @@ -340,8 +405,8 @@ sub select_view { if (ref($direction)) { $dirvec = $direction; } else { - if ($direction eq 'iso') { - $dirvec = VIEW_ISO; + if ($direction eq 'default') { + $dirvec = VIEW_DEFAULT; } elsif ($direction eq 'left') { $dirvec = VIEW_LEFT; } elsif ($direction eq 'right') { @@ -356,22 +421,18 @@ sub select_view { $dirvec = VIEW_REAR; } } - - $self->_sphi($dirvec->[0]); - $self->_stheta($dirvec->[1]); - - # Avoid gimbal lock. - $self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX; - $self->_stheta(0) if $self->_stheta < 0; - - # View everything. - $self->volumes_bounding_box->defined - ? $self->zoom_to_volumes - : $self->zoom_to_bed; - - $self->on_viewport_changed->() if $self->on_viewport_changed; - $self->_dirty(1); - $self->Refresh; + my $bb = $self->volumes_bounding_box; + if (! $bb->empty) { + $self->_sphi($dirvec->[0]); + $self->_stheta($dirvec->[1]); + # Avoid gimball lock. + $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; + $self->_stheta(0) if $self->_stheta < 0; + # View everything. + $self->zoom_to_bounding_box($bb); + $self->on_viewport_changed->() if $self->on_viewport_changed; + $self->Refresh; + } } sub zoom_to_bounding_box { @@ -380,7 +441,7 @@ sub zoom_to_bounding_box { # calculate the zoom factor needed to adjust viewport to # bounding box - my $max_size = max(@{$bb->size}) * 1.05; + my $max_size = max(@{$bb->size}) * 2; my $min_viewport_size = min($self->GetSizeWH); $self->_zoom($min_viewport_size / $max_size); @@ -725,12 +786,19 @@ sub InitGL { $self->init(1); my $shader; -# $shader = $self->{shader} = new Slic3r::GUI::GLShader; + $shader = $self->{shader} = new Slic3r::GUI::GLShader; if ($self->{shader}) { my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader); print $info if $info; + + ($self->{layer_preview_z_texture_id}) = glGenTextures_p(1); + glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glBindTexture(GL_TEXTURE_2D, 0); } - + glClearColor(0, 0, 0, 1); glColor3f(1, 0, 0); glEnable(GL_DEPTH_TEST); @@ -808,11 +876,15 @@ sub Render { glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0); glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1); glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1); + + # Head light + glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); if ($self->enable_picking) { # Render the object for picking. # FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing. # Better to use software ray-casting on a bounding-box hierarchy. + glDisable(GL_MULTISAMPLE); glDisable(GL_LIGHTING); $self->draw_volumes(1); glFlush(); @@ -839,6 +911,7 @@ sub Render { glFlush(); glFinish(); glEnable(GL_LIGHTING); + glEnable(GL_MULTISAMPLE); } # draw fixed background @@ -938,7 +1011,7 @@ sub Render { # draw objects $self->draw_volumes; - + # draw cutting plane if (defined $self->cutting_plane_z) { my $plane_z = $self->cutting_plane_z; @@ -957,6 +1030,8 @@ sub Render { glEnable(GL_CULL_FACE); glDisable(GL_BLEND); } + + $self->draw_active_object_annotations; glFlush(); @@ -967,18 +1042,64 @@ sub draw_volumes { # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. my ($self, $fakecolor) = @_; - $self->{shader}->Enable if (! $fakecolor && $self->{shader}); - glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); + # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth), + # where x, y is the window size divided by $self->_zoom. + my ($cw, $ch) = $self->GetSizeWH; + my $bar_width = 70; + my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom)); + my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom)); + my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); + my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. : + ($ch - $mouse_pos->y - 1.) / ($ch - 1); + foreach my $volume_idx (0..$#{$self->volumes}) { my $volume = $self->volumes->[$volume_idx]; - - if ($fakecolor) { + + my $shader_active = 0; + if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->{layer_height_texture_data} && $volume->{layer_height_texture_cells}) { + $self->{shader}->Enable; + my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row'); + my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized'); + my $z_cursor_id = $self->{shader}->Map('z_cursor'); + die if ! defined($z_to_texture_row_id); + die if ! defined($z_texture_row_to_normalized_id); + die if ! defined($z_cursor_id); + my $ncells = $volume->{layer_height_texture_cells}; + my $z_max = $volume->{bounding_box}->z_max; + glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max)); + glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height}); + glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative); + glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LEVEL, 0); +# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + if (1) { + glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); +# glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +# glPixelStorei(GL_UNPACK_ROW_LENGTH, $self->{layer_preview_z_texture_width}); + glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, + GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr); + glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, + GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4)); + } else { + glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, + 0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr); + glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width}/2, $self->{layer_preview_z_texture_height}/2, + 0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr + $self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4); + } + +# my $nlines = ceil($ncells / ($self->{layer_preview_z_texture_width} - 1)); + + $shader_active = 1; + } elsif ($fakecolor) { # Object picking mode. Render the object with a color encoding the object index. my $r = ($volume_idx & 0x000000FF) >> 0; my $g = ($volume_idx & 0x0000FF00) >> 8; @@ -1055,8 +1176,13 @@ sub draw_volumes { } glVertexPointer_c(3, GL_FLOAT, 0, 0); - glNormalPointer_c(GL_FLOAT, 0, 0); + glNormalPointer_c(GL_FLOAT, 0, 0); glPopMatrix(); + + if ($shader_active) { + glBindTexture(GL_TEXTURE_2D, 0); + $self->{shader}->Disable; + } } glDisableClientState(GL_NORMAL_ARRAY); glDisable(GL_BLEND); @@ -1069,8 +1195,93 @@ sub draw_volumes { glVertexPointer_c(3, GL_FLOAT, 0, 0); } glDisableClientState(GL_VERTEX_ARRAY); +} - $self->{shader}->Disable if (! $fakecolor && $self->{shader}); +sub draw_active_object_annotations { + # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. + my ($self) = @_; + + return if (! $self->{shader} || ! $self->layer_editing_enabled); + + my $volume; + foreach my $volume_idx (0..$#{$self->volumes}) { + my $v = $self->volumes->[$volume_idx]; + if ($v->selected && $v->{layer_height_texture_data} && $v->{layer_height_texture_cells}) { + $volume = $v; + last; + } + } + return if (! $volume); + + # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth), + # where x, y is the window size divided by $self->_zoom. + my ($cw, $ch) = $self->GetSizeWH; + my $bar_width = 70; + my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom)); + my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom)); + my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); + my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. : + ($ch - $mouse_pos->y - 1.) / ($ch - 1); + + $self->{shader}->Enable; + my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row'); + my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized'); + my $z_cursor_id = $self->{shader}->Map('z_cursor'); + my $ncells = $volume->{layer_height_texture_cells}; + my $z_max = $volume->{bounding_box}->z_max; + glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max)); + glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height}); + glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative); + glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); + glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, + 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, + GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr); + glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, + GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4)); + + # Render the color bar. + glDisable(GL_DEPTH_TEST); + # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth), + # where x, y is the window size divided by $self->_zoom. + glPushMatrix(); + glLoadIdentity(); + # Paint the overlay. + glBegin(GL_QUADS); + glVertex3f($bar_left, $bar_bottom, 0); + glVertex3f($bar_right, $bar_bottom, 0); + glVertex3f($bar_right, $bar_top, $volume->{bounding_box}->z_max); + glVertex3f($bar_left, $bar_top, $volume->{bounding_box}->z_max); + glEnd(); + glBindTexture(GL_TEXTURE_2D, 0); + $self->{shader}->Disable; + + # Paint the graph. + my $object_idx = int($volume->select_group_id / 1000000); + my $print_object = $self->{print}->get_object($object_idx); + my $max_z = unscale($print_object->size->z); + my $profile = $print_object->layer_height_profile; + my $layer_height = $print_object->config->get('layer_height'); + # Baseline + glColor3f(0., 0., 0.); + glBegin(GL_LINE_STRIP); + glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_bottom); + glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_top); + glEnd(); + # Curve + glColor3f(0., 0., 1.); + glBegin(GL_LINE_STRIP); + for (my $i = 0; $i < int(@{$profile}); $i += 2) { + my $z = $profile->[$i]; + my $h = $profile->[$i+1]; + glVertex3f($bar_left + $h * ($bar_right - $bar_left) / 0.45, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z); + } + glEnd(); + # Revert the matrices. + glPopMatrix(); + glEnable(GL_DEPTH_TEST); } sub _report_opengl_state @@ -1106,57 +1317,61 @@ sub _vertex_shader { return <<'VERTEX'; #version 110 +#define LIGHT_TOP_DIR 0., 1., 0. +#define LIGHT_TOP_DIFFUSE 0.2 +#define LIGHT_TOP_SPECULAR 0.3 + +#define LIGHT_FRONT_DIR 0., 0., 1. +#define LIGHT_FRONT_DIFFUSE 0.5 +#define LIGHT_FRONT_SPECULAR 0.3 + +#define INTENSITY_AMBIENT 0.1 + +uniform float z_to_texture_row; +varying float intensity_specular; +varying float intensity_tainted; varying float object_z; void main() { - vec3 normal, lightDir, viewVector, halfVector; - vec4 diffuse, ambient, globalAmbient, specular = vec4(0.0); - float NdotL,NdotHV; - + vec3 eye, normal, lightDir, viewVector, halfVector; + float NdotL, NdotHV; + +// eye = gl_ModelViewMatrixInverse[3].xyz; + eye = vec3(0., 0., 1.); + // First transform the normal into eye space and normalize the result. normal = normalize(gl_NormalMatrix * gl_Normal); // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space. // Also since we're talking about a directional light, the position field is actually direction. - lightDir = normalize(vec3(gl_LightSource[0].position)); + lightDir = vec3(LIGHT_TOP_DIR); + halfVector = normalize(lightDir + eye); // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. NdotL = max(dot(normal, lightDir), 0.0); - - // Compute the diffuse, ambient and globalAmbient terms. -// diffuse = NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse); -// ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient; - diffuse = NdotL * (gl_Color * gl_LightSource[0].diffuse); - ambient = gl_Color * gl_LightSource[0].ambient; - globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient; - - // compute the specular term if NdotL is larger than zero - if (NdotL > 0.0) { - NdotHV = max(dot(normal, normalize(gl_LightSource[0].halfVector.xyz)),0.0); - specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess); - } + + intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + intensity_specular = 0.; + +// if (NdotL > 0.0) +// intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess); // Perform the same lighting calculation for the 2nd light source. - lightDir = normalize(vec3(gl_LightSource[1].position)); + lightDir = vec3(LIGHT_FRONT_DIR); + halfVector = normalize(lightDir + eye); NdotL = max(dot(normal, lightDir), 0.0); -// diffuse += NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[1].diffuse); -// ambient += gl_FrontMaterial.ambient * gl_LightSource[1].ambient; - diffuse += NdotL * (gl_Color * gl_LightSource[1].diffuse); - ambient += gl_Color * gl_LightSource[1].ambient; + intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE; - // compute the specular term if NdotL is larger than zero - if (NdotL > 0.0) { - NdotHV = max(dot(normal, normalize(gl_LightSource[1].halfVector.xyz)),0.0); - specular += gl_FrontMaterial.specular * gl_LightSource[1].specular * pow(NdotHV,gl_FrontMaterial.shininess); - } - - gl_FrontColor = globalAmbient + diffuse + ambient + specular; - gl_FrontColor.a = 1.; - - gl_Position = ftransform(); + // compute the specular term if NdotL is larger than zero + if (NdotL > 0.0) + intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess); + + // Scaled to widths of the Z texture. object_z = gl_Vertex.z / gl_Vertex.w; + + gl_Position = ftransform(); } VERTEX @@ -1165,17 +1380,39 @@ VERTEX sub _fragment_shader { return <<'FRAGMENT'; #version 110 + #define M_PI 3.1415926535897932384626433832795 +// 2D texture (1D texture split by the rows) of color along the object Z axis. +uniform sampler2D z_texture; +// Scaling from the Z texture rows coordinate to the normalized texture row coordinate. +uniform float z_to_texture_row; +uniform float z_texture_row_to_normalized; + +varying float intensity_specular; +varying float intensity_tainted; varying float object_z; +uniform float z_cursor; +uniform float z_cursor_band_width; void main() { - float layer_height = 0.25; - float layer_height2 = 0.5 * layer_height; - float layer_center = floor(object_z / layer_height) * layer_height + layer_height2; - float intensity = cos(M_PI * 0.7 * (layer_center - object_z) / layer_height); - gl_FragColor = gl_Color * intensity; + float object_z_row = z_to_texture_row * object_z; + // Index of the row in the texture. + float z_texture_row = floor(object_z_row); + // Normalized coordinate from 0. to 1. + float z_texture_col = object_z_row - z_texture_row; +// float z_blend = 0.5 + 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) / 3.))); +// float z_blend = 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.5; + float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.25; + // Scale z_texture_row to normalized coordinates. + // Sample the Z texture. + gl_FragColor = + vec4(intensity_specular, intensity_specular, intensity_specular, 1.) + +// intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -2.5); + (1. - z_blend) * intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -200.) + + z_blend * vec4(1., 1., 0., 0.); + // and reset the transparency. gl_FragColor.a = 1.; } @@ -1189,6 +1426,8 @@ use Moo; has 'bounding_box' => (is => 'ro', required => 1); has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); has 'color' => (is => 'ro', required => 1); +# An ID containing the object ID, volume ID and instance ID. +has 'composite_id' => (is => 'rw', default => sub { -1 }); # An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance. has 'select_group_id' => (is => 'rw', default => sub { -1 }); # An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance. @@ -1210,6 +1449,26 @@ has 'tverts' => (is => 'rw'); # The offsets stores tripples of (z_top, qverts_idx, tverts_idx) in a linear array. has 'offsets' => (is => 'rw'); +# RGBA texture along the Z axis of an object, to visualize layers by stripes colored by their height. +has 'layer_height_texture_data' => (is => 'rw'); +# Number of texture cells. +has 'layer_height_texture_cells' => (is => 'rw'); + +sub object_idx { + my ($self) = @_; + return $self->composite_id / 1000000; +} + +sub volume_idx { + my ($self) = @_; + return ($self->composite_id / 1000) % 1000; +} + +sub instance_idx { + my ($self) = @_; + return $self->composite_id % 1000; +} + sub transformed_bounding_box { my ($self) = @_; @@ -1235,8 +1494,6 @@ __PACKAGE__->mk_accessors(qw( color_by select_by drag_by - volumes_by_object - _objects_by_volumes )); sub new { @@ -1246,14 +1503,12 @@ sub new { $self->color_by('volume'); # object | volume $self->select_by('object'); # object | volume | instance $self->drag_by('instance'); # object | instance - $self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ] - $self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ] return $self; } sub load_object { - my ($self, $model, $obj_idx, $instance_idxs) = @_; + my ($self, $model, $print, $obj_idx, $instance_idxs) = @_; my $model_object; if ($model->isa('Slic3r::Model::Object')) { @@ -1265,6 +1520,19 @@ sub load_object { } $instance_idxs ||= [0..$#{$model_object->instances}]; + + # Object will have a single common layer height texture for all volumes. + my $layer_height_texture_data; + my $layer_height_texture_cells; + if ($print && $obj_idx < $print->object_count) { + # Generate the layer height texture. Allocate data for the 0th and 1st mipmap levels. + $layer_height_texture_data = OpenGL::Array->new($self->{layer_preview_z_texture_width}*$self->{layer_preview_z_texture_height}*5, GL_UNSIGNED_BYTE); +# $print->get_object($obj_idx)->update_layer_height_profile_from_ranges(); + $layer_height_texture_cells = $print->get_object($obj_idx)->generate_layer_height_texture( + $layer_height_texture_data->ptr, + $self->{layer_preview_z_texture_height}, + $self->{layer_preview_z_texture_width}); + } my @volumes_idx = (); foreach my $volume_idx (0..$#{$model_object->volumes}) { @@ -1287,16 +1555,18 @@ sub load_object { # not correspond to the color of the filament. my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; $color->[3] = $volume->modifier ? 0.5 : 1; + print "Reloading object $volume_idx, $instance_idx\n"; push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( bounding_box => $mesh->bounding_box, color => $color, ); + $v->composite_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx); if ($self->select_by eq 'object') { $v->select_group_id($obj_idx*1000000); } elsif ($self->select_by eq 'volume') { $v->select_group_id($obj_idx*1000000 + $volume_idx*1000); } elsif ($self->select_by eq 'instance') { - $v->select_group_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx); + $v->select_group_id($v->composite_id); } if ($self->drag_by eq 'object') { $v->drag_group_id($obj_idx*1000); @@ -1304,15 +1574,18 @@ sub load_object { $v->drag_group_id($obj_idx*1000 + $instance_idx); } push @volumes_idx, my $scene_volume_idx = $#{$self->volumes}; - $self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $volume_idx, $instance_idx ]; my $verts = Slic3r::GUI::_3DScene::GLVertexArray->new; $verts->load_mesh($mesh); $v->tverts($verts); + + if (! $volume->modifier) { + $v->layer_height_texture_data($layer_height_texture_data); + $v->layer_height_texture_cells($layer_height_texture_cells); + } } } - $self->volumes_by_object->{$obj_idx} = [@volumes_idx]; return @volumes_idx; } @@ -1651,19 +1924,4 @@ sub _extrusionentity_to_verts { $tverts); } -sub object_idx { - my ($self, $volume_idx) = @_; - return $self->_objects_by_volumes->{$volume_idx}[0]; -} - -sub volume_idx { - my ($self, $volume_idx) = @_; - return $self->_objects_by_volumes->{$volume_idx}[1]; -} - -sub instance_idx { - my ($self, $volume_idx) = @_; - return $self->_objects_by_volumes->{$volume_idx}[2]; -} - 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 54f66e221..ef9868735 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -8,10 +8,11 @@ use utf8; use File::Basename qw(basename dirname); use List::Util qw(sum first max); use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad); +use LWP::UserAgent; use threads::shared qw(shared_clone); use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc :panel :sizer :toolbar :window wxTheApp :notebook :combobox); -use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED +use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED); use base 'Wx::Panel'; @@ -30,6 +31,7 @@ use constant TB_SCALE => &Wx::NewId; use constant TB_SPLIT => &Wx::NewId; use constant TB_CUT => &Wx::NewId; use constant TB_SETTINGS => &Wx::NewId; +use constant TB_LAYER_EDITING => &Wx::NewId; # package variables to avoid passing lexicals to threads our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType; @@ -94,7 +96,7 @@ sub new { # Initialize 3D plater if ($Slic3r::GUI::have_OpenGL) { - $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config}); + $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config}); $self->{preview_notebook}->AddPage($self->{canvas3D}, '3D'); $self->{canvas3D}->set_on_select_object($on_select_object); $self->{canvas3D}->set_on_double_click($on_double_click); @@ -154,6 +156,9 @@ sub new { $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddSeparator; $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); + + # FIXME add a button for layer editing + $self->{htoolbar}->AddCheckTool(TB_LAYER_EDITING, "Layer editing", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); } else { my %tbar_buttons = ( add => "Add…", @@ -168,12 +173,15 @@ sub new { split => "Split", cut => "Cut…", settings => "Settings…", + layer_editing => "Layer editing", ); $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) { $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); $self->{btoolbar}->Add($self->{"btn_$_"}); } + $self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + $self->{btoolbar}->Add($self->{"btn_layer_editing"}); } $self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize, @@ -256,6 +264,7 @@ sub new { EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; }); EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog }); + EVT_TOOL($self, TB_LAYER_EDITING, sub { $self->on_layer_editing_toggled($self->{htoolbar}->GetToolState(TB_LAYER_EDITING)); }); } else { EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; }); EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove @@ -269,6 +278,7 @@ sub new { EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; }); EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog }); EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog }); + EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); }); } $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self)) @@ -464,6 +474,13 @@ sub _on_select_preset { $self->on_config_change($self->GetFrame->config); } +sub on_layer_editing_toggled { + my ($self, $new_state) = @_; + print "on_layer_editing_toggled $new_state\n"; + $self->{canvas3D}->layer_editing_enabled($new_state); + $self->{canvas3D}->update; +} + sub GetFrame { my ($self) = @_; return &Wx::GetTopLevelParent($self); @@ -1481,6 +1498,8 @@ sub on_thumbnail_made { # (i.e. when an object is added/removed/moved/rotated/scaled) sub update { my ($self, $force_autocenter) = @_; + + print "Platter - update\n"; if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) { $self->{model}->center_instances_around_point($self->bed_centerf); @@ -1679,7 +1698,7 @@ sub object_list_changed { my $have_objects = @{$self->{objects}} ? 1 : 0; my $method = $have_objects ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method - for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode); + for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing); if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) { $self->{btn_reslice}->Disable; @@ -1712,6 +1731,7 @@ sub selection_changed { if ($self->{object_info_size}) { # have we already loaded the info pane? if ($have_sel) { my $model_object = $self->{model}->objects->[$obj_idx]; + $model_object->print_info; my $model_instance = $model_object->instances->[0]; $self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size})); $self->{object_info_materials}->SetLabel($model_object->materials_count); diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 07818a2b5..ce56c67b4 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -12,7 +12,7 @@ use base qw(Slic3r::GUI::3DScene Class::Accessor); sub new { my $class = shift; - my ($parent, $objects, $model, $config) = @_; + my ($parent, $objects, $model, $print, $config) = @_; my $self = $class->SUPER::new($parent); $self->enable_picking(1); @@ -22,6 +22,7 @@ sub new { $self->{objects} = $objects; $self->{model} = $model; + $self->{print} = $print; $self->{config} = $config; $self->{on_select_object} = sub {}; $self->{on_instances_moved} = sub {}; @@ -31,7 +32,7 @@ sub new { my $obj_idx = undef; if ($volume_idx != -1) { - $obj_idx = $self->object_idx($volume_idx); + $obj_idx = $self->volumes->[$volume_idx]->object_idx; } $self->{on_select_object}->($obj_idx) if $self->{on_select_object}; @@ -42,8 +43,8 @@ sub new { my %done = (); # prevent moving instances twice foreach my $volume_idx (@volume_idxs) { my $volume = $self->volumes->[$volume_idx]; - my $obj_idx = $self->object_idx($volume_idx); - my $instance_idx = $self->instance_idx($volume_idx); + my $obj_idx = $volume->object_idx; + my $instance_idx = $volume->instance_idx; next if $done{"${obj_idx}_${instance_idx}"}; $done{"${obj_idx}_${instance_idx}"} = 1; @@ -89,7 +90,7 @@ sub update { $self->update_bed_size; foreach my $obj_idx (0..$#{$self->{model}->objects}) { - my @volume_idxs = $self->load_object($self->{model}, $obj_idx); + my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx); if ($self->{objects}[$obj_idx]->selected) { $self->select_volume($_) for @volume_idxs; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 9e4b33976..7889b3d66 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -114,7 +114,7 @@ sub new { my $canvas; if ($Slic3r::GUI::have_OpenGL) { $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); - $canvas->load_object($self->{model_object}, undef, [0]); + $canvas->load_object($self->{model_object}, undef, undef, [0]); $canvas->set_auto_bed_shape; $canvas->SetSize([500,500]); $canvas->SetMinSize($canvas->GetSize); @@ -244,7 +244,7 @@ sub _update { } $self->{canvas}->reset_objects; - $self->{canvas}->load_object($_, undef, [0]) for @objects; + $self->{canvas}->load_object($_, undef, undef, [0]) for @objects; $self->{canvas}->SetCuttingPlane( $self->{cut_options}{z}, [@expolygons], diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 4882233a6..51a58366c 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -22,6 +22,7 @@ sub new { my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); my $object = $self->{model_object} = $params{model_object}; + my $print_object = $self->{print_object} = $params{print_object}; # create TreeCtrl my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100], @@ -82,7 +83,7 @@ sub new { $self->reload_tree($canvas->volume_idx($volume_idx)); }); - $canvas->load_object($self->{model_object}, undef, [0]); + $canvas->load_object($self->{model_object}, undef, undef, [0]); $canvas->set_auto_bed_shape; $canvas->SetSize([500,500]); $canvas->zoom_to_volumes; From e5b77e08de61a6cceeace813d27dc0a25852b0ae Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 18:55:04 +0100 Subject: [PATCH 47/82] Perl crazy macros strike again, they collide with boost threads. --- xs/src/xsinit.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index eafdf1522..79abc2f54 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -43,10 +43,14 @@ extern "C" { #undef do_close #undef bind #undef seed +#undef push +#undef pop #ifdef _MSC_VER // Undef some of the macros set by Perl , which cause compilation errors on Win32 - #undef send #undef connect + #undef seek + #undef send + #undef write #endif /* _MSC_VER */ } #endif From ce7717e4506c35a0eddc8876c188663d014c85e8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 18:59:35 +0100 Subject: [PATCH 48/82] Fix compilation on Linux. --- xs/src/libslic3r/SlicingAdaptive.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/SlicingAdaptive.cpp b/xs/src/libslic3r/SlicingAdaptive.cpp index d501d6f62..ff0da7636 100644 --- a/xs/src/libslic3r/SlicingAdaptive.cpp +++ b/xs/src/libslic3r/SlicingAdaptive.cpp @@ -67,7 +67,7 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet continue; // compute cusp-height for this facet and store minimum of all heights float normal_z = m_face_normal_z[ordered_id]; - height = std::min(height, (normal_z == 0) ? 9999 : abs(cusp_value / normal_z)); + height = std::min(height, (normal_z == 0.f) ? 9999.f : std::abs(cusp_value / normal_z)); } } From 06540f73f742da98986c1390a9c3ce0b141cbb24 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 19:13:33 +0100 Subject: [PATCH 49/82] Try to fix compilation problems due to Perl crazy macros. --- xs/src/libslic3r/utils.cpp | 54 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 0f298e954..b66d58489 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -1,3 +1,30 @@ +#include +#include +#include + +namespace Slic3r { + +static boost::log::trivial::severity_level logSeverity = boost::log::trivial::fatal; + +void set_logging_level(unsigned int level) +{ + switch (level) { + case 0: logSeverity = boost::log::trivial::fatal; break; + case 1: logSeverity = boost::log::trivial::error; break; + case 2: logSeverity = boost::log::trivial::warning; break; + case 3: logSeverity = boost::log::trivial::info; break; + case 4: logSeverity = boost::log::trivial::debug; break; + default: logSeverity = boost::log::trivial::trace; break; + } + + boost::log::core::get()->set_filter + ( + boost::log::trivial::severity >= logSeverity + ); +} + +} // namespace Slic3r + #ifdef SLIC3R_HAS_BROKEN_CROAK // Some Strawberry Perl builds (mainly the latest 64bit builds) have a broken mechanism @@ -66,30 +93,3 @@ confess_at(const char *file, int line, const char *func, } #endif - -#include -#include -#include - -namespace Slic3r { - -static boost::log::trivial::severity_level logSeverity = boost::log::trivial::fatal; - -void set_logging_level(unsigned int level) -{ - switch (level) { - case 0: logSeverity = boost::log::trivial::fatal; break; - case 1: logSeverity = boost::log::trivial::error; break; - case 2: logSeverity = boost::log::trivial::warning; break; - case 3: logSeverity = boost::log::trivial::info; break; - case 4: logSeverity = boost::log::trivial::debug; break; - default: logSeverity = boost::log::trivial::trace; break; - } - - boost::log::core::get()->set_filter - ( - boost::log::trivial::severity >= logSeverity - ); -} - -} // namespace Slic3r From e22d007ab7a49edd1633e9950729806cadfc0783 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 23:46:50 +0100 Subject: [PATCH 50/82] Fixed typo, missing class name. --- xs/src/libslic3r/Slicing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp index 82813770f..0de781c64 100644 --- a/xs/src/libslic3r/Slicing.cpp +++ b/xs/src/libslic3r/Slicing.cpp @@ -18,7 +18,7 @@ namespace Slic3r { -SlicingParameters create_from_config( +SlicingParameters SlicingParameters::create_from_config( const PrintConfig &print_config, const PrintObjectConfig &object_config, coordf_t object_height, From b2a5a1d22f67eba636d41f49c7684f2480fd3242 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Dec 2016 18:59:18 +0100 Subject: [PATCH 51/82] Added a move constructor / assignment operator to the old Clipper library PolyTree class. --- xs/src/clipper.hpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index c8009e64e..47de7b182 100644 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -153,17 +153,35 @@ private: PolyNode* GetNextSiblingUp() const; void AddChild(PolyNode& child); friend class Clipper; //to access Index - friend class ClipperOffset; + friend class ClipperOffset; + friend class PolyTree; //to implement the PolyTree::move operator }; class PolyTree: public PolyNode { public: - ~PolyTree(){Clear();}; + PolyTree() {} + PolyTree(PolyTree &&src) { *this = std::move(src); } + virtual ~PolyTree(){Clear();}; + PolyTree& operator=(PolyTree &&src) { + AllNodes = std::move(src.AllNodes); + Contour = std::move(src.Contour); + Childs = std::move(src.Childs); + Parent = nullptr; + Index = src.Index; + m_IsOpen = src.m_IsOpen; + m_jointype = src.m_jointype; + m_endtype = src.m_endtype; + for (size_t i = 0; i < Childs.size(); ++ i) + Childs[i]->Parent = this; + return *this; + } PolyNode* GetFirst() const; void Clear(); int Total() const; private: + PolyTree(const PolyTree &src) = delete; + PolyTree& operator=(const PolyTree &src) = delete; PolyNodes AllNodes; friend class Clipper; //to access AllNodes }; From 6582182e0c47ffd231c2d84808715a4209108fe2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Dec 2016 19:22:23 +0100 Subject: [PATCH 52/82] Adapted to the new ClipperUtils.hpp interface by @alexrj --- lib/Slic3r/Geometry/Clipper.pm | 6 +- lib/Slic3r/Print/Object.pm | 2 +- xs/src/libslic3r/BridgeDetector.cpp | 134 ++- xs/src/libslic3r/BridgeDetector.hpp | 1 - xs/src/libslic3r/ClipperUtils.cpp | 956 ++++++---------------- xs/src/libslic3r/ClipperUtils.hpp | 260 +++--- xs/src/libslic3r/EdgeGrid.hpp | 3 +- xs/src/libslic3r/ExPolygon.cpp | 29 +- xs/src/libslic3r/ExtrusionEntity.cpp | 18 +- xs/src/libslic3r/ExtrusionEntity.hpp | 20 + xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp | 2 +- xs/src/libslic3r/Fill/FillBase.cpp | 3 +- xs/src/libslic3r/Fill/FillConcentric.cpp | 2 +- xs/src/libslic3r/Fill/FillHoneycomb.cpp | 4 +- xs/src/libslic3r/Fill/FillPlanePath.cpp | 2 +- xs/src/libslic3r/Fill/FillRectilinear.cpp | 2 +- xs/src/libslic3r/GCode.cpp | 3 +- xs/src/libslic3r/Layer.cpp | 15 +- xs/src/libslic3r/LayerRegion.cpp | 3 +- xs/src/libslic3r/MotionPlanner.cpp | 8 +- xs/src/libslic3r/PerimeterGenerator.cpp | 43 +- xs/src/libslic3r/Polygon.cpp | 7 +- xs/src/libslic3r/Print.cpp | 11 +- xs/src/libslic3r/PrintObject.cpp | 14 +- xs/src/libslic3r/SupportMaterial.cpp | 33 +- xs/src/libslic3r/TriangleMesh.cpp | 17 +- xs/src/perlglue.cpp | 3 +- xs/t/11_clipper.t | 48 +- xs/xsp/Clipper.xsp | 69 +- xs/xsp/Polyline.xsp | 2 +- xs/xsp/Surface.xsp | 2 +- 31 files changed, 600 insertions(+), 1122 deletions(-) diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 16dfa6f6b..37fc89914 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -5,9 +5,9 @@ use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(offset offset_ex - diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER - JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex + diff_ex diff union_ex intersection_ex JT_ROUND JT_MITER + JT_SQUARE is_counter_clockwise offset2 offset2_ex intersection intersection_pl diff_pl union - union_pt_chained diff_ppl intersection_ppl); + union_pt_chained); 1; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 18768b828..720294cd0 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -7,7 +7,7 @@ use List::Util qw(min max sum first); use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path epsilon); use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex - offset offset_ex offset2 offset2_ex intersection_ppl JT_MITER); + offset offset_ex offset2 offset2_ex JT_MITER); use Slic3r::Print::State ':steps'; use Slic3r::Surface ':types'; diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/xs/src/libslic3r/BridgeDetector.cpp index 7e54bcf69..fa27134dc 100644 --- a/xs/src/libslic3r/BridgeDetector.cpp +++ b/xs/src/libslic3r/BridgeDetector.cpp @@ -40,13 +40,13 @@ void BridgeDetector::initialize() this->angle = -1.; // Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors. - Polygons grown = offset(this->expolygons, float(this->spacing)); + Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing)); // Detect possible anchoring edges of this bridging region. // Detect what edges lie on lower slices by turning bridge contour and holes // into polylines and then clipping them with each lower slice's contour. // Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()). - intersection(to_polylines(grown), this->lower_slices.contours(), &this->_edges); + this->_edges = intersection_pl(to_polylines(grown), this->lower_slices.contours()); #ifdef SLIC3R_DEBUG printf(" bridge has " PRINTF_ZU " support(s)\n", this->_edges.size()); @@ -117,7 +117,7 @@ BridgeDetector::detect_angle() double total_length = 0; double max_length = 0; { - Lines clipped_lines = intersection(lines, clip_area); + Lines clipped_lines = intersection_ln(lines, clip_area); for (size_t i = 0; i < clipped_lines.size(); ++i) { const Line &line = clipped_lines[i]; if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) { @@ -203,76 +203,72 @@ std::vector BridgeDetector::bridge_direction_candidates() const return angles; } -void -BridgeDetector::coverage(double angle, Polygons* coverage) const +Polygons BridgeDetector::coverage(double angle) const { - if (angle == -1) angle = this->angle; - if (angle == -1) return; + if (angle == -1) + angle = this->angle; - // Get anchors, convert them to Polygons and rotate them. - Polygons anchors = to_polygons(this->_anchor_regions); - polygons_rotate(anchors, PI/2.0 - angle); - Polygons covered; - for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) - { - // Clone our expolygon and rotate it so that we work with vertical lines. - ExPolygon expolygon = *it_expoly; - expolygon.rotate(PI/2.0 - angle); + + if (angle != -1) { + + // Get anchors, convert them to Polygons and rotate them. + Polygons anchors = to_polygons(this->_anchor_regions); + polygons_rotate(anchors, PI/2.0 - angle); - /* Outset the bridge expolygon by half the amount we used for detecting anchors; - we'll use this one to generate our trapezoids and be sure that their vertices - are inside the anchors and not on their contours leading to false negatives. */ - ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing)); - - // Compute trapezoids according to a vertical orientation - Polygons trapezoids; - for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) - it->get_trapezoids2(&trapezoids, PI/2.0); - - for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { - Lines supported = intersection(trapezoid->lines(), anchors); - size_t n_supported = 0; - // not nice, we need a more robust non-numeric check - for (size_t i = 0; i < supported.size(); ++i) - if (supported[i].length() >= this->spacing) - ++ n_supported; - if (n_supported >= 2) - covered.push_back(STDMOVE(*trapezoid)); + for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) + { + // Clone our expolygon and rotate it so that we work with vertical lines. + ExPolygon expolygon = *it_expoly; + expolygon.rotate(PI/2.0 - angle); + + /* Outset the bridge expolygon by half the amount we used for detecting anchors; + we'll use this one to generate our trapezoids and be sure that their vertices + are inside the anchors and not on their contours leading to false negatives. */ + ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing)); + + // Compute trapezoids according to a vertical orientation + Polygons trapezoids; + for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) + it->get_trapezoids2(&trapezoids, PI/2.0); + + for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { + Lines supported = intersection_ln(trapezoid->lines(), anchors); + size_t n_supported = 0; + // not nice, we need a more robust non-numeric check + for (size_t i = 0; i < supported.size(); ++i) + if (supported[i].length() >= this->spacing) + ++ n_supported; + if (n_supported >= 2) + covered.push_back(STDMOVE(*trapezoid)); + } } + + // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids + // instead of exact overlaps. + covered = union_(covered); + + // Intersect trapezoids with actual bridge area to remove extra margins and append it to result. + polygons_rotate(covered, -(PI/2.0 - angle)); + covered = intersection(covered, to_polygons(this->expolygons)); + + /* + if (0) { + my @lines = map @{$_->lines}, @$trapezoids; + $_->rotate(-(PI/2 - $angle), [0,0]) for @lines; + + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "coverage_" . rad2deg($angle) . ".svg", + expolygons => [$self->expolygon], + green_expolygons => $self->_anchor_regions, + red_expolygons => $coverage, + lines => \@lines, + ); + } + */ } - - // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids - // instead of exact overlaps. - covered = union_(covered); - - // Intersect trapezoids with actual bridge area to remove extra margins and append it to result. - polygons_rotate(covered, -(PI/2.0 - angle)); - intersection(covered, to_polygons(this->expolygons), coverage); - - /* - if (0) { - my @lines = map @{$_->lines}, @$trapezoids; - $_->rotate(-(PI/2 - $angle), [0,0]) for @lines; - - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "coverage_" . rad2deg($angle) . ".svg", - expolygons => [$self->expolygon], - green_expolygons => $self->_anchor_regions, - red_expolygons => $coverage, - lines => \@lines, - ); - } - */ -} - -Polygons -BridgeDetector::coverage(double angle) const -{ - Polygons pp; - this->coverage(angle, &pp); - return pp; + return covered; } /* This method returns the bridge edges (as polylines) that are not supported @@ -288,9 +284,7 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) { // get unsupported bridge edges (both contour and holes) - Polylines unuspported_polylines; - diff(to_polylines(*it_expoly), grown_lower, &unuspported_polylines); - Lines unsupported_lines = to_lines(unuspported_polylines); + Lines unsupported_lines = to_lines(diff_pl(to_polylines(*it_expoly), grown_lower)); /* Split into individual segments and filter out edges parallel to the bridging angle TODO: angle tolerance should probably be based on segment length and flow width, so that we build supports whenever there's a chance that at least one or two bridge diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/xs/src/libslic3r/BridgeDetector.hpp index 825ce10b6..b04db63e4 100644 --- a/xs/src/libslic3r/BridgeDetector.hpp +++ b/xs/src/libslic3r/BridgeDetector.hpp @@ -32,7 +32,6 @@ public: BridgeDetector(ExPolygon _expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); bool detect_angle(); - void coverage(double angle, Polygons* coverage) const; Polygons coverage(double angle = -1) const; void unsupported_edges(double angle, Polylines* unsupported) const; Polylines unsupported_edges(double angle = -1) const; diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 5cc143540..e0de33586 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -25,144 +25,35 @@ bool clipper_export_enabled = false; // For debugging the Clipper library, for providing bug reports to the Clipper author. bool export_clipper_input_polygons_bin(const char *path, const ClipperLib::Paths &input_subject, const ClipperLib::Paths &input_clip) { - FILE *pfile = fopen(path, "wb"); - if (pfile == NULL) - return false; + FILE *pfile = fopen(path, "wb"); + if (pfile == NULL) + return false; - uint32_t sz = uint32_t(input_subject.size()); - fwrite(&sz, 1, sizeof(sz), pfile); - for (size_t i = 0; i < input_subject.size(); ++i) { - const ClipperLib::Path &path = input_subject[i]; - sz = uint32_t(path.size()); - ::fwrite(&sz, 1, sizeof(sz), pfile); - ::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile); - } - sz = uint32_t(input_clip.size()); - ::fwrite(&sz, 1, sizeof(sz), pfile); - for (size_t i = 0; i < input_clip.size(); ++i) { - const ClipperLib::Path &path = input_clip[i]; - sz = uint32_t(path.size()); - ::fwrite(&sz, 1, sizeof(sz), pfile); - ::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile); - } - ::fclose(pfile); - return true; + uint32_t sz = uint32_t(input_subject.size()); + fwrite(&sz, 1, sizeof(sz), pfile); + for (size_t i = 0; i < input_subject.size(); ++i) { + const ClipperLib::Path &path = input_subject[i]; + sz = uint32_t(path.size()); + ::fwrite(&sz, 1, sizeof(sz), pfile); + ::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile); + } + sz = uint32_t(input_clip.size()); + ::fwrite(&sz, 1, sizeof(sz), pfile); + for (size_t i = 0; i < input_clip.size(); ++i) { + const ClipperLib::Path &path = input_clip[i]; + sz = uint32_t(path.size()); + ::fwrite(&sz, 1, sizeof(sz), pfile); + ::fwrite(path.data(), sizeof(ClipperLib::IntPoint), sz, pfile); + } + ::fclose(pfile); + return true; err: - ::fclose(pfile); - return false; + ::fclose(pfile); + return false; } #endif /* CLIPPER_UTILS_DEBUG */ -//----------------------------------------------------------- -// legacy code from Clipper documentation -void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons* expolygons) -{ - size_t cnt = expolygons->size(); - expolygons->resize(cnt + 1); - ClipperPath_to_Slic3rMultiPoint(polynode.Contour, &(*expolygons)[cnt].contour); - (*expolygons)[cnt].holes.resize(polynode.ChildCount()); - for (int i = 0; i < polynode.ChildCount(); ++i) - { - ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour, &(*expolygons)[cnt].holes[i]); - //Add outer polygons contained by (nested within) holes ... - for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j) - AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons); - } -} - -void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons* expolygons) -{ - PROFILE_FUNC(); - expolygons->clear(); - for (int i = 0; i < polytree.ChildCount(); ++i) - AddOuterPolyNodeToExPolygons(*polytree.Childs[i], expolygons); -} -//----------------------------------------------------------- - -template -void -ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output) -{ - PROFILE_FUNC(); - output->points.clear(); - output->points.reserve(input.size()); - for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) - output->points.push_back(Slic3r::Point( (*pit).X, (*pit).Y )); -} -template void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, Slic3r::Polygon* output); - -template -void -ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output) -{ - PROFILE_FUNC(); - output->clear(); - output->reserve(input.size()); - for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) { - typename T::value_type p; - ClipperPath_to_Slic3rMultiPoint(*it, &p); - output->push_back(p); - } -} - -void -ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output) -{ - PROFILE_FUNC(); - - // init Clipper - ClipperLib::Clipper clipper; - clipper.Clear(); - - // perform union - clipper.AddPaths(input, ClipperLib::ptSubject, true); - ClipperLib::PolyTree polytree; - clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // offset results work with both EvenOdd and NonZero - - // write to ExPolygons object - output->clear(); - PolyTreeToExPolygons(polytree, output); -} - -void -Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output) -{ - PROFILE_FUNC(); - - output->clear(); - output->reserve(input.points.size()); - for (Slic3r::Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) - output->push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); -} - -void -Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input, ClipperLib::Path* output) -{ - PROFILE_FUNC(); - - output->clear(); - output->reserve(input.points.size()); - for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit) - output->push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); -} - -template -void -Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output) -{ - PROFILE_FUNC(); - - output->clear(); - output->reserve(input.size()); - for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) { - // std::vector< IntPoint >, IntPoint is a pair of int64_t - ClipperLib::Path p; - Slic3rMultiPoint_to_ClipperPath(*it, &p); - output->push_back(p); - } -} - void scaleClipperPolygon(ClipperLib::Path &polygon) { PROFILE_FUNC(); @@ -205,130 +96,129 @@ void unscaleClipperPolygons(ClipperLib::Paths &polygons) } } -void -offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta, - ClipperLib::JoinType joinType, double miterLimit) +//----------------------------------------------------------- +// legacy code from Clipper documentation +void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, ExPolygons* expolygons) +{ + size_t cnt = expolygons->size(); + expolygons->resize(cnt + 1); + (*expolygons)[cnt].contour = ClipperPath_to_Slic3rMultiPoint(polynode.Contour); + (*expolygons)[cnt].holes.resize(polynode.ChildCount()); + for (int i = 0; i < polynode.ChildCount(); ++i) + { + (*expolygons)[cnt].holes[i] = ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour); + //Add outer polygons contained by (nested within) holes ... + for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j) + AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons); + } +} + +ExPolygons +PolyTreeToExPolygons(ClipperLib::PolyTree& polytree) { - PROFILE_FUNC(); - // read input - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polygons, &input); + ExPolygons retval; + for (int i = 0; i < polytree.ChildCount(); ++i) + AddOuterPolyNodeToExPolygons(*polytree.Childs[i], &retval); + return retval; +} +//----------------------------------------------------------- + +template +T +ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input) +{ + T retval; + for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) + retval.points.push_back(Point( (*pit).X, (*pit).Y )); + return retval; +} + +template +T +ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input) +{ + T retval; + for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) + retval.push_back(ClipperPath_to_Slic3rMultiPoint(*it)); + return retval; +} + +ExPolygons +ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input) +{ + // init Clipper + ClipperLib::Clipper clipper; + clipper.Clear(); + // perform union + clipper.AddPaths(input, ClipperLib::ptSubject, true); + ClipperLib::PolyTree polytree; + clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // offset results work with both EvenOdd and NonZero + + // write to ExPolygons object + return PolyTreeToExPolygons(polytree); +} + +ClipperLib::Path +Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input) +{ + ClipperLib::Path retval; + for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) + retval.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); + return retval; +} + +ClipperLib::Path +Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input) +{ + ClipperLib::Path output; + output.reserve(input.points.size()); + for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit) + output.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); + return output; +} + +template +ClipperLib::Paths +Slic3rMultiPoints_to_ClipperPaths(const T &input) +{ + ClipperLib::Paths retval; + for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) + retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it)); + return retval; +} + +ClipperLib::Paths _offset(ClipperLib::Paths &input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) +{ // scale input scaleClipperPolygons(input); // perform offset ClipperLib::ClipperOffset co; - if (joinType == jtRound) { - co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); - } else { + if (joinType == jtRound) + co.ArcTolerance = miterLimit; + else co.MiterLimit = miterLimit; - } - { - PROFILE_BLOCK(offset_AddPaths); - co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); - } - { - PROFILE_BLOCK(offset_Execute); - co.Execute(*retval, delta * float(CLIPPER_OFFSET_SCALE)); - } + co.AddPaths(input, joinType, endType); + ClipperLib::Paths retval; + co.Execute(retval, delta * float(CLIPPER_OFFSET_SCALE)); // unscale output - unscaleClipperPolygons(*retval); + unscaleClipperPolygons(retval); + return retval; } -void -offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta, - ClipperLib::JoinType joinType, double miterLimit) +ClipperLib::Paths _offset(ClipperLib::Path &input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) { - // perform offset - ClipperLib::Paths output; - offset(polygons, &output, delta, joinType, miterLimit); - - // convert into ExPolygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -Slic3r::Polygons -offset(const Slic3r::Polygons &polygons, const float delta, - ClipperLib::JoinType joinType, double miterLimit) -{ - Slic3r::Polygons pp; - offset(polygons, &pp, delta, joinType, miterLimit); - return pp; -} - -void -offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, - ClipperLib::JoinType joinType, double miterLimit) -{ - // read input - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polylines, &input); - - // scale input - scaleClipperPolygons(input); - - // perform offset - ClipperLib::ClipperOffset co; - if (joinType == jtRound) { - co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); - } else { - co.MiterLimit = miterLimit; - } - co.AddPaths(input, joinType, ClipperLib::etOpenButt); - co.Execute(*retval, delta * float(CLIPPER_OFFSET_SCALE)); - - // unscale output - unscaleClipperPolygons(*retval); -} - -void -offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta, - ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset(polylines, &output, delta, joinType, miterLimit); - - // convert into ExPolygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -void -offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta, - ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - Slic3r::ExPolygons expp; - offset(surface.expolygon, &expp, delta, joinType, miterLimit); - - // clone the input surface for each expolygon we got - retval->clear(); - retval->reserve(expp.size()); - for (ExPolygons::iterator it = expp.begin(); it != expp.end(); ++it) { - Surface s = surface; // clone - s.expolygon = *it; - retval->push_back(s); - } -} - -void -offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta, - ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset(polygons, &output, delta, joinType, miterLimit); - - // convert into ExPolygons - ClipperPaths_to_Slic3rExPolygons(output, retval); + ClipperLib::Paths paths; + paths.push_back(input); + return _offset(paths, endType, delta, joinType, miterLimit); } // This is a safe variant of the polygon offset, tailored for a single ExPolygon: // a single polygon with multiple non-overlapping holes. // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours. -void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta, +ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit) { // printf("new ExPolygon offset\n"); @@ -336,8 +226,7 @@ void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); ClipperLib::Paths contours; { - ClipperLib::Path input; - Slic3rMultiPoint_to_ClipperPath(expolygon.contour, &input); + ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(expolygon.contour); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) @@ -353,8 +242,7 @@ void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const { holes.reserve(expolygon.holes.size()); for (Polygons::const_iterator it_hole = expolygon.holes.begin(); it_hole != expolygon.holes.end(); ++ it_hole) { - ClipperLib::Path input; - Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole, &input); + ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) @@ -375,43 +263,18 @@ void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const clipper.Clear(); clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(holes, ClipperLib::ptClip, true); - clipper.Execute(ClipperLib::ctDifference, *retval, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } // 4) Unscale the output. - unscaleClipperPolygons(*retval); -} - -Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, - ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset(expolygon, &output, delta, joinType, miterLimit); - - // convert into ExPolygons - Slic3r::Polygons retval; - ClipperPaths_to_Slic3rMultiPoints(output, &retval); - return retval; -} - -Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, - ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset(expolygon, &output, delta, joinType, miterLimit); - - // convert into ExPolygons - Slic3r::ExPolygons retval; - ClipperPaths_to_Slic3rExPolygons(output, &retval); - return retval; + unscaleClipperPolygons(output); + return output; } // This is a safe variant of the polygon offset, tailored for a single ExPolygon: // a single polygon with multiple non-overlapping holes. // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours. -void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta, +ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit) { // printf("new ExPolygon offset\n"); @@ -429,8 +292,7 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con for (Slic3r::ExPolygons::const_iterator it_expoly = expolygons.begin(); it_expoly != expolygons.end(); ++ it_expoly) { // 1) Offset the outer contour. { - ClipperLib::Path input; - Slic3rMultiPoint_to_ClipperPath(it_expoly->contour, &input); + ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(it_expoly->contour); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) @@ -446,8 +308,7 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con // 2) Offset the holes one by one, collect the results. { for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) { - ClipperLib::Path input; - Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole, &input); + ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) @@ -469,71 +330,20 @@ void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, con clipper.Clear(); clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(holes, ClipperLib::ptClip, true); - clipper.Execute(ClipperLib::ctDifference, *retval, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } // 4) Unscale the output. - unscaleClipperPolygons(*retval); + unscaleClipperPolygons(output); + return output; } -Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, - ClipperLib::JoinType joinType, double miterLimit) +ClipperLib::Paths +_offset2(const Polygons &polygons, const float delta1, const float delta2, + const ClipperLib::JoinType joinType, const double miterLimit) { - // perform offset - ClipperLib::Paths output; - offset(expolygons, &output, delta, joinType, miterLimit); - - // convert into ExPolygons - Slic3r::Polygons retval; - ClipperPaths_to_Slic3rMultiPoints(output, &retval); - return retval; -} - -Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, - ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset(expolygons, &output, delta, joinType, miterLimit); - - // convert into ExPolygons - Slic3r::ExPolygons retval; - ClipperPaths_to_Slic3rExPolygons(output, &retval); - return retval; -} - -Slic3r::ExPolygons -offset_ex(const Slic3r::Polygons &polygons, const float delta, - ClipperLib::JoinType joinType, double miterLimit) -{ - Slic3r::ExPolygons expp; - offset(polygons, &expp, delta, joinType, miterLimit); - return expp; -} - -void -offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, - const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) -{ - if (delta1 * delta2 >= 0) { - // Both deltas are the same signum - offset(polygons, retval, delta1 + delta2, joinType, miterLimit); - return; - } -#ifdef CLIPPER_UTILS_DEBUG - BoundingBox bbox = get_extents(polygons); - coordf_t stroke_width = scale_(0.005); - static int iRun = 0; - ++ iRun; - bool flipY = false; - SVG svg(debug_out_path("offset2-%d.svg", iRun), bbox, scale_(1.), flipY); - for (Slic3r::Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it) - svg.draw(it->lines(), "gray", stroke_width); -#endif /* CLIPPER_UTILS_DEBUG */ - // read input - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polygons, &input); + ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polygons); // scale input scaleClipperPolygons(input); @@ -541,7 +351,7 @@ offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float // prepare ClipperOffset object ClipperLib::ClipperOffset co; if (joinType == jtRound) { - co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); + co.ArcTolerance = miterLimit; } else { co.MiterLimit = miterLimit; } @@ -550,74 +360,48 @@ offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float ClipperLib::Paths output1; co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); co.Execute(output1, delta1 * float(CLIPPER_OFFSET_SCALE)); -#ifdef CLIPPER_UTILS_DEBUG - svg.draw(output1, 1. / double(CLIPPER_OFFSET_SCALE), "red", stroke_width); -#endif /* CLIPPER_UTILS_DEBUG */ // perform second offset co.Clear(); co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon); - co.Execute(*retval, delta2 * float(CLIPPER_OFFSET_SCALE)); -#ifdef CLIPPER_UTILS_DEBUG - svg.draw(*retval, 1. / double(CLIPPER_OFFSET_SCALE), "green", stroke_width); -#endif /* CLIPPER_UTILS_DEBUG */ - + ClipperLib::Paths retval; + co.Execute(retval, delta2 * float(CLIPPER_OFFSET_SCALE)); + // unscale output - unscaleClipperPolygons(*retval); + unscaleClipperPolygons(retval); + return retval; } -void -offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1, - const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) +Polygons +offset2(const Polygons &polygons, const float delta1, const float delta2, + const ClipperLib::JoinType joinType, const double miterLimit) { // perform offset - ClipperLib::Paths output; - offset2(polygons, &output, delta1, delta2, joinType, miterLimit); + ClipperLib::Paths output = _offset2(polygons, delta1, delta2, joinType, miterLimit); // convert into ExPolygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); + return ClipperPaths_to_Slic3rMultiPoints(output); } -Slic3r::Polygons -offset2(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) -{ - Slic3r::Polygons pp; - offset2(polygons, &pp, delta1, delta2, joinType, miterLimit); - return pp; -} - -void -offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1, - const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) +ExPolygons +offset2_ex(const Polygons &polygons, const float delta1, const float delta2, + const ClipperLib::JoinType joinType, const double miterLimit) { // perform offset - ClipperLib::Paths output; - offset2(polygons, &output, delta1, delta2, joinType, miterLimit); + ClipperLib::Paths output = _offset2(polygons, delta1, delta2, joinType, miterLimit); // convert into ExPolygons - ClipperPaths_to_Slic3rExPolygons(output, retval); -} - -Slic3r::ExPolygons -offset2_ex(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) -{ - Slic3r::ExPolygons expp; - offset2(polygons, &expp, delta1, delta2, joinType, miterLimit); - return expp; + return ClipperPaths_to_Slic3rExPolygons(output); } template -void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, T* retval, const ClipperLib::PolyFillType fillType, const bool safety_offset_) +T +_clipper_do(const ClipperLib::ClipType clipType, const Polygons &subject, + const Polygons &clip, const ClipperLib::PolyFillType fillType, const bool safety_offset_) { - PROFILE_BLOCK(_clipper_do_polygons); - // read input - ClipperLib::Paths input_subject, input_clip; - Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); - Slic3rMultiPoints_to_ClipperPaths(clip, &input_clip); + ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); + ClipperLib::Paths input_clip = Slic3rMultiPoints_to_ClipperPaths(clip); // perform safety offset if (safety_offset_) { @@ -633,36 +417,23 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &su clipper.Clear(); // add polygons - { - PROFILE_BLOCK(_clipper_do_polygons_AddPaths); - clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); - clipper.AddPaths(input_clip, ClipperLib::ptClip, true); - -#ifdef CLIPPER_UTILS_DEBUG - if (clipper_export_enabled) { - static int iRun = 0; - export_clipper_input_polygons_bin(debug_out_path("_clipper_do_polygons_AddPaths-polygons-%d", ++iRun).c_str(), input_subject, input_clip); - } -#endif /* CLIPPER_UTILS_DEBUG */ - } + clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); + clipper.AddPaths(input_clip, ClipperLib::ptClip, true); // perform operation - { - PROFILE_BLOCK(_clipper_do_polygons_Execute); - clipper.Execute(clipType, *retval, fillType, fillType); - } + T retval; + clipper.Execute(clipType, retval, fillType, fillType); + return retval; } -void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, ClipperLib::PolyTree* retval, const ClipperLib::PolyFillType fillType, +ClipperLib::PolyTree +_clipper_do(const ClipperLib::ClipType clipType, const Polylines &subject, + const Polygons &clip, const ClipperLib::PolyFillType fillType, const bool safety_offset_) { - PROFILE_BLOCK(_clipper_do_polylines); - // read input - ClipperLib::Paths input_subject, input_clip; - Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); - Slic3rMultiPoints_to_ClipperPaths(clip, &input_clip); + ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); + ClipperLib::Paths input_clip = Slic3rMultiPoints_to_ClipperPaths(clip); // perform safety offset if (safety_offset_) safety_offset(&input_clip); @@ -672,298 +443,126 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &s clipper.Clear(); // add polygons - { - PROFILE_BLOCK(_clipper_do_polylines_AddPaths); - clipper.AddPaths(input_subject, ClipperLib::ptSubject, false); - clipper.AddPaths(input_clip, ClipperLib::ptClip, true); - } + clipper.AddPaths(input_subject, ClipperLib::ptSubject, false); + clipper.AddPaths(input_clip, ClipperLib::ptClip, true); // perform operation - { - PROFILE_BLOCK(_clipper_do_polylines_Execute); - clipper.Execute(clipType, *retval, fillType, fillType); - } + ClipperLib::PolyTree retval; + clipper.Execute(clipType, retval, fillType, fillType); + return retval; } -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_) +Polygons +_clipper(ClipperLib::ClipType clipType, const Polygons &subject, + const Polygons &clip, bool safety_offset_) +{ + return ClipperPaths_to_Slic3rMultiPoints(_clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_)); +} + +ExPolygons +_clipper_ex(ClipperLib::ClipType clipType, const Polygons &subject, + const Polygons &clip, bool safety_offset_) +{ + return PolyTreeToExPolygons(_clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_)); +} + +Polylines +_clipper_pl(ClipperLib::ClipType clipType, const Polylines &subject, + const Polygons &clip, bool safety_offset_) { - PROFILE_FUNC(); - // perform operation ClipperLib::Paths output; - _clipper_do(clipType, subject, clip, &output, ClipperLib::pftNonZero, safety_offset_); - - // convert into Polygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); + ClipperLib::PolyTreeToPaths(_clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_), output); + return ClipperPaths_to_Slic3rMultiPoints(output); } -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_) -{ - PROFILE_FUNC(); - // perform operation - ClipperLib::PolyTree polytree; - _clipper_do(clipType, subject, clip, &polytree, ClipperLib::pftNonZero, safety_offset_); - - // convert into ExPolygons - PolyTreeToExPolygons(polytree, retval); -} - -void _clipper_pl(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_) -{ - PROFILE_FUNC(); - // perform operation - ClipperLib::PolyTree polytree; - _clipper_do(clipType, subject, clip, &polytree, ClipperLib::pftNonZero, safety_offset_); - - // convert into Polylines - ClipperLib::Paths output; - ClipperLib::PolyTreeToPaths(polytree, output); - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -void _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, - const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_) -{ - // convert Lines to Polylines - Slic3r::Polylines polylines; - polylines.reserve(subject.size()); - for (Slic3r::Lines::const_iterator line = subject.begin(); line != subject.end(); ++line) - polylines.push_back(*line); - - // perform operation - _clipper_pl(clipType, polylines, clip, &polylines, safety_offset_); - - // convert Polylines to Lines - for (Slic3r::Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) - retval->push_back(*polyline); -} - -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_) +Polylines +_clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, + const Polygons &clip, bool safety_offset_) { // transform input polygons into polylines - Slic3r::Polylines polylines; + Polylines polylines; polylines.reserve(subject.size()); - for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon) + for (Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon) polylines.push_back(*polygon); // implicit call to split_at_first_point() // perform clipping - _clipper_pl(clipType, polylines, clip, retval, safety_offset_); + Polylines retval = _clipper_pl(clipType, polylines, clip, safety_offset_); /* If the split_at_first_point() call above happens to split the polygon inside the clipping area we would get two consecutive polylines instead of a single one, so we go through them in order to recombine continuous polylines. */ - for (size_t i = 0; i < retval->size(); ++i) { - for (size_t j = i+1; j < retval->size(); ++j) { - if ((*retval)[i].points.back().coincides_with((*retval)[j].points.front())) { + for (size_t i = 0; i < retval.size(); ++i) { + for (size_t j = i+1; j < retval.size(); ++j) { + if (retval[i].points.back().coincides_with(retval[j].points.front())) { /* If last point of i coincides with first point of j, append points of j to i and delete j */ - (*retval)[i].points.insert((*retval)[i].points.end(), (*retval)[j].points.begin()+1, (*retval)[j].points.end()); - retval->erase(retval->begin() + j); + retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end()); + retval.erase(retval.begin() + j); --j; - } else if ((*retval)[i].points.front().coincides_with((*retval)[j].points.back())) { + } else if (retval[i].points.front().coincides_with(retval[j].points.back())) { /* If first point of i coincides with last point of j, prepend points of j to i and delete j */ - (*retval)[i].points.insert((*retval)[i].points.begin(), (*retval)[j].points.begin(), (*retval)[j].points.end()-1); - retval->erase(retval->begin() + j); + retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1); + retval.erase(retval.begin() + j); --j; - } else if ((*retval)[i].points.front().coincides_with((*retval)[j].points.front())) { + } else if (retval[i].points.front().coincides_with(retval[j].points.front())) { /* Since Clipper does not preserve orientation of polylines, also check the case when first point of i coincides with first point of j. */ - (*retval)[j].reverse(); - (*retval)[i].points.insert((*retval)[i].points.begin(), (*retval)[j].points.begin(), (*retval)[j].points.end()-1); - retval->erase(retval->begin() + j); + retval[j].reverse(); + retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1); + retval.erase(retval.begin() + j); --j; - } else if ((*retval)[i].points.back().coincides_with((*retval)[j].points.back())) { + } else if (retval[i].points.back().coincides_with(retval[j].points.back())) { /* Since Clipper does not preserve orientation of polylines, also check the case when last point of i coincides with last point of j. */ - (*retval)[j].reverse(); - (*retval)[i].points.insert((*retval)[i].points.end(), (*retval)[j].points.begin()+1, (*retval)[j].points.end()); - retval->erase(retval->begin() + j); + retval[j].reverse(); + retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end()); + retval.erase(retval.begin() + j); --j; } } - } -} - -template -void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_) -{ - _clipper(ClipperLib::ctDifference, subject, clip, retval, safety_offset_); -} -template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); -template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); -template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); - -template -void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_) -{ - Slic3r::Polygons pp; - for (Slic3r::ExPolygons::const_iterator ex = clip.begin(); ex != clip.end(); ++ex) { - Slic3r::Polygons ppp = *ex; - pp.insert(pp.end(), ppp.begin(), ppp.end()); } - diff(subject, pp, retval, safety_offset_); -} -template void diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); - -template -void diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_) -{ - Slic3r::Polygons pp; - for (Slic3r::ExPolygons::const_iterator ex = subject.begin(); ex != subject.end(); ++ex) { - Slic3r::Polygons ppp = *ex; - pp.insert(pp.end(), ppp.begin(), ppp.end()); - } - diff(pp, clip, retval, safety_offset_); -} - -Slic3r::Polygons -diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_) -{ - Slic3r::Polygons pp; - diff(subject, clip, &pp, safety_offset_); - return pp; -} - -template -Slic3r::ExPolygons -diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_) -{ - Slic3r::ExPolygons expp; - diff(subject, clip, &expp, safety_offset_); - return expp; -} -template Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_); -template Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_); - -template -void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_) -{ - _clipper(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_); -} -template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); -template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); -template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); - -template -SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_) -{ - SubjectType pp; - intersection(subject, clip, &pp, safety_offset_); - return pp; -} - -template Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template Slic3r::Polylines intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template Slic3r::Lines intersection(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_); - -Slic3r::ExPolygons -intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_) -{ - Slic3r::ExPolygons expp; - intersection(subject, clip, &expp, safety_offset_); - return expp; -} - -template -bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_) -{ - SubjectType retval; - intersection(subject, clip, &retval, safety_offset_); - return !retval.empty(); -} -template bool intersects(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template bool intersects(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template bool intersects(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_); - -void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, - bool safety_offset_) -{ - _clipper(ClipperLib::ctXor, subject, clip, retval, safety_offset_); -} - -template -void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_) -{ - Slic3r::Polygons p; - _clipper(ClipperLib::ctUnion, subject, p, retval, safety_offset_); -} -template void union_(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool safety_offset_); -template void union_(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_); - -Slic3r::Polygons -union_(const Slic3r::Polygons &subject, bool safety_offset) -{ - Polygons pp; - union_(subject, &pp, safety_offset); - return pp; -} - -Slic3r::ExPolygons -union_ex(const Slic3r::Polygons &subject, bool safety_offset) -{ - ExPolygons expp; - union_(subject, &expp, safety_offset); - return expp; -} - -Slic3r::ExPolygons -union_ex(const Slic3r::Surfaces &subject, bool safety_offset) -{ - Polygons pp; - for (Slic3r::Surfaces::const_iterator s = subject.begin(); s != subject.end(); ++s) { - Polygons spp = *s; - pp.insert(pp.end(), spp.begin(), spp.end()); - } - return union_ex(pp, safety_offset); -} - -void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset) -{ - Polygons pp = subject1; - pp.insert(pp.end(), subject2.begin(), subject2.end()); - union_(pp, retval, safety_offset); -} - -Slic3r::Polygons -union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset) -{ - Polygons pp; - for (Slic3r::ExPolygons::const_iterator it = subject1.begin(); it != subject1.end(); ++it) { - Polygons spp = *it; - pp.insert(pp.end(), spp.begin(), spp.end()); - } - for (Slic3r::ExPolygons::const_iterator it = subject2.begin(); it != subject2.end(); ++it) { - Polygons spp = *it; - pp.insert(pp.end(), spp.begin(), spp.end()); - } - Polygons retval; - union_(pp, &retval, safety_offset); return retval; } -void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_) +Lines +_clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons &clip, + bool safety_offset_) { - Slic3r::Polygons clip; - _clipper_do(ClipperLib::ctUnion, subject, clip, retval, ClipperLib::pftEvenOdd, safety_offset_); + // convert Lines to Polylines + Polylines polylines; + polylines.reserve(subject.size()); + for (Lines::const_iterator line = subject.begin(); line != subject.end(); ++line) + polylines.push_back(*line); + + // perform operation + polylines = _clipper_pl(clipType, polylines, clip, safety_offset_); + + // convert Polylines to Lines + Lines retval; + for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) + retval.push_back(*polyline); + return retval; } -void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_) +ClipperLib::PolyTree +union_pt(const Polygons &subject, bool safety_offset_) { - ClipperLib::PolyTree pt; - union_pt(subject, &pt, safety_offset_); - if (&subject == retval) - // It is safe to use the same variable for input and output, because this function makes - // a temporary copy of the results. - retval->clear(); - traverse_pt(pt.Childs, retval); + return _clipper_do(ClipperLib::ctUnion, subject, Polygons(), ClipperLib::pftEvenOdd, safety_offset_); } -static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval) +Polygons +union_pt_chained(const Polygons &subject, bool safety_offset_) +{ + ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_); + + Polygons retval; + traverse_pt(polytree.Childs, &retval); + return retval; +} + +void +traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval) { /* use a nearest neighbor search to order these children TODO: supply start_near to chained_path() too? */ @@ -985,21 +584,19 @@ static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval) // traverse the next depth traverse_pt((*it)->Childs, retval); - Polygon p; - ClipperPath_to_Slic3rMultiPoint((*it)->Contour, &p); + Polygon p = ClipperPath_to_Slic3rMultiPoint((*it)->Contour); retval->push_back(p); if ((*it)->IsHole()) retval->back().reverse(); // ccw } } -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear) +Polygons +simplify_polygons(const Polygons &subject, bool preserve_collinear) { - PROFILE_FUNC(); - // convert into Clipper polygons - ClipperLib::Paths input_subject, output; - Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); + ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); + ClipperLib::Paths output; if (preserve_collinear) { ClipperLib::Clipper c; c.PreserveCollinear(true); @@ -1011,23 +608,18 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval } // convert into Slic3r polygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); + return ClipperPaths_to_Slic3rMultiPoints(output); } -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear) +ExPolygons +simplify_polygons_ex(const Polygons &subject, bool preserve_collinear) { - PROFILE_FUNC(); - if (!preserve_collinear) { - Polygons polygons; - simplify_polygons(subject, &polygons, preserve_collinear); - union_(polygons, retval); - return; + return union_ex(simplify_polygons(subject, preserve_collinear)); } // convert into Clipper polygons - ClipperLib::Paths input_subject; - Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); + ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); ClipperLib::PolyTree polytree; @@ -1038,7 +630,7 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retv c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); // convert into ExPolygons - PolyTreeToExPolygons(polytree, retval); + return PolyTreeToExPolygons(polytree); } void safety_offset(ClipperLib::Paths* paths) @@ -1051,15 +643,15 @@ void safety_offset(ClipperLib::Paths* paths) // perform offset (delta = scale 1e-05) ClipperLib::ClipperOffset co; #ifdef CLIPPER_UTILS_DEBUG - if (clipper_export_enabled) { - static int iRun = 0; - export_clipper_input_polygons_bin(debug_out_path("safety_offset-polygons-%d", ++iRun).c_str(), *paths, ClipperLib::Paths()); - } + if (clipper_export_enabled) { + static int iRun = 0; + export_clipper_input_polygons_bin(debug_out_path("safety_offset-polygons-%d", ++iRun).c_str(), *paths, ClipperLib::Paths()); + } #endif /* CLIPPER_UTILS_DEBUG */ ClipperLib::Paths out; for (size_t i = 0; i < paths->size(); ++ i) { ClipperLib::Path &path = (*paths)[i]; - co.Clear(); + co.Clear(); co.MiterLimit = 2; bool ccw = ClipperLib::Orientation(path); if (! ccw) @@ -1092,23 +684,19 @@ void safety_offset(ClipperLib::Paths* paths) Polygons top_level_islands(const Slic3r::Polygons &polygons) { - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polygons, &input); // init Clipper ClipperLib::Clipper clipper; clipper.Clear(); // perform union - clipper.AddPaths(input, ClipperLib::ptSubject, true); + clipper.AddPaths(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::ptSubject, true); ClipperLib::PolyTree polytree; clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // Convert only the top level islands to the output. Polygons out; out.reserve(polytree.ChildCount()); - for (int i = 0; i < polytree.ChildCount(); ++i) { - out.push_back(Polygon()); - ClipperPath_to_Slic3rMultiPoint(polytree.Childs[i]->Contour, &out.back()); - } + for (int i = 0; i < polytree.ChildCount(); ++i) + out.push_back(ClipperPath_to_Slic3rMultiPoint(polytree.Childs[i]->Contour)); return out; } -} // namespace Slic3r \ No newline at end of file +} \ No newline at end of file diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 8efebdfad..0a28c3932 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -20,166 +20,198 @@ void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPoly void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); //----------------------------------------------------------- -void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output); +ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); template -void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output); +ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const T &input); template -void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output); +T ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input); template -void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output); -void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output); - -void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale); +T ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input); +Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input); // offset Polygons -void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); - -// This is a safe variant of the polygon offset, tailored for a single ExPolygon: -// a single polygon with multiple non-overlapping holes. -// Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours. -void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit); -Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit); +ClipperLib::Paths _offset(ClipperLib::Path &input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); +ClipperLib::Paths _offset(ClipperLib::Paths &input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); +inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) + { return ClipperPaths_to_Slic3rMultiPoints(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } +inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) + { return ClipperPaths_to_Slic3rMultiPoints(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } // offset Polylines -void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtSquare, - double miterLimit = 3); -void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtSquare, - double miterLimit = 3); -void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtSquare, - double miterLimit = 3); +inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) + { return ClipperPaths_to_Slic3rMultiPoints(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } +inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) + { return ClipperPaths_to_Slic3rMultiPoints(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } -void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, - ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); +// offset expolygons and surfaces +ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit); +ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit); +inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) + { return ClipperPaths_to_Slic3rMultiPoints(_offset(expolygon, delta, joinType, miterLimit)); } +inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) + { return ClipperPaths_to_Slic3rMultiPoints(_offset(expolygons, delta, joinType, miterLimit)); } +inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) + { return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } +inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) + { return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } +inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) + { return ClipperPaths_to_Slic3rExPolygons(_offset(expolygon, delta, joinType, miterLimit)); } +inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) + { return ClipperPaths_to_Slic3rExPolygons(_offset(expolygons, delta, joinType, miterLimit)); } -void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, - const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1, +ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); -void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1, - const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); template -void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, T* retval, bool safety_offset_); -void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, ClipperLib::Paths* retval, bool safety_offset_); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); -void _clipper_pl(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); -void _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, - const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_); +T _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, + const Slic3r::Polygons &clip, const ClipperLib::PolyFillType fillType, bool safety_offset_ = false); -template -void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); +ClipperLib::PolyTree _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, + const Slic3r::Polygons &clip, const ClipperLib::PolyFillType fillType, bool safety_offset_ = false); -template -void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false); +Slic3r::Polygons _clipper(ClipperLib::ClipType clipType, + const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType, + const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, + const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, + const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, + const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); -Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); - -template -Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false); - -inline void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_ = false) +// diff +inline Slic3r::Polygons +diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) { - _clipper_pl(ClipperLib::ctDifference, subject, clip, retval, safety_offset_); + return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_); } -inline void diff(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_ = false) +inline Slic3r::ExPolygons +diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) { - _clipper_ln(ClipperLib::ctDifference, subject, clip, retval, safety_offset_); + return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_); } -template -void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); - -inline void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_ = false) +inline Slic3r::ExPolygons +diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) { - _clipper_pl(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_); + return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_); } -inline void intersection(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_ = false) +inline Slic3r::Polygons +diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) { - _clipper_ln(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_); + return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_); } -template -SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +inline Slic3r::Polylines +diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_); +} -Slic3r::ExPolygons -intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +inline Slic3r::Polylines +diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_); +} -template -bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +inline Slic3r::Lines +diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_); +} + +// intersection +inline Slic3r::Polygons +intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_); +} + +inline Slic3r::ExPolygons +intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_); +} + +inline Slic3r::ExPolygons +intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_); +} + +inline Slic3r::Polygons +intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_); +} + +inline Slic3r::Polylines +intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_); +} inline Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) { - Slic3r::Polylines polylines_out; - _clipper_pl(ClipperLib::ctIntersection, subject, clip, &polylines_out, safety_offset_); - return polylines_out; + return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_); } -void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, - bool safety_offset_ = false); +inline Slic3r::Lines +intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_); +} -template -void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false); +// union +inline Slic3r::Polygons +union_(const Slic3r::Polygons &subject, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_); +} -Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset = false); -Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false); -Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset = false); +inline Slic3r::Polygons +union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_); +} -void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset = false); -Slic3r::Polygons union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset = false); +inline Slic3r::ExPolygons +union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_); +} -void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_ = false); -void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_ = false); -static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval); +inline Slic3r::ExPolygons +union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); +} -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear = false); -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear = false); +inline Slic3r::ExPolygons +union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); +} + + +ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false); +Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false); +void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval); + +/* OTHER */ +Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); +Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false); void safety_offset(ClipperLib::Paths* paths); @@ -187,4 +219,4 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons); } -#endif +#endif \ No newline at end of file diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp index f194b4644..2473ce423 100644 --- a/xs/src/libslic3r/EdgeGrid.hpp +++ b/xs/src/libslic3r/EdgeGrid.hpp @@ -12,8 +12,9 @@ namespace Slic3r { namespace EdgeGrid { -struct Grid +class Grid { +public: Grid(); ~Grid(); diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index fbb616ebb..06ba28ed2 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -99,9 +99,7 @@ ExPolygon::contains(const Line &line) const bool ExPolygon::contains(const Polyline &polyline) const { - Polylines pl_out; - diff((Polylines)polyline, *this, &pl_out); - return pl_out.empty(); + return diff_pl((Polylines)polyline, *this).empty(); } bool @@ -115,8 +113,7 @@ ExPolygon::contains(const Polylines &polylines) const svg.draw_outline(*this); svg.draw(polylines, "blue"); #endif - Polylines pl_out; - diff(polylines, *this, &pl_out); + Polylines pl_out = diff_pl(polylines, *this); #if 0 svg.draw(pl_out, "red"); #endif @@ -162,8 +159,7 @@ ExPolygon::overlaps(const ExPolygon &other) const svg.draw_outline(*this); svg.draw_outline(other, "blue"); #endif - Polylines pl_out; - intersection((Polylines)other, *this, &pl_out); + Polylines pl_out = intersection_pl((Polylines)other, *this); #if 0 svg.draw(pl_out, "red"); #endif @@ -396,11 +392,8 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const poly[3].y = bb.max.y; // intersect with this expolygon - Polygons trapezoids; - intersection(poly, *this, &trapezoids); - // append results to return value - polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end()); + polygons_append(*polygons, intersection(poly, to_polygons(*this))); } } @@ -434,16 +427,13 @@ ExPolygon::triangulate_pp(Polygons* polygons) const // convert polygons std::list input; - Polygons pp = *this; - simplify_polygons(pp, &pp, true); - ExPolygons expp; - union_(pp, &expp); + ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true)); for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { // contour { TPPLPoly p; - p.Init(ex->contour.points.size()); + p.Init(int(ex->contour.points.size())); //printf(PRINTF_ZU "\n0\n", ex->contour.points.size()); for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) { p[ point-ex->contour.points.begin() ].x = point->x; @@ -480,8 +470,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const Polygon p; p.points.resize(num_points); for (long i = 0; i < num_points; ++i) { - p.points[i].x = (*poly)[i].x; - p.points[i].y = (*poly)[i].y; + p.points[i].x = coord_t((*poly)[i].x); + p.points[i].y = coord_t((*poly)[i].y); } polygons->push_back(p); } @@ -490,8 +480,7 @@ ExPolygon::triangulate_pp(Polygons* polygons) const void ExPolygon::triangulate_p2t(Polygons* polygons) const { - ExPolygons expp; - simplify_polygons(*this, &expp, true); + ExPolygons expp = simplify_polygons_ex(*this, true); for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { // TODO: prevent duplicate points diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 624cdccbc..9f59ca386 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -13,19 +13,13 @@ namespace Slic3r { void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { - // perform clipping - Polylines clipped; - intersection(this->polyline, collection, &clipped); - return this->_inflate_collection(clipped, retval); + this->_inflate_collection(intersection_pl(this->polyline, collection), retval); } void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { - // perform clipping - Polylines clipped; - diff(this->polyline, collection, &clipped); - return this->_inflate_collection(clipped, retval); + this->_inflate_collection(diff_pl(this->polyline, collection), retval); } void @@ -58,9 +52,7 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const { - Polygons tmp; - offset(this->polyline, &tmp, scale_(this->width/2) + scaled_epsilon); - polygons_append(out, STDMOVE(tmp)); + polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon)); } void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const @@ -68,9 +60,7 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale // Instantiating the Flow class to get the line spacing. // Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler. Flow flow(this->width, this->height, 0.f, this->is_bridge()); - Polygons tmp; - offset(this->polyline, &tmp, 0.5f * flow.scaled_spacing() + scaled_epsilon); - polygons_append(out, STDMOVE(tmp)); + polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); } bool diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 7ce053a10..dfc28d035 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -194,6 +194,26 @@ class ExtrusionLoop : public ExtrusionEntity Polyline as_polyline() const { return this->polygon().split_at_first_point(); } }; +inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +{ + dst.reserve(dst.size() + polylines.size()); + for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { + dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height)); + dst.back().polyline = *it_polyline; + } +} + +#if SLIC3R_CPPVER >= 11 +inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +{ + dst.reserve(dst.size() + polylines.size()); + for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { + dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height)); + dst.back().polyline = std::move(*it_polyline); + } +} +#endif // SLIC3R_CPPVER >= 11 + inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) { dst.reserve(dst.size() + polylines.size()); diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp index c37328c69..c763952d6 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -168,7 +168,7 @@ void Fill3DHoneycomb::_fill_surface_single( it->translate(bb.min.x, bb.min.y); // clip pattern to boundaries - intersection(polylines, (Polygons)expolygon, &polylines); + polylines = intersection_pl(polylines, (Polygons)expolygon); // connect lines if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections diff --git a/xs/src/libslic3r/Fill/FillBase.cpp b/xs/src/libslic3r/Fill/FillBase.cpp index d19615aa9..7258e11fe 100644 --- a/xs/src/libslic3r/Fill/FillBase.cpp +++ b/xs/src/libslic3r/Fill/FillBase.cpp @@ -45,8 +45,7 @@ Fill* Fill::new_from_type(const std::string &type) Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms) { // Perform offset. - Slic3r::ExPolygons expp; - offset(surface->expolygon, &expp, -0.5*scale_(this->spacing)); + Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(-0.5*scale_(this->spacing))); // Create the infills for each of the regions. Polylines polylines_out; for (size_t i = 0; i < expp.size(); ++ i) diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/xs/src/libslic3r/Fill/FillConcentric.cpp index 27914866d..156e3478a 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.cpp +++ b/xs/src/libslic3r/Fill/FillConcentric.cpp @@ -33,7 +33,7 @@ void FillConcentric::_fill_surface_single( // generate paths from the outermost to the innermost, to avoid // adhesion problems of the first central tiny loops - union_pt_chained(loops, &loops, false); + loops = union_pt_chained(loops, false); // split paths using a nearest neighbor search size_t iPathFirst = polylines_out.size(); diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp index b264d80cc..22dea85da 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp @@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single( Polylines p; for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it) p.push_back((Polyline)(*it)); - intersection(p, (Polygons)expolygon, &paths); + paths = intersection_pl(p, to_polygons(expolygon)); } // connect paths @@ -122,7 +122,7 @@ void FillHoneycomb::_fill_surface_single( } // clip paths again to prevent connection segments from crossing the expolygon boundaries - intersection(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)), &paths); + paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON))); // Move the polylines to the output, avoid a deep copy. size_t j = polylines_out.size(); polylines_out.resize(j + paths.size(), Polyline()); diff --git a/xs/src/libslic3r/Fill/FillPlanePath.cpp b/xs/src/libslic3r/Fill/FillPlanePath.cpp index 34e155a06..f71ef95a1 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.cpp +++ b/xs/src/libslic3r/Fill/FillPlanePath.cpp @@ -44,7 +44,7 @@ void FillPlanePath::_fill_surface_single( coord_t(floor(it->x * distance_between_lines + 0.5)), coord_t(floor(it->y * distance_between_lines + 0.5)))); // intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines); - intersection(polylines, (Polygons)expolygon, &polylines); + polylines = intersection_pl(polylines, to_polygons(expolygon)); /* if (1) { diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index 5559a5437..991adc0b3 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -63,7 +63,7 @@ void FillRectilinear::_fill_surface_single( pts.push_back(it->a); pts.push_back(it->b); } - Polylines polylines = intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), false); + Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(expolygon), scale_(0.02)), false); // FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines! const float INFILL_OVERLAP_OVER_SPACING = 0.3f; diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index ee76e75d9..e23e53465 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -315,8 +315,7 @@ GCode::change_layer(const Layer &layer) // avoid computing islands and overhangs if they're not needed if (this->config.avoid_crossing_perimeters) { - ExPolygons islands; - union_(layer.slices, &islands, true); + ExPolygons islands = union_ex(layer.slices, true); this->avoid_crossing_perimeters.init_layer_mp(islands); } diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index d3399d46e..929460169 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -105,7 +105,7 @@ Layer::make_slices() FOREACH_LAYERREGION(this, layerm) { polygons_append(slices_p, to_polygons((*layerm)->slices)); } - union_(slices_p, &slices); + slices = union_ex(slices_p); } this->slices.expolygons.clear(); @@ -132,15 +132,11 @@ Layer::merge_slices() if (this->regions.size() == 1) { // Optimization, also more robust. Don't merge classified pieces of layerm->slices, // but use the non-split islands of a layer. For a single region print, these shall be equal. - this->regions.front()->slices.surfaces.clear(); - surfaces_append(this->regions.front()->slices.surfaces, this->slices.expolygons, stInternal); + this->regions.front()->slices.set(this->slices.expolygons, stInternal); } else { FOREACH_LAYERREGION(this, layerm) { - ExPolygons expp; // without safety offset, artifacts are generated (GH #2494) - union_(to_polygons(STDMOVE((*layerm)->slices.surfaces)), &expp, true); - (*layerm)->slices.surfaces.clear(); - surfaces_append((*layerm)->slices.surfaces, expp, stInternal); + (*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal); } } } @@ -223,7 +219,7 @@ Layer::make_perimeters() } // merge the surfaces assigned to each group for (std::map::const_iterator it = slices.begin(); it != slices.end(); ++it) - surfaces_append(new_slices.surfaces, union_ex(it->second, true), it->second.front()); + new_slices.append(union_ex(it->second, true), it->second.front()); } // make perimeters @@ -236,8 +232,7 @@ Layer::make_perimeters() // Separate the fill surfaces. ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices); (*l)->fill_expolygons = expp; - (*l)->fill_surfaces.surfaces.clear(); - surfaces_append((*l)->fill_surfaces.surfaces, STDMOVE(expp), fill_surfaces.surfaces.front()); + (*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front()); } } } diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 618396963..f0063114a 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -52,8 +52,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped() Polygons fill_boundaries = to_polygons(this->fill_expolygons); this->fill_surfaces.surfaces.clear(); for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface) - surfaces_append( - this->fill_surfaces.surfaces, + this->fill_surfaces.append( intersection_ex(to_polygons(surface->expolygon), fill_boundaries), surface->surface_type); } diff --git a/xs/src/libslic3r/MotionPlanner.cpp b/xs/src/libslic3r/MotionPlanner.cpp index 21afb37c4..9fc1695c0 100644 --- a/xs/src/libslic3r/MotionPlanner.cpp +++ b/xs/src/libslic3r/MotionPlanner.cpp @@ -142,7 +142,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to) { // grow our environment slightly in order for simplify_by_visibility() // to work best by considering moves on boundaries valid as well - ExPolygonCollection grown_env(offset_ex(env.env, +SCALED_EPSILON)); + ExPolygonCollection grown_env(offset_ex(env.env.expolygons, +SCALED_EPSILON)); if (island_idx == -1) { /* If 'from' or 'to' are not inside our env, they were connected using the @@ -155,12 +155,12 @@ MotionPlanner::shortest_path(const Point &from, const Point &to) if (!grown_env.contains(from)) { // delete second point while the line connecting first to third crosses the // boundaries as many times as the current first to second - while (polyline.points.size() > 2 && intersection((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) { + while (polyline.points.size() > 2 && intersection_ln((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) { polyline.points.erase(polyline.points.begin() + 1); } } if (!grown_env.contains(to)) { - while (polyline.points.size() > 2 && intersection((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) { + while (polyline.points.size() > 2 && intersection_ln((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) { polyline.points.erase(polyline.points.end() - 2); } } @@ -294,7 +294,7 @@ MotionPlannerEnv::nearest_env_point(const Point &from, const Point &to) const size_t result = from.nearest_waypoint_index(pp, to); // as we assume 'from' is outside env, any node will require at least one crossing - if (intersection((Lines)Line(from, pp[result]), this->island).size() > 1) { + if (intersection_ln((Lines)Line(from, pp[result]), this->island).size() > 1) { // discard result pp.erase(pp.begin() + result); } else { diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index 299e4c787..730e5e501 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -314,8 +314,7 @@ PerimeterGenerator::process() coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE); // append infill areas to fill_surfaces - surfaces_append( - this->fill_surfaces->surfaces, + this->fill_surfaces->append( offset2_ex( pp, -inset -min_perimeter_infill_spacing/2, @@ -353,36 +352,24 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, if (this->config->overhangs && this->layer_id > 0 && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) { // get non-overhang paths by intersecting this loop with the grown lower slices - { - Polylines polylines; - intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines); - - for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { - ExtrusionPath path(role); - path.polyline = *polyline; - path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm; - path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width; - path.height = this->layer_height; - paths.push_back(path); - } - } + extrusion_paths_append( + paths, + intersection_pl(loop->polygon, this->_lower_slices_p), + role, + is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm, + is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width, + this->layer_height); // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between // the loop centerline and original lower slices is >= half nozzle diameter - { - Polylines polylines; - diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines); - - for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { - ExtrusionPath path(erOverhangPerimeter); - path.polyline = *polyline; - path.mm3_per_mm = this->_mm3_per_mm_overhang; - path.width = this->overhang_flow.width; - path.height = this->overhang_flow.height; - paths.push_back(path); - } - } + extrusion_paths_append( + paths, + diff_pl(loop->polygon, this->_lower_slices_p), + erOverhangPerimeter, + this->_mm3_per_mm_overhang, + this->overhang_flow.width, + this->overhang_flow.height); // reapply the nearest point search for starting point // We allow polyline reversal because Clipper may have randomly diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp index 056c285ec..60ccd792d 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/xs/src/libslic3r/Polygon.cpp @@ -112,9 +112,7 @@ double Polygon::area() const bool Polygon::is_counter_clockwise() const { - ClipperLib::Path p; - Slic3rMultiPoint_to_ClipperPath(*this, &p); - return ClipperLib::Orientation(p); + return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); } bool @@ -190,8 +188,7 @@ Polygon::simplify(double tolerance) const Polygons pp; pp.push_back(p); - simplify_polygons(pp, &pp); - return pp; + return simplify_polygons(pp); } void diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index f396409e7..3583c627b 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -608,20 +608,15 @@ Print::validate() const object->model_object()->instances.front()->transform_polygon(&convex_hull); // grow convex hull with the clearance margin - { - Polygons grown_hull; - offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)); - convex_hull = grown_hull.front(); - } + convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); // now we check that no instance of convex_hull intersects any of the previously checked object instances for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) { Polygon p = convex_hull; p.translate(*copy); - if (intersects(a, p)) + if (! intersection(a, p).empty()) return "Some objects are too close; your extruder will collide with them."; - - union_(a, p, &a); + polygons_append(a, p); } } } diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 90449d64f..8d6ddaf21 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -449,7 +449,7 @@ void PrintObject::detect_surfaces_type() { Polygons topbottom = to_polygons(top); polygons_append(topbottom, to_polygons(bottom)); - surfaces_append(layerm->slices.surfaces, + layerm->slices.append( #if 0 offset2_ex(diff(layerm_slices_surfaces, topbottom, true), -offset, offset), #else @@ -458,8 +458,8 @@ void PrintObject::detect_surfaces_type() stInternal); } - surfaces_append(layerm->slices.surfaces, STDMOVE(top)); - surfaces_append(layerm->slices.surfaces, STDMOVE(bottom)); + layerm->slices.append(STDMOVE(top)); + layerm->slices.append(STDMOVE(bottom)); // Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", // $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; @@ -865,7 +865,7 @@ PrintObject::bridge_over_infill() #endif // compute the remaning internal solid surfaces as difference - ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true); + ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true); to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true); // build the new collection of fill_surfaces @@ -1011,7 +1011,7 @@ void PrintObject::_slice() if (my_parts.empty()) continue; // Remove such parts from original region. - other_layerm->slices.set(diff_ex(other_slices, my_parts), stInternal); + other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal); // Append new parts to our region. layerm->slices.append(std::move(my_parts), stInternal); } @@ -1039,7 +1039,7 @@ end: if (layer->regions.size() == 1) { // single region LayerRegion *layerm = layer->regions.front(); - layerm->slices.set(offset_ex(to_polygons(std::move(layerm->slices.surfaces)), delta), stInternal); + layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal); } else { if (delta < 0) { // multiple regions, shrinking @@ -1060,7 +1060,7 @@ end: Polygons processed; for (size_t region_id = 0;; ++ region_id) { LayerRegion *layerm = layer->regions[region_id]; - ExPolygons slices = offset_ex(to_polygons(layerm->slices.surfaces), delta); + ExPolygons slices = offset_ex(to_expolygons(layerm->slices.surfaces), delta); if (region_id > 0) // Trim by the slices of already processed regions. slices = diff_ex(to_polygons(std::move(slices)), processed); diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 47ce5d6db..8bf9e9fc7 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -575,7 +575,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it) it->points[0].x += 1; - diff(overhang_perimeters, lower_grown_slices, &overhang_perimeters); + overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); // only consider straight overhangs // only consider overhangs having endpoints inside layer's slices @@ -588,13 +588,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ if (it->is_straight()) { it->extend_start(fw); it->extend_end(fw); - if (layer.slices.contains(it->first_point()) && layer.slices.contains(it->last_point())) { + if (layer.slices.contains(it->first_point()) && layer.slices.contains(it->last_point())) // Offset a polyline into a polygon. - Polylines tmp; tmp.push_back(*it); - Polygons out; - offset(tmp, &out, 0.5f * w + 10.f); - polygons_append(bridged_perimeters, out); - } + polygons_append(bridged_perimeters, offset(*it, 0.5f * w + 10.f)); } } bridged_perimeters = union_(bridged_perimeters); @@ -611,13 +607,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ Polygons unsupported_bridge_polygons; for (Polylines::const_iterator it = layerm.unsupported_bridge_edges.polylines.begin(); - it != layerm.unsupported_bridge_edges.polylines.end(); ++ it) { + it != layerm.unsupported_bridge_edges.polylines.end(); ++ it) // Offset a polyline into a polygon. - Polylines tmp; tmp.push_back(*it); - Polygons out; - offset(tmp, &out, scale_(SUPPORT_MATERIAL_MARGIN)); - polygons_append(unsupported_bridge_polygons, out); - } + polygons_append(unsupported_bridge_polygons, offset(*it, scale_(SUPPORT_MATERIAL_MARGIN))); polygons_append(diff_polygons, intersection(unsupported_bridge_polygons, bridges)); } else { // just remove bridged areas @@ -1596,10 +1588,8 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const // Positions of the loop centers. Polygons circles; Polygons overhang_with_margin = offset(overhang_polygons, 0.5f * flow.scaled_width()); - for (Polygons::const_iterator it_contact = top_contact_polygons.begin(); it_contact != top_contact_polygons.end(); ++ it_contact) { - Polylines tmp; - tmp.push_back(it_contact->split_at_first_point()); - if (! intersection(tmp, overhang_with_margin).empty()) { + for (Polygons::const_iterator it_contact = top_contact_polygons.begin(); it_contact != top_contact_polygons.end(); ++ it_contact) + if (! intersection_pl(it_contact->split_at_first_point(), overhang_with_margin).empty()) { external_loops.push_back(*it_contact); Points positions_new = it_contact->equally_spaced_points(circle_distance); for (Points::const_iterator it_center = positions_new.begin(); it_center != positions_new.end(); ++ it_center) { @@ -1609,7 +1599,6 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const circle_new.points[i].translate(*it_center); } } - } // Apply a pattern to the loop. loops0 = diff(external_loops, circles); } @@ -1628,7 +1617,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const loop_lines.reserve(loop_polygons.size()); for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it) loop_lines.push_back(it->split_at_first_point()); - loop_lines = intersection(loop_lines, offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN))); + loop_lines = intersection_pl(loop_lines, offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN))); } // add the contact infill area to the interface area @@ -1636,9 +1625,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const // extrusions are left inside the circles; however it creates // a very large gap between loops and contact_infill_polygons, so maybe another // solution should be found to achieve both goals - Polygons thick_loop_lines; - offset(loop_lines, &thick_loop_lines, float(circle_radius * 1.1)); - top_contact_layer.layer->polygons = diff(top_contact_layer.layer->polygons, std::move(thick_loop_lines)); + top_contact_layer.layer->polygons = diff(top_contact_layer.layer->polygons, offset(loop_lines, float(circle_radius * 1.1))); // Transform loops into ExtrusionPath objects. extrusion_entities_append_paths( @@ -1857,7 +1844,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // TODO: use brim ordering algorithm Polygons to_infill_polygons = to_polygons(to_infill); // TODO: use offset2_ex() - to_infill = offset_ex(to_infill_polygons, - flow.scaled_spacing()); + to_infill = offset_ex(to_infill, - flow.scaled_spacing()); extrusion_entities_append_paths( support_layer.support_fills.entities, to_polylines(STDMOVE(to_infill_polygons)), diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index d372b33ea..71e34f3b8 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -370,10 +370,7 @@ TriangleMesh::horizontal_projection() const } // the offset factor was tuned using groovemount.stl - offset(pp, &pp, 0.01 / SCALING_FACTOR); - ExPolygons retval; - union_(pp, &retval, true); - return retval; + return union_ex(offset(pp, 0.01 / SCALING_FACTOR), true); } Polygon @@ -852,17 +849,15 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) c of the loops, since the Orientation() function provided by Clipper would do the same, thus repeating the calculation */ Polygons::const_iterator loop = loops.begin() + *loop_idx; - if (area[*loop_idx] > +EPSILON) { + if (area[*loop_idx] > +EPSILON) p_slices.push_back(*loop); - } else if (area[*loop_idx] < -EPSILON) { - diff(p_slices, *loop, &p_slices); - } + else if (area[*loop_idx] < -EPSILON) + p_slices = diff(p_slices, *loop); } // perform a safety offset to merge very close facets (TODO: find test case for this) double safety_offset = scale_(0.0499); - ExPolygons ex_slices; - offset2(p_slices, &ex_slices, +safety_offset, -safety_offset); + ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset); #ifdef SLIC3R_TRIANGLEMESH_DEBUG size_t holes_count = 0; @@ -874,7 +869,7 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) c #endif // append to the supplied collection - slices->insert(slices->end(), ex_slices.begin(), ex_slices.end()); + expolygons_append(*slices, ex_slices); } void diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 1544a2608..df4eab4c8 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -532,8 +532,7 @@ SV* polynode2perl(const ClipperLib::PolyNode& node) { HV* hv = newHV(); - Slic3r::Polygon p; - ClipperPath_to_Slic3rMultiPoint(node.Contour, &p); + Slic3r::Polygon p = ClipperPath_to_Slic3rMultiPoint(node.Contour); if (node.IsHole()) { (void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) ); } else { diff --git a/xs/t/11_clipper.t b/xs/t/11_clipper.t index 8709c0108..321d3048c 100644 --- a/xs/t/11_clipper.t +++ b/xs/t/11_clipper.t @@ -5,7 +5,7 @@ use warnings; use List::Util qw(sum); use Slic3r::XS; -use Test::More tests => 23; +use Test::More tests => 16; my $square = Slic3r::Polygon->new( # ccw [200, 100], @@ -121,41 +121,6 @@ if (0) { # Clipper does not preserve polyline orientation is_deeply $result->[0]->pp, [[200,150], [100,150]], 'clipped line orientation is preserved'; } -if (0) { # Clipper does not preserve polyline orientation - my $result = Slic3r::Geometry::Clipper::intersection_ppl([$hole_in_square], [$square]); - is_deeply $result->[0]->pp, $hole_in_square->split_at_first_point->pp, - 'intersection_ppl - clipping cw polygon as polyline preserves winding order'; -} - -{ - my $square2 = $square->clone; - $square2->translate(50,50); - { - my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]); - is scalar(@$result), 1, 'intersection_ppl - result contains a single line'; - is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points'; - # Clipper does not preserve polyline orientation so we only check the middle point - ###ok $result->[0][0]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order'; - ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order'; - ###ok $result->[0][2]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order'; - } -} - -{ - my $square2 = $square->clone; - $square2->reverse; - $square2->translate(50,50); - { - my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]); - is scalar(@$result), 1, 'intersection_ppl - result contains a single line'; - is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points'; - # Clipper does not preserve polyline orientation so we only check the middle point - ###ok $result->[0][0]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order'; - ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order'; - ###ok $result->[0][2]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order'; - } -} - { # Clipper bug #96 (our issue #2028) my $subject = Slic3r::Polyline->new( @@ -168,17 +133,6 @@ if (0) { # Clipper does not preserve polyline orientation is scalar(@$result), 1, 'intersection_pl - result is not empty'; } -{ - my $subject = Slic3r::Polygon->new( - [44730000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000] - ); - my $clip = [ - Slic3r::Polygon->new([75200000,45200000],[54800000,45200000],[54800000,24800000],[75200000,24800000]), - ]; - my $result = Slic3r::Geometry::Clipper::intersection_ppl([$subject], $clip); - is scalar(@$result), 1, 'intersection_ppl - result is not empty'; -} - { # Clipper bug #122 my $subject = [ diff --git a/xs/xsp/Clipper.xsp b/xs/xsp/Clipper.xsp index 6a28ea3ca..7457c8540 100644 --- a/xs/xsp/Clipper.xsp +++ b/xs/xsp/Clipper.xsp @@ -27,7 +27,7 @@ offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3) ClipperLib::JoinType joinType double miterLimit CODE: - offset(polygons, &RETVAL, delta, joinType, miterLimit); + RETVAL = offset(polygons, delta, joinType, miterLimit); OUTPUT: RETVAL @@ -38,7 +38,7 @@ offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3) ClipperLib::JoinType joinType double miterLimit CODE: - offset(polygons, &RETVAL, delta, joinType, miterLimit); + RETVAL = offset_ex(polygons, delta, joinType, miterLimit); OUTPUT: RETVAL @@ -50,7 +50,7 @@ offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3 ClipperLib::JoinType joinType double miterLimit CODE: - offset2(polygons, &RETVAL, delta1, delta2, joinType, miterLimit); + RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit); OUTPUT: RETVAL @@ -62,7 +62,7 @@ offset2_ex(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit ClipperLib::JoinType joinType double miterLimit CODE: - offset2(polygons, &RETVAL, delta1, delta2, joinType, miterLimit); + RETVAL = offset2_ex(polygons, delta1, delta2, joinType, miterLimit); OUTPUT: RETVAL @@ -72,7 +72,7 @@ diff(subject, clip, safety_offset = false) Polygons clip bool safety_offset CODE: - diff(subject, clip, &RETVAL, safety_offset); + RETVAL = diff(subject, clip, safety_offset); OUTPUT: RETVAL @@ -82,7 +82,7 @@ diff_ex(subject, clip, safety_offset = false) Polygons clip bool safety_offset CODE: - diff(subject, clip, &RETVAL, safety_offset); + RETVAL = diff_ex(subject, clip, safety_offset); OUTPUT: RETVAL @@ -91,16 +91,7 @@ diff_pl(subject, clip) Polylines subject Polygons clip CODE: - diff(subject, clip, &RETVAL); - OUTPUT: - RETVAL - -Polylines -diff_ppl(subject, clip) - Polygons subject - Polygons clip - CODE: - diff(subject, clip, &RETVAL); + RETVAL = diff_pl(subject, clip); OUTPUT: RETVAL @@ -110,7 +101,7 @@ intersection(subject, clip, safety_offset = false) Polygons clip bool safety_offset CODE: - intersection(subject, clip, &RETVAL, safety_offset); + RETVAL = intersection(subject, clip, safety_offset); OUTPUT: RETVAL @@ -120,7 +111,7 @@ intersection_ex(subject, clip, safety_offset = false) Polygons clip bool safety_offset CODE: - intersection(subject, clip, &RETVAL, safety_offset); + RETVAL = intersection_ex(subject, clip, safety_offset); OUTPUT: RETVAL @@ -129,26 +120,7 @@ intersection_pl(subject, clip) Polylines subject Polygons clip CODE: - intersection(subject, clip, &RETVAL); - OUTPUT: - RETVAL - -Polylines -intersection_ppl(subject, clip) - Polygons subject - Polygons clip - CODE: - intersection(subject, clip, &RETVAL); - OUTPUT: - RETVAL - -ExPolygons -xor_ex(subject, clip, safety_offset = false) - Polygons subject - Polygons clip - bool safety_offset - CODE: - xor_(subject, clip, &RETVAL, safety_offset); + RETVAL = intersection_pl(subject, clip); OUTPUT: RETVAL @@ -157,7 +129,7 @@ union(subject, safety_offset = false) Polygons subject bool safety_offset CODE: - union_(subject, &RETVAL, safety_offset); + RETVAL = union_(subject, safety_offset); OUTPUT: RETVAL @@ -166,20 +138,7 @@ union_ex(subject, safety_offset = false) Polygons subject bool safety_offset CODE: - union_(subject, &RETVAL, safety_offset); - OUTPUT: - RETVAL - -SV* -union_pt(subject, safety_offset = false) - Polygons subject - bool safety_offset - CODE: - // perform operation - ClipperLib::PolyTree polytree; - union_pt(subject, &polytree, safety_offset); - - RETVAL = polynode_children_2_perl(polytree); + RETVAL = union_ex(subject, safety_offset); OUTPUT: RETVAL @@ -188,7 +147,7 @@ union_pt_chained(subject, safety_offset = false) Polygons subject bool safety_offset CODE: - union_pt_chained(subject, &RETVAL, safety_offset); + RETVAL = union_pt_chained(subject, safety_offset); OUTPUT: RETVAL @@ -196,7 +155,7 @@ Polygons simplify_polygons(subject) Polygons subject CODE: - simplify_polygons(subject, &RETVAL); + RETVAL = simplify_polygons(subject); OUTPUT: RETVAL diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index 176826290..60d7c6aca 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -85,7 +85,7 @@ Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3) ClipperLib::JoinType joinType double miterLimit CODE: - offset(*THIS, &RETVAL, delta, joinType, miterLimit); + RETVAL = offset(*THIS, delta, joinType, miterLimit); OUTPUT: RETVAL diff --git a/xs/xsp/Surface.xsp b/xs/xsp/Surface.xsp index 4e3bbbb69..379774f0a 100644 --- a/xs/xsp/Surface.xsp +++ b/xs/xsp/Surface.xsp @@ -88,7 +88,7 @@ Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3) ClipperLib::JoinType joinType double miterLimit CODE: - offset(*THIS, &RETVAL, delta, joinType, miterLimit); + surfaces_append(RETVAL, offset_ex(THIS->expolygon, delta, joinType, miterLimit), *THIS); OUTPUT: RETVAL From e64dcf5e59bf945c016b14b8539f8871993de415 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Dec 2016 19:39:20 +0100 Subject: [PATCH 53/82] VS2013 could compile this, let's hope GCC will as well. --- xs/src/libslic3r/ClipperUtils.cpp | 8 ++++---- xs/src/libslic3r/ClipperUtils.hpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index e0de33586..69ba3bfa3 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -188,7 +188,7 @@ Slic3rMultiPoints_to_ClipperPaths(const T &input) return retval; } -ClipperLib::Paths _offset(ClipperLib::Paths &input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) +ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) { // scale input scaleClipperPolygons(input); @@ -208,11 +208,11 @@ ClipperLib::Paths _offset(ClipperLib::Paths &input, ClipperLib::EndType endType, return retval; } -ClipperLib::Paths _offset(ClipperLib::Path &input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) +ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) { ClipperLib::Paths paths; - paths.push_back(input); - return _offset(paths, endType, delta, joinType, miterLimit); + paths.push_back(std::move(input)); + return _offset(std::move(paths), endType, delta, joinType, miterLimit); } // This is a safe variant of the polygon offset, tailored for a single ExPolygon: diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 0a28c3932..6d3fe73ee 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -30,8 +30,8 @@ T ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input); Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input); // offset Polygons -ClipperLib::Paths _offset(ClipperLib::Path &input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); -ClipperLib::Paths _offset(ClipperLib::Paths &input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); +ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); +ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) { return ClipperPaths_to_Slic3rMultiPoints(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) From ddea33d93a972ee639b8f9311277c88bad6438b8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Dec 2016 19:52:28 +0100 Subject: [PATCH 54/82] Fixed compilation of ClipperUtils on GCC, which is more strict than VS2013 compiler. --- xs/src/libslic3r/ClipperUtils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 69ba3bfa3..4b6eb77ad 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -463,7 +463,8 @@ ExPolygons _clipper_ex(ClipperLib::ClipType clipType, const Polygons &subject, const Polygons &clip, bool safety_offset_) { - return PolyTreeToExPolygons(_clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_)); + ClipperLib::PolyTree polytree = _clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_); + return PolyTreeToExPolygons(polytree); } Polylines From 852b542913a32866d0ca6ce76cb2a9221175b4f9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Dec 2016 21:30:56 +0100 Subject: [PATCH 55/82] Had troubles with the template instantiation on OSX, so I reverted them to overloaded functions. This is certainly easier to read and work with anyway. --- xs/src/libslic3r/ClipperUtils.cpp | 40 +++++++++++++++++++++---------- xs/src/libslic3r/ClipperUtils.hpp | 26 ++++++++++---------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 4b6eb77ad..99df3b7d1 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -133,13 +133,21 @@ ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input) return retval; } -template -T -ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input) +Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input) { - T retval; + Slic3r::Polygons retval; + retval.reserve(input.size()); for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) - retval.push_back(ClipperPath_to_Slic3rMultiPoint(*it)); + retval.push_back(ClipperPath_to_Slic3rMultiPoint(*it)); + return retval; +} + +Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input) +{ + Slic3r::Polylines retval; + retval.reserve(input.size()); + for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) + retval.push_back(ClipperPath_to_Slic3rMultiPoint(*it)); return retval; } @@ -178,12 +186,18 @@ Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input) return output; } -template -ClipperLib::Paths -Slic3rMultiPoints_to_ClipperPaths(const T &input) +ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input) { ClipperLib::Paths retval; - for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) + for (Polygons::const_iterator it = input.begin(); it != input.end(); ++it) + retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it)); + return retval; +} + +ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input) +{ + ClipperLib::Paths retval; + for (Polylines::const_iterator it = input.begin(); it != input.end(); ++it) retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it)); return retval; } @@ -380,7 +394,7 @@ offset2(const Polygons &polygons, const float delta1, const float delta2, ClipperLib::Paths output = _offset2(polygons, delta1, delta2, joinType, miterLimit); // convert into ExPolygons - return ClipperPaths_to_Slic3rMultiPoints(output); + return ClipperPaths_to_Slic3rPolygons(output); } ExPolygons @@ -456,7 +470,7 @@ Polygons _clipper(ClipperLib::ClipType clipType, const Polygons &subject, const Polygons &clip, bool safety_offset_) { - return ClipperPaths_to_Slic3rMultiPoints(_clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_)); + return ClipperPaths_to_Slic3rPolygons(_clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_)); } ExPolygons @@ -473,7 +487,7 @@ _clipper_pl(ClipperLib::ClipType clipType, const Polylines &subject, { ClipperLib::Paths output; ClipperLib::PolyTreeToPaths(_clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_), output); - return ClipperPaths_to_Slic3rMultiPoints(output); + return ClipperPaths_to_Slic3rPolylines(output); } Polylines @@ -609,7 +623,7 @@ simplify_polygons(const Polygons &subject, bool preserve_collinear) } // convert into Slic3r polygons - return ClipperPaths_to_Slic3rMultiPoints(output); + return ClipperPaths_to_Slic3rPolygons(output); } ExPolygons diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 6d3fe73ee..28d1a054b 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -20,36 +20,36 @@ void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPoly void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); //----------------------------------------------------------- -ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); -template -ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const T &input); -template -T ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input); -template -T ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input); +template T ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input); +ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); +ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input); +ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input); +Slic3r::Polygon ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input); +Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input); +Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input); Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input); // offset Polygons ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rMultiPoints(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } + { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rMultiPoints(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } + { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } // offset Polylines inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) - { return ClipperPaths_to_Slic3rMultiPoints(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } + { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) - { return ClipperPaths_to_Slic3rMultiPoints(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } + { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } // offset expolygons and surfaces ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit); ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit); inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rMultiPoints(_offset(expolygon, delta, joinType, miterLimit)); } + { return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); } inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rMultiPoints(_offset(expolygons, delta, joinType, miterLimit)); } + { return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); } inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) { return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) From 2cd96cabcdfaf1b98ab784923e4cd276a5fd2703 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Dec 2016 22:13:02 +0100 Subject: [PATCH 56/82] Another take to get rid of the templates from the ClipperUtil.hpp to compile cleanly on OSX. --- xs/src/libslic3r/ClipperUtils.cpp | 32 +++++++++++++++++-------------- xs/src/libslic3r/ClipperUtils.hpp | 11 ++--------- xs/src/perlglue.cpp | 2 +- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 99df3b7d1..7625cba49 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -102,11 +102,11 @@ void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, ExPolygons* ex { size_t cnt = expolygons->size(); expolygons->resize(cnt + 1); - (*expolygons)[cnt].contour = ClipperPath_to_Slic3rMultiPoint(polynode.Contour); + (*expolygons)[cnt].contour = ClipperPath_to_Slic3rPolygon(polynode.Contour); (*expolygons)[cnt].holes.resize(polynode.ChildCount()); for (int i = 0; i < polynode.ChildCount(); ++i) { - (*expolygons)[cnt].holes[i] = ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour); + (*expolygons)[cnt].holes[i] = ClipperPath_to_Slic3rPolygon(polynode.Childs[i]->Contour); //Add outer polygons contained by (nested within) holes ... for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j) AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons); @@ -123,11 +123,17 @@ PolyTreeToExPolygons(ClipperLib::PolyTree& polytree) } //----------------------------------------------------------- -template -T -ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input) +Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input) { - T retval; + Polygon retval; + for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) + retval.points.push_back(Point( (*pit).X, (*pit).Y )); + return retval; +} + +Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input) +{ + Polyline retval; for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) retval.points.push_back(Point( (*pit).X, (*pit).Y )); return retval; @@ -138,7 +144,7 @@ Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input) Slic3r::Polygons retval; retval.reserve(input.size()); for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) - retval.push_back(ClipperPath_to_Slic3rMultiPoint(*it)); + retval.push_back(ClipperPath_to_Slic3rPolygon(*it)); return retval; } @@ -147,7 +153,7 @@ Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input Slic3r::Polylines retval; retval.reserve(input.size()); for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) - retval.push_back(ClipperPath_to_Slic3rMultiPoint(*it)); + retval.push_back(ClipperPath_to_Slic3rPolyline(*it)); return retval; } @@ -441,7 +447,7 @@ _clipper_do(const ClipperLib::ClipType clipType, const Polygons &subject, } ClipperLib::PolyTree -_clipper_do(const ClipperLib::ClipType clipType, const Polylines &subject, +_clipper_do_pl(const ClipperLib::ClipType clipType, const Polylines &subject, const Polygons &clip, const ClipperLib::PolyFillType fillType, const bool safety_offset_) { @@ -486,7 +492,7 @@ _clipper_pl(ClipperLib::ClipType clipType, const Polylines &subject, const Polygons &clip, bool safety_offset_) { ClipperLib::Paths output; - ClipperLib::PolyTreeToPaths(_clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_), output); + ClipperLib::PolyTreeToPaths(_clipper_do_pl(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_), output); return ClipperPaths_to_Slic3rPolylines(output); } @@ -598,9 +604,7 @@ traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval) for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) { // traverse the next depth traverse_pt((*it)->Childs, retval); - - Polygon p = ClipperPath_to_Slic3rMultiPoint((*it)->Contour); - retval->push_back(p); + retval->push_back(ClipperPath_to_Slic3rPolygon((*it)->Contour)); if ((*it)->IsHole()) retval->back().reverse(); // ccw } } @@ -710,7 +714,7 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons) Polygons out; out.reserve(polytree.ChildCount()); for (int i = 0; i < polytree.ChildCount(); ++i) - out.push_back(ClipperPath_to_Slic3rMultiPoint(polytree.Childs[i]->Contour)); + out.push_back(ClipperPath_to_Slic3rPolygon(polytree.Childs[i]->Contour)); return out; } diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 28d1a054b..d6060386a 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -20,11 +20,11 @@ void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPoly void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); //----------------------------------------------------------- -template T ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input); ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input); ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input); -Slic3r::Polygon ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input); +Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input); +Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input); Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input); Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input); Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input); @@ -69,13 +69,6 @@ Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delt const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); -template -T _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, const ClipperLib::PolyFillType fillType, bool safety_offset_ = false); - -ClipperLib::PolyTree _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, const ClipperLib::PolyFillType fillType, bool safety_offset_ = false); - Slic3r::Polygons _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType, diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index df4eab4c8..cae956fe3 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -532,7 +532,7 @@ SV* polynode2perl(const ClipperLib::PolyNode& node) { HV* hv = newHV(); - Slic3r::Polygon p = ClipperPath_to_Slic3rMultiPoint(node.Contour); + Slic3r::Polygon p = ClipperPath_to_Slic3rPolygon(node.Contour); if (node.IsHole()) { (void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) ); } else { From ae2e37b4bd3b345aded594eb92b0a33c68fcfc5e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Dec 2016 22:46:28 +0100 Subject: [PATCH 57/82] Added diagnostics of @LIBS, @INCS to the Build.PL Extended MANIFEST with new files. --- xs/Build.PL | 4 ++++ xs/MANIFEST | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/xs/Build.PL b/xs/Build.PL index e014ddbcf..a1aff0088 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -212,6 +212,10 @@ if ($cpp_guess->is_gcc) { } } +print "\n"; +print 'With @INC: ', join(', ', map "\"$_\"", @INC), "\n"; +print 'With @LIBS: ', join(', ', map "\"$_\"", @LIBS), "\n"; + my $build = Module::Build::WithXSpp->new( module_name => 'Slic3r::XS', dist_abstract => 'XS code for Slic3r', diff --git a/xs/MANIFEST b/xs/MANIFEST index 70d83afa7..8ea48192b 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -32,6 +32,8 @@ src/libslic3r/ExtrusionEntityCollection.cpp src/libslic3r/ExtrusionEntityCollection.hpp src/libslic3r/ExtrusionSimulator.cpp src/libslic3r/ExtrusionSimulator.hpp +src/libslic3r/Fill/Fill.cpp +src/libslic3r/Fill/Fill.hpp src/libslic3r/Fill/FillBase.cpp src/libslic3r/Fill/FillBase.hpp src/libslic3r/Fill/FillConcentric.cpp @@ -54,6 +56,8 @@ src/libslic3r/GCodeSender.cpp src/libslic3r/GCodeSender.hpp src/libslic3r/GCodeWriter.cpp src/libslic3r/GCodeWriter.hpp +src/libslic3r/GCode/Analyzer.cpp +src/libslic3r/GCode/Analyzer.hpp src/libslic3r/GCode/PressureEqualizer.cpp src/libslic3r/GCode/PressureEqualizer.hpp src/libslic3r/Geometry.cpp @@ -88,6 +92,10 @@ src/libslic3r/PrintConfig.cpp src/libslic3r/PrintConfig.hpp src/libslic3r/PrintObject.cpp src/libslic3r/PrintRegion.cpp +src/libslic3r/Slicing.cpp +src/libslic3r/Slicing.hpp +src/libslic3r/SlicingAdaptive.cpp +src/libslic3r/SlicingAdaptive.hpp src/libslic3r/SupportMaterial.cpp src/libslic3r/SupportMaterial.hpp src/libslic3r/Surface.cpp @@ -99,6 +107,7 @@ src/libslic3r/SVG.hpp src/libslic3r/TriangleMesh.cpp src/libslic3r/TriangleMesh.hpp src/libslic3r/utils.cpp +src/libslic3r/Utils.hpp src/perlglue.cpp src/poly2tri/common/shapes.cc src/poly2tri/common/shapes.h From 79ff013ea7c196a195e94085ab8404cf58c752bf Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 14 Dec 2016 15:09:12 +0100 Subject: [PATCH 58/82] Fixed crashes on shader destruction on OSX. --- lib/Slic3r/GUI/GLShader.pm | 3 +++ lib/Slic3r/GUI/Plater.pm | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/GLShader.pm b/lib/Slic3r/GUI/GLShader.pm index 81298f44c..766fe0807 100644 --- a/lib/Slic3r/GUI/GLShader.pm +++ b/lib/Slic3r/GUI/GLShader.pm @@ -18,6 +18,9 @@ package Slic3r::GUI::GLShader; use OpenGL(':all'); +# Avoid cloning this class by the worker threads. +sub CLONE_SKIP { 1 } + # Shader constructor sub new { diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ef9868735..2ba5a1ae5 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -264,7 +264,11 @@ sub new { EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; }); EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog }); - EVT_TOOL($self, TB_LAYER_EDITING, sub { $self->on_layer_editing_toggled($self->{htoolbar}->GetToolState(TB_LAYER_EDITING)); }); + EVT_TOOL($self, TB_LAYER_EDITING, sub { + my $state = $self->{canvas3D}->layer_editing_enabled; + $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state); + $self->on_layer_editing_toggled(! $state); + }); } else { EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; }); EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove @@ -476,7 +480,6 @@ sub _on_select_preset { sub on_layer_editing_toggled { my ($self, $new_state) = @_; - print "on_layer_editing_toggled $new_state\n"; $self->{canvas3D}->layer_editing_enabled($new_state); $self->{canvas3D}->update; } From c79e1cc89b8b4e1723749bec564895c6974c796c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 14 Dec 2016 15:36:18 +0100 Subject: [PATCH 59/82] Fixed UI bug on OSX, experimental features made switchable through environment. --- lib/Slic3r/GUI/3DScene.pm | 18 ++++++++++++++++-- lib/Slic3r/GUI/Plater.pm | 11 +++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 2be09e596..76440e54f 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -191,6 +191,12 @@ sub new { return $self; } +sub Destroy { + my ($self) = @_; + $self->DestroyGL; + return $self->SUPER::Destroy; +} + sub _first_selected_object_id { my ($self) = @_; for my $i (0..$#{$self->volumes}) { @@ -786,7 +792,8 @@ sub InitGL { $self->init(1); my $shader; - $shader = $self->{shader} = new Slic3r::GUI::GLShader; + $shader = $self->{shader} = new Slic3r::GUI::GLShader + if (defined($ENV{'SLIC3R_EXPERIMENTAL'} && defined($ENV{'SLIC3R_EXPERIMENTAL'} == 1); if ($self->{shader}) { my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader); print $info if $info; @@ -839,7 +846,14 @@ sub InitGL { glEnable(GL_COLOR_MATERIAL); glEnable(GL_MULTISAMPLE); } - + +sub DestroyGL { + my $self = shift; + if ($self->init && $self->GetContext) { + delete $self->{shader}; + } +} + sub Render { my ($self, $dc) = @_; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 2ba5a1ae5..119087a8d 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -11,7 +11,7 @@ use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad); use LWP::UserAgent; use threads::shared qw(shared_clone); use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc - :panel :sizer :toolbar :window wxTheApp :notebook :combobox); + :panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap); use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED); @@ -158,7 +158,8 @@ sub new { $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); # FIXME add a button for layer editing - $self->{htoolbar}->AddCheckTool(TB_LAYER_EDITING, "Layer editing", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, undef, 'Layer Editing'); + if (defined($ENV{'SLIC3R_EXPERIMENTAL'} && defined($ENV{'SLIC3R_EXPERIMENTAL'} == 1); } else { my %tbar_buttons = ( add => "Add…", @@ -180,8 +181,10 @@ sub new { $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); $self->{btoolbar}->Add($self->{"btn_$_"}); } - $self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - $self->{btoolbar}->Add($self->{"btn_layer_editing"}); + if (defined($ENV{'SLIC3R_EXPERIMENTAL'} && defined($ENV{'SLIC3R_EXPERIMENTAL'} == 1) { + $self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + $self->{btoolbar}->Add($self->{"btn_layer_editing"}); + } } $self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize, From ff0a8956ee6b2c31275fcd51894235cefb4fa5fd Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 14 Dec 2016 15:42:38 +0100 Subject: [PATCH 60/82] Fixed previous checkin. --- lib/Slic3r/GUI/3DScene.pm | 2 +- lib/Slic3r/GUI/Plater.pm | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 76440e54f..374544dfe 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -793,7 +793,7 @@ sub InitGL { my $shader; $shader = $self->{shader} = new Slic3r::GUI::GLShader - if (defined($ENV{'SLIC3R_EXPERIMENTAL'} && defined($ENV{'SLIC3R_EXPERIMENTAL'} == 1); + if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1); if ($self->{shader}) { my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader); print $info if $info; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 119087a8d..ead1fc4fe 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -158,8 +158,8 @@ sub new { $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); # FIXME add a button for layer editing - $self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, undef, 'Layer Editing'); - if (defined($ENV{'SLIC3R_EXPERIMENTAL'} && defined($ENV{'SLIC3R_EXPERIMENTAL'} == 1); + $self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, undef, 'Layer Editing') + if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1); } else { my %tbar_buttons = ( add => "Add…", @@ -181,7 +181,7 @@ sub new { $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); $self->{btoolbar}->Add($self->{"btn_$_"}); } - if (defined($ENV{'SLIC3R_EXPERIMENTAL'} && defined($ENV{'SLIC3R_EXPERIMENTAL'} == 1) { + if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1) { $self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); $self->{btoolbar}->Add($self->{"btn_layer_editing"}); } From a870b6973324a6f4fda6d6f57fa8f2f98590a43f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 14 Dec 2016 15:43:26 +0100 Subject: [PATCH 61/82] Fixed static linking against boost. --- xs/Build.PL | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xs/Build.PL b/xs/Build.PL index a1aff0088..b3e000f5c 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -120,14 +120,14 @@ my $have_boost = 0; my @boost_libraries = qw(system thread log); # we need these # check without explicit lib path (works on Linux) -if (! $mswin) { +if (!$ENV{SLIC3R_STATIC} && ! $mswin) { $have_boost = 1 if check_lib( lib => [ map "boost_${_}", @boost_libraries ], ); } -if (!$ENV{SLIC3R_STATIC} && $have_boost) { +if ($have_boost) { # The boost library was detected by check_lib on Linux. push @LIBS, map "-lboost_${_}", @boost_libraries; } else { @@ -139,7 +139,7 @@ if (!$ENV{SLIC3R_STATIC} && $have_boost) { my @files = glob "$path/${lib_prefix}system*$lib_ext"; next if !@files; - if ($files[0] =~ /${lib_prefix}system([^.]+)$lib_ext$/) { + if ($files[0] =~ /${lib_prefix}system([^.]*)$lib_ext$/) { # Suffix contains the version number, the build type etc. my $suffix = $1; # Verify existence of all required boost libraries at $path. From 56e87e3bdb15ee22b5b435a7263b56583c791c88 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 14 Dec 2016 16:51:27 +0100 Subject: [PATCH 62/82] Set BOOST_LOG_DYN_LINK symbol when linking with dynamic boost libraries. --- xs/Build.PL | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/xs/Build.PL b/xs/Build.PL index b3e000f5c..e64a9a399 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -119,12 +119,16 @@ if (defined $ENV{BOOST_LIBRARYDIR}) { my $have_boost = 0; my @boost_libraries = qw(system thread log); # we need these -# check without explicit lib path (works on Linux) -if (!$ENV{SLIC3R_STATIC} && ! $mswin) { - $have_boost = 1 - if check_lib( - lib => [ map "boost_${_}", @boost_libraries ], - ); +if (!$ENV{SLIC3R_STATIC}) { + # Dynamic linking of boost libraries. + push @cflags, qw(-BOOST_LOG_DYN_LINK); + if (! $mswin) { + # Check without explicit lib path (works on Linux and OSX). + $have_boost = 1 + if check_lib( + lib => [ map "boost_${_}", @boost_libraries ], + ); + } } if ($have_boost) { From c1f4d06be24c0ee41c2c5301f3c01aff6252b8dd Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 15 Dec 2016 09:46:41 +0100 Subject: [PATCH 63/82] define -DBOOST_LOG_DYN_LINK for boost::log --- xs/Build.PL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/Build.PL b/xs/Build.PL index e64a9a399..1cce94940 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -121,7 +121,7 @@ my @boost_libraries = qw(system thread log); # we need these if (!$ENV{SLIC3R_STATIC}) { # Dynamic linking of boost libraries. - push @cflags, qw(-BOOST_LOG_DYN_LINK); + push @cflags, qw(-DBOOST_LOG_DYN_LINK); if (! $mswin) { # Check without explicit lib path (works on Linux and OSX). $have_boost = 1 From 955bc957bac5afafca26ead0894c9fdf1e0a3a20 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 20 Dec 2016 12:19:13 +0100 Subject: [PATCH 64/82] New supports now do rafts at least to the extent the test cases run through. New supports enabled, old supports will go away soon. --- lib/Slic3r/GUI/3DScene.pm | 9 +- lib/Slic3r/Print/Object.pm | 57 ++-- lib/Slic3r/Print/SupportMaterial.pm | 1 + xs/MANIFEST | 1 - xs/src/libslic3r/EdgeGrid.cpp | 35 ++- xs/src/libslic3r/EdgeGrid.hpp | 2 +- xs/src/libslic3r/Fill/FillRectilinear2.cpp | 6 +- xs/src/libslic3r/Print.hpp | 1 + xs/src/libslic3r/PrintObject.cpp | 7 + xs/src/libslic3r/Slicing.cpp | 27 +- xs/src/libslic3r/Slicing.hpp | 18 ++ xs/src/libslic3r/SupportMaterial.cpp | 315 ++++++++++----------- xs/src/libslic3r/SupportMaterial.hpp | 65 +---- xs/src/libslic3r/SurfaceCollection.cpp | 14 +- xs/src/libslic3r/libslic3r.h | 2 +- xs/xsp/Print.xsp | 1 + xs/xsp/SupportMaterial.xsp | 26 -- 17 files changed, 279 insertions(+), 308 deletions(-) delete mode 100644 xs/xsp/SupportMaterial.xsp diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 374544dfe..8bc57f53d 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -377,7 +377,9 @@ sub mouse_event { $self->_layer_height_edited(undef); } elsif ($e->Moving) { $self->_mouse_pos($pos); - $self->Refresh; + # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor + # hovers over. + $self->Refresh if ($self->enable_picking); } else { $e->Skip(); } @@ -1047,9 +1049,10 @@ sub Render { $self->draw_active_object_annotations; - glFlush(); - $self->SwapBuffers(); + + # Calling glFinish has a performance penalty, but it seems to fix some OpenGL driver hang-up with extremely large scenes. + glFinish(); } sub draw_volumes { diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 720294cd0..c0d80629c 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -260,44 +260,35 @@ sub generate_support_material { $self->clear_support_layers; - if ((!$self->config->support_material && $self->config->raft_layers == 0) || scalar(@{$self->layers}) < 2) { - $self->set_step_done(STEP_SUPPORTMATERIAL); - return; + if (($self->config->support_material || $self->config->raft_layers > 0) && scalar(@{$self->layers}) > 1) { + $self->print->status_cb->(85, "Generating support material"); + if (0) { + # Old supports, Perl implementation. + my $first_layer_flow = Slic3r::Flow->new_from_width( + width => ($self->print->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), + role => FLOW_ROLE_SUPPORT_MATERIAL, + nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ] + // $self->print->config->nozzle_diameter->[0], + layer_height => $self->config->get_abs_value('first_layer_height'), + bridge_flow_ratio => 0, + ); + my $support_material = Slic3r::Print::SupportMaterial->new( + print_config => $self->print->config, + object_config => $self->config, + first_layer_flow => $first_layer_flow, + flow => $self->support_material_flow, + interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), + ); + $support_material->generate($self); + } else { + # New supports, C++ implementation. + $self->_generate_support_material; + } } - $self->print->status_cb->(85, "Generating support material"); - - $self->_support_material->generate($self); $self->set_step_done(STEP_SUPPORTMATERIAL); } -sub _support_material { - my ($self) = @_; - - my $first_layer_flow = Slic3r::Flow->new_from_width( - width => ($self->print->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), - role => FLOW_ROLE_SUPPORT_MATERIAL, - nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ] - // $self->print->config->nozzle_diameter->[0], - layer_height => $self->config->get_abs_value('first_layer_height'), - bridge_flow_ratio => 0, - ); - - if (1) { - # Old supports, Perl implementation. - return Slic3r::Print::SupportMaterial->new( - print_config => $self->print->config, - object_config => $self->config, - first_layer_flow => $first_layer_flow, - flow => $self->support_material_flow, - interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), - ); - } else { - # New supports, C++ implementation. - return Slic3r::Print::SupportMaterial2->new($self); - } -} - # Idempotence of this method is guaranteed by the fact that we don't remove things from # fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. sub clip_fill_surfaces { diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index b4caffc7d..8b77e8808 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -20,6 +20,7 @@ has 'interface_flow' => (is => 'rw', required => 1); use constant DEBUG_CONTACT_ONLY => 0; # increment used to reach MARGIN in steps to avoid trespassing thin objects +use constant MARGIN => 1.5; use constant MARGIN_STEP => MARGIN/3; # generate a tree-like structure to save material diff --git a/xs/MANIFEST b/xs/MANIFEST index 8ea48192b..01b9e1cd3 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -209,7 +209,6 @@ xsp/Polygon.xsp xsp/Polyline.xsp xsp/PolylineCollection.xsp xsp/Print.xsp -xsp/SupportMaterial.xsp xsp/Surface.xsp xsp/SurfaceCollection.xsp xsp/TriangleMesh.xsp diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp index 4d1432722..b39b12ab8 100644 --- a/xs/src/libslic3r/EdgeGrid.cpp +++ b/xs/src/libslic3r/EdgeGrid.cpp @@ -1222,17 +1222,39 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo return true; } -Polygons EdgeGrid::Grid::contours_simplified() const +Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const { typedef std::unordered_multimap EndPointMapType; + // 0) Prepare a binary grid. + size_t cell_rows = m_rows + 2; + size_t cell_cols = m_cols + 2; + std::vector cell_inside(cell_rows * cell_cols, false); + for (int r = 0; r < int(cell_rows); ++ r) + for (int c = 0; c < int(cell_cols); ++ c) + cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1); + // Fill in empty cells, which have a left / right neighbor filled. + // Fill in empty cells, which have the top / bottom neighbor filled. + { + std::vector cell_inside2(cell_inside); + for (int r = 1; r + 1 < int(cell_rows); ++ r) { + for (int c = 1; c + 1 < int(cell_cols); ++ c) { + int addr = r * cell_cols + c; + if ((cell_inside2[addr - 1] && cell_inside2[addr + 1]) || + (cell_inside2[addr - cell_cols] && cell_inside2[addr + cell_cols])) + cell_inside[addr] = true; + } + } + } + // 1) Collect the lines. std::vector lines; EndPointMapType start_point_to_line_idx; for (int r = 0; r <= int(m_rows); ++ r) { for (int c = 0; c <= int(m_cols); ++ c) { - bool left = cell_inside_or_crossing(r , c-1); - bool top = cell_inside_or_crossing(r-1, c ); - bool current = cell_inside_or_crossing(r , c ); + int addr = (r + 1) * cell_cols + c + 1; + bool left = cell_inside[addr - 1]; + bool top = cell_inside[addr - cell_cols]; + bool current = cell_inside[addr]; if (left != current) { lines.push_back( left ? @@ -1312,7 +1334,6 @@ Polygons EdgeGrid::Grid::contours_simplified() const // Remove collineaer points. Points pts; pts.reserve(poly.points.size()); - coord_t downscale = 5; for (size_t j = 0; j < poly.points.size(); ++ j) { size_t j0 = (j == 0) ? poly.points.size() - 1 : j - 1; size_t j2 = (j + 1 == poly.points.size()) ? 0 : j + 1; @@ -1320,8 +1341,8 @@ Polygons EdgeGrid::Grid::contours_simplified() const if (v.x != 0 && v.y != 0) { // This is a corner point. Copy it to the output contour. Point p = poly.points[j]; - p.y += (v.x < 0) ? downscale : -downscale; - p.x += (v.y > 0) ? downscale : -downscale; + p.y += (v.x < 0) ? - offset : offset; + p.x += (v.y > 0) ? - offset : offset; pts.push_back(p); } } diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp index 2473ce423..a0b23211e 100644 --- a/xs/src/libslic3r/EdgeGrid.hpp +++ b/xs/src/libslic3r/EdgeGrid.hpp @@ -58,7 +58,7 @@ public: const size_t cols() const { return m_cols; } // For supports: Contours enclosing the rasterized edges. - Polygons contours_simplified() const; + Polygons contours_simplified(coord_t offset) const; protected: struct Cell { diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp index 3f7232e06..5122de144 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp @@ -882,7 +882,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP Point refpt = rotate_vector.second.rotated(- rotate_vector.first); // _align_to_grid will not work correctly with positive pattern_shift. coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing; - refpt.x -= (pattern_shift_scaled > 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); + refpt.x -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); bounding_box.merge(_align_to_grid( bounding_box.min, Point(line_spacing, line_spacing), @@ -892,7 +892,9 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP // Intersect a set of euqally spaced vertical lines wiht expolygon. // n_vlines = ceil(bbox_width / line_spacing) size_t n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / line_spacing; - coord_t x0 = bounding_box.min.x + (line_spacing + SCALED_EPSILON) / 2; + coord_t x0 = bounding_box.min.x; + if (full_infill) + x0 += (line_spacing + SCALED_EPSILON) / 2; #ifdef SLIC3R_DEBUG static int iRun = 0; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index f97f888cd..e8b0e0a03 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -159,6 +159,7 @@ public: void bridge_over_infill(); void _make_perimeters(); void _infill(); + void _generate_support_material(); private: Print* _print; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 8d6ddaf21..3c92aad24 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -2,6 +2,7 @@ #include "BoundingBox.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" +#include "SupportMaterial.hpp" #include @@ -1243,4 +1244,10 @@ PrintObject::_infill() this->state.set_done(posInfill); } +void PrintObject::_generate_support_material() +{ + PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters()); + support_material.generate(*this); +} + } // namespace Slic3r diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp index 0de781c64..5bd6bfc27 100644 --- a/xs/src/libslic3r/Slicing.cpp +++ b/xs/src/libslic3r/Slicing.cpp @@ -33,10 +33,18 @@ SlicingParameters SlicingParameters::create_from_config( SlicingParameters params; params.layer_height = object_config.layer_height.value; + params.first_print_layer_height = first_layer_height; params.first_object_layer_height = first_layer_height; params.object_print_z_min = 0.; params.object_print_z_max = object_height; params.base_raft_layers = object_config.raft_layers.value; + params.soluble_interface = soluble_interface; + + if (! soluble_interface) { + params.gap_raft_object = object_config.support_material_contact_distance.value; + params.gap_object_support = object_config.support_material_contact_distance.value; + params.gap_support_object = object_config.support_material_contact_distance.value; + } if (params.base_raft_layers > 0) { params.interface_raft_layers = (params.base_raft_layers + 1) / 2; @@ -70,18 +78,21 @@ SlicingParameters SlicingParameters::create_from_config( if (params.has_raft()) { // Raise first object layer Z by the thickness of the raft itself plus the extra distance required by the support material logic. //FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case. - coordf_t print_z = first_layer_height + object_config.support_material_contact_distance.value; if (params.raft_layers() == 1) { + // There is only the contact layer. params.contact_raft_layer_height = first_layer_height; + params.raft_contact_top_z = first_layer_height; } else { - print_z += - // Number of the base raft layers is decreased by the first layer, which has already been added to print_z. - coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height + - // Number of the interface raft layers is decreased by the contact layer. - coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height + - params.contact_raft_layer_height; + assert(params.base_raft_layers > 0); + assert(params.interface_raft_layers > 0); + // Number of the base raft layers is decreased by the first layer. + params.raft_base_top_z = first_layer_height + coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height; + // Number of the interface raft layers is decreased by the contact layer. + params.raft_interface_top_z = params.raft_base_top_z + coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height; + params.raft_contact_top_z = params.raft_interface_top_z + params.contact_raft_layer_height; } - params.object_print_z_min = print_z; + coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object; + params.object_print_z_min = print_z; params.object_print_z_max += print_z; } diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp index 349be2e4a..02ae7dd9e 100644 --- a/xs/src/libslic3r/Slicing.hpp +++ b/xs/src/libslic3r/Slicing.hpp @@ -53,6 +53,10 @@ struct SlicingParameters // or by the variable layer thickness table. coordf_t layer_height; + // First layer height of the print, this may be used for the first layer of the raft + // or for the first layer of the print. + coordf_t first_print_layer_height; + // Thickness of the first layer. This is either the first print layer thickness if printed without a raft, // or a bridging flow thickness if printed over a non-soluble raft, // or a normal layer height if printed over a soluble raft. @@ -61,6 +65,16 @@ struct SlicingParameters // If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow. bool first_object_layer_bridging; + // Soluble interface? (PLA soluble in water, HIPS soluble in lemonen) + // otherwise the interface must be broken off. + bool soluble_interface; + // Gap when placing object over raft. + coordf_t gap_raft_object; + // Gap when placing support over object. + coordf_t gap_object_support; + // Gap when placing object over support. + coordf_t gap_support_object; + // Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm, // or by an interactive layer height editor. coordf_t min_layer_height; @@ -69,6 +83,10 @@ struct SlicingParameters // Bottom and top of the printed object. // If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height. // Otherwise object_print_z_min is equal to the raft height. + coordf_t raft_base_top_z; + coordf_t raft_interface_top_z; + coordf_t raft_contact_top_z; + // In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer. coordf_t object_print_z_min; coordf_t object_print_z_max; }; diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 8bf9e9fc7..7806e36a5 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -31,60 +31,35 @@ namespace Slic3r { #define PILLAR_SIZE (2.5) #define PILLAR_SPACING 10 -PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object) : +PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : m_object (object), m_print_config (&object->print()->config), m_object_config (&object->config), + m_slicing_params (slicing_params), m_first_layer_flow (Flow::new_from_config_width( frSupportMaterial, + // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width, object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1), - object->config.get_abs_value("first_layer_height"), + slicing_params.first_print_layer_height, false )), m_support_material_flow (Flow::new_from_config_width( frSupportMaterial, + // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1), - object->config.layer_height.value, + slicing_params.layer_height, false)), m_support_material_interface_flow(Flow::new_from_config_width( frSupportMaterialInterface, - (object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, + // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. + (object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1), object->config.layer_height.value, false)), - m_soluble_interface (object->config.support_material_contact_distance.value == 0), - m_support_material_raft_base_flow(0, 0, 0, false), - m_support_material_raft_interface_flow(0, 0, 0, false), - m_support_material_raft_contact_flow(0, 0, 0, false), - - m_has_raft (object->config.raft_layers.value > 0), - m_num_base_raft_layers (0), - m_num_interface_raft_layers (0), - m_num_contact_raft_layers (0), - - // If set, the raft contact layer is laid with round strings, which are easily detachable - // from both the below and above layes. - // Otherwise a normal flow is used and the strings are squashed against the layer below, - // creating a firm bond with the layer below and making the interface top surface flat. -#if 1 - // This is the standard Slic3r behavior. - m_raft_contact_layer_bridging(false), - m_object_1st_layer_bridging (true), -#else - // This is more akin to what Simplify3D or Zortrax do. - m_raft_contact_layer_bridging(true), - m_object_1st_layer_bridging (false), -#endif - - m_raft_height (0.), - m_raft_base_height (0.), - m_raft_interface_height (0.), - m_raft_contact_height (0.), - // 50 mirons layer m_support_layer_height_min (0.05), m_support_layer_height_max (0.), @@ -101,79 +76,6 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object // the support layers will be synchronized with the object layers exactly, no layer will be combined. m_combine_support_layers (true) { - // Based on the raft style and size, initialize the raft layers and the 1st object layer attributes. - - size_t num_raft_layers = m_object_config->raft_layers.value; - - //FIXME better to draw thin strings, which are easier to remove from the object. - if (m_has_raft) - { - if (m_raft_contact_layer_bridging) - m_support_material_raft_contact_flow = Flow::new_from_spacing( - m_support_material_raft_interface_flow.spacing(), - m_support_material_raft_interface_flow.nozzle_diameter, - m_support_material_raft_interface_flow.height, - true); - - if (m_raft_contact_layer_bridging && num_raft_layers == 1) - // The bridging contact layer will not bond to the bed well on its own. - // Ensure there is at least the 1st layer printed with a firm squash. - ++ num_raft_layers; - - // Split the raft layers into a single contact layer - // and an equal number of interface and base layers, - // with m_num_interface_raft_layers >= m_num_base_raft_layers. - m_num_contact_raft_layers = 1; - m_num_interface_raft_layers = num_raft_layers / 2; - m_num_base_raft_layers = num_raft_layers - m_num_contact_raft_layers - m_num_interface_raft_layers; - assert(m_num_interface_raft_layers >= m_num_base_raft_layers); - assert(m_num_contact_raft_layers + m_num_base_raft_layers + m_num_interface_raft_layers == num_raft_layers); - - m_raft_contact_height = m_num_contact_raft_layers * m_support_material_raft_contact_flow.height; - if (m_num_base_raft_layers > 0) { - m_raft_base_height = first_layer_height() + (m_num_base_raft_layers - 1) * m_support_material_raft_base_flow.height; - m_raft_interface_height = m_num_interface_raft_layers * m_support_material_raft_interface_flow.height; - } else if (m_num_interface_raft_layers > 0) { - m_raft_base_height = 0; - m_raft_interface_height = first_layer_height() + (m_num_interface_raft_layers - 1) * m_support_material_raft_interface_flow.height; - } else { - m_raft_base_height = 0; - m_raft_interface_height = 0; - } - m_raft_height = m_raft_base_height + m_raft_interface_height + m_raft_contact_height; - - // Find the layer height of the 1st object layer. - if (m_object_1st_layer_bridging) { - // Use an average nozzle diameter. - std::set extruders = m_object->print()->object_extruders(); - coordf_t nozzle_dmr = 0; - for (std::set::const_iterator it = extruders.begin(); it != extruders.end(); ++ it) { - nozzle_dmr += m_object->print()->config.nozzle_diameter.get_at(*it); - } - nozzle_dmr /= extruders.size(); - m_object_1st_layer_height = nozzle_dmr; - } else { - m_object_1st_layer_height = m_object->config.layer_height.value; - for (t_layer_height_ranges::const_iterator it = m_object->layer_height_ranges.begin(); it != m_object->layer_height_ranges.end(); ++ it) { - if (m_object_1st_layer_height >= it->first.first && m_object_1st_layer_height <= it->first.second) { - m_object_1st_layer_height = it->second; - break; - } - } - } - - m_object_1st_layer_gap = m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value; - m_object_1st_layer_print_z = m_raft_height + m_object_1st_layer_gap + m_object_1st_layer_height; - } - else - { - // No raft. - m_raft_contact_layer_bridging = false; - m_object_1st_layer_bridging = false; - m_object_1st_layer_height = m_first_layer_flow.height; - m_object_1st_layer_gap = 0; - m_object_1st_layer_print_z = m_object_1st_layer_height; - } } // Using the std::deque as an allocator. @@ -286,7 +188,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // There is a contact layer below the 1st object layer in the bottom contacts. // There is also a 1st intermediate layer containing bases of support columns. // Extend the bases of the support columns and create the raft base. - Polygons raft = this->generate_raft_base(object, bottom_contacts, intermediate_layers); + MyLayersPtr raft_layers = this->generate_raft_base(object, top_contacts, intermediate_layers, layer_storage); /* // If we wanted to apply some special logic to the first support layers lying on @@ -323,7 +225,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Install support layers into the object. MyLayersPtr layers_sorted; - layers_sorted.reserve(bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size()); + layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size()); + layers_append(layers_sorted, raft_layers); layers_append(layers_sorted, bottom_contacts); layers_append(layers_sorted, top_contacts); layers_append(layers_sorted, intermediate_layers); @@ -351,7 +254,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths"; // Generate the actual toolpaths and save them into each layer. - this->generate_toolpaths(object, raft, bottom_contacts, top_contacts, intermediate_layers, interface_layers); + this->generate_toolpaths(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers); BOOST_LOG_TRIVIAL(info) << "Support generator - End"; } @@ -670,7 +573,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ MyLayer &new_layer = layer_allocate(layer_storage, sltTopContact); const Layer *layer_below = (layer_id > 0) ? object.get_layer(layer_id - 1) : NULL; new_layer.idx_object_layer_above = layer_id; - if (m_soluble_interface) { + if (m_slicing_params.soluble_interface) { // Align the contact surface height with a layer immediately below the supported layer. new_layer.height = layer_below ? // Interface layer will be synchronized with the object. @@ -984,16 +887,16 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // Grow top surfaces so that interface and support generation are generated // with some spacing from object - it looks we don't need the actual // top shapes so this can be done here - layer_new.height = m_soluble_interface ? + layer_new.height = m_slicing_params.soluble_interface ? // Align the interface layer with the object's layer height. object.get_layer(layer_id + 1)->height : // Place a bridge flow interface layer over the top surface. m_support_material_interface_flow.nozzle_diameter; layer_new.print_z = layer.print_z + layer_new.height + - (m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value); + (m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value); layer_new.bottom_z = layer.print_z; layer_new.idx_object_layer_below = layer_id; - layer_new.bridging = ! m_soluble_interface; + layer_new.bridging = ! m_slicing_params.soluble_interface; //FIXME how much to inflate the top surface? layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width())); #ifdef SLIC3R_DEBUG @@ -1011,7 +914,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // Create an EdgeGrid, initialize it with projection, initialize signed distance field. Slic3r::EdgeGrid::Grid grid; - coord_t grid_resolution = scale_(1.5f); + coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing(); + coord_t grid_resolution = scale_(support_spacing); // scale_(1.5f); BoundingBox bbox = get_extents(projection); bbox.offset(20); bbox.align_to_grid(grid_resolution); @@ -1020,7 +924,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta grid.calculate_sdf(); // Extract a bounding contour from the grid. - Polygons projection_simplified = grid.contours_simplified(); + Polygons projection_simplified = grid.contours_simplified(-5); #ifdef SLIC3R_DEBUG { BoundingBox bbox = get_extents(projection); @@ -1031,12 +935,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta svg.draw(union_ex(projection_simplified, false), "red", 0.5); } #endif /* SLIC3R_DEBUG */ - projection = std::move(projection_simplified); + layer_support_areas[layer_id] = diff( + grid.contours_simplified(m_support_material_flow.scaled_spacing()/2 + 5), + to_polygons(layer.slices.expolygons), + true); - // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. + // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. // projection = diff(projection, touching); - projection = diff(projection, to_polygons(layer.slices.expolygons), true); - layer_support_areas[layer_id] = projection; + projection = diff(projection_simplified, to_polygons(layer.slices.expolygons), true); +// layer_support_areas[layer_id] = projection; } std::reverse(bottom_contacts.begin(), bottom_contacts.end()); } // ! top_contacts.empty() @@ -1058,7 +965,7 @@ void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts( // For all top contact layers overlapping with the thick bottom contact layer: for (size_t idx_top = idx_top_first; idx_top < top_contacts.size(); ++ idx_top) { MyLayer &layer_top = *top_contacts[idx_top]; - coordf_t interface_z = m_soluble_interface ? + coordf_t interface_z = m_slicing_params.soluble_interface ? (layer_top.bottom_z + EPSILON) : (layer_top.bottom_z - m_support_layer_height_min); if (interface_z < layer_bottom.print_z) { @@ -1093,9 +1000,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int return intermediate_layers; std::sort(extremes.begin(), extremes.end()); - // Top of the 0th layer. - coordf_t top_z_0th = this->raft_base_height() + this->raft_interface_height(); - assert(extremes.front().z() > top_z_0th && extremes.front().z() >= this->first_layer_height()); + assert(extremes.front().z() > m_slicing_params.raft_interface_top_z && extremes.front().z() >= m_slicing_params.first_print_layer_height); // Generate intermediate layers. // The first intermediate layer is the same as the 1st layer if there is no raft, @@ -1103,7 +1008,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int // Intermediate layers are always printed with a normal etrusion flow (non-bridging). for (size_t idx_extreme = 0; idx_extreme < extremes.size(); ++ idx_extreme) { LayerExtreme *extr1 = (idx_extreme == 0) ? NULL : &extremes[idx_extreme-1]; - coordf_t extr1z = (extr1 == NULL) ? top_z_0th : extr1->z(); + coordf_t extr1z = (extr1 == NULL) ? m_slicing_params.raft_interface_top_z : extr1->z(); LayerExtreme &extr2 = extremes[idx_extreme]; coordf_t extr2z = extr2.z(); coordf_t dist = extr2z - extr1z; @@ -1112,7 +1017,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int size_t n_layers_extra = size_t(ceil(dist / m_support_layer_height_max)); assert(n_layers_extra > 0); coordf_t step = dist / coordf_t(n_layers_extra); - if (! m_soluble_interface && ! m_synchronize_support_layers_with_object && extr2.layer->layer_type == sltTopContact) { + if (! m_slicing_params.soluble_interface && ! m_synchronize_support_layers_with_object && extr2.layer->layer_type == sltTopContact) { assert(extr2.layer->height == 0.); // This is a top interface layer, which does not have a height assigned yet. Do it now. if (m_synchronize_support_layers_with_object) { @@ -1345,43 +1250,74 @@ void PrintObjectSupportMaterial::generate_base_layers( #endif /* SLIC3R_DEBUG */ } -Polygons PrintObjectSupportMaterial::generate_raft_base( +PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raft_base( const PrintObject &object, - const MyLayersPtr &bottom_contacts, - MyLayersPtr &intermediate_layers) const + const MyLayersPtr &top_contacts, + MyLayersPtr &intermediate_layers, + MyLayerStorage &layer_storage) const { - assert(! bottom_contacts.empty()); - + // Areas covered by the raft, supporting the raft interface and the support columns. Polygons raft_polygons; - #if 0 + // How much to inflate the support columns to be stable. This also applies to the 1st layer, if no raft layers are to be printed. const float inflate_factor = scale_(3.); - if (this->has_raft()) { - MyLayer &contacts = *bottom_contacts.front(); - MyLayer &columns_base = *intermediate_layers.front(); - if (m_num_base_raft_layers == 0 && m_num_interface_raft_layers == 0 && m_num_contact_raft_layers == 1) { - // Having only the contact layer, which has the height of the 1st layer. - // We are free to merge the contacts with the columns_base, they will be printed the same way. - polygons_append(contacts.polygons, offset(columns_base.polygons, inflate_factor)); - contacts.polygons = union_(contacts.polygons); - } else { - // Having multiple raft layers. - assert(m_num_interface_raft_layers > 0); - // Extend the raft base by the bases of the support columns, add the raft contacts. - raft_polygons = raft_interface_polygons; - //FIXME make the offset configurable. - polygons_append(raft_polygons, offset(columns_base.polygons, inflate_factor)); - raft_polygons = union_(raft_polygons); - } - } else { - // No raft. The 1st intermediate layer contains the bases of the support columns. - // Expand the polygons, but trim with the object. - MyLayer &columns_base = *intermediate_layers.front(); - columns_base.polygons = diff( - offset(columns_base.polygons, inflate_factor), - offset(m_object->get_layer(0), safety_factor); + MyLayer *contacts = top_contacts.empty() ? nullptr : top_contacts.front(); + MyLayer *columns_base = intermediate_layers.empty() ? nullptr : intermediate_layers.front(); + if (contacts != nullptr && contacts->print_z > m_slicing_params.raft_contact_top_z + EPSILON) + // This is not the raft contact layer. + contacts = nullptr; + + // Output vector. + MyLayersPtr raft_layers; + + // Expand the 1st intermediate layer, which contains the bases of the support columns. + Polygons base; + if (columns_base != nullptr) { + base = offset(columns_base->polygons, inflate_factor); + // Modify the 1st intermediate layer with the expanded support columns. + columns_base->polygons = diff( + base, + offset(m_object->layers.front()->slices.expolygons, scale_(m_gap_xy))); + if (contacts != nullptr) + columns_base->polygons = diff(columns_base->polygons, contacts->polygons); + } + if (m_slicing_params.has_raft() && contacts != nullptr) { + // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. + base = union_(base, offset(contacts->polygons, inflate_factor)); } - #endif - return raft_polygons; + if (m_slicing_params.has_raft() && m_slicing_params.raft_layers() > 1 && ! base.empty()) { + // Do not add the raft contact layer, only add the raft layers below the contact layer. + // Insert the 1st layer. + { + MyLayer &new_layer = layer_allocate(layer_storage, (m_slicing_params.base_raft_layers > 0) ? sltRaftBase : sltRaftInterface); + raft_layers.push_back(&new_layer); + new_layer.print_z = m_slicing_params.first_print_layer_height; + new_layer.height = m_slicing_params.first_print_layer_height; + new_layer.bottom_z = 0.; + new_layer.polygons = base; + } + // Insert the base layers. + for (size_t i = 1; i < m_slicing_params.base_raft_layers; ++ i) { + coordf_t print_z = raft_layers.back()->print_z; + MyLayer &new_layer = layer_allocate(layer_storage, sltRaftBase); + raft_layers.push_back(&new_layer); + new_layer.print_z = print_z + m_slicing_params.base_raft_layer_height; + new_layer.height = m_slicing_params.base_raft_layer_height; + new_layer.bottom_z = print_z; + new_layer.polygons = base; + } + // Insert the interface layers. + for (size_t i = 1; i < m_slicing_params.interface_raft_layers; ++ i) { + coordf_t print_z = raft_layers.back()->print_z; + MyLayer &new_layer = layer_allocate(layer_storage, sltRaftInterface); + raft_layers.push_back(&new_layer); + new_layer.print_z = print_z + m_slicing_params.interface_raft_layer_height; + new_layer.height = m_slicing_params.interface_raft_layer_height; + new_layer.bottom_z = print_z; + new_layer.polygons = base; + } + } + + return raft_layers; } // Convert some of the intermediate layers into top/bottom interface layers. @@ -1474,6 +1410,7 @@ static inline void fill_expolygons_generate_paths( FillParams fill_params; fill_params.density = density; fill_params.complete = true; + fill_params.dont_adjust = true; for (ExPolygons::const_iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) { Surface surface(stInternal, *it_expolygon); extrusion_entities_append_paths( @@ -1495,6 +1432,7 @@ static inline void fill_expolygons_generate_paths( FillParams fill_params; fill_params.density = density; fill_params.complete = true; + fill_params.dont_adjust = true; for (ExPolygons::iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) { Surface surface(stInternal, std::move(*it_expolygon)); extrusion_entities_append_paths( @@ -1636,7 +1574,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const void PrintObjectSupportMaterial::generate_toolpaths( const PrintObject &object, - const Polygons &raft, + const MyLayersPtr &raft_layers, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, const MyLayersPtr &intermediate_layers, @@ -1667,7 +1605,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( std::unique_ptr filler_interface = std::unique_ptr(Fill::new_from_type(ipRectilinear)); std::unique_ptr filler_support = std::unique_ptr(Fill::new_from_type(infill_pattern)); { - BoundingBox bbox_object = object.bounding_box(); +// BoundingBox bbox_object = object.bounding_box(); + BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); filler_interface->set_bounding_box(bbox_object); filler_support->set_bounding_box(bbox_object); } @@ -1694,12 +1633,64 @@ void PrintObjectSupportMaterial::generate_toolpaths( }, ); */ + // Insert the raft base layers. + size_t support_layer_id = 0; + for (; support_layer_id < size_t(std::max(0, int(m_slicing_params.raft_layers()) - 1)); ++ support_layer_id) { + SupportLayer &support_layer = *object.support_layers[support_layer_id]; + assert(support_layer_id < raft_layers.size()); + MyLayer &raft_layer = *raft_layers[support_layer_id]; + //FIXME When paralellizing, each thread shall have its own copy of the fillers. + Fill *filler = filler_support.get(); + filler->angle = 0.; + // We don't use $base_flow->spacing because we need a constant spacing + // value that guarantees that all layers are correctly aligned. + Flow flow(m_support_material_flow.width, raft_layer.height, m_support_material_flow.nozzle_diameter, raft_layer.bridging); + filler->spacing = m_support_material_flow.spacing(); + float density = support_density; + // find centerline of the external loop/extrusions + ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ? + // union_ex(base_polygons, true) : + offset2_ex(raft_layer.polygons, SCALED_EPSILON, - SCALED_EPSILON) : + offset2_ex(raft_layer.polygons, SCALED_EPSILON, - SCALED_EPSILON - 0.5*flow.scaled_width()); + if (support_layer_id == 0) { + // Base flange. + filler = filler_interface.get(); + filler->angle = m_object_config->support_material_angle + 90.; + density = 0.5f; + flow = m_first_layer_flow; + // use the proper spacing for first layer as we don't need to align + // its pattern to the other layers + //FIXME When paralellizing, each thread shall have its own copy of the fillers. + filler->spacing = flow.spacing(); + } else if (with_sheath) { + // Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove. + // TODO: use brim ordering algorithm + Polygons to_infill_polygons = to_polygons(to_infill); + // TODO: use offset2_ex() + to_infill = offset_ex(to_infill, - flow.scaled_spacing()); + extrusion_entities_append_paths( + support_layer.support_fills.entities, + to_polylines(STDMOVE(to_infill_polygons)), + erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); + } + fill_expolygons_generate_paths( + // Destination + support_layer.support_fills.entities, + // Regions to fill + STDMOVE(to_infill), + // Filler and its parameters + filler, density, + // Extrusion parameters + erSupportMaterial, flow); + + } + // Indices of the 1st layer in their respective container at the support layer height. size_t idx_layer_bottom_contact = 0; size_t idx_layer_top_contact = 0; size_t idx_layer_intermediate = 0; size_t idx_layer_inteface = 0; - for (size_t support_layer_id = 0; support_layer_id < object.support_layers.size(); ++ support_layer_id) + for (; support_layer_id < object.support_layers.size(); ++ support_layer_id) { SupportLayer &support_layer = *object.support_layers[support_layer_id]; @@ -1754,7 +1745,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // If no loops are allowed, we treat the contact layer exactly as a generic interface layer. if (interface_layer.could_merge(top_contact_layer)) interface_layer.merge(std::move(top_contact_layer)); - } + } if (! interface_layer.empty() && ! base_layer.empty()) { // turn base support into interface when it's contained in our holes @@ -1814,7 +1805,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. Flow flow(m_support_material_flow.width, base_layer.layer->height, m_support_material_flow.nozzle_diameter, base_layer.layer->bridging); - filler->spacing = flow.spacing(); + filler->spacing = m_support_material_flow.spacing(); float density = support_density; // find centerline of the external loop/extrusions ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ? diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index abcaf676a..c9cbee488 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -3,6 +3,7 @@ #include "Flow.hpp" #include "PrintConfig.hpp" +#include "Slicing.hpp" namespace Slic3r { @@ -22,7 +23,8 @@ class PrintObjectSupportMaterial public: enum SupporLayerType { sltUnknown = 0, - sltRaft, + sltRaftBase, + sltRaftInterface, stlFirstLayer, sltBottomContact, sltBottomInterface, @@ -118,37 +120,17 @@ public: typedef std::deque MyLayerStorage; public: - PrintObjectSupportMaterial(const PrintObject *object); + PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params); // Height of the 1st layer is user configured as it is important for the print // to stick to he print bed. coordf_t first_layer_height() const { return m_object_config->first_layer_height.value; } // Is raft enabled? - bool has_raft() const { return m_has_raft; } + bool has_raft() const { return m_slicing_params.has_raft(); } // Has any support? bool has_support() const { return m_object_config->support_material.value; } - // How many raft layers are there below the 1st object layer? - // The 1st object layer_id will be offsetted by this number. - size_t num_raft_layers() const { return m_object_config->raft_layers.value; } - // num_raft_layers() == num_raft_base_layers() + num_raft_interface_layers() + num_raft_contact_layers(). - size_t num_raft_base_layers() const { return m_num_base_raft_layers; } - size_t num_raft_interface_layers() const { return m_num_interface_raft_layers; } - size_t num_raft_contact_layers() const { return m_num_contact_raft_layers; } - - coordf_t raft_height() const { return m_raft_height; } - coordf_t raft_base_height() const { return m_raft_base_height; } - coordf_t raft_interface_height() const { return m_raft_interface_height; } - coordf_t raft_contact_height() const { return m_raft_contact_height; } - bool raft_bridging() const { return m_raft_contact_layer_bridging; } - - // 1st layer of the object will be printed depeding on the raft settings. - coordf_t first_object_layer_print_z() const { return m_object_1st_layer_print_z; } - coordf_t first_object_layer_height() const { return m_object_1st_layer_height; } - coordf_t first_object_layer_gap() const { return m_object_1st_layer_gap; } - bool first_object_layer_bridging() const { return m_object_1st_layer_bridging; } - // Generate support material for the object. // New support layers will be added to the object, // with extrusion paths and islands filled in for each support layer. @@ -185,10 +167,11 @@ private: MyLayersPtr &intermediate_layers, std::vector &layer_support_areas) const; - Polygons generate_raft_base( + MyLayersPtr generate_raft_base( const PrintObject &object, - const MyLayersPtr &bottom_contacts, - MyLayersPtr &intermediate_layers) const; + const MyLayersPtr &top_contacts, + MyLayersPtr &intermediate_layers, + MyLayerStorage &layer_storage) const; MyLayersPtr generate_interface_layers( const PrintObject &object, @@ -205,7 +188,7 @@ private: // Produce the actual G-code. void generate_toolpaths( const PrintObject &object, - const Polygons &raft, + const MyLayersPtr &raft_layers, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, const MyLayersPtr &intermediate_layers, @@ -214,36 +197,14 @@ private: const PrintObject *m_object; const PrintConfig *m_print_config; const PrintObjectConfig *m_object_config; + SlicingParameters m_slicing_params; Flow m_first_layer_flow; Flow m_support_material_flow; + coordf_t m_support_material_spacing; Flow m_support_material_interface_flow; - bool m_soluble_interface; + coordf_t m_support_material_interface_spacing; - Flow m_support_material_raft_base_flow; - Flow m_support_material_raft_interface_flow; - Flow m_support_material_raft_contact_flow; - - bool m_has_raft; - size_t m_num_base_raft_layers; - size_t m_num_interface_raft_layers; - size_t m_num_contact_raft_layers; - // If set, the raft contact layer is laid with round strings, which are easily detachable - // from both the below and above layes. - // Otherwise a normal flow is used and the strings are squashed against the layer below, - // creating a firm bond with the layer below and making the interface top surface flat. - coordf_t m_raft_height; - coordf_t m_raft_base_height; - coordf_t m_raft_interface_height; - coordf_t m_raft_contact_height; - bool m_raft_contact_layer_bridging; - - coordf_t m_object_1st_layer_print_z; - coordf_t m_object_1st_layer_height; - coordf_t m_object_1st_layer_gap; - bool m_object_1st_layer_bridging; - - coordf_t m_object_layer_height_max; coordf_t m_support_layer_height_min; coordf_t m_support_layer_height_max; coordf_t m_support_interface_layer_height_max; diff --git a/xs/src/libslic3r/SurfaceCollection.cpp b/xs/src/libslic3r/SurfaceCollection.cpp index cfeae4ccc..70272fede 100644 --- a/xs/src/libslic3r/SurfaceCollection.cpp +++ b/xs/src/libslic3r/SurfaceCollection.cpp @@ -8,22 +8,12 @@ namespace Slic3r { SurfaceCollection::operator Polygons() const { - Polygons polygons; - for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { - Polygons surface_p = surface->expolygon; - polygons.insert(polygons.end(), surface_p.begin(), surface_p.end()); - } - return polygons; + return to_polygons(surfaces); } SurfaceCollection::operator ExPolygons() const { - ExPolygons expp; - expp.reserve(this->surfaces.size()); - for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { - expp.push_back(surface->expolygon); - } - return expp; + return to_expolygons(surfaces); } void diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 89db6d81e..d2a3c7c86 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -123,7 +123,7 @@ parallelize(std::queue queue, boost::function func, if (threads_count == 0) threads_count = 2; boost::mutex queue_mutex; boost::thread_group workers; - for (int i = 0; i < fminf(threads_count, queue.size()); i++) + for (int i = 0; i < std::min(threads_count, int(queue.size())); ++ i) workers.add_thread(new boost::thread(&_parallelize_do, &queue, &queue_mutex, func)); workers.join_all(); } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 7461b9d91..4496f303c 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -119,6 +119,7 @@ _constant() void bridge_over_infill(); void _make_perimeters(); void _infill(); + void _generate_support_material(); void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) %code%{ diff --git a/xs/xsp/SupportMaterial.xsp b/xs/xsp/SupportMaterial.xsp deleted file mode 100644 index 01a53efa7..000000000 --- a/xs/xsp/SupportMaterial.xsp +++ /dev/null @@ -1,26 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#include "libslic3r/SupportMaterial.hpp" -%} - -%name{Slic3r::Print::SupportMaterial2} class PrintObjectSupportMaterial { - PrintObjectSupportMaterial(PrintObject *print_object); - ~PrintObjectSupportMaterial(); - - void generate(PrintObject *object) - %code{% THIS->generate(*object); %}; -}; - -%package{Slic3r::Print::SupportMaterial}; -%{ - -SV* -MARGIN() - PROTOTYPE: - CODE: - RETVAL = newSVnv(SUPPORT_MATERIAL_MARGIN); - OUTPUT: RETVAL - -%} From 479f71662516717ea2c996b1241aa80338aaa99c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 21 Dec 2016 16:00:41 +0100 Subject: [PATCH 65/82] Improvements in the manual layer width editor. --- lib/Slic3r/GUI/3DScene.pm | 192 ++++++++++++++++++++++------------- xs/src/libslic3r/Slicing.cpp | 57 +++++++++-- xs/src/libslic3r/Slicing.hpp | 10 +- xs/xsp/Print.xsp | 2 +- 4 files changed, 178 insertions(+), 83 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 8bc57f53d..8acb9ba27 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -19,7 +19,8 @@ package Slic3r::GUI::3DScene::Base; use strict; use warnings; -use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS); +use Wx qw(:timer); +use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_TIMER); # must load OpenGL *before* Wx::GLCanvas use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use base qw(Wx::GLCanvas Class::Accessor); @@ -143,6 +144,10 @@ sub new { $self->{layer_preview_z_texture_width} = 512; $self->{layer_preview_z_texture_height} = 512; $self->{layer_height_edit_band_width} = 2.; + $self->{layer_height_edit_strength} = 0.005; + $self->{layer_height_edit_last_object_id} = -1; + $self->{layer_height_edit_last_z} = 0.; + $self->{layer_height_edit_last_action} = 0; $self->reset_objects; @@ -157,42 +162,24 @@ sub new { $self->Resize( $self->GetSizeWH ); $self->Refresh; }); - EVT_MOUSEWHEEL($self, sub { - my ($self, $e) = @_; - - # Calculate the zoom delta and apply it to the current zoom factor - my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta(); - $zoom = max(min($zoom, 4), -4); - $zoom /= 10; - $self->_zoom($self->_zoom / (1-$zoom)); - - # In order to zoom around the mouse point we need to translate - # the camera target - my $size = Slic3r::Pointf->new($self->GetSizeWH); - my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #- - $self->_camera_target->translate( - # ($pos - $size/2) represents the vector from the viewport center - # to the mouse point. By multiplying it by $zoom we get the new, - # transformed, length of such vector. - # Since we want that point to stay fixed, we move our camera target - # in the opposite direction by the delta of the length of such vector - # ($zoom - 1). We then scale everything by 1/$self->_zoom since - # $self->_camera_target is expressed in terms of model units. - -($pos->x - $size->x/2) * ($zoom) / $self->_zoom, - -($pos->y - $size->y/2) * ($zoom) / $self->_zoom, - 0, - ) if 0; - $self->on_viewport_changed->() if $self->on_viewport_changed; - $self->_dirty(1); - $self->Refresh; - }); + EVT_MOUSEWHEEL($self, \&mouse_wheel_event); EVT_MOUSE_EVENTS($self, \&mouse_event); + $self->{layer_height_edit_timer_id} = &Wx::NewId(); + $self->{layer_height_edit_timer} = Wx::Timer->new($self, $self->{layer_height_edit_timer_id}); + EVT_TIMER($self, $self->{layer_height_edit_timer_id}, sub { + my ($self, $event) = @_; + return if ! $self->_layer_height_edited; + return if $self->{layer_height_edit_last_object_id} == -1; + $self->_variable_layer_thickness_action(undef, 1); + }); + return $self; } sub Destroy { my ($self) = @_; + $self->{layer_height_edit_timer}->Stop; $self->DestroyGL; return $self->SUPER::Destroy; } @@ -207,43 +194,74 @@ sub _first_selected_object_id { return -1; } +# Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. +sub _variable_layer_thickness_bar_rect { + my ($self) = @_; + my ($cw, $ch) = $self->GetSizeWH; + my $bar_width = 70; + return ($cw - $bar_width, 0, $cw, $ch); +} + +sub _variable_layer_thickness_bar_rect_mouse_inside { + my ($self, $mouse_evt) = @_; + my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect; + return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; +} + +sub _variable_layer_thickness_bar_mouse_cursor_z { + my ($self, $object_idx, $mouse_evt) = @_; + my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect; + return unscale($self->{print}->get_object($object_idx)->size->z) * ($bar_bottom - $mouse_evt->GetY - 1.) / ($bar_bottom - $bar_top); +} + +sub _variable_layer_thickness_action { + my ($self, $mouse_event, $do_modification) = @_; + # A volume is selected. Test, whether hovering over a layer thickness bar. + if (defined($mouse_event)) { + $self->{layer_height_edit_last_z} = $self->_variable_layer_thickness_bar_mouse_cursor_z($self->{layer_height_edit_last_object_id}, $mouse_event); + $self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1); + } + if ($self->{layer_height_edit_last_object_id} != -1) { + $self->{print}->get_object($self->{layer_height_edit_last_object_id})->adjust_layer_height_profile( + $self->{layer_height_edit_last_z}, + $self->{layer_height_edit_strength}, + $self->{layer_height_edit_band_width}, + $self->{layer_height_edit_last_action}); + $self->{print}->get_object($self->{layer_height_edit_last_object_id})->generate_layer_height_texture( + $self->volumes->[$self->{layer_height_edit_last_object_id}]->layer_height_texture_data->ptr, + $self->{layer_preview_z_texture_height}, + $self->{layer_preview_z_texture_width}); + $self->Refresh; + # Automatic action on mouse down with the same coordinate. + $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS); + } +} + sub mouse_event { my ($self, $e) = @_; my $pos = Slic3r::Pointf->new($e->GetPositionXY); + my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id : -1; + if ($e->Entering && &Wx::wxMSW) { # wxMSW needs focus in order to catch mouse wheel events $self->SetFocus; } elsif ($e->LeftDClick) { - $self->on_double_click->() - if $self->on_double_click; + if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { + } elsif ($self->on_double_click) { + $self->on_double_click->(); + } } elsif ($e->LeftDown || $e->RightDown) { # If user pressed left or right button we first check whether this happened # on a volume or not. my $volume_idx = $self->_hover_volume_idx // -1; $self->_layer_height_edited(0); - if ($self->layer_editing_enabled && $self->{print}) { - my $object_idx_selected = $self->_first_selected_object_id; - if ($object_idx_selected != -1) { - # A volume is selected. Test, whether hovering over a layer thickness bar. - my ($cw, $ch) = $self->GetSizeWH; - my $bar_width = 70; - if ($e->GetX >= $cw - $bar_width) { - # Start editing the layer height. - $self->_layer_height_edited(1); - my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1); -# print "Modifying height profile at $z\n"; -# $self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightDown ? - 0.05 : 0.05, 2., 0); - $self->{print}->get_object($object_idx_selected)->generate_layer_height_texture( - $self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr, - $self->{layer_preview_z_texture_height}, - $self->{layer_preview_z_texture_width}); - $self->Refresh; - } - } - } - - if (! $self->_layer_height_edited) { + if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { + # A volume is selected and the mouse is hovering over a layer thickness bar. + # Start editing the layer height. + $self->_layer_height_edited(1); + $self->_variable_layer_thickness_action($e, 1); + } else { # Select volume in this 3D canvas. # Don't deselect a volume if layer editing is enabled. We want the object to stay selected # during the scene manipulation. @@ -304,21 +322,8 @@ sub mouse_event { $self->_dragged(1); $self->Refresh; } elsif ($e->Dragging) { - if ($self->_layer_height_edited) { - my $object_idx_selected = $self->_first_selected_object_id; - if ($object_idx_selected != -1) { - # A volume is selected. Test, whether hovering over a layer thickness bar. - my ($cw, $ch) = $self->GetSizeWH; - my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1); -# print "Modifying height profile at $z\n"; - my $strength = 0.005; - $self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightIsDown ? - $strength : $strength, 2., $e->ShiftDown ? 1 : 0); - $self->{print}->get_object($object_idx_selected)->generate_layer_height_texture( - $self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr, - $self->{layer_preview_z_texture_height}, - $self->{layer_preview_z_texture_width}); - $self->Refresh; - } + if ($self->_layer_height_edited && $object_idx_selected != -1) { + $self->_variable_layer_thickness_action($e, 0); } elsif ($e->LeftIsDown) { # if dragging over blank area with left button, rotate if (defined $self->_drag_start_pos) { @@ -375,6 +380,7 @@ sub mouse_event { $self->_drag_start_xy(undef); $self->_dragged(undef); $self->_layer_height_edited(undef); + $self->{layer_height_edit_timer}->Stop; } elsif ($e->Moving) { $self->_mouse_pos($pos); # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor @@ -385,6 +391,49 @@ sub mouse_event { } } +sub mouse_wheel_event { + my ($self, $e) = @_; + + if ($self->layer_editing_enabled && $self->{print}) { + my $object_idx_selected = $self->_first_selected_object_id; + if ($object_idx_selected != -1) { + # A volume is selected. Test, whether hovering over a layer thickness bar. + if ($self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { + # Adjust the width of the selection. + $self->{layer_height_edit_band_width} = max(min($self->{layer_height_edit_band_width} * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5); + $self->Refresh; + return; + } + } + } + + # Calculate the zoom delta and apply it to the current zoom factor + my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta(); + $zoom = max(min($zoom, 4), -4); + $zoom /= 10; + $self->_zoom($self->_zoom / (1-$zoom)); + + # In order to zoom around the mouse point we need to translate + # the camera target + my $size = Slic3r::Pointf->new($self->GetSizeWH); + my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #- + $self->_camera_target->translate( + # ($pos - $size/2) represents the vector from the viewport center + # to the mouse point. By multiplying it by $zoom we get the new, + # transformed, length of such vector. + # Since we want that point to stay fixed, we move our camera target + # in the opposite direction by the delta of the length of such vector + # ($zoom - 1). We then scale everything by 1/$self->_zoom since + # $self->_camera_target is expressed in terms of model units. + -($pos->x - $size->x/2) * ($zoom) / $self->_zoom, + -($pos->y - $size->y/2) * ($zoom) / $self->_zoom, + 0, + ) if 0; + $self->on_viewport_changed->() if $self->on_viewport_changed; + $self->_dirty(1); + $self->Refresh; +} + # Reset selection. sub reset_objects { my ($self) = @_; @@ -1084,14 +1133,17 @@ sub draw_volumes { my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row'); my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized'); my $z_cursor_id = $self->{shader}->Map('z_cursor'); + my $z_cursor_band_width_id = $self->{shader}->Map('z_cursor_band_width'); die if ! defined($z_to_texture_row_id); die if ! defined($z_texture_row_to_normalized_id); die if ! defined($z_cursor_id); + die if ! defined($z_cursor_band_width_id); my $ncells = $volume->{layer_height_texture_cells}; my $z_max = $volume->{bounding_box}->z_max; glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max)); glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height}); glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative); + glUniform1fARB($z_cursor_band_width_id, $self->{layer_height_edit_band_width}); glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LEVEL, 0); # glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); @@ -1421,7 +1473,7 @@ void main() float z_texture_col = object_z_row - z_texture_row; // float z_blend = 0.5 + 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) / 3.))); // float z_blend = 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.5; - float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.25; + float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; // Scale z_texture_row to normalized coordinates. // Sample the Z texture. gl_FragColor = diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp index 5bd6bfc27..8c3902ba3 100644 --- a/xs/src/libslic3r/Slicing.cpp +++ b/xs/src/libslic3r/Slicing.cpp @@ -290,7 +290,7 @@ void adjust_layer_height_profile( coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, - int action) + LayerHeightEditActionType action) { // Constrain the profile variability by the 1st layer height. std::pair z_span_variable = @@ -320,8 +320,10 @@ void adjust_layer_height_profile( // 2) Is it possible to apply the delta? switch (action) { - case 0: - default: + case LAYER_HEIGHT_EDIT_ACTION_DECREASE: + layer_thickness_delta = - layer_thickness_delta; + // fallthrough + case LAYER_HEIGHT_EDIT_ACTION_INCREASE: if (layer_thickness_delta > 0) { if (current_layer_height >= slicing_params.max_layer_height - EPSILON) return; @@ -332,12 +334,16 @@ void adjust_layer_height_profile( layer_thickness_delta = std::max(layer_thickness_delta, slicing_params.min_layer_height - current_layer_height); } break; - case 1: + case LAYER_HEIGHT_EDIT_ACTION_REDUCE: + case LAYER_HEIGHT_EDIT_ACTION_SMOOTH: layer_thickness_delta = std::abs(layer_thickness_delta); layer_thickness_delta = std::min(layer_thickness_delta, std::abs(slicing_params.layer_height - current_layer_height)); if (layer_thickness_delta < EPSILON) return; break; + default: + assert(false); + break; } // 3) Densify the profile inside z +- band_width/2, remove duplicate Zs from the height profile inside the band. @@ -354,6 +360,7 @@ void adjust_layer_height_profile( assert(i >= 0 && i + 1 < layer_height_profile.size()); profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + i + 2); coordf_t zz = lo; + size_t i_resampled_start = profile_new.size(); while (zz < hi) { size_t next = i + 2; coordf_t z1 = layer_height_profile[i]; @@ -368,11 +375,11 @@ void adjust_layer_height_profile( coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.; coordf_t height_new = height; switch (action) { - case 0: - default: + case LAYER_HEIGHT_EDIT_ACTION_INCREASE: + case LAYER_HEIGHT_EDIT_ACTION_DECREASE: height += weight * layer_thickness_delta; break; - case 1: + case LAYER_HEIGHT_EDIT_ACTION_REDUCE: { coordf_t delta = height - slicing_params.layer_height; coordf_t step = weight * layer_thickness_delta; @@ -382,6 +389,14 @@ void adjust_layer_height_profile( height += step; break; } + case LAYER_HEIGHT_EDIT_ACTION_SMOOTH: + { + // Don't modify the profile during resampling process, do it at the next step. + break; + } + default: + assert(false); + break; } // Avoid entering a too short segment. if (profile_new[profile_new.size() - 2] + EPSILON < zz) { @@ -396,15 +411,35 @@ void adjust_layer_height_profile( } i += 2; + assert(i > 0); + size_t i_resampled_end = profile_new.size(); if (i < layer_height_profile.size()) { - if (profile_new[profile_new.size() - 2] + z_step < layer_height_profile[i]) { - profile_new.push_back(profile_new[profile_new.size() - 2] + z_step); - profile_new.push_back(layer_height_profile[i + 1]); - } + assert(zz >= layer_height_profile[i - 2]); + assert(zz <= layer_height_profile[i]); +// profile_new.push_back(zz); +// profile_new.push_back(layer_height_profile[i + 1]); profile_new.insert(profile_new.end(), layer_height_profile.begin() + i, layer_height_profile.end()); } layer_height_profile = std::move(profile_new); + if (action == LAYER_HEIGHT_EDIT_ACTION_SMOOTH) { + size_t n_rounds = 6; + for (size_t i_round = 0; i_round < n_rounds; ++ i_round) { + profile_new = layer_height_profile; + for (size_t i = i_resampled_start; i < i_resampled_end; i += 2) { + coordf_t zz = profile_new[i]; + coordf_t t = std::abs(zz - z) < 0.5 * band_width ? (0.25 + 0.25 * cos(2. * M_PI * (zz - z) / band_width)) : 0.; + assert(t >= 0. && t <= 0.5000001); + if (i == 0) + layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i + 3]; + else if (i + 1 == profile_new.size()) + layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i - 1]; + else + layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + 0.5 * t * (profile_new[i - 1] + profile_new[i + 3]); + } + } + } + assert(layer_height_profile.size() > 2); assert(layer_height_profile.size() % 2 == 0); assert(layer_height_profile[0] == 0.); diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp index 02ae7dd9e..5105b9518 100644 --- a/xs/src/libslic3r/Slicing.hpp +++ b/xs/src/libslic3r/Slicing.hpp @@ -103,13 +103,21 @@ extern std::vector layer_height_profile_adaptive( const t_layer_height_ranges &layer_height_ranges, const ModelVolumePtrs &volumes); + +enum LayerHeightEditActionType { + LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0, + LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1, + LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2, + LAYER_HEIGHT_EDIT_ACTION_SMOOTH = 3 +}; + extern void adjust_layer_height_profile( const SlicingParameters &slicing_params, std::vector &layer_height_profile, coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, - int action); + LayerHeightEditActionType action); // Produce object layers as pairs of low / high layer boundaries, stored into a linear vector. // The object layers are based at z=0, ignoring the raft layers. diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 4496f303c..d2fca6105 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -125,7 +125,7 @@ _constant() %code%{ THIS->update_layer_height_profile(); adjust_layer_height_profile( - THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, action); + THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); %}; int generate_layer_height_texture(void *data, int rows, int cols, bool level_of_detail_2nd_level = true) From 17db5bff8d722369b648d41644782772f285ecd0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 2 Jan 2017 16:51:43 +0100 Subject: [PATCH 66/82] Fix of Number of solid top/bottom layers ignored, Ensure Vertical Shell Thickness interferes with solid top layer count https://github.com/prusa3d/Slic3r/issues/79 https://github.com/prusa3d/Slic3r/issues/60 --- xs/src/libslic3r/ClipperUtils.cpp | 118 ++++++++++++++++++++---------- xs/src/libslic3r/LayerRegion.cpp | 8 +- xs/src/libslic3r/Surface.hpp | 1 + 3 files changed, 87 insertions(+), 40 deletions(-) diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 7625cba49..b04a018a7 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -278,7 +278,9 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, // 3) Subtract holes from the contours. ClipperLib::Paths output; - { + if (holes.empty()) { + output = std::move(contours); + } else { ClipperLib::Clipper clipper; clipper.Clear(); clipper.AddPaths(contours, ClipperLib::ptSubject, true); @@ -291,26 +293,22 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, return output; } -// This is a safe variant of the polygon offset, tailored for a single ExPolygon: -// a single polygon with multiple non-overlapping holes. -// Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours. +// This is a safe variant of the polygons offset, tailored for multiple ExPolygons. +// It is required, that the input expolygons do not overlap and that the holes of each ExPolygon don't intersect with their respective outer contours. +// Each ExPolygon is offsetted separately, then the offsetted ExPolygons are united. ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit) { -// printf("new ExPolygon offset\n"); const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); - ClipperLib::Paths contours; - ClipperLib::Paths holes; - contours.reserve(expolygons.size()); - { - size_t n_holes = 0; - for (size_t i = 0; i < expolygons.size(); ++ i) - n_holes += expolygons[i].holes.size(); - holes.reserve(n_holes); - } - + // Offsetted ExPolygons before they are united. + ClipperLib::Paths contours_cummulative; + contours_cummulative.reserve(expolygons.size()); + // How many non-empty offsetted expolygons were actually collected into contours_cummulative? + // If only one, then there is no need to do a final union. + size_t expolygons_collected = 0; for (Slic3r::ExPolygons::const_iterator it_expoly = expolygons.begin(); it_expoly != expolygons.end(); ++ it_expoly) { // 1) Offset the outer contour. + ClipperLib::Paths contours; { ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(it_expoly->contour); scaleClipperPolygon(input); @@ -320,37 +318,81 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt else co.MiterLimit = miterLimit; co.AddPath(input, joinType, ClipperLib::etClosedPolygon); - ClipperLib::Paths out; - co.Execute(out, delta_scaled); - contours.insert(contours.end(), out.begin(), out.end()); + co.Execute(contours, delta_scaled); } + if (contours.empty()) + // No need to try to offset the holes. + continue; - // 2) Offset the holes one by one, collect the results. - { - for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) { - ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole); - scaleClipperPolygon(input); - ClipperLib::ClipperOffset co; - if (joinType == jtRound) - co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); - else - co.MiterLimit = miterLimit; - co.AddPath(input, joinType, ClipperLib::etClosedPolygon); - ClipperLib::Paths out; - co.Execute(out, - delta_scaled); - holes.insert(holes.end(), out.begin(), out.end()); + if (it_expoly->holes.empty()) { + // No need to subtract holes from the offsetted expolygon, we are done. + contours_cummulative.insert(contours_cummulative.end(), contours.begin(), contours.end()); + ++ expolygons_collected; + } else { + // 2) Offset the holes one by one, collect the offsetted holes. + ClipperLib::Paths holes; + { + for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) { + ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole); + scaleClipperPolygon(input); + ClipperLib::ClipperOffset co; + if (joinType == jtRound) + co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); + else + co.MiterLimit = miterLimit; + co.AddPath(input, joinType, ClipperLib::etClosedPolygon); + ClipperLib::Paths out; + co.Execute(out, - delta_scaled); + holes.insert(holes.end(), out.begin(), out.end()); + } + } + + // 3) Subtract holes from the contours. + if (holes.empty()) { + // No hole remaining after an offset. Just copy the outer contour. + contours_cummulative.insert(contours_cummulative.end(), contours.begin(), contours.end()); + ++ expolygons_collected; + } else if (delta < 0) { + // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. + // Subtract the offsetted holes from the offsetted contours. + ClipperLib::Clipper clipper; + clipper.Clear(); + clipper.AddPaths(contours, ClipperLib::ptSubject, true); + clipper.AddPaths(holes, ClipperLib::ptClip, true); + ClipperLib::Paths output; + clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + if (! output.empty()) { + contours_cummulative.insert(contours_cummulative.end(), output.begin(), output.end()); + ++ expolygons_collected; + } else { + // The offsetted holes have eaten up the offsetted outer contour. + } + } else { + // Positive offset. As long as the Clipper offset does what one expects it to do, the offsetted hole will have a smaller + // area than the original hole or even disappear, therefore there will be no new intersections. + // Just collect the reversed holes. + contours_cummulative.reserve(contours.size() + holes.size()); + contours_cummulative.insert(contours_cummulative.end(), contours.begin(), contours.end()); + // Reverse the holes in place. + for (size_t i = 0; i < holes.size(); ++ i) + std::reverse(holes[i].begin(), holes[i].end()); + contours_cummulative.insert(contours_cummulative.end(), holes.begin(), holes.end()); + ++ expolygons_collected; } } } - // 3) Subtract holes from the contours. + // 4) Unite the offsetted expolygons. ClipperLib::Paths output; - { + if (expolygons_collected > 1 && delta > 0) { + // There is a chance that the outwards offsetted expolygons may intersect. Perform a union. ClipperLib::Clipper clipper; - clipper.Clear(); - clipper.AddPaths(contours, ClipperLib::ptSubject, true); - clipper.AddPaths(holes, ClipperLib::ptClip, true); - clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + clipper.Clear(); + clipper.AddPaths(contours_cummulative, ClipperLib::ptSubject, true); + clipper.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + } else { + // Negative offset. The shrunk expolygons shall not mutually intersect. Just copy the output. + output = std::move(contours_cummulative); } // 4) Unscale the output. diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index f0063114a..5536baf74 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -302,8 +302,10 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) polygons_append(polys, STDMOVE(s1)); for (size_t j = i + 1; j < top.size(); ++ j) { Surface &s2 = top[j]; - if (! s2.empty() && surfaces_could_merge(s1, s2)) + if (! s2.empty() && surfaces_could_merge(s1, s2)) { polygons_append(polys, STDMOVE(s2)); + s2.clear(); + } } if (s1.surface_type == stTop) // Trim the top surfaces by the bottom surfaces. This gives the priority to the bottom surfaces. @@ -326,8 +328,10 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) polygons_append(polys, STDMOVE(s1)); for (size_t j = i + 1; j < internal.size(); ++ j) { Surface &s2 = internal[j]; - if (! s2.empty() && surfaces_could_merge(s1, s2)) + if (! s2.empty() && surfaces_could_merge(s1, s2)) { polygons_append(polys, STDMOVE(s2)); + s2.clear(); + } } ExPolygons new_expolys = diff_ex(polys, new_polygons); polygons_append(new_polygons, to_polygons(new_expolys)); diff --git a/xs/src/libslic3r/Surface.hpp b/xs/src/libslic3r/Surface.hpp index adf71cc06..9ad86d0ed 100644 --- a/xs/src/libslic3r/Surface.hpp +++ b/xs/src/libslic3r/Surface.hpp @@ -57,6 +57,7 @@ public: operator Polygons() const; double area() const; bool empty() const { return expolygon.empty(); } + void clear() { expolygon.clear(); } bool is_solid() const; bool is_external() const; bool is_internal() const; From 7a2572a0fbade0f30e75951da9b3d8ecf027395d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 3 Jan 2017 10:51:19 +0100 Subject: [PATCH 67/82] Improved debugging of "ensure vertical wall thickness" feature. --- xs/src/libslic3r/PrintObject.cpp | 61 ++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 3c92aad24..e8184f578 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -8,6 +8,10 @@ #include +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING +#define SLIC3R_DEBUG +#endif + // #define SLIC3R_DEBUG // Make assert active if SLIC3R_DEBUG @@ -537,8 +541,19 @@ PrintObject::discover_vertical_shells() { PROFILE_BLOCK(discover_vertical_shells_region_layer); +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + static size_t debug_idx = 0; + ++ debug_idx; +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + Layer *layer = this->layers[idx_layer]; LayerRegion *layerm = layer->get_region(idx_region); + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial"); + layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-initial"); +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + Flow solid_infill_flow = layerm->flow(frSolidInfill); coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness. @@ -551,16 +566,16 @@ PrintObject::discover_vertical_shells() if (1) { PROFILE_BLOCK(discover_vertical_shells_region_layer_collect); -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING +#if 0 +// #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { - static size_t idx = 0; - SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", idx), this->bounding_box()); + Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box()); for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) { if (n < 0 || n >= (int)this->layers.size()) continue; ExPolygons &expolys = this->layers[n]->perimeter_expolygons; for (size_t i = 0; i < expolys.size(); ++ i) { - SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", idx, n, i), get_extents(expolys[i])); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i])); svg.draw(expolys[i]); svg.draw_outline(expolys[i].contour, "black", scale_(0.05)); svg.draw_outline(expolys[i].holes, "blue", scale_(0.05)); @@ -571,7 +586,6 @@ PrintObject::discover_vertical_shells() svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05)); } } - ++ idx; } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // Reset the top / bottom inflated regions caches of entries, which are out of the moving window. @@ -629,8 +643,7 @@ PrintObject::discover_vertical_shells() } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { - static size_t idx = 0; - SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", idx ++), get_extents(shell)); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell); svg.draw_outline(shell, "black", scale_(0.05)); svg.Close(); @@ -653,8 +666,7 @@ PrintObject::discover_vertical_shells() #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { - static size_t idx = 0; - SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", idx ++), get_extents(shell)); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell_ex); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); @@ -663,8 +675,7 @@ PrintObject::discover_vertical_shells() #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { - static size_t idx = 0; - SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", idx ++), get_extents(shell)); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternal), "yellow", 0.5); svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternal), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); @@ -672,8 +683,7 @@ PrintObject::discover_vertical_shells() svg.Close(); } { - static size_t idx = 0; - SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell)); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); @@ -681,8 +691,7 @@ PrintObject::discover_vertical_shells() svg.Close(); } { - static size_t idx = 0; - SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell)); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); @@ -693,7 +702,7 @@ PrintObject::discover_vertical_shells() // Trim the shells region by the internal & internal void surfaces. const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid, stInternalSolid }; - const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 2)); + const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 3)); shell = intersection(shell, polygonsInternal, true); polygons_append(shell, diff(polygonsInternal, holes)); if (shell.empty()) @@ -735,8 +744,7 @@ PrintObject::discover_vertical_shells() ExPolygons new_internal_solid = intersection_ex(polygonsInternal, shell, false); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { - static size_t idx = 0; - SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", idx ++), get_extents(shell_before)); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before)); // Source shell. svg.draw(union_ex(shell_before, true)); // Shell trimmed to the internal surfaces. @@ -761,11 +769,9 @@ PrintObject::discover_vertical_shells() #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { - static size_t idx = 0; - SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05)); - SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05)); - SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05)); - ++ idx; + SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", debug_idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05)); + SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", debug_idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05)); + SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", debug_idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05)); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -775,12 +781,15 @@ PrintObject::discover_vertical_shells() layerm->fill_surfaces.append(new_internal, stInternal); layerm->fill_surfaces.append(new_internal_void, stInternalVoid); layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); + } // for each layer #ifdef SLIC3R_DEBUG_SLICE_PROCESSING - layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells"); - layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells"); + for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++idx_layer) { + LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); + layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final"); + layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final"); + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - } // for each layer } // for each region // Write the profiler measurements to file From 4b2bc29b148b264e9cb3cbae7da5757df472dd8b Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 9 Apr 2016 12:54:32 -0500 Subject: [PATCH 68/82] set support spacing option if support is off --- lib/Slic3r/GUI/Tab.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index a744b4c47..8bb06c63d 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -914,7 +914,7 @@ sub _update { for qw(support_material_threshold support_material_pattern support_material_with_sheath support_material_spacing support_material_synchronize_layers support_material_angle support_material_interface_layers dont_support_bridges - support_material_extrusion_width support_material_contact_distance); + support_material_extrusion_width support_material_contact_distance support_material_xy_spacing); $self->get_field($_)->toggle($have_support_material && $have_support_interface) for qw(support_material_interface_spacing support_material_interface_extruder support_material_interface_speed support_material_interface_contact_loops); From f63c21f0f3d1e29fae0af7165bc838927dafbcbf Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 9 Apr 2016 12:47:08 -0500 Subject: [PATCH 69/82] Added a feature to increase gap between object in XY and generated supports. Advanced feature, may not generate supports that are printable. --- lib/Slic3r/Print/SupportMaterial.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 3dd221c1c..e8b3a1316 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -372,7 +372,7 @@ sub object_top { # grow top surfaces so that interface and support generation are generated # with some spacing from object - it looks we don't need the actual # top shapes so this can be done here - $top{ $layer->print_z } = offset($touching, $self->flow->scaled_width); + $top{ $layer->print_z } = offset($touching, $self->flow->scaled_width + ($self->object_config->support_material_xy_spacing / 0.000001) ); } # remove the areas that touched from the projection that will continue on @@ -627,7 +627,7 @@ sub clip_with_object { # We leave a gap equal to a full extrusion width. $support->{$i} = diff( $support->{$i}, - offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width), + offset([ map @$_, map @{$_->slices}, @layers ], +($self->flow->scaled_width +($self->object_config->support_material_xy_spacing / 0.000001))), ); } } @@ -640,7 +640,7 @@ sub generate_toolpaths { # shape of contact area my $contact_loops = $self->object_config->support_material_interface_contact_loops ? 1 : 0; - my $circle_radius = 1.5 * $interface_flow->scaled_width; + my $circle_radius = 1.5 * $interface_flow->scaled_width - ($self->object_config->support_material_xy_spacing / 0.000001) ; my $circle_distance = 3 * $circle_radius; my $circle = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ], (5*PI/3, 4*PI/3, PI, 2*PI/3, PI/3, 0)); @@ -710,7 +710,7 @@ sub generate_toolpaths { # generate the outermost loop # find centerline of the external loop (or any other kind of extrusions should the loop be skipped) - $contact = offset($contact, -$_interface_flow->scaled_width/2); + $contact = offset($contact, -($_interface_flow->scaled_width + ($self->object_config->support_material_xy_spacing / 0.000001)) /2); my @loops0 = (); { From bb22f1dc8a73b782564ae2b8eb6d5f6b30f89a10 Mon Sep 17 00:00:00 2001 From: slicer-builder Date: Sat, 19 Mar 2016 22:30:02 -0500 Subject: [PATCH 70/82] prototyping tweakable XY supports; option should be present now in advanced support menu. Can't figure out what's going on atm; support should be moving but isn't --- lib/Slic3r/GUI/Tab.pm | 2 ++ lib/Slic3r/Print/SupportMaterial.pm | 7 ++++--- slic3r.pl | 3 +++ xs/src/libslic3r/PrintConfig.cpp | 7 +++++++ xs/src/libslic3r/PrintConfig.hpp | 2 ++ xs/src/libslic3r/PrintObject.cpp | 1 + 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 8bb06c63d..3cf914862 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -523,6 +523,7 @@ sub build { max_volumetric_extrusion_rate_slope_positive max_volumetric_extrusion_rate_slope_negative perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed support_material_speed + support_material_xy_spacing support_material_interface_speed bridge_speed gap_fill_speed travel_speed first_layer_speed @@ -648,6 +649,7 @@ sub build { $optgroup->append_single_option_line('support_material_interface_spacing'); $optgroup->append_single_option_line('support_material_interface_contact_loops'); $optgroup->append_single_option_line('support_material_buildplate_only'); + $optgroup->append_single_option_line('support_material_xy_spacing'); $optgroup->append_single_option_line('dont_support_bridges'); $optgroup->append_single_option_line('support_material_synchronize_layers'); } diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index e8b3a1316..72f09269f 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -372,7 +372,7 @@ sub object_top { # grow top surfaces so that interface and support generation are generated # with some spacing from object - it looks we don't need the actual # top shapes so this can be done here - $top{ $layer->print_z } = offset($touching, $self->flow->scaled_width + ($self->object_config->support_material_xy_spacing / 0.000001) ); + $top{ $layer->print_z } = offset($touching, $self->flow->scaled_width + $self->object_config->support_material_xy_spacing); } # remove the areas that touched from the projection that will continue on @@ -624,10 +624,11 @@ sub clip_with_object { # $layer->slices contains the full shape of layer, thus including # perimeter's width. $support contains the full shape of support # material, thus including the width of its foremost extrusion. - # We leave a gap equal to a full extrusion width. + # We leave a gap equal to a full extrusion width + an offset + # if the user wants to play around with this setting. $support->{$i} = diff( $support->{$i}, - offset([ map @$_, map @{$_->slices}, @layers ], +($self->flow->scaled_width +($self->object_config->support_material_xy_spacing / 0.000001))), + offset([ map @$_, map @{$_->slices}, @layers ], +$self->object_config->support_material_xy_spacing), ); } } diff --git a/slic3r.pl b/slic3r.pl index f6fe4bb4f..651efa483 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -426,6 +426,9 @@ $j Support material angle in degrees (range: 0-90, default: $config->{support_material_angle}) --support-material-contact-distance Vertical distance between object and support material (0+, default: $config->{support_material_contact_distance}) + + --support-material-xy-spacing + horizontal distance between object and support material (0+, default: $config->{support_material_xy_spacing}) --support-material-interface-layers Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers}) --support-material-interface-spacing diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 25f0c626f..c530b30aa 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1121,6 +1121,13 @@ PrintConfigDef::PrintConfigDef() def->cli = "support-material!"; def->default_value = new ConfigOptionBool(false); + def = this->add("support_material_xy_spacing", coFloat); + def->label = "Extra separation between part and support (mm)"; + def->category = "Support material"; + def->tooltip = "Add offset to support material separation (0 is 1 extrusion width)."; + def->cli = "support-material-xy-spacing=f"; + def->default_value = new ConfigOptionFloat(0.0); + def = this->add("support_material_angle", coInt); def->label = "Pattern angle"; def->category = "Support material"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 8e66d1482..c14889c88 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -164,6 +164,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig ConfigOptionBool support_material_synchronize_layers; ConfigOptionInt support_material_threshold; ConfigOptionBool support_material_with_sheath; + ConfigOptionFloat support_material_xy_spacing; ConfigOptionFloat xy_size_compensation; PrintObjectConfig(bool initialize = true) : StaticPrintConfig() { @@ -198,6 +199,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig OPT_PTR(support_material_spacing); OPT_PTR(support_material_speed); OPT_PTR(support_material_synchronize_layers); + OPT_PTR(support_material_xy_spacing); OPT_PTR(support_material_threshold); OPT_PTR(support_material_with_sheath); OPT_PTR(xy_size_compensation); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 93a924b1d..c323f5d9d 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -221,6 +221,7 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Thu, 5 Jan 2017 09:09:26 +0100 Subject: [PATCH 71/82] Improved debugging of the EdgeGrid distance field function. --- xs/src/libslic3r/EdgeGrid.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp index b39b12ab8..07d08f390 100644 --- a/xs/src/libslic3r/EdgeGrid.cpp +++ b/xs/src/libslic3r/EdgeGrid.cpp @@ -839,6 +839,8 @@ void EdgeGrid::Grid::calculate_sdf() } #if 0 + static int iRun = 0; + ++ iRun; //#ifdef SLIC3R_GUI { wxImage img(ncols, nrows); @@ -862,7 +864,7 @@ void EdgeGrid::Grid::calculate_sdf() } } } - img.SaveFile(debug_out_path("unsigned_df.png"), wxBITMAP_TYPE_PNG); + img.SaveFile(debug_out_path("unsigned_df-%d.png", iRun), wxBITMAP_TYPE_PNG); } { wxImage img(ncols, nrows); @@ -895,7 +897,7 @@ void EdgeGrid::Grid::calculate_sdf() } } } - img.SaveFile(debug_out_path("signed_df.png"), wxBITMAP_TYPE_PNG); + img.SaveFile(debug_out_path("signed_df-%d.png", iRun), wxBITMAP_TYPE_PNG); } #endif /* SLIC3R_GUI */ @@ -1020,7 +1022,7 @@ void EdgeGrid::Grid::calculate_sdf() } } } - img.SaveFile(debug_out_path("signed_df-signs.png"), wxBITMAP_TYPE_PNG); + img.SaveFile(debug_out_path("signed_df-signs-%d.png", iRun), wxBITMAP_TYPE_PNG); } #endif /* SLIC3R_GUI */ @@ -1049,7 +1051,7 @@ void EdgeGrid::Grid::calculate_sdf() } } } - img.SaveFile(debug_out_path("signed_df2.png"), wxBITMAP_TYPE_PNG); + img.SaveFile(debug_out_path("signed_df2-%d.png", iRun), wxBITMAP_TYPE_PNG); } #endif /* SLIC3R_GUI */ } @@ -1364,17 +1366,18 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo ++iRun; const coord_t search_radius = grid.resolution() * 2; - const coord_t display_blend_radius = grid.resolution() * 5; + const coord_t display_blend_radius = grid.resolution() * 2; for (coord_t r = 0; r < h; ++r) { for (coord_t c = 0; c < w; ++ c) { unsigned char *pxl = data + (((h - r - 1) * w) + c) * 3; Point pt(c * resolution + bbox.min.x, r * resolution + bbox.min.y); coordf_t min_dist; - bool on_segment; -// if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) { + bool on_segment = true; + #if 0 + if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) { + #else if (grid.signed_distance(pt, search_radius, min_dist)) { - //FIXME - on_segment = true; + #endif float s = 255 * std::abs(min_dist) / float(display_blend_radius); int is = std::max(0, std::min(255, int(floor(s + 0.5f)))); if (min_dist < 0) { @@ -1383,9 +1386,9 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo pxl[1] = 255 - is; pxl[2] = 255 - is; } else { - pxl[0] = 128; - pxl[1] = 128; - pxl[2] = 255 - is; + pxl[0] = 255; + pxl[1] = 0; + pxl[2] = 255 - is; } } else { From b42c5d6dfa242c2001a36aa2127f3c191a605cf1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 5 Jan 2017 09:10:16 +0100 Subject: [PATCH 72/82] Extended the SVG exporting class with export_expolygons() calculating the contour bounds internally. --- xs/src/libslic3r/SVG.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/SVG.hpp b/xs/src/libslic3r/SVG.hpp index 83bb586c9..3d459804c 100644 --- a/xs/src/libslic3r/SVG.hpp +++ b/xs/src/libslic3r/SVG.hpp @@ -12,7 +12,7 @@ namespace Slic3r { class SVG { - public: +public: bool arrows; std::string fill, stroke; Point origin; @@ -89,6 +89,10 @@ public: static void export_expolygons(const char *path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0); static void export_expolygons(const std::string &path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0) { export_expolygons(path.c_str(), bbox, expolygons, stroke_outer, stroke_holes, stroke_width); } + static void export_expolygons(const char *path, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0) + { export_expolygons(path, get_extents(expolygons), expolygons, stroke_outer, stroke_holes, stroke_width); } + static void export_expolygons(const std::string &path, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0) + { export_expolygons(path.c_str(), get_extents(expolygons), expolygons, stroke_outer, stroke_holes, stroke_width); } }; } From bbdaa44acb4d5147a957988e76debd8c4f13d86b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 5 Jan 2017 09:11:36 +0100 Subject: [PATCH 73/82] Const version of a PrintObject::get_region(). Made get_region() inline. --- xs/src/libslic3r/Print.cpp | 6 ------ xs/src/libslic3r/Print.hpp | 3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 3583c627b..e04d4ac79 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -133,12 +133,6 @@ Print::clear_regions() this->delete_region(i); } -PrintRegion* -Print::get_region(size_t idx) -{ - return regions.at(idx); -} - PrintRegion* Print::add_region() { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index e8b0e0a03..a0392290d 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -206,7 +206,8 @@ class Print bool reload_model_instances(); // methods for handling regions - PrintRegion* get_region(size_t idx); + PrintRegion* get_region(size_t idx) { return regions.at(idx); } + const PrintRegion* get_region(size_t idx) const { return regions.at(idx); } PrintRegion* add_region(); // methods for handling state From 1ba03af2da2203564f3919929ca56893ede922d0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 5 Jan 2017 09:12:24 +0100 Subject: [PATCH 74/82] Made the config value support_material_xy_spacing relative to the external perimeter width. --- slic3r.pl | 4 ++-- xs/src/libslic3r/PrintConfig.cpp | 14 +++++++++----- xs/src/libslic3r/PrintConfig.hpp | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/slic3r.pl b/slic3r.pl index 651efa483..a593f93c9 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -426,9 +426,9 @@ $j Support material angle in degrees (range: 0-90, default: $config->{support_material_angle}) --support-material-contact-distance Vertical distance between object and support material (0+, default: $config->{support_material_contact_distance}) - --support-material-xy-spacing - horizontal distance between object and support material (0+, default: $config->{support_material_xy_spacing}) + "XY separation between an object and its support. If expressed as percentage (for example 50%), + it will be calculated over external perimeter width (default: half of exteral perimeter width) --support-material-interface-layers Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers}) --support-material-interface-spacing diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index c530b30aa..8556783fc 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1121,12 +1121,16 @@ PrintConfigDef::PrintConfigDef() def->cli = "support-material!"; def->default_value = new ConfigOptionBool(false); - def = this->add("support_material_xy_spacing", coFloat); - def->label = "Extra separation between part and support (mm)"; + def = this->add("support_material_xy_spacing", coFloatOrPercent); + def->label = "XY separation between an object and its support"; def->category = "Support material"; - def->tooltip = "Add offset to support material separation (0 is 1 extrusion width)."; - def->cli = "support-material-xy-spacing=f"; - def->default_value = new ConfigOptionFloat(0.0); + def->tooltip = "XY separation between an object and its support. If expressed as percentage (for example 50%), it will be calculated over external perimeter width."; + def->sidetext = "mm or %"; + def->cli = "support-material-xy-spacing=s"; + def->ratio_over = "external_perimeter_extrusion_width"; + def->min = 0; + // Default is half the external perimeter width. + def->default_value = new ConfigOptionFloatOrPercent(50, true); def = this->add("support_material_angle", coInt); def->label = "Pattern angle"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index c14889c88..c8933bcd9 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -164,7 +164,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig ConfigOptionBool support_material_synchronize_layers; ConfigOptionInt support_material_threshold; ConfigOptionBool support_material_with_sheath; - ConfigOptionFloat support_material_xy_spacing; + ConfigOptionFloatOrPercent support_material_xy_spacing; ConfigOptionFloat xy_size_compensation; PrintObjectConfig(bool initialize = true) : StaticPrintConfig() { From d9ea3df85f49eb735dc56ad9d7c599824433512b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 5 Jan 2017 09:14:59 +0100 Subject: [PATCH 75/82] Another step to make the new C++ supports working. Added support_material_xy_spacing configuration. --- xs/src/libslic3r/SupportMaterial.cpp | 873 ++++++++++++--------------- xs/src/libslic3r/SupportMaterial.hpp | 84 +-- 2 files changed, 426 insertions(+), 531 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 7806e36a5..7b07fdf74 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -31,6 +31,10 @@ namespace Slic3r { #define PILLAR_SIZE (2.5) #define PILLAR_SPACING 10 +//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3. +//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 +#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. + PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : m_object (object), m_print_config (&object->print()->config), @@ -41,41 +45,43 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width, - object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1), - slicing_params.first_print_layer_height, - false - )), + float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), + float(slicing_params.first_print_layer_height), + false)), m_support_material_flow (Flow::new_from_config_width( frSupportMaterial, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, - object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1), - slicing_params.layer_height, + float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), + float(slicing_params.layer_height), false)), m_support_material_interface_flow(Flow::new_from_config_width( frSupportMaterialInterface, // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, - object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1), - object->config.layer_height.value, + float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)), + float(slicing_params.layer_height), false)), // 50 mirons layer m_support_layer_height_min (0.05), - m_support_layer_height_max (0.), - m_support_interface_layer_height_max(0.), - - m_gap_extra_above (0.2), - m_gap_extra_below (0.2), - m_gap_xy (0.2), - - // If enabled, the support layers will be synchronized with object layers. - // This does not prevent the support layers to be combined. - m_synchronize_support_layers_with_object(false), - // If disabled and m_synchronize_support_layers_with_object, - // the support layers will be synchronized with the object layers exactly, no layer will be combined. - m_combine_support_layers (true) + m_support_layer_height_max (0.) { + if (m_object_config->support_material_interface_layers.value == 0) { + // No interface layers allowed, print everything with the base support pattern. + m_support_material_interface_flow = m_support_material_flow; + } + + // Evaluate the XY gap between the object outer perimeters and the support structures. + coordf_t external_perimeter_width = 0.; + for (std::map>::const_iterator it_region = object->region_volumes.begin(); it_region != object->region_volumes.end(); ++ it_region) { + const PrintRegionConfig &config = object->print()->get_region(it_region->first)->config; + coordf_t width = config.external_perimeter_extrusion_width.get_abs_value(slicing_params.layer_height); + if (width <= 0.) + width = m_print_config->nozzle_diameter.get_at(config.perimeter_extruder-1); + external_perimeter_width = std::max(external_perimeter_width, width); + } + m_gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); } // Using the std::deque as an allocator. @@ -107,12 +113,11 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) coordf_t max_object_layer_height = 0.; for (size_t i = 0; i < object.layer_count(); ++ i) - max_object_layer_height = std::max(max_object_layer_height, object.get_layer(i)->height); + max_object_layer_height = std::max(max_object_layer_height, object.layers[i]->height); if (m_support_layer_height_max == 0) m_support_layer_height_max = std::max(max_object_layer_height, 0.75 * m_support_material_flow.nozzle_diameter); - if (m_support_interface_layer_height_max == 0) - m_support_interface_layer_height_max = std::max(max_object_layer_height, 0.75 * m_support_material_interface_flow.nozzle_diameter); +// m_support_interface_layer_height_max = std::max(max_object_layer_height, 0.75 * m_support_material_interface_flow.nozzle_diameter); // Layer instances will be allocated by std::deque and they will be kept until the end of this function call. // The layers will be referenced by various LayersPtr (of type std::vector) @@ -134,60 +139,67 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG static int iRun = 0; iRun ++; - for (MyLayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) { - const MyLayer &layer = *(*it); - ::Slic3r::SVG svg(debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer.print_z), get_extents(layer.polygons)); - Slic3r::ExPolygons expolys = union_ex(layer.polygons, false); - svg.draw(expolys); - } + for (MyLayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) + Slic3r::SVG::export_expolygons( + debug_out_path("support-top-contacts-%d-%lf.svg", iRun, (*it)->print_z), + union_ex((*it)->polygons, false)); #endif /* SLIC3R_DEBUG */ BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts"; // Determine the bottom contact surfaces of the supports over the top surfaces of the object. // Depending on whether the support is soluble or not, the contact layer thickness is decided. + // layer_support_areas contains the per object layer support areas. These per object layer support areas + // may get merged and trimmed by this->generate_base_layers() if the support layers are not synchronized with object layers. std::vector layer_support_areas; MyLayersPtr bottom_contacts = this->bottom_contact_layers_and_layer_support_areas( object, top_contacts, layer_storage, layer_support_areas); +#ifdef SLIC3R_DEBUG + for (size_t layer_id = 0; layer_id < object.layers.size(); ++ layer_id) + Slic3r::SVG::export_expolygons( + debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers[layer_id]->print_z), + union_ex(layer_support_areas[layer_id], false)); +#endif /* SLIC3R_DEBUG */ + BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts"; // Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion // and unwanted strong bonds to the object. - // Rather trim the top contacts by their overlapping bottom contacts to leave a gap instead of over extruding. + // Rather trim the top contacts by their overlapping bottom contacts to leave a gap instead of over extruding + // top contacts over the bottom contacts. this->trim_top_contacts_by_bottom_contacts(object, bottom_contacts, top_contacts); BOOST_LOG_TRIVIAL(info) << "Support generator - Creating intermediate layers - indices"; - // Generate empty intermediate layers between the top / bottom support contact layers, + // Allocate empty layers between the top / bottom support contact layers + // as placeholders for the base and intermediate support layers. // The layers may or may not be synchronized with the object layers, depending on the configuration. // For example, a single nozzle multi material printing will need to generate a waste tower, which in turn - // wastes less material, if there are as little layers as possible, therefore minimizing the material swaps. + // wastes less material, if there are as little tool changes as possible. MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers( object, bottom_contacts, top_contacts, layer_storage, max_object_layer_height); + this->trim_support_layers_by_object(object, top_contacts, m_support_layer_height_min, 0., m_gap_xy); + BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers"; - // Fill in intermediate layers between the top / bottom support contact layers, trimmed by the object. + // Fill in intermediate layers between the top / bottom support contact layers, trimm them by the object. this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers, layer_support_areas); #ifdef SLIC3R_DEBUG - for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) { - const MyLayer &layer = *(*it); - ::Slic3r::SVG svg(debug_out_path("support-base-layers-%d-%lf.svg", iRun, layer.print_z), get_extents(layer.polygons)); - Slic3r::ExPolygons expolys = union_ex(layer.polygons, false); - svg.draw(expolys); - } + for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) + Slic3r::SVG::export_expolygons( + debug_out_path("support-base-layers-%d-%lf.svg", iRun, (*it)->print_z), + union_ex((*it)->polygons, false)); #endif /* SLIC3R_DEBUG */ BOOST_LOG_TRIVIAL(info) << "Support generator - Creating raft"; - // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette without holes. - // Add the bottom contacts to the raft, inflate the support bases. - // There is a contact layer below the 1st object layer in the bottom contacts. + // If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled. // There is also a 1st intermediate layer containing bases of support columns. - // Extend the bases of the support columns and create the raft base. + // Inflate the bases of the support columns and create the raft base under the object. MyLayersPtr raft_layers = this->generate_raft_base(object, top_contacts, intermediate_layers, layer_storage); /* @@ -205,12 +217,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) object, bottom_contacts, top_contacts, intermediate_layers, layer_storage); #ifdef SLIC3R_DEBUG - for (MyLayersPtr::const_iterator it = interface_layers.begin(); it != interface_layers.end(); ++ it) { - const MyLayer &layer = *(*it); - ::Slic3r::SVG svg(debug_out_path("support-interface-layers-%d-%lf.svg", iRun, layer.print_z), get_extents(layer.polygons)); - Slic3r::ExPolygons expolys = union_ex(layer.polygons, false); - svg.draw(expolys); - } + for (MyLayersPtr::const_iterator it = interface_layers.begin(); it != interface_layers.end(); ++ it) + Slic3r::SVG::export_expolygons( + debug_out_path("support-interface-layers-%d-%lf.svg", iRun, (*it)->print_z), + union_ex((*it)->polygons, false)); #endif /* SLIC3R_DEBUG */ /* @@ -223,7 +233,14 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) BOOST_LOG_TRIVIAL(info) << "Support generator - Creating layers"; +// raft_layers.clear(); +// bottom_contacts.clear(); +// top_contacts.clear(); +// intermediate_layers.clear(); +// interface_layers.clear(); + // Install support layers into the object. + // A support layer installed on a PrintObject has a unique print_z. MyLayersPtr layers_sorted; layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size()); layers_append(layers_sorted, raft_layers); @@ -231,8 +248,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) layers_append(layers_sorted, top_contacts); layers_append(layers_sorted, intermediate_layers); layers_append(layers_sorted, interface_layers); + // Sort the layers lexicographically by a raising print_z and a decreasing height. std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare()); - int layer_id = 0; for (int i = 0; i < int(layers_sorted.size());) { // Find the last layer with the same print_z, find the minimum layer height of all. @@ -242,6 +259,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) height_min = std::min(height_min, layers_sorted[j]->height); object.add_support_layer(layer_id, height_min, layers_sorted[i]->print_z); if (layer_id > 0) { + // Inter-link the support layers into a linked list. SupportLayer *sl1 = object.support_layers[object.support_layer_count()-2]; SupportLayer *sl2 = object.support_layers.back(); sl1->upper_layer = sl2; @@ -259,7 +277,8 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) BOOST_LOG_TRIVIAL(info) << "Support generator - End"; } -void collect_region_slices_by_type(const Layer &layer, SurfaceType surface_type, Polygons &out) +// Collect all polygons of all regions in a layer with a given surface type. +Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_type) { // 1) Count the new polygons first. size_t n_polygons_new = 0; @@ -274,7 +293,8 @@ void collect_region_slices_by_type(const Layer &layer, SurfaceType surface_type, } // 2) Collect the new polygons. - out.reserve(out.size() + n_polygons_new); + Polygons out; + out.reserve(n_polygons_new); for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) { const LayerRegion ®ion = *(*it_region); const SurfaceCollection &slices = region.slices; @@ -284,59 +304,24 @@ void collect_region_slices_by_type(const Layer &layer, SurfaceType surface_type, polygons_append(out, surface.expolygon); } } -} -Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_type) -{ - Polygons out; - collect_region_slices_by_type(layer, surface_type, out); return out; } -// Collect outer contours of all expolygons in all layer region slices. -void collect_region_slices_outer(const Layer &layer, Polygons &out) -{ - // 1) Count the new polygons first. - size_t n_polygons_new = 0; - for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) { - const LayerRegion ®ion = *(*it_region); - n_polygons_new += region.slices.surfaces.size(); - } - - // 2) Collect the new polygons. - out.reserve(out.size() + n_polygons_new); - for (LayerRegionPtrs::const_iterator it_region = layer.regions.begin(); it_region != layer.regions.end(); ++ it_region) { - const LayerRegion ®ion = *(*it_region); - for (Surfaces::const_iterator it = region.slices.surfaces.begin(); it != region.slices.surfaces.end(); ++ it) - out.push_back(it->expolygon.contour); - } -} - -// Collect outer contours of all expolygons in all layer region slices. -Polygons collect_region_slices_outer(const Layer &layer) -{ - Polygons out; - collect_region_slices_outer(layer, out); - return out; -} - -// Collect outer contours of all expolygons in all layer region slices. -void collect_slices_outer(const Layer &layer, Polygons &out) -{ - out.reserve(out.size() + layer.slices.expolygons.size()); - for (ExPolygons::const_iterator it = layer.slices.expolygons.begin(); it != layer.slices.expolygons.end(); ++ it) - out.push_back(it->contour); -} - -// Collect outer contours of all expolygons in all layer region slices. +// Collect outer contours of all slices of this layer. +// This is useful for calculating the support base with holes filled. Polygons collect_slices_outer(const Layer &layer) { Polygons out; - collect_slices_outer(layer, out); + out.reserve(out.size() + layer.slices.expolygons.size()); + for (ExPolygons::const_iterator it = layer.slices.expolygons.begin(); it != layer.slices.expolygons.end(); ++ it) + out.push_back(it->contour); return out; } -// Find the top contact surfaces of the support or the raft. +// Generate top contact layers supporting overhangs. +// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. +// If supports over bed surface only are requested, don't generate contact layers over an object. PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers( const PrintObject &object, MyLayerStorage &layer_storage) const { @@ -345,56 +330,37 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ ++ iRun; #endif /* SLIC3R_DEBUG */ - // Output layers, sorte by top Z. + // Output layers, sorted by top Z. MyLayersPtr contact_out; // If user specified a custom angle threshold, convert it to radians. - double threshold_rad = 0.; - if (m_object_config->support_material_threshold.value > 0) { - threshold_rad = M_PI * double(m_object_config->support_material_threshold.value + 1) / 180.; // +1 makes the threshold inclusive - // Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); - } + // Zero means automatic overhang detection. + double threshold_rad = (m_object_config->support_material_threshold.value > 0) ? + M_PI * double(m_object_config->support_material_threshold.value + 1) / 180. : // +1 makes the threshold inclusive + 0.; - // Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces - // and subtract $buildplate_only_top_surfaces from the contact surfaces, so + // Build support on a build plate only? If so, then collect top surfaces into buildplate_only_top_surfaces + // and subtract buildplate_only_top_surfaces from the contact surfaces, so // there is no contact surface supported by a top surface. - bool buildplate_only = m_object_config->support_material.value && m_object_config->support_material_buildplate_only.value; + bool buildplate_only = this->build_plate_only(); Polygons buildplate_only_top_surfaces; // Determine top contact areas. - for (size_t layer_id = 0; layer_id < object.layer_count(); ++ layer_id) { - // Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers. - // So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers. - if (this->has_raft()) { - if (! this->has_support() && layer_id > 0) - // If we are only going to generate raft. Just check for the 'overhangs' of the first object layer. - break; - // Check for the overhangs at any object layer including the 1st layer. - } else if (layer_id == 0) { - // No raft, 1st object layer cannot be supported by a support contact layer as it sticks directly to print bed. - continue; - } - - const Layer &layer = *object.get_layer(layer_id); - - if (buildplate_only) { - // Collect the top surfaces up to this layer and merge them. - Polygons projection_new = collect_region_slices_by_type(layer, stTop); - if (! projection_new.empty()) { - // Merge the new top surfaces with the preceding top surfaces. - // Apply the safety offset to the newly added polygons, so they will connect - // with the polygons collected before, - // but don't apply the safety offset during the union operation as it would - // inflate the polygons over and over. - projection_new = offset(projection_new, scale_(0.01)); - polygons_append(buildplate_only_top_surfaces, projection_new); - buildplate_only_top_surfaces = union_(buildplate_only_top_surfaces, false); // don't apply the safety offset. - } - } + // If generating raft only (no support), only calculate top contact areas for the 0th layer. + size_t num_layers = this->has_support() ? object.layer_count() : 1; + // If having a raft, start with 0th layer, otherwise with 1st layer. + // Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers. + // So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers. + for (size_t layer_id = this->has_raft() ? 0 : 1; layer_id < num_layers; ++ layer_id) + { + const Layer &layer = *object.layers[layer_id]; // Detect overhangs and contact areas needed to support them. + // Collect overhangs and contacts of all regions of this layer supported by the layer immediately below. Polygons overhang_polygons; Polygons contact_polygons; + Polygons slices_margin_cached; + float slices_margin_cached_offset = -1.; if (layer_id == 0) { // This is the first object layer, so the object is being printed on a raft and // we're here just to get the object footprint for the raft. @@ -404,39 +370,51 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ contact_polygons = offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN)); } else { // Generate overhang / contact_polygons for non-raft layers. - const Layer &lower_layer = *object.get_layer(int(layer_id)-1); + const Layer &lower_layer = *object.layers[layer_id-1]; + if (buildplate_only) { + // Merge the new slices with the preceding slices. + // Apply the safety offset to the newly added polygons, so they will connect + // with the polygons collected before, + // but don't apply the safety offset during the union operation as it would + // inflate the polygons over and over. + polygons_append(buildplate_only_top_surfaces, offset(lower_layer.slices.expolygons, scale_(0.01))); + buildplate_only_top_surfaces = union_(buildplate_only_top_surfaces, false); // don't apply the safety offset. + } + for (LayerRegionPtrs::const_iterator it_layerm = layer.regions.begin(); it_layerm != layer.regions.end(); ++ it_layerm) { const LayerRegion &layerm = *(*it_layerm); // Extrusion width accounts for the roundings of the extrudates. // It is the maximum widh of the extrudate. - coord_t fw = layerm.flow(frExternalPerimeter).scaled_width(); - coordf_t lower_layer_offset = + float fw = float(layerm.flow(frExternalPerimeter).scaled_width()); + float lower_layer_offset = (layer_id < m_object_config->support_material_enforce_layers.value) ? // Enforce a full possible support, ignore the overhang angle. - 0 : + 0.f : (threshold_rad > 0. ? // Overhang defined by an angle. - scale_(lower_layer.height * cos(threshold_rad) / sin(threshold_rad)) : + float(scale_(lower_layer.height / tan(threshold_rad))) : // Overhang defined by half the extrusion width. - 0.5 * fw); + 0.5f * fw); // Overhang polygons for this layer and region. Polygons diff_polygons; - if (lower_layer_offset == 0.) { + Polygons layerm_polygons = to_polygons(layerm.slices); + Polygons lower_layer_polygons = to_polygons(lower_layer.slices.expolygons); + if (lower_layer_offset == 0.f) { // Support everything. - diff_polygons = diff( - (Polygons)layerm.slices, - (Polygons)lower_layer.slices); + diff_polygons = diff(layerm_polygons, lower_layer_polygons); } else { - // Get the regions needing a suport. - diff_polygons = diff( - (Polygons)layerm.slices, - offset((Polygons)lower_layer.slices, lower_layer_offset)); - // Collapse very tiny spots. - diff_polygons = offset2(diff_polygons, -0.1*fw, +0.1*fw); + // Get the regions needing a suport, collapse very tiny spots. + //FIXME cache the lower layer offset if this layer has multiple regions. + diff_polygons = offset2( + diff(layerm_polygons, + offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)), + -0.1f*fw, +0.1f*fw); if (diff_polygons.empty()) continue; // Offset the support regions back to a full overhang, restrict them to the full overhang. - diff_polygons = diff(intersection(offset(diff_polygons, lower_layer_offset), (Polygons)layerm.slices), (Polygons)lower_layer.slices); + diff_polygons = diff( + intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), + lower_layer_polygons); } if (diff_polygons.empty()) continue; @@ -452,68 +430,65 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ if (m_object_config->dont_support_bridges) { // compute the area of bridging perimeters // Note: this is duplicate code from GCode.pm, we need to refactor - - Polygons bridged_perimeters; - { - Flow bridge_flow = layerm.flow(frPerimeter, true); - - coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at( - layerm.region()->config.perimeter_extruder-1); - Polygons lower_grown_slices = offset((Polygons)lower_layer.slices, 0.5f*scale_(nozzle_diameter)); - - // TODO: split_at_first_point() could split a bridge mid-way - Polylines overhang_perimeters; - for (ExtrusionEntitiesPtr::const_iterator it_island = layerm.perimeters.entities.begin(); it_island != layerm.perimeters.entities.end(); ++ it_island) { - const ExtrusionEntityCollection *island = dynamic_cast(*it_island); - assert(island != NULL); - for (size_t i = 0; i < island->entities.size(); ++ i) { - ExtrusionEntity *entity = island->entities[i]; - ExtrusionLoop *loop = dynamic_cast(entity); - overhang_perimeters.push_back(loop ? - loop->as_polyline() : - dynamic_cast(entity)->polyline); + if (true) { + Polygons bridged_perimeters; + { + Flow bridge_flow = layerm.flow(frPerimeter, true); + coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(layerm.region()->config.perimeter_extruder-1); + Polygons lower_grown_slices = offset(lower_layer_polygons, 0.5f*float(scale_(nozzle_diameter)), SUPPORT_SURFACES_OFFSET_PARAMETERS); + + // Collect perimeters of this layer. + // TODO: split_at_first_point() could split a bridge mid-way + Polylines overhang_perimeters; + for (ExtrusionEntitiesPtr::const_iterator it_island = layerm.perimeters.entities.begin(); it_island != layerm.perimeters.entities.end(); ++ it_island) { + const ExtrusionEntityCollection *island = dynamic_cast(*it_island); + assert(island != NULL); + for (size_t i = 0; i < island->entities.size(); ++ i) { + ExtrusionEntity *entity = island->entities[i]; + ExtrusionLoop *loop = dynamic_cast(entity); + overhang_perimeters.push_back(loop ? + loop->as_polyline() : + dynamic_cast(entity)->polyline); + } } - } - - // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() - for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it) - it->points[0].x += 1; - overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); - - // only consider straight overhangs - // only consider overhangs having endpoints inside layer's slices - // convert bridging polylines into polygons by inflating them with their thickness - // since we're dealing with bridges, we can't assume width is larger than spacing, - // so we take the largest value and also apply safety offset to be ensure no gaps - // are left in between - coordf_t w = std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()); - for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it) { - if (it->is_straight()) { - it->extend_start(fw); - it->extend_end(fw); - if (layer.slices.contains(it->first_point()) && layer.slices.contains(it->last_point())) - // Offset a polyline into a polygon. - polygons_append(bridged_perimeters, offset(*it, 0.5f * w + 10.f)); + + // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() + for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it) + it->points[0].x += 1; + // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters. + overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); + + // only consider straight overhangs + // only consider overhangs having endpoints inside layer's slices + // convert bridging polylines into polygons by inflating them with their thickness + // since we're dealing with bridges, we can't assume width is larger than spacing, + // so we take the largest value and also apply safety offset to be ensure no gaps + // are left in between + float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing())); + for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it) { + if (it->is_straight()) { + // This is a bridge + it->extend_start(fw); + it->extend_end(fw); + if (layer.slices.contains(it->first_point()) && layer.slices.contains(it->last_point())) + // Offset a polyline into a polygon. + polygons_append(bridged_perimeters, offset(*it, 0.5f * w + 10.f)); + } } + bridged_perimeters = union_(bridged_perimeters); } - bridged_perimeters = union_(bridged_perimeters); - } - - if (1) { // remove the entire bridges and only support the unsupported edges Polygons bridges; for (Surfaces::const_iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it) if (it->surface_type == stBottomBridge && it->bridge_angle != -1) polygons_append(bridges, it->expolygon); - polygons_append(bridged_perimeters, bridges); - diff_polygons = diff(diff_polygons, bridged_perimeters, true); - - Polygons unsupported_bridge_polygons; - for (Polylines::const_iterator it = layerm.unsupported_bridge_edges.polylines.begin(); - it != layerm.unsupported_bridge_edges.polylines.end(); ++ it) - // Offset a polyline into a polygon. - polygons_append(unsupported_bridge_polygons, offset(*it, scale_(SUPPORT_MATERIAL_MARGIN))); - polygons_append(diff_polygons, intersection(unsupported_bridge_polygons, bridges)); + diff_polygons = diff(diff_polygons, bridges, true); + polygons_append(bridges, bridged_perimeters); + polygons_append(diff_polygons, + intersection( + // Offset unsupported edges into polygons. + offset(layerm.unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS), + bridges)); } else { // just remove bridged areas diff_polygons = diff(diff_polygons, layerm.bridged, true); @@ -530,14 +505,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ continue; #ifdef SLIC3R_DEBUG - { - ::Slic3r::SVG svg(debug_out_path("support-top-contacts-filtered-run%d-layer%d-region%d.svg", iRun, layer_id, it_layerm - layer.regions.begin()), get_extents(diff_polygons)); - Slic3r::ExPolygons expolys = union_ex(diff_polygons, false); - svg.draw(expolys); - } + Slic3r::SVG::export_expolygons( + debug_out_path("support-top-contacts-filtered-run%d-layer%d-region%d.svg", iRun, layer_id, it_layerm - layer.regions.begin()), + union_ex(diff_polygons, false)); #endif /* SLIC3R_DEBUG */ - polygons_append(overhang_polygons, diff_polygons); + if (this->has_contact_loops()) + polygons_append(overhang_polygons, diff_polygons); // Let's define the required contact area by using a max gap of half the upper // extrusion width and extending the area according to the configured margin. @@ -545,11 +519,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // on the other side of the object (if it's very thin). { //FIMXE 1) Make the offset configurable, 2) Make the Z span configurable. - Polygons slices_margin = offset((Polygons)lower_layer.slices, float(0.5*fw)); - if (buildplate_only) { - // Trim the inflated contact surfaces by the top surfaces as well. - polygons_append(slices_margin, buildplate_only_top_surfaces); - slices_margin = union_(slices_margin); + float slices_margin_offset = float(0.5*fw); + if (slices_margin_cached_offset != slices_margin_offset) { + slices_margin_cached_offset = slices_margin_offset; + slices_margin_cached = offset(lower_layer.slices.expolygons, slices_margin_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS); + if (buildplate_only) { + // Trim the inflated contact surfaces by the top surfaces as well. + polygons_append(slices_margin_cached, buildplate_only_top_surfaces); + slices_margin_cached = union_(slices_margin_cached); + } } // Offset the contact polygons outside. for (size_t i = 0; i < NUM_MARGIN_STEPS; ++ i) { @@ -560,7 +538,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ ClipperLib::jtRound, // round mitter limit scale_(0.05)), - slices_margin); + slices_margin_cached); } } polygons_append(contact_polygons, diff_polygons); @@ -571,13 +549,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ if (! contact_polygons.empty()) { // get the average nozzle diameter used on this layer MyLayer &new_layer = layer_allocate(layer_storage, sltTopContact); - const Layer *layer_below = (layer_id > 0) ? object.get_layer(layer_id - 1) : NULL; + const Layer *layer_below = (layer_id > 0) ? object.layers[layer_id - 1] : NULL; new_layer.idx_object_layer_above = layer_id; if (m_slicing_params.soluble_interface) { // Align the contact surface height with a layer immediately below the supported layer. new_layer.height = layer_below ? // Interface layer will be synchronized with the object. - object.get_layer(layer_id - 1)->height : + object.layers[layer_id-1]->height : // Don't know the thickness of the raft layer yet. 0.; new_layer.print_z = layer.print_z - layer.height; @@ -598,23 +576,35 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ n_nozzle_dmrs += 3; } nozzle_dmr /= coordf_t(n_nozzle_dmrs); - new_layer.print_z = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance; - if (m_synchronize_support_layers_with_object && layer_below) { - int layer_id_below = layer_id - 1; - const Layer *layer_above = layer_below; - for (;;) { - if (layer_below->print_z - layer_below->height < new_layer.print_z - m_support_layer_height_max) { - // layer_below is too low. + new_layer.print_z = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance; + new_layer.bottom_z = new_layer.print_z; + new_layer.height = 0.; + if (this->synchronize_layers()) { + // Align bottom of this layer with a top of the closest object layer + // while not trespassing into the 1st layer and keeping the support layer thickness bounded. + int layer_id_below = int(layer_id) - 1; + for (; layer_id_below >= 0; -- layer_id_below) { + layer_below = object.layers[layer_id_below]; + if (layer_below->print_z <= new_layer.print_z - m_support_layer_height_min) { + // This is a feasible support layer height. + new_layer.bottom_z = layer_below->print_z; + new_layer.height = new_layer.print_z - new_layer.bottom_z; + assert(new_layer.height <= m_support_layer_height_max); break; + } + } + if (layer_id_below == -1) { + // Could not align with any of the top surfaces of object layers. + if (this->has_raft()) { + // If having a raft, all the other layers will be aligned one with the other. + } else { + // Give up, ignore this layer. + continue; } } - new_layer.height = 0.; - new_layer.bottom_z = new_layer.print_z - new_layer.height; - } else if (layer_below) { + } else { // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and // its height will be set adaptively later on. - new_layer.height = 0.; - new_layer.bottom_z = new_layer.print_z; } } @@ -625,14 +615,34 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ if (new_layer.print_z < this->first_layer_height() + m_support_layer_height_min) continue; - new_layer.polygons.swap(contact_polygons); +#if 1 + { + // Create an EdgeGrid, initialize it with projection, initialize signed distance field. + Slic3r::EdgeGrid::Grid grid; + coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing(); + coord_t grid_resolution = scale_(support_spacing); // scale_(1.5f); + BoundingBox bbox = get_extents(contact_polygons); + bbox.offset(20); + bbox.align_to_grid(grid_resolution); + grid.set_bbox(bbox); + grid.create(contact_polygons, grid_resolution); + grid.calculate_sdf(); + // Extract a bounding contour from the grid, trim by the object. + contact_polygons = diff( + grid.contours_simplified(m_support_material_flow.scaled_spacing()/2 + 5), + slices_margin_cached, + true); + } +#endif + + new_layer.polygons = std::move(contact_polygons); // Store the overhang polygons as the aux_polygons. - // The overhang polygons are used in the path generator for planning of the contact circles. - new_layer.aux_polygons = new Polygons(); - new_layer.aux_polygons->swap(overhang_polygons); + // The overhang polygons are used in the path generator for planning of the contact loops. + // if (this->has_contact_loops()) + new_layer.aux_polygons = new Polygons(std::move(overhang_polygons)); contact_out.push_back(&new_layer); - if (0) { + if (0) { // Slic3r::SVG::output("out\\contact_" . $contact_z . ".svg", // green_expolygons => union_ex($buildplate_only_top_surfaces), // blue_expolygons => union_ex(\@contact), @@ -645,169 +655,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ return contact_out; } -#if 0 -typedef std::unordered_set PointHashMap; - -void fillet(Polygon &poly, PointHashMap &new_points_hash_map) -{ - if (poly.points.size() < 3) - // an invalid contour will not be modified. - return; - - // Flag describing a contour point. - std::vector point_flag(std::vector(poly.points.size(), 0)); - - // Does a point belong to new points? - for (size_t i = 0; i < poly.points.size(); ++ i) - if (new_points_hash_map.find(poly.points[i]) != new_points_hash_map.end()) - // Mark the point as from the new contour. - point_flag[i] = 1; - - // Mark the intersection points between the old and new contours. - size_t j = poly.points.size() - 1; - bool has_some = false; - for (size_t i = 0; i < poly.points.size(); j = i, ++ i) - if ((point_flag[i] ^ point_flag[j]) & 1) { - point_flag[(point_flag[i] & 1) ? j : i] |= 2; - has_some = true; - } - if (! has_some) - return; - -#ifdef SLIC3R_DEBUG - static int iRun = 0; - ++ iRun; - { - FILE *pfile = ::fopen(debug_out_path("fillet-in-%d.bin", iRun).c_str(), "wb"); - size_t cnt = poly.points.size(); - ::fwrite(&cnt, 1, sizeof(cnt), pfile); - ::fwrite(poly.points.data(), cnt, sizeof(Point), pfile); - cnt = new_points_hash_map.size(); - ::fwrite(&cnt, 1, sizeof(cnt), pfile); - for (PointHashMap::iterator it = new_points_hash_map.begin(); it != new_points_hash_map.end(); ++ it) { - const Point &pt = *it; - ::fwrite(&pt, 1, sizeof(Point), pfile); - } - ::fclose(pfile); - } - ::Slic3r::SVG svg(debug_out_path("fillet-%d.svg", iRun), get_extents(poly)); - svg.draw(poly, "black", scale_(0.05)); - for (size_t i = 0; i < poly.points.size(); ++ i) { - const Point &pt1 = poly.points[i]; - const Point &pt2 = poly.points[(i+1)%poly.points.size()]; - if (new_points_hash_map.find(pt1) != new_points_hash_map.end()) - svg.draw(Line(pt1, pt2), "red", scale_(0.035)); - if (new_points_hash_map.find(pt1) != new_points_hash_map.end() && - new_points_hash_map.find(pt2) != new_points_hash_map.end()) - svg.draw(Line(pt1, pt2), "red", scale_(0.05)); - } -#endif - - // Mark a range of points around the intersection points. - const double rounding_range = scale_(1.5); - std::vector pts; - pts.reserve(poly.points.size()); - for (int i = 0; i < int(poly.points.size()); ++ i) { - if (point_flag[i] & 2) { - point_flag[i] |= 4; - // Extend a filetting span left / right from i by an Euclidian distance of rounding_range. - double d = 0.f; - const Point *pt = &poly.points[i]; - for (int j = 1; j < int(poly.points.size()); ++ j) { - int idx = (i + j) % poly.points.size(); - const Point *pt2 = &poly.points[idx]; - d += pt->distance_to(*pt2); - if (d > rounding_range) - break; - point_flag[idx] |= 4; - //pt = pt2; - } - for (int j = 1; j < int(poly.points.size()); ++ j) { - int idx = (i + int(poly.points.size()) - j) % poly.points.size(); - const Point *pt2 = &poly.points[idx]; - d += pt->distance_to(*pt2); - if (d > rounding_range) - break; - point_flag[idx] |= 4; - //pt = pt2; - } - } - pts.push_back(Pointf(poly.points[i].x, poly.points[i].y)); - } - - //FIXME avoid filetting over long edges. Insert new points into long edges at the ends of the filetting interval. - - // Perform the filetting over the marked vertices. - std::vector pts2(pts); - double laplacian_weight = 0.5; - for (size_t i_round = 0; i_round < 5; ++ i_round) { - for (size_t i = 0; i < int(pts.size()); ++ i) { - if (point_flag[i] & 4) { - size_t prev = (i == 0) ? pts.size() - 1 : i - 1; - size_t next = (i + 1 == pts.size()) ? 0 : i + 1; - Pointf &p0 = pts[prev]; - Pointf &p1 = pts[i]; - Pointf &p2 = pts[next]; - // Is the point reflex? - coordf_t c = cross(p1 - p0, p2 - p1); - if (c < 0) - // The point is reflex, perform Laplacian smoothing. - pts2[i] = (1. - laplacian_weight) * pts[i] + (0.5 * laplacian_weight) * (pts[prev] + pts[next]); - } - } - pts.swap(pts2); - } - - // Mark vertices representing short edges for removal. - - // Convert the filetted points back, remove points marked for removal. - j = 0; - for (size_t i = 0; i < poly.points.size(); ++ i) { - if (point_flag[i] & 8) - // Remove this point. - continue; - if (point_flag[i] & 4) - // Update the point coordinates. - poly.points[i] = Point(pts[i].x, pts[i].y); - if (j < i) - poly.points[j] = poly.points[i]; - ++ j; - } - if (j < poly.points.size()) - poly.points.erase(poly.points.begin() + j, poly.points.end()); - -#ifdef SLIC3R_DEBUG - svg.draw_outline(poly, "blue", scale_(0.025)); -#endif /* SLIC3R_DEBUG */ -} - -void fillet(Polygons &polygons, PointHashMap &new_points_hash_map) -{ - for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it) - fillet(*it, new_points_hash_map); -} - -void union_and_fillet(Polygons &polygons, size_t n_polygons_old) -{ - if (n_polygons_old == polygons.size()) - // No new polygons. - return; - - // Fill in the new_points hash table with points of new contours. - PointHashMap new_points; - for (size_t i = n_polygons_old; i < polygons.size(); ++ i) { - const Polygon &poly = polygons[i]; - for (size_t j = 0; j < poly.points.size(); ++ j) - new_points.insert(poly.points[j]); - } - // Merge the newly added regions. Don't use the safety offset, the offset has been added already. - polygons = union_(polygons, false); - // Fillet transition between the old and new points. - fillet(polygons, new_points); -} -#endif - -// Collect +// Generate bottom contact layers supporting the top contact layers. +// For a soluble interface material synchronize the layer heights with the object, +// otherwise set the layer height to a bridging flow of a support interface nozzle. PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage, std::vector &layer_support_areas) const @@ -838,31 +688,20 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta Polygons top; if (! m_object_config->support_material_buildplate_only) top = collect_region_slices_by_type(layer, stTop); - size_t projection_size_old = projection.size(); // Collect projections of all contact areas above or at the same level as this top surface. for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) { Polygons polygons_new; // Contact surfaces are expanded away from the object, trimmed by the object. // Use a slight positive offset to overlap the touching regions. polygons_append(polygons_new, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON)); - size_t size1 = polygons_new.size(); // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. // Use a slight positive offset to overlap the touching regions. polygons_append(polygons_new, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON)); -#if 0 - union_and_fillet(polygons_new, size1); -#else - union_(polygons_new); -#endif - polygons_append(projection, std::move(polygons_new)); + polygons_append(projection, union_(polygons_new)); } if (projection.empty()) continue; -#if 0 - union_and_fillet(projection, projection_size_old); -#else - union_(projection); -#endif + projection = union_(projection); #ifdef SLIC3R_DEBUG { BoundingBox bbox = get_extents(projection); @@ -870,13 +709,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta ::Slic3r::SVG svg(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), bbox); svg.draw(union_ex(top, false), "blue", 0.5f); svg.draw(union_ex(projection, true), "red", 0.5f); + svg.draw_outline(union_ex(projection, true), "red", "blue", scale_(0.1f)); svg.draw(layer.slices.expolygons, "green", 0.5f); } #endif /* SLIC3R_DEBUG */ // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any // top surfaces above layer.print_z falls onto this top surface. - // touching are the contact surfaces supported exclusively by this top surfaaces. + // Touching are the contact surfaces supported exclusively by this top surfaces. // Don't use a safety offset as it has been applied during insertion of polygons. if (! top.empty()) { Polygons touching = intersection(top, projection, false); @@ -898,13 +738,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta layer_new.idx_object_layer_below = layer_id; layer_new.bridging = ! m_slicing_params.soluble_interface; //FIXME how much to inflate the top surface? - layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width())); + layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS); #ifdef SLIC3R_DEBUG - { - ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), get_extents(layer_new.polygons)); - Slic3r::ExPolygons expolys = union_ex(layer_new.polygons, false); - svg.draw(expolys); - } + Slic3r::SVG::export_expolygons( + debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), + union_ex(layer_new.polygons, false)); #endif /* SLIC3R_DEBUG */ } } // ! top.empty() @@ -929,12 +767,20 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta { BoundingBox bbox = get_extents(projection); bbox.merge(get_extents(projection_simplified)); - ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-simplified-%d-%d.svg", iRun, layer_id), bbox); svg.draw(union_ex(projection, false), "blue", 0.5); svg.draw(union_ex(projection_simplified, false), "red", 0.5); + #ifdef SLIC3R_GUI + bbox.min.x -= scale_(5.f); + bbox.min.y -= scale_(5.f); + bbox.max.x += scale_(5.f); + bbox.max.y += scale_(5.f); + EdgeGrid::save_png(grid, bbox, scale_(0.1f), debug_out_path("support-bottom-contacts-df-%d-%d.png", iRun, layer_id).c_str()); + #endif /* SLIC3R_GUI */ } #endif /* SLIC3R_DEBUG */ + // Cache the slice of a support volume. The support volume is expanded by 1/2 of support material flow spacing + // to allow a placement of suppot zig-zag snake along the grid lines. layer_support_areas[layer_id] = diff( grid.contours_simplified(m_support_material_flow.scaled_spacing()/2 + 5), to_polygons(layer.slices.expolygons), @@ -948,6 +794,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta std::reverse(bottom_contacts.begin(), bottom_contacts.end()); } // ! top_contacts.empty() + trim_support_layers_by_object(object, bottom_contacts, m_support_layer_height_min, 0., m_gap_xy); + return bottom_contacts; } @@ -965,9 +813,11 @@ void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts( // For all top contact layers overlapping with the thick bottom contact layer: for (size_t idx_top = idx_top_first; idx_top < top_contacts.size(); ++ idx_top) { MyLayer &layer_top = *top_contacts[idx_top]; - coordf_t interface_z = m_slicing_params.soluble_interface ? - (layer_top.bottom_z + EPSILON) : - (layer_top.bottom_z - m_support_layer_height_min); + coordf_t interface_z = (layer_top.print_z == layer_top.bottom_z) ? + // Layer height has not been decided yet. + (layer_top.bottom_z - m_support_layer_height_min) : + // Layer height has already been assigned. + (layer_top.bottom_z + EPSILON); if (interface_z < layer_bottom.print_z) { // Layers overlap. Trim layer_top with layer_bottom. layer_top.polygons = diff(layer_top.polygons, layer_bottom.polygons); @@ -977,6 +827,21 @@ void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts( } } +// A helper for sorting the top / bottom contact layers by their contact with the touching support layer: +// Top contact surfaces (those supporting overhangs) are sorted by their bottom print Z, +// bottom contact surfaces (those supported by top object surfaces) are sorted by their top print Z. +struct LayerExtreme +{ + LayerExtreme(PrintObjectSupportMaterial::MyLayer *alayer, bool ais_top) : layer(alayer), is_top(ais_top) {} + PrintObjectSupportMaterial::MyLayer *layer; + // top or bottom extreme + bool is_top; + + coordf_t z() const { return is_top ? layer->print_z : layer->print_z - layer->height; } + + bool operator<(const LayerExtreme &other) const { return z() < other.z(); } +}; + PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_support_layers( const PrintObject &object, const MyLayersPtr &bottom_contacts, @@ -1002,28 +867,29 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int assert(extremes.front().z() > m_slicing_params.raft_interface_top_z && extremes.front().z() >= m_slicing_params.first_print_layer_height); + bool synchronize = m_slicing_params.soluble_interface || this->synchronize_layers(); + // Generate intermediate layers. // The first intermediate layer is the same as the 1st layer if there is no raft, // or the bottom of the first intermediate layer is aligned with the bottom of the raft contact layer. // Intermediate layers are always printed with a normal etrusion flow (non-bridging). + size_t idx_layer_object = 0; for (size_t idx_extreme = 0; idx_extreme < extremes.size(); ++ idx_extreme) { LayerExtreme *extr1 = (idx_extreme == 0) ? NULL : &extremes[idx_extreme-1]; coordf_t extr1z = (extr1 == NULL) ? m_slicing_params.raft_interface_top_z : extr1->z(); LayerExtreme &extr2 = extremes[idx_extreme]; coordf_t extr2z = extr2.z(); coordf_t dist = extr2z - extr1z; - assert(dist > 0.); + assert(dist >= 0.); + if (dist == 0.) + continue; // Insert intermediate layers. size_t n_layers_extra = size_t(ceil(dist / m_support_layer_height_max)); assert(n_layers_extra > 0); coordf_t step = dist / coordf_t(n_layers_extra); - if (! m_slicing_params.soluble_interface && ! m_synchronize_support_layers_with_object && extr2.layer->layer_type == sltTopContact) { - assert(extr2.layer->height == 0.); + if (! synchronize && extr2.layer->layer_type == sltTopContact) { // This is a top interface layer, which does not have a height assigned yet. Do it now. - if (m_synchronize_support_layers_with_object) { - //FIXME - // Find the - } + assert(extr2.layer->height == 0.); extr2.layer->height = step; extr2.layer->bottom_z = extr2z = extr2.layer->print_z - step; -- n_layers_extra; @@ -1053,29 +919,55 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int n_layers_extra = size_t(ceil(dist / m_support_layer_height_max)); step = dist / coordf_t(n_layers_extra); } + coordf_t extr2z_large_steps = extr2z; + if (synchronize) { + // Synchronize support layers with the object layers. + if (object.layers.front()->print_z - extr1z > m_support_layer_height_max) { + // Generate the initial couple of layers before reaching the 1st object layer print_z level. + extr2z_large_steps = object.layers.front()->print_z; + dist = extr2z_large_steps - extr1z; + assert(dist >= 0.); + n_layers_extra = size_t(ceil(dist / m_support_layer_height_max)); + step = dist / coordf_t(n_layers_extra); + } + } + // Take the largest allowed step in the Z axis until extr2z_large_steps is reached. for (size_t i = 0; i < n_layers_extra; ++ i) { MyLayer &layer_new = layer_allocate(layer_storage, stlIntermediate); if (i + 1 == n_layers_extra) { - // Last intermediate layer added. Align the last entered layer with extr2z exactly. + // Last intermediate layer added. Align the last entered layer with extr2z_large_steps exactly. layer_new.bottom_z = (i == 0) ? extr1z : intermediate_layers.back()->print_z; - layer_new.print_z = extr2z; + layer_new.print_z = extr2z_large_steps; layer_new.height = layer_new.print_z - layer_new.bottom_z; } else { // Intermediate layer, not the last added. layer_new.height = step; - layer_new.bottom_z = (i + 1 == n_layers_extra) ? extr2z : extr1z + i * step; + layer_new.bottom_z = extr1z + i * step; layer_new.print_z = layer_new.bottom_z + step; } intermediate_layers.push_back(&layer_new); } + if (synchronize) { + // Emit support layers synchronized with object layers. + extr1z = extr2z_large_steps; + while (extr1z < extr2z) { + //while (idx_layer_object < object.layers.size() && object.layers[idx_layer_object].print_z < extr1z) + // idx_layer_object + } + } } +#ifdef _DEBUG + for (size_t i = 0; i < top_contacts.size(); ++i) + assert(top_contacts[i]->height > 0.); +#endif /* _DEBUG */ + return intermediate_layers; } // At this stage there shall be intermediate_layers allocated between bottom_contacts and top_contacts, but they have no polygons assigned. -// Also the bottom/top_contacts shall have a thickness assigned already. +// Also the bottom/top_contacts shall have a layer thickness assigned already. void PrintObjectSupportMaterial::generate_base_layers( const PrintObject &object, const MyLayersPtr &bottom_contacts, @@ -1108,34 +1000,11 @@ void PrintObjectSupportMaterial::generate_base_layers( // New polygons for layer_intermediate. Polygons polygons_new; -#if 0 - // Add polygons projected from the intermediate layer above. - if (idx_intermediate + 1 < int(intermediate_layers.size())) - polygons_append(polygons_new, intermediate_layers[idx_intermediate+1]->polygons); - - if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z) { - // Contact surfaces are expanded away from the object, trimmed by the object. - // Use a slight positive offset to overlap the touching regions. - Polygons polygons_new2; - polygons_append(polygons_new2, offset(top_contacts[idx_top_contact_above]->polygons, SCALED_EPSILON)); - size_t size2 = polygons_new2.size(); - // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. - // Use a slight positive offset to overlap the touching regions. - polygons_append(polygons_new2, offset(*top_contacts[idx_top_contact_above]->aux_polygons, SCALED_EPSILON)); - union_and_fillet(polygons_new2, size2); - if (! polygons_new2.empty()) { - size_t polygons_size_old = polygons_new.size(); - polygons_append(polygons_new, std::move(polygons_new2)); - union_and_fillet(polygons_new, polygons_size_old); - } - } -#else // Use the precomputed layer_support_areas. while (idx_object_layer_above > 0 && object.get_layer(idx_object_layer_above - 1)->print_z > layer_intermediate.print_z - EPSILON) -- idx_object_layer_above; polygons_new = layer_support_areas[idx_object_layer_above]; -#endif - + // Polygons to trim polygons_new. Polygons polygons_trimming; @@ -1199,39 +1068,46 @@ void PrintObjectSupportMaterial::generate_base_layers( } */ } - + #ifdef SLIC3R_DEBUG - for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++it) { - const MyLayer &layer = *(*it); - ::Slic3r::SVG svg(debug_out_path("support-intermediate-layers-untrimmed-%d-%lf.svg", iRun, layer.print_z), get_extents(layer.polygons)); - Slic3r::ExPolygons expolys = union_ex(layer.polygons, false); - svg.draw(expolys); - } + for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++it) + ::Slic3r::SVG::export_expolygons( + debug_out_path("support-intermediate-layers-untrimmed-%d-%lf.svg", iRun, (*it)->print_z), + union_ex((*it)->polygons, false)); + ++ iRun; #endif /* SLIC3R_DEBUG */ + trim_support_layers_by_object(object, intermediate_layers, m_support_layer_height_min, m_support_layer_height_min, m_gap_xy); +} + +void PrintObjectSupportMaterial::trim_support_layers_by_object( + const PrintObject &object, + MyLayersPtr &support_layers, + const coordf_t gap_extra_above, + const coordf_t gap_extra_below, + const coordf_t gap_xy) const +{ //FIXME This could be trivially parallelized. - const coordf_t gap_extra_above = 0.1f; - const coordf_t gap_extra_below = 0.1f; - const coord_t gap_xy_scaled = m_support_material_flow.scaled_width(); + const coord_t gap_xy_scaled = scale_(gap_xy); size_t idx_object_layer_overlapping = 0; // For all intermediate support layers: - for (MyLayersPtr::iterator it_layer = intermediate_layers.begin(); it_layer != intermediate_layers.end(); ++ it_layer) { - BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - trimmming layer " << - (it_layer - intermediate_layers.begin()) << " of " << intermediate_layers.size(); + for (MyLayersPtr::iterator it_layer = support_layers.begin(); it_layer != support_layers.end(); ++ it_layer) { + BOOST_LOG_TRIVIAL(trace) << "Support generator - trim_support_layers_by_object - trimmming layer " << + (it_layer - support_layers.begin()) << " of " << support_layers.size(); - MyLayer &layer_intermediate = *(*it_layer); - if (layer_intermediate.polygons.empty()) + MyLayer &support_layer = *(*it_layer); + if (support_layer.polygons.empty()) // Empty support layer, nothing to trim. continue; // Find the overlapping object layers including the extra above / below gap. while (idx_object_layer_overlapping < object.layer_count() && - object.get_layer(idx_object_layer_overlapping)->print_z < layer_intermediate.print_z - layer_intermediate.height - gap_extra_below + EPSILON) + object.get_layer(idx_object_layer_overlapping)->print_z < support_layer.print_z - support_layer.height - gap_extra_below + EPSILON) ++ idx_object_layer_overlapping; // Collect all the object layers intersecting with this layer. Polygons polygons_trimming; for (int i = idx_object_layer_overlapping; i < object.layer_count(); ++ i) { const Layer &object_layer = *object.get_layer(i); - if (object_layer.print_z - object_layer.height > layer_intermediate.print_z + gap_extra_above - EPSILON) + if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) break; polygons_append(polygons_trimming, (Polygons)object_layer.slices); } @@ -1240,14 +1116,10 @@ void PrintObjectSupportMaterial::generate_base_layers( // perimeter's width. $support contains the full shape of support // material, thus including the width of its foremost extrusion. // We leave a gap equal to a full extrusion width. - layer_intermediate.polygons = diff( - layer_intermediate.polygons, - offset(polygons_trimming, gap_xy_scaled)); + support_layer.polygons = diff( + support_layer.polygons, + offset(polygons_trimming, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } - -#ifdef SLIC3R_DEBUG - ++ iRun; -#endif /* SLIC3R_DEBUG */ } PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raft_base( @@ -1276,13 +1148,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf // Modify the 1st intermediate layer with the expanded support columns. columns_base->polygons = diff( base, - offset(m_object->layers.front()->slices.expolygons, scale_(m_gap_xy))); + offset(m_object->layers.front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (contacts != nullptr) columns_base->polygons = diff(columns_base->polygons, contacts->polygons); } if (m_slicing_params.has_raft() && contacts != nullptr) { // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. - base = union_(base, offset(contacts->polygons, inflate_factor)); + base = union_(base, offset(contacts->polygons, inflate_factor, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } if (m_slicing_params.has_raft() && m_slicing_params.raft_layers() > 1 && ! base.empty()) { // Do not add the raft contact layer, only add the raft layers below the contact layer. @@ -1458,9 +1330,11 @@ struct MyLayerExtruded this->layer->bridging == other.layer->bridging; } + // Merge regions, perform boolean union over the merged polygons. void merge(MyLayerExtruded &&other) { assert(could_merge(other)); Slic3r::polygons_append(layer->polygons, std::move(other.layer->polygons)); + layer->polygons = union_(layer->polygons, true); other.layer->polygons.clear(); } @@ -1480,7 +1354,7 @@ typedef std::vector MyLayerExtrudedPtrs; struct LoopInterfaceProcessor { LoopInterfaceProcessor(coordf_t circle_r) : - n_contact_loops(1), + n_contact_loops(0), circle_radius(circle_r), circle_distance(circle_r * 3.) { @@ -1511,7 +1385,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const flow.height = float(top_contact_layer.layer->height); Polygons overhang_polygons; - if (top_contact_layer.layer->aux_polygons != nullptr) + // if (top_contact_layer.layer->aux_polygons != nullptr) overhang_polygons = std::move(*top_contact_layer.layer->aux_polygons); // Generate the outermost loop. @@ -1616,7 +1490,12 @@ void PrintObjectSupportMaterial::generate_toolpaths( coordf_t interface_density = std::min(1., m_support_material_interface_flow.spacing() / interface_spacing); coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing(); coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing); - + if (m_object_config->support_material_interface_layers.value == 0) { + // No interface layers allowed, print everything with the base support pattern. + interface_spacing = support_spacing; + interface_density = support_density; + } + //FIXME Parallelize the support generator: /* Slic3r::parallelize( @@ -1738,8 +1617,14 @@ void PrintObjectSupportMaterial::generate_toolpaths( if (m_object_config->support_material_interface_layers == 0) { // If no interface layers were requested, we treat the contact layer exactly as a generic base layer. - if (base_layer.could_merge(top_contact_layer)) - base_layer.merge(std::move(top_contact_layer)); + if (base_layer.could_merge(top_contact_layer)) + base_layer.merge(std::move(top_contact_layer)); + else if (base_layer.empty() && !top_contact_layer.empty() && !top_contact_layer.layer->bridging) + std::swap(base_layer, top_contact_layer); + if (base_layer.could_merge(bottom_contact_layer)) + base_layer.merge(std::move(bottom_contact_layer)); + else if (base_layer.empty() && !bottom_contact_layer.empty() && !bottom_contact_layer.layer->bridging) + std::swap(base_layer, bottom_contact_layer); } else { loop_interface_processor.generate(top_contact_layer, m_support_material_interface_flow); // If no loops are allowed, we treat the contact layer exactly as a generic interface layer. @@ -1820,7 +1705,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( blue_expolygons => $to_infill, ); } */ - if (support_layer_id == 0) { + if (base_layer.layer->bottom_z < EPSILON) { // Base flange. filler = filler_interface.get(); filler->angle = m_object_config->support_material_angle + 90.; @@ -1860,8 +1745,12 @@ void PrintObjectSupportMaterial::generate_toolpaths( bottom_contact_layer.layer->height, m_support_material_interface_flow.nozzle_diameter, bottom_contact_layer.layer->bridging); - filler_interface->angle = interface_angle; - filler_interface->spacing = interface_flow.spacing(); + filler_interface->angle = (m_object_config->support_material_interface_layers.value == 0) ? + // If zero interface layers are configured, use the same angle as for the base layers. + angles[support_layer_id % angles.size()] : + // Use interface angle for the interface layers. + interface_angle; + filler_interface->spacing = m_support_material_interface_flow.spacing(); fill_expolygons_generate_paths( // Destination support_layer.support_fills.entities, diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index c9cbee488..3a319dd2e 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -21,20 +21,33 @@ class PrintObjectConfig; class PrintObjectSupportMaterial { public: + // Support layer type to be used by MyLayer. This type carries a much more detailed information + // about the support layer type than the final support layers stored in a PrintObject. enum SupporLayerType { sltUnknown = 0, + // Ratft base layer, to be printed with the support material. sltRaftBase, + // Raft interface layer, to be printed with the support interface material. sltRaftInterface, - stlFirstLayer, + // Bottom contact layer placed over a top surface of an object. To be printed with a support interface material. sltBottomContact, + // Dense interface layer, to be printed with the support interface material. + // This layer is separated from an object by an sltBottomContact layer. sltBottomInterface, + // Sparse base support layer, to be printed with a support material. sltBase, + // Dense interface layer, to be printed with the support interface material. + // This layer is separated from an object with sltTopContact layer. sltTopInterface, + // Top contact layer directly supporting an overhang. To be printed with a support interface material. sltTopContact, - // Some undecided type yet. It will turn into stlBase first, then it may turn into stlBottomInterface or stlTopInterface. + // Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface. stlIntermediate, }; + // A support layer type used internally by the SupportMaterial class. This class carries a much more detailed + // information about the support layer than the layers stored in the PrintObject, mainly + // the MyLayer is aware of the bridging flow and the interface gaps between the object and the support. class MyLayer { public: @@ -59,6 +72,7 @@ public: return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging; } + // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. bool operator<(const MyLayer &layer2) const { if (print_z < layer2.print_z) { return true; @@ -74,12 +88,12 @@ public: } SupporLayerType layer_type; - // Z used for printing in unscaled coordinates + // Z used for printing, in unscaled coordinates. coordf_t print_z; - // Bottom height of this layer. For soluble layers, bottom_z + height = print_z, + // Bottom Z of this layer. For soluble layers, bottom_z + height = print_z, // otherwise bottom_z + gap + height = print_z. coordf_t bottom_z; - // layer height in unscaled coordinates + // Layer height in unscaled coordinates. coordf_t height; // Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers. // If this is not a contact layer, it will be set to size_t(-1). @@ -93,31 +107,15 @@ public: // Polygons to be filled by the support pattern. Polygons polygons; // Currently for the contact layers only: Overhangs are stored here. + // MyLayer owns the aux_polygons, they are freed by the destructor. Polygons *aux_polygons; }; - struct LayerExtreme - { - LayerExtreme(MyLayer *alayer, bool ais_top) : layer(alayer), is_top(ais_top) {} - MyLayer *layer; - // top or bottom extreme - bool is_top; - - coordf_t z() const { return is_top ? layer->print_z : layer->print_z - layer->height; } - - bool operator<(const LayerExtreme &other) const { return z() < other.z(); } - }; - -/* - struct LayerPrintZ_Hash { - size_t operator()(const MyLayer &layer) const { - return std::hash()(layer.print_z)^std::hash()(layer.height)^size_t(layer.bridging); - } - }; -*/ - - typedef std::vector MyLayersPtr; + // Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained + // up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future, + // which would allocate layers by multiple chunks. typedef std::deque MyLayerStorage; + typedef std::vector MyLayersPtr; public: PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params); @@ -130,6 +128,10 @@ public: bool has_raft() const { return m_slicing_params.has_raft(); } // Has any support? bool has_support() const { return m_object_config->support_material.value; } + bool build_plate_only() const { return this->has_support() && m_object_config->support_material_buildplate_only.value; } + + bool synchronize_layers() const { return m_object_config->support_material_synchronize_layers.value; } + bool has_contact_loops() const { return m_object_config->support_material_interface_contact_loops.value; } // Generate support material for the object. // New support layers will be added to the object, @@ -160,6 +162,7 @@ private: MyLayerStorage &layer_storage, const coordf_t max_object_layer_height) const; + // Fill in the base layers with polygons. void generate_base_layers( const PrintObject &object, const MyLayersPtr &bottom_contacts, @@ -167,12 +170,15 @@ private: MyLayersPtr &intermediate_layers, std::vector &layer_support_areas) const; + // Generate raft layers, also expand the 1st support layer + // in case there is no raft layer to improve support adhesion. MyLayersPtr generate_raft_base( const PrintObject &object, const MyLayersPtr &top_contacts, MyLayersPtr &intermediate_layers, MyLayerStorage &layer_storage) const; + // Turn some of the base layers into interface layers. MyLayersPtr generate_interface_layers( const PrintObject &object, const MyLayersPtr &bottom_contacts, @@ -180,6 +186,15 @@ private: MyLayersPtr &intermediate_layers, MyLayerStorage &layer_storage) const; + // Trim support layers by an object to leave a defined gap between + // the support volume and the object. + void trim_support_layers_by_object( + const PrintObject &object, + MyLayersPtr &support_layers, + const coordf_t gap_extra_above, + const coordf_t gap_extra_below, + const coordf_t gap_xy) const; + /* void generate_pillars_shape(); void clip_with_shape(); @@ -194,31 +209,22 @@ private: const MyLayersPtr &intermediate_layers, const MyLayersPtr &interface_layers) const; + // Following objects are not owned by SupportMaterial class. const PrintObject *m_object; const PrintConfig *m_print_config; const PrintObjectConfig *m_object_config; + // Pre-calculated parameters shared between the object slicer and the support generator, + // carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc. SlicingParameters m_slicing_params; Flow m_first_layer_flow; Flow m_support_material_flow; - coordf_t m_support_material_spacing; Flow m_support_material_interface_flow; - coordf_t m_support_material_interface_spacing; coordf_t m_support_layer_height_min; coordf_t m_support_layer_height_max; - coordf_t m_support_interface_layer_height_max; - coordf_t m_gap_extra_above; - coordf_t m_gap_extra_below; - coordf_t m_gap_xy; - - // If enabled, the support layers will be synchronized with object layers. - // This does not prevent the support layers to be combined. - bool m_synchronize_support_layers_with_object; - // If disabled and m_synchronize_support_layers_with_object, - // the support layers will be synchronized with the object layers exactly, no layer will be combined. - bool m_combine_support_layers; + coordf_t m_gap_xy; }; } // namespace Slic3r From fa1506c833e41924de82ba8b78ff564f69b3f7e7 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 11 Jan 2017 13:42:41 +0100 Subject: [PATCH 76/82] Bugfix of duplicate support print paths. --- xs/src/libslic3r/SupportMaterial.cpp | 106 ++++++++++++++++++++++++--- xs/src/libslic3r/SupportMaterial.hpp | 2 +- 2 files changed, 96 insertions(+), 12 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 7b07fdf74..e9c555541 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -35,6 +35,73 @@ namespace Slic3r { //#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. + +const char* support_surface_type_to_color_name(const PrintObjectSupportMaterial::SupporLayerType surface_type) +{ + switch (surface_type) { + case PrintObjectSupportMaterial::sltTopContact: return "rgb(255,0,0)"; // "red"; + case PrintObjectSupportMaterial::sltTopInterface: return "rgb(0,255,0)"; // "green"; + case PrintObjectSupportMaterial::sltBase: return "rgb(0,0,255)"; // "blue"; + case PrintObjectSupportMaterial::sltBottomInterface:return "rgb(255,255,128)"; // yellow + case PrintObjectSupportMaterial::sltBottomContact: return "rgb(255,0,255)"; // magenta + case PrintObjectSupportMaterial::sltRaftInterface: return "rgb(0,255,255)"; + case PrintObjectSupportMaterial::sltRaftBase: return "rgb(128,128,128)"; + case PrintObjectSupportMaterial::sltUnknown: return "rgb(128,0,0)"; // maroon + default: return "rgb(64,64,64)"; + }; +} + +Point export_support_surface_type_legend_to_svg_box_size() +{ + return Point(scale_(1.+10.*8.), scale_(3.)); +} + +void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos) +{ + // 1st row + coord_t pos_x0 = pos.x + scale_(1.); + coord_t pos_x = pos_x0; + coord_t pos_y = pos.y + scale_(1.5); + coord_t step_x = scale_(10.); + svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltTopContact)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "top iface" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltTopInterface)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "base" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltBase)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "bottom iface" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltBottomInterface)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltBottomContact)); + // 2nd row + pos_x = pos_x0; + pos_y = pos.y+scale_(2.8); + svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltRaftInterface)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltRaftBase)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "unknown" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltUnknown)); + pos_x += step_x; + svg.draw_legend(Point(pos_x, pos_y), "intermediate" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltIntermediate)); +} + +void export_print_z_polygons_to_svg(const char *path, PrintObjectSupportMaterial::MyLayer ** const layers, size_t n_layers) +{ + BoundingBox bbox; + for (int i = 0; i < n_layers; ++ i) + bbox.merge(get_extents(layers[i]->polygons)); + Point legend_size = export_support_surface_type_legend_to_svg_box_size(); + Point legend_pos(bbox.min.x, bbox.max.y); + bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y)); + SVG svg(path, bbox); + const float transparency = 0.5f; + for (int i = 0; i < n_layers; ++ i) + svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency); + for (int i = 0; i < n_layers; ++ i) + svg.draw(to_lines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type)); + export_support_surface_type_legend_to_svg(svg, legend_pos); + svg.Close(); +} + PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : m_object (object), m_print_config (&object->print()->config), @@ -251,13 +318,22 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Sort the layers lexicographically by a raising print_z and a decreasing height. std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare()); int layer_id = 0; + assert(object.support_layers.empty()); for (int i = 0; i < int(layers_sorted.size());) { - // Find the last layer with the same print_z, find the minimum layer height of all. + // Find the last layer with roughly the same print_z, find the minimum layer height of all. + // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. int j = i + 1; - coordf_t height_min = layers_sorted[i]->height; - for (; j < layers_sorted.size() && layers_sorted[i]->print_z == layers_sorted[j]->print_z; ++ j) - height_min = std::min(height_min, layers_sorted[j]->height); - object.add_support_layer(layer_id, height_min, layers_sorted[i]->print_z); + coordf_t zmax = layers_sorted[i]->print_z + EPSILON; + for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ; + // Assign an average print_z to the set of layers with nearly equal print_z. + coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z); + coordf_t height_min = layers_sorted[i]->height; + for (int u = i; u < j; ++u) { + MyLayer &layer = *layers_sorted[u]; + layer.print_z = zavg; + height_min = std::min(height_min, layer.height); + } + object.add_support_layer(layer_id, height_min, zavg); if (layer_id > 0) { // Inter-link the support layers into a linked list. SupportLayer *sl1 = object.support_layers[object.support_layer_count()-2]; @@ -265,6 +341,9 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) sl1->upper_layer = sl2; sl2->lower_layer = sl1; } +#ifdef SLIC3R_DEBUG + export_print_z_polygons_to_svg(debug_out_path("support-%d-%lf.svg", iRun, zavg).c_str(), layers_sorted.data() + i, j - i); +#endif i = j; ++ layer_id; } @@ -639,7 +718,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Store the overhang polygons as the aux_polygons. // The overhang polygons are used in the path generator for planning of the contact loops. // if (this->has_contact_loops()) - new_layer.aux_polygons = new Polygons(std::move(overhang_polygons)); + new_layer.aux_polygons = new Polygons(std::move(overhang_polygons)); contact_out.push_back(&new_layer); if (0) { @@ -901,7 +980,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int extr2.layer->bottom_z = extr2z = this->first_layer_height(); extr2.layer->height = extr2.layer->print_z - extr2.layer->bottom_z; // 2) Insert a new intermediate layer. - MyLayer &layer_new = layer_allocate(layer_storage, stlIntermediate); + MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); layer_new.bottom_z = extr1z; layer_new.print_z = this->first_layer_height(); layer_new.height = layer_new.print_z - layer_new.bottom_z; @@ -909,7 +988,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int continue; } } else if (extr1z + step < this->first_layer_height()) { - MyLayer &layer_new = layer_allocate(layer_storage, stlIntermediate); + MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); layer_new.bottom_z = extr1z; layer_new.print_z = extr1z = this->first_layer_height(); layer_new.height = layer_new.print_z - layer_new.bottom_z; @@ -933,7 +1012,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int } // Take the largest allowed step in the Z axis until extr2z_large_steps is reached. for (size_t i = 0; i < n_layers_extra; ++ i) { - MyLayer &layer_new = layer_allocate(layer_storage, stlIntermediate); + MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); if (i + 1 == n_layers_extra) { // Last intermediate layer added. Align the last entered layer with extr2z_large_steps exactly. layer_new.bottom_z = (i == 0) ? extr1z : intermediate_layers.back()->print_z; @@ -1051,6 +1130,7 @@ void PrintObjectSupportMaterial::generate_base_layers( polygons_new, polygons_trimming, true); // safety offset to merge the touching source polygons + layer_intermediate.layer_type = sltBase; /* if (0) { @@ -1515,8 +1595,11 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Insert the raft base layers. size_t support_layer_id = 0; for (; support_layer_id < size_t(std::max(0, int(m_slicing_params.raft_layers()) - 1)); ++ support_layer_id) { - SupportLayer &support_layer = *object.support_layers[support_layer_id]; assert(support_layer_id < raft_layers.size()); + SupportLayer &support_layer = *object.support_layers[support_layer_id]; + assert(support_layer.support_fills.entities.empty()); + assert(support_layer.support_interface_fills.entities.empty()); + assert(support_layer.support_islands.expolygons.empty()); MyLayer &raft_layer = *raft_layers[support_layer_id]; //FIXME When paralellizing, each thread shall have its own copy of the fillers. Fill *filler = filler_support.get(); @@ -1635,9 +1718,10 @@ void PrintObjectSupportMaterial::generate_toolpaths( if (! interface_layer.empty() && ! base_layer.empty()) { // turn base support into interface when it's contained in our holes // (this way we get wider interface anchoring) + //FIXME one wants to fill in the inner most holes of the interfaces, not all the holes. Polygons islands = top_level_islands(interface_layer.layer->polygons); - base_layer.layer->polygons = diff(base_layer.layer->polygons, islands); polygons_append(interface_layer.layer->polygons, intersection(base_layer.layer->polygons, islands)); + base_layer.layer->polygons = diff(base_layer.layer->polygons, islands); } // interface and contact infill diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 3a319dd2e..48c5fc3bd 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -42,7 +42,7 @@ public: // Top contact layer directly supporting an overhang. To be printed with a support interface material. sltTopContact, // Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface. - stlIntermediate, + sltIntermediate, }; // A support layer type used internally by the SupportMaterial class. This class carries a much more detailed From bd3daeed5adfa5922668390ae460d85402ee5455 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 11 Jan 2017 13:43:33 +0100 Subject: [PATCH 77/82] Slightly more efficient PrintObject Layer destruction. --- xs/src/libslic3r/Layer.cpp | 5 ----- xs/src/libslic3r/Layer.hpp | 2 +- xs/src/libslic3r/PrintObject.cpp | 16 ++++++++++++---- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index 929460169..c40c036e7 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -313,9 +313,4 @@ SupportLayer::SupportLayer(size_t id, PrintObject *object, coordf_t height, { } -SupportLayer::~SupportLayer() -{ -} - - } diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index a5ba3f804..02cb19655 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -152,7 +152,7 @@ public: protected: SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z); - virtual ~SupportLayer(); + virtual ~SupportLayer() {} }; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index c323f5d9d..56a45613f 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -133,8 +133,12 @@ PrintObject::layer_count() const void PrintObject::clear_layers() { - for (int i = this->layers.size()-1; i >= 0; --i) - this->delete_layer(i); + for (size_t i = 0; i < this->layers.size(); ++ i) { + Layer *layer = this->layers[i]; + layer->upper_layer = layer->lower_layer = nullptr; + delete layer; + } + this->layers.clear(); } Layer* @@ -162,8 +166,12 @@ PrintObject::support_layer_count() const void PrintObject::clear_support_layers() { - for (int i = this->support_layers.size()-1; i >= 0; --i) - this->delete_support_layer(i); + for (size_t i = 0; i < this->support_layers.size(); ++ i) { + Layer *layer = this->support_layers[i]; + layer->upper_layer = layer->lower_layer = nullptr; + delete layer; + } + this->support_layers.clear(); } SupportLayer* From 87964eb57acc18285ae31aced6ec3054ccdf5a52 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 11 Jan 2017 13:44:11 +0100 Subject: [PATCH 78/82] Move semantics for the ExPolygon constructor. --- xs/src/libslic3r/ExPolygon.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 7d1a609ea..589649fa0 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -13,9 +13,14 @@ typedef std::vector ExPolygons; class ExPolygon { - public: +public: + ExPolygon() {} + ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {} + ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {} + Polygon contour; Polygons holes; + operator Points() const; operator Polygons() const; operator Polylines() const; From c632d08550601b0f6ba646c08076137b924be8b0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 11 Jan 2017 14:37:53 +0100 Subject: [PATCH 79/82] Only compile debug output functions for SupportGenerator if SLIC3R_DEBUG is enabled. --- xs/src/libslic3r/SupportMaterial.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index e9c555541..b99a50a9e 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -35,7 +35,7 @@ namespace Slic3r { //#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. - +#ifdef SLIC3R_DEBUG const char* support_surface_type_to_color_name(const PrintObjectSupportMaterial::SupporLayerType surface_type) { switch (surface_type) { @@ -101,6 +101,7 @@ void export_print_z_polygons_to_svg(const char *path, PrintObjectSupportMaterial export_support_surface_type_legend_to_svg(svg, legend_pos); svg.Close(); } +#endif /* SLIC3R_DEBUG */ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : m_object (object), From c2ba5901e46ec7c6773c27453965ea6ddb5427ff Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 11 Jan 2017 14:38:24 +0100 Subject: [PATCH 80/82] Copy / move assign operators for ExPolygon. --- xs/src/libslic3r/ExPolygon.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 589649fa0..636b07cc7 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -18,6 +18,9 @@ public: ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {} ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {} + ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; } + ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; } + Polygon contour; Polygons holes; From f0cf7adf84c356f496eaf6c746e97a6f7090b39d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 11 Jan 2017 17:22:28 +0100 Subject: [PATCH 81/82] Bugfix of a new support - first layer thickness of the support. --- xs/src/libslic3r/SupportMaterial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index b99a50a9e..7d41b265f 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -988,7 +988,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int intermediate_layers.push_back(&layer_new); continue; } - } else if (extr1z + step < this->first_layer_height()) { + } else if (extr1z + step > this->first_layer_height()) { MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate); layer_new.bottom_z = extr1z; layer_new.print_z = extr1z = this->first_layer_height(); From 41fbec90630509bcc60435cfc5935a451ce7d3e6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 11 Jan 2017 18:05:03 +0100 Subject: [PATCH 82/82] Only synchronize intermediate layers if explicitely asked for. --- xs/src/libslic3r/SupportMaterial.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 7d41b265f..6f9810a33 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -947,7 +947,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int assert(extremes.front().z() > m_slicing_params.raft_interface_top_z && extremes.front().z() >= m_slicing_params.first_print_layer_height); - bool synchronize = m_slicing_params.soluble_interface || this->synchronize_layers(); +// bool synchronize = m_slicing_params.soluble_interface || this->synchronize_layers(); + bool synchronize = this->synchronize_layers(); // Generate intermediate layers. // The first intermediate layer is the same as the 1st layer if there is no raft,