#include "SLAPrint.hpp" #include "ClipperUtils.hpp" #include "ExtrusionEntity.hpp" #include "Fill/Fill.hpp" #include "Geometry.hpp" #include "Surface.hpp" #include #include #include namespace Slic3r { void SLAPrint::slice() { TriangleMesh mesh = this->model->mesh(); mesh.repair(); // align to origin taking raft into account this->bb = mesh.bounding_box(); if (this->config.raft_layers > 0) { this->bb.min.x -= this->config.raft_offset.value; this->bb.min.y -= this->config.raft_offset.value; this->bb.max.x += this->config.raft_offset.value; this->bb.max.y += this->config.raft_offset.value; } mesh.translate(0, 0, -bb.min.z); this->bb.translate(0, 0, -bb.min.z); // if we are generating a raft, first_layer_height will not affect mesh slicing const float lh = this->config.layer_height.value; const float first_lh = this->config.first_layer_height.value; // generate the list of Z coordinates for mesh slicing // (we slice each layer at half of its thickness) this->layers.clear(); { const float first_slice_lh = (this->config.raft_layers > 0) ? lh : first_lh; this->layers.push_back(Layer(first_slice_lh/2, first_slice_lh)); } while (this->layers.back().print_z + lh/2 <= mesh.stl.stats.max.z) { this->layers.push_back(Layer(this->layers.back().print_z + lh/2, this->layers.back().print_z + lh)); } // perform slicing and generate layers { std::vector slice_z; for (size_t i = 0; i < this->layers.size(); ++i) slice_z.push_back(this->layers[i].slice_z); std::vector slices; TriangleMeshSlicer(&mesh).slice(slice_z, &slices); for (size_t i = 0; i < slices.size(); ++i) this->layers[i].slices.expolygons = slices[i]; } // generate infill if (this->config.fill_density < 100) { std::unique_ptr fill(Fill::new_from_type(this->config.fill_pattern.value)); fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); fill->min_spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); fill->angle = Geometry::deg2rad(this->config.fill_angle.value); fill->density = this->config.fill_density.value/100; parallelize( 0, this->layers.size()-1, boost::bind(&SLAPrint::_infill_layer, this, _1, fill.get()), this->config.threads.value ); } // generate support material this->sm_pillars.clear(); ExPolygons overhangs; if (this->config.support_material) { // flatten and merge all the overhangs { Polygons pp; for (std::vector::const_iterator it = this->layers.begin()+1; it != this->layers.end(); ++it) pp += diff(it->slices, (it - 1)->slices); overhangs = union_ex(pp); } // generate points following the shape of each island Points pillars_pos; const coordf_t spacing = scale_(this->config.support_material_spacing); const coordf_t radius = scale_(this->sm_pillars_radius()); for (ExPolygons::const_iterator it = overhangs.begin(); it != overhangs.end(); ++it) { // leave a radius/2 gap between pillars and contour to prevent lateral adhesion for (float inset = radius * 1.5;; inset += spacing) { // inset according to the configured spacing Polygons curr = offset(*it, -inset); if (curr.empty()) break; // generate points along the contours for (Polygons::const_iterator pg = curr.begin(); pg != curr.end(); ++pg) { Points pp = pg->equally_spaced_points(spacing); for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) pillars_pos.push_back(*p); } } } // for each pillar, check which layers it applies to for (Points::const_iterator p = pillars_pos.begin(); p != pillars_pos.end(); ++p) { SupportPillar pillar(*p); bool object_hit = false; // check layers top-down for (int i = this->layers.size()-1; i >= 0; --i) { // check whether point is void in this layer if (!this->layers[i].slices.contains(*p)) { // no slice contains the point, so it's in the void if (pillar.top_layer > 0) { // we have a pillar, so extend it pillar.bottom_layer = i + this->config.raft_layers; } else if (object_hit) { // we don't have a pillar and we're below the object, so create one pillar.top_layer = i + this->config.raft_layers; } } else { if (pillar.top_layer > 0) { // we have a pillar which is not needed anymore, so store it and initialize a new potential pillar this->sm_pillars.push_back(pillar); pillar = SupportPillar(*p); } object_hit = true; } } if (pillar.top_layer > 0) this->sm_pillars.push_back(pillar); } } // generate a solid raft if requested // (do this after support material because we take support material shape into account) if (this->config.raft_layers > 0) { ExPolygons raft = this->layers.front().slices + overhangs; // take support material into account raft = offset_ex(raft, scale_(this->config.raft_offset)); for (int i = this->config.raft_layers; i >= 1; --i) { this->layers.insert(this->layers.begin(), Layer(0, first_lh + lh * (i-1))); this->layers.front().slices = raft; } // prepend total raft height to all sliced layers for (size_t i = this->config.raft_layers; i < this->layers.size(); ++i) this->layers[i].print_z += first_lh + lh * (this->config.raft_layers-1); } } void SLAPrint::_infill_layer(size_t i, const Fill* _fill) { Layer &layer = this->layers[i]; const float shell_thickness = this->config.get_abs_value("perimeter_extrusion_width", this->config.layer_height.value); // In order to detect what regions of this layer need to be solid, // perform an intersection with layers within the requested shell thickness. Polygons internal = layer.slices; for (size_t j = 0; j < this->layers.size(); ++j) { const Layer &other = this->layers[j]; if (std::abs(other.print_z - layer.print_z) > shell_thickness) continue; if (j == 0 || j == this->layers.size()-1) { internal.clear(); break; } else if (i != j) { internal = intersection(internal, other.slices); if (internal.empty()) break; } } // If we have no internal infill, just print the whole layer as a solid slice. if (internal.empty()) return; layer.solid = false; const Polygons infill = offset(layer.slices, -scale_(shell_thickness)); // Generate solid infill layer.solid_infill << diff_ex(infill, internal, true); // Generate internal infill { std::unique_ptr fill(_fill->clone()); fill->layer_id = i; fill->z = layer.print_z; ExtrusionPath templ(erInternalInfill); templ.width = fill->spacing(); const ExPolygons internal_ex = intersection_ex(infill, internal); for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) { Polylines polylines = fill->fill_surface(Surface(stInternal, *it)); layer.infill.append(polylines, templ); } } // Generate perimeter(s). layer.perimeters << diff_ex( layer.slices, offset(layer.slices, -scale_(shell_thickness)) ); } void SLAPrint::write_svg(const std::string &outputfile) const { const Sizef3 size = this->bb.size(); const double support_material_radius = sm_pillars_radius(); FILE* f = fopen(outputfile.c_str(), "w"); fprintf(f, "\n" "\n" "\n" "\n" , size.x, size.y, SLIC3R_VERSION); for (size_t i = 0; i < this->layers.size(); ++i) { const Layer &layer = this->layers[i]; fprintf(f, "\t\n", i, layer.print_z, layer.slice_z, layer.print_z - ((i == 0) ? 0. : this->layers[i-1].print_z) ); if (layer.solid) { const ExPolygons &slices = layer.slices.expolygons; for (ExPolygons::const_iterator it = slices.begin(); it != slices.end(); ++it) { std::string pd = this->_SVG_path_d(*it); fprintf(f,"\t\t\n", pd.c_str(), "white", "black", "0", unscale(unscale(it->area())) ); } } else { // Perimeters. for (ExPolygons::const_iterator it = layer.perimeters.expolygons.begin(); it != layer.perimeters.expolygons.end(); ++it) { std::string pd = this->_SVG_path_d(*it); fprintf(f,"\t\t\n", pd.c_str(), "white", "black", "0" ); } // Solid infill. for (ExPolygons::const_iterator it = layer.solid_infill.expolygons.begin(); it != layer.solid_infill.expolygons.end(); ++it) { std::string pd = this->_SVG_path_d(*it); fprintf(f,"\t\t\n", pd.c_str(), "white", "black", "0" ); } // Internal infill. for (ExtrusionEntitiesPtr::const_iterator it = layer.infill.entities.begin(); it != layer.infill.entities.end(); ++it) { const ExPolygons infill = union_ex((*it)->grow()); for (ExPolygons::const_iterator e = infill.begin(); e != infill.end(); ++e) { std::string pd = this->_SVG_path_d(*e); fprintf(f,"\t\t\n", pd.c_str(), "white", "black", "0" ); } } } // don't print support material in raft layers if (i >= (size_t)this->config.raft_layers) { // look for support material pillars belonging to this layer for (std::vector::const_iterator it = this->sm_pillars.begin(); it != this->sm_pillars.end(); ++it) { if (!(it->top_layer >= i && it->bottom_layer <= i)) continue; // generate a conic tip float radius = fminf( support_material_radius, (it->top_layer - i + 1) * this->config.layer_height.value ); fprintf(f,"\t\t\n", unscale(it->x) - this->bb.min.x, size.y - (unscale(it->y) - this->bb.min.y), radius ); } } fprintf(f,"\t\n"); } fprintf(f,"\n"); } coordf_t SLAPrint::sm_pillars_radius() const { coordf_t radius = this->config.support_material_extrusion_width.get_abs_value(this->config.support_material_spacing)/2; if (radius == 0) radius = this->config.support_material_spacing / 3; // auto return radius; } std::string SLAPrint::_SVG_path_d(const Polygon &polygon) const { const Sizef3 size = this->bb.size(); std::ostringstream d; d << "M "; for (Points::const_iterator p = polygon.points.begin(); p != polygon.points.end(); ++p) { d << unscale(p->x) - this->bb.min.x << " "; d << size.y - (unscale(p->y) - this->bb.min.y) << " "; // mirror Y coordinates as SVG uses downwards Y } d << "z"; return d.str(); } std::string SLAPrint::_SVG_path_d(const ExPolygon &expolygon) const { std::string pd; const Polygons pp = expolygon; for (Polygons::const_iterator mp = pp.begin(); mp != pp.end(); ++mp) pd += this->_SVG_path_d(*mp) + " "; return pd; } }