mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-04 12:00:38 +08:00
334 lines
14 KiB
C++
334 lines
14 KiB
C++
#include "SLAPrint.hpp"
|
|
#include "ClipperUtils.hpp"
|
|
#include "ExtrusionEntity.hpp"
|
|
#include "Fill/Fill.hpp"
|
|
#include "Geometry.hpp"
|
|
#include "Surface.hpp"
|
|
#include <iostream>
|
|
#include <complex>
|
|
#include <cstdio>
|
|
|
|
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<float> slice_z;
|
|
for (size_t i = 0; i < this->layers.size(); ++i)
|
|
slice_z.push_back(this->layers[i].slice_z);
|
|
|
|
std::vector<ExPolygons> slices;
|
|
TriangleMeshSlicer<Z>(&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(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<size_t>(
|
|
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<Layer>::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(_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,
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
|
|
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
|
|
"<svg width=\"%f\" height=\"%f\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:slic3r=\"http://slic3r.org/namespaces/slic3r\" viewport-fill=\"black\">\n"
|
|
"<!-- Generated using Slic3r %s http://slic3r.org/ -->\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<g id=\"layer%zu\" slic3r:z=\"%0.4f\" slic3r:slice-z=\"%0.4f\" slic3r:layer-height=\"%0.4f\">\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<path d=\"%s\" style=\"fill: %s; stroke: %s; stroke-width: %s; fill-type: evenodd\" slic3r:area=\"%0.4f\" />\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<path d=\"%s\" style=\"fill: %s; stroke: %s; stroke-width: %s; fill-type: evenodd\" slic3r:type=\"perimeter\" />\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<path d=\"%s\" style=\"fill: %s; stroke: %s; stroke-width: %s; fill-type: evenodd\" slic3r:type=\"infill\" />\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<path d=\"%s\" style=\"fill: %s; stroke: %s; stroke-width: %s; fill-type: evenodd\" slic3r:type=\"infill\" />\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<SupportPillar>::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<circle cx=\"%f\" cy=\"%f\" r=\"%f\" stroke-width=\"0\" fill=\"white\" slic3r:type=\"support\" />\n",
|
|
unscale(it->x) - this->bb.min.x,
|
|
size.y - (unscale(it->y) - this->bb.min.y),
|
|
radius
|
|
);
|
|
}
|
|
}
|
|
|
|
fprintf(f,"\t</g>\n");
|
|
}
|
|
fprintf(f,"</svg>\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;
|
|
}
|
|
|
|
}
|