2014-08-03 17:28:40 +00:00
|
|
|
#include "Print.hpp"
|
|
|
|
#include "BoundingBox.hpp"
|
2014-12-24 09:20:55 +00:00
|
|
|
#include "ClipperUtils.hpp"
|
2014-11-09 11:25:59 +00:00
|
|
|
#include "Geometry.hpp"
|
2016-09-26 11:56:24 +00:00
|
|
|
#include "SVG.hpp"
|
2014-08-03 17:28:40 +00:00
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
|
|
|
|
PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox)
|
2015-07-28 21:29:25 +00:00
|
|
|
: typed_slices(false),
|
|
|
|
_print(print),
|
|
|
|
_model_object(model_object)
|
2014-08-03 17:28:40 +00:00
|
|
|
{
|
|
|
|
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
|
|
|
|
{
|
|
|
|
// Translate meshes so that our toolpath generation algorithms work with smaller
|
|
|
|
// XY coordinates; this translation is an optimization and not strictly required.
|
|
|
|
// A cloned mesh will be aligned to 0 before slicing in _slice_region() since we
|
|
|
|
// don't assume it's already aligned and we don't alter the original position in model.
|
|
|
|
// We store the XY translation so that we can place copies correctly in the output G-code
|
|
|
|
// (copies are expressed in G-code coordinates and this translation is not publicly exposed).
|
|
|
|
this->_copies_shift = Point(
|
|
|
|
scale_(modobj_bbox.min.x), scale_(modobj_bbox.min.y));
|
|
|
|
|
|
|
|
// Scale the object size and store it
|
|
|
|
Pointf3 size = modobj_bbox.size();
|
|
|
|
this->size = Point3(scale_(size.x), scale_(size.y), scale_(size.z));
|
|
|
|
}
|
2014-11-12 22:28:42 +00:00
|
|
|
|
|
|
|
this->reload_model_instances();
|
|
|
|
this->layer_height_ranges = model_object->layer_height_ranges;
|
2014-08-03 17:28:40 +00:00
|
|
|
}
|
|
|
|
|
2014-11-12 23:34:56 +00:00
|
|
|
bool
|
|
|
|
PrintObject::add_copy(const Pointf &point)
|
|
|
|
{
|
|
|
|
Points points = this->_copies;
|
|
|
|
points.push_back(Point::new_scale(point.x, point.y));
|
|
|
|
return this->set_copies(points);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
PrintObject::delete_last_copy()
|
|
|
|
{
|
|
|
|
Points points = this->_copies;
|
|
|
|
points.pop_back();
|
|
|
|
return this->set_copies(points);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
PrintObject::delete_all_copies()
|
|
|
|
{
|
|
|
|
Points points;
|
|
|
|
return this->set_copies(points);
|
|
|
|
}
|
|
|
|
|
2014-11-12 22:28:42 +00:00
|
|
|
bool
|
2014-11-09 11:25:59 +00:00
|
|
|
PrintObject::set_copies(const Points &points)
|
|
|
|
{
|
|
|
|
this->_copies = points;
|
|
|
|
|
|
|
|
// order copies with a nearest neighbor search and translate them by _copies_shift
|
|
|
|
this->_shifted_copies.clear();
|
|
|
|
this->_shifted_copies.reserve(points.size());
|
|
|
|
|
|
|
|
// order copies with a nearest-neighbor search
|
|
|
|
std::vector<Points::size_type> ordered_copies;
|
|
|
|
Slic3r::Geometry::chained_path(points, ordered_copies);
|
|
|
|
|
|
|
|
for (std::vector<Points::size_type>::const_iterator it = ordered_copies.begin(); it != ordered_copies.end(); ++it) {
|
|
|
|
Point copy = points[*it];
|
|
|
|
copy.translate(this->_copies_shift);
|
|
|
|
this->_shifted_copies.push_back(copy);
|
|
|
|
}
|
|
|
|
|
2014-11-12 22:28:42 +00:00
|
|
|
bool invalidated = false;
|
|
|
|
if (this->_print->invalidate_step(psSkirt)) invalidated = true;
|
|
|
|
if (this->_print->invalidate_step(psBrim)) invalidated = true;
|
|
|
|
return invalidated;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
PrintObject::reload_model_instances()
|
|
|
|
{
|
|
|
|
Points copies;
|
|
|
|
for (ModelInstancePtrs::const_iterator i = this->_model_object->instances.begin(); i != this->_model_object->instances.end(); ++i) {
|
|
|
|
copies.push_back(Point::new_scale((*i)->offset.x, (*i)->offset.y));
|
|
|
|
}
|
|
|
|
return this->set_copies(copies);
|
2014-11-09 11:25:59 +00:00
|
|
|
}
|
|
|
|
|
2015-01-19 17:53:04 +00:00
|
|
|
BoundingBox
|
|
|
|
PrintObject::bounding_box() const
|
2014-11-30 20:58:41 +00:00
|
|
|
{
|
|
|
|
// since the object is aligned to origin, bounding box coincides with size
|
|
|
|
Points pp;
|
|
|
|
pp.push_back(Point(0,0));
|
|
|
|
pp.push_back(this->size);
|
2015-01-19 17:53:04 +00:00
|
|
|
return BoundingBox(pp);
|
2014-11-30 20:58:41 +00:00
|
|
|
}
|
|
|
|
|
2014-08-03 17:28:40 +00:00
|
|
|
void
|
|
|
|
PrintObject::add_region_volume(int region_id, int volume_id)
|
|
|
|
{
|
|
|
|
region_volumes[region_id].push_back(volume_id);
|
|
|
|
}
|
|
|
|
|
2014-11-30 21:01:46 +00:00
|
|
|
/* This is the *total* layer count (including support layers)
|
|
|
|
this value is not supposed to be compared with Layer::id
|
|
|
|
since they have different semantics */
|
2014-08-03 17:28:40 +00:00
|
|
|
size_t
|
2014-11-30 21:01:46 +00:00
|
|
|
PrintObject::total_layer_count() const
|
|
|
|
{
|
|
|
|
return this->layer_count() + this->support_layer_count();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
PrintObject::layer_count() const
|
2014-08-03 17:28:40 +00:00
|
|
|
{
|
|
|
|
return this->layers.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PrintObject::clear_layers()
|
|
|
|
{
|
|
|
|
for (int i = this->layers.size()-1; i >= 0; --i)
|
|
|
|
this->delete_layer(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
Layer*
|
|
|
|
PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
|
|
|
|
{
|
|
|
|
Layer* layer = new Layer(id, this, height, print_z, slice_z);
|
|
|
|
layers.push_back(layer);
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PrintObject::delete_layer(int idx)
|
|
|
|
{
|
|
|
|
LayerPtrs::iterator i = this->layers.begin() + idx;
|
|
|
|
delete *i;
|
|
|
|
this->layers.erase(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
2014-11-30 21:01:46 +00:00
|
|
|
PrintObject::support_layer_count() const
|
2014-08-03 17:28:40 +00:00
|
|
|
{
|
|
|
|
return this->support_layers.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PrintObject::clear_support_layers()
|
|
|
|
{
|
|
|
|
for (int i = this->support_layers.size()-1; i >= 0; --i)
|
|
|
|
this->delete_support_layer(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
SupportLayer*
|
|
|
|
PrintObject::get_support_layer(int idx)
|
|
|
|
{
|
|
|
|
return this->support_layers.at(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
SupportLayer*
|
2015-01-30 17:45:30 +00:00
|
|
|
PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z)
|
2014-08-03 17:28:40 +00:00
|
|
|
{
|
2015-01-30 17:45:30 +00:00
|
|
|
SupportLayer* layer = new SupportLayer(id, this, height, print_z, -1);
|
2014-08-03 17:28:40 +00:00
|
|
|
support_layers.push_back(layer);
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PrintObject::delete_support_layer(int idx)
|
|
|
|
{
|
|
|
|
SupportLayerPtrs::iterator i = this->support_layers.begin() + idx;
|
|
|
|
delete *i;
|
|
|
|
this->support_layers.erase(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
PrintObject::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
|
|
|
{
|
|
|
|
std::set<PrintObjectStep> steps;
|
|
|
|
|
|
|
|
// this method only accepts PrintObjectConfig and PrintRegionConfig option keys
|
|
|
|
for (std::vector<t_config_option_key>::const_iterator opt_key = opt_keys.begin(); opt_key != opt_keys.end(); ++opt_key) {
|
|
|
|
if (*opt_key == "perimeters"
|
|
|
|
|| *opt_key == "extra_perimeters"
|
|
|
|
|| *opt_key == "gap_fill_speed"
|
|
|
|
|| *opt_key == "overhangs"
|
2014-11-30 19:38:42 +00:00
|
|
|
|| *opt_key == "first_layer_extrusion_width"
|
2014-08-03 17:28:40 +00:00
|
|
|
|| *opt_key == "perimeter_extrusion_width"
|
2015-02-01 11:08:25 +00:00
|
|
|
|| *opt_key == "infill_overlap"
|
2014-08-03 17:28:40 +00:00
|
|
|
|| *opt_key == "thin_walls"
|
|
|
|
|| *opt_key == "external_perimeters_first") {
|
|
|
|
steps.insert(posPerimeters);
|
2015-04-29 17:22:44 +00:00
|
|
|
} else if (*opt_key == "layer_height"
|
2014-08-03 17:28:40 +00:00
|
|
|
|| *opt_key == "first_layer_height"
|
|
|
|
|| *opt_key == "xy_size_compensation"
|
|
|
|
|| *opt_key == "raft_layers") {
|
|
|
|
steps.insert(posSlice);
|
|
|
|
} else if (*opt_key == "support_material"
|
|
|
|
|| *opt_key == "support_material_angle"
|
|
|
|
|| *opt_key == "support_material_extruder"
|
|
|
|
|| *opt_key == "support_material_extrusion_width"
|
|
|
|
|| *opt_key == "support_material_interface_layers"
|
|
|
|
|| *opt_key == "support_material_interface_extruder"
|
|
|
|
|| *opt_key == "support_material_interface_spacing"
|
|
|
|
|| *opt_key == "support_material_interface_speed"
|
2016-10-04 11:54:10 +00:00
|
|
|
|| *opt_key == "support_material_buildplate_only"
|
2014-08-03 17:28:40 +00:00
|
|
|
|| *opt_key == "support_material_pattern"
|
|
|
|
|| *opt_key == "support_material_spacing"
|
|
|
|
|| *opt_key == "support_material_threshold"
|
2016-10-04 12:38:13 +00:00
|
|
|
|| *opt_key == "support_material_with_sheath"
|
2014-11-30 19:38:42 +00:00
|
|
|
|| *opt_key == "dont_support_bridges"
|
|
|
|
|| *opt_key == "first_layer_extrusion_width") {
|
2014-08-03 17:28:40 +00:00
|
|
|
steps.insert(posSupportMaterial);
|
|
|
|
} else if (*opt_key == "interface_shells"
|
|
|
|
|| *opt_key == "infill_only_where_needed"
|
2014-12-08 20:23:42 +00:00
|
|
|
|| *opt_key == "infill_every_layers"
|
|
|
|
|| *opt_key == "solid_infill_every_layers"
|
2014-08-03 17:28:40 +00:00
|
|
|
|| *opt_key == "bottom_solid_layers"
|
|
|
|
|| *opt_key == "top_solid_layers"
|
2014-11-06 23:53:15 +00:00
|
|
|
|| *opt_key == "solid_infill_below_area"
|
2014-08-03 17:28:40 +00:00
|
|
|
|| *opt_key == "infill_extruder"
|
2014-12-16 23:34:00 +00:00
|
|
|
|| *opt_key == "solid_infill_extruder"
|
2016-10-16 20:11:19 +00:00
|
|
|
|| *opt_key == "infill_extrusion_width"
|
|
|
|
|| *opt_key == "ensure_vertical_shell_thickness") {
|
2014-08-03 17:28:40 +00:00
|
|
|
steps.insert(posPrepareInfill);
|
2014-11-26 23:38:05 +00:00
|
|
|
} else if (*opt_key == "external_fill_pattern"
|
|
|
|
|| *opt_key == "fill_angle"
|
2014-08-03 17:28:40 +00:00
|
|
|
|| *opt_key == "fill_pattern"
|
2014-11-30 19:38:42 +00:00
|
|
|
|| *opt_key == "top_infill_extrusion_width"
|
|
|
|
|| *opt_key == "first_layer_extrusion_width") {
|
2014-08-03 17:28:40 +00:00
|
|
|
steps.insert(posInfill);
|
|
|
|
} else if (*opt_key == "fill_density"
|
|
|
|
|| *opt_key == "solid_infill_extrusion_width") {
|
|
|
|
steps.insert(posPerimeters);
|
|
|
|
steps.insert(posPrepareInfill);
|
|
|
|
} else if (*opt_key == "external_perimeter_extrusion_width"
|
|
|
|
|| *opt_key == "perimeter_extruder") {
|
|
|
|
steps.insert(posPerimeters);
|
|
|
|
steps.insert(posSupportMaterial);
|
|
|
|
} else if (*opt_key == "bridge_flow_ratio") {
|
|
|
|
steps.insert(posPerimeters);
|
|
|
|
steps.insert(posInfill);
|
|
|
|
} else if (*opt_key == "seam_position"
|
2016-09-13 11:30:00 +00:00
|
|
|
|| *opt_key == "seam_preferred_direction"
|
|
|
|
|| *opt_key == "seam_preferred_direction_jitter"
|
2014-08-03 17:28:40 +00:00
|
|
|
|| *opt_key == "support_material_speed"
|
|
|
|
|| *opt_key == "bridge_speed"
|
|
|
|
|| *opt_key == "external_perimeter_speed"
|
|
|
|
|| *opt_key == "infill_speed"
|
|
|
|
|| *opt_key == "perimeter_speed"
|
|
|
|
|| *opt_key == "small_perimeter_speed"
|
|
|
|
|| *opt_key == "solid_infill_speed"
|
|
|
|
|| *opt_key == "top_solid_infill_speed") {
|
|
|
|
// these options only affect G-code export, so nothing to invalidate
|
|
|
|
} else {
|
|
|
|
// for legacy, if we can't handle this option let's invalidate all steps
|
|
|
|
return this->invalidate_all_steps();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool invalidated = false;
|
|
|
|
for (std::set<PrintObjectStep>::const_iterator step = steps.begin(); step != steps.end(); ++step) {
|
|
|
|
if (this->invalidate_step(*step)) invalidated = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return invalidated;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
PrintObject::invalidate_step(PrintObjectStep step)
|
|
|
|
{
|
|
|
|
bool invalidated = this->state.invalidate(step);
|
|
|
|
|
|
|
|
// propagate to dependent steps
|
|
|
|
if (step == posPerimeters) {
|
|
|
|
this->invalidate_step(posPrepareInfill);
|
|
|
|
this->_print->invalidate_step(psSkirt);
|
|
|
|
this->_print->invalidate_step(psBrim);
|
|
|
|
} else if (step == posPrepareInfill) {
|
|
|
|
this->invalidate_step(posInfill);
|
|
|
|
} else if (step == posInfill) {
|
|
|
|
this->_print->invalidate_step(psSkirt);
|
|
|
|
this->_print->invalidate_step(psBrim);
|
|
|
|
} else if (step == posSlice) {
|
|
|
|
this->invalidate_step(posPerimeters);
|
|
|
|
this->invalidate_step(posSupportMaterial);
|
2014-08-07 23:37:39 +00:00
|
|
|
} else if (step == posSupportMaterial) {
|
|
|
|
this->_print->invalidate_step(psSkirt);
|
|
|
|
this->_print->invalidate_step(psBrim);
|
2014-08-03 17:28:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return invalidated;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
PrintObject::invalidate_all_steps()
|
|
|
|
{
|
|
|
|
// make a copy because when invalidating steps the iterators are not working anymore
|
|
|
|
std::set<PrintObjectStep> steps = this->state.started;
|
|
|
|
|
|
|
|
bool invalidated = false;
|
|
|
|
for (std::set<PrintObjectStep>::const_iterator step = steps.begin(); step != steps.end(); ++step) {
|
|
|
|
if (this->invalidate_step(*step)) invalidated = true;
|
|
|
|
}
|
|
|
|
return invalidated;
|
|
|
|
}
|
|
|
|
|
2015-03-06 08:56:58 +00:00
|
|
|
bool
|
|
|
|
PrintObject::has_support_material() const
|
|
|
|
{
|
|
|
|
return this->config.support_material
|
|
|
|
|| this->config.raft_layers > 0
|
|
|
|
|| this->config.support_material_enforce_layers > 0;
|
|
|
|
}
|
|
|
|
|
2015-10-26 22:23:03 +00:00
|
|
|
void
|
|
|
|
PrintObject::process_external_surfaces()
|
|
|
|
{
|
|
|
|
FOREACH_REGION(this->_print, region) {
|
|
|
|
size_t region_id = region - this->_print->regions.begin();
|
|
|
|
|
|
|
|
FOREACH_LAYER(this, layer_it) {
|
|
|
|
const Layer* lower_layer = (layer_it == this->layers.begin())
|
|
|
|
? NULL
|
|
|
|
: *(layer_it-1);
|
|
|
|
|
|
|
|
(*layer_it)->get_region(region_id)->process_external_surfaces(lower_layer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-26 11:56:24 +00:00
|
|
|
void
|
|
|
|
PrintObject::discover_vertical_shells()
|
|
|
|
{
|
|
|
|
for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
|
2016-10-16 20:11:19 +00:00
|
|
|
if (! this->_print->regions[idx_region]->config.ensure_vertical_shell_thickness.value)
|
|
|
|
continue;
|
2016-09-26 11:56:24 +00:00
|
|
|
for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++ idx_layer) {
|
2016-10-22 20:25:00 +00:00
|
|
|
Layer *layer = this->layers[idx_layer];
|
|
|
|
LayerRegion *layerm = layer->get_region(idx_region);
|
|
|
|
Flow solid_infill_flow = layerm->flow(frSolidInfill);
|
|
|
|
coord_t infill_line_spacing = solid_infill_flow.scaled_spacing();
|
2016-09-26 11:56:24 +00:00
|
|
|
// Find a union of perimeters below / above this surface to guarantee a minimum shell thickness.
|
|
|
|
Polygons shell;
|
2016-10-22 20:25:00 +00:00
|
|
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
2016-09-26 11:56:24 +00:00
|
|
|
ExPolygons shell_ex;
|
2016-10-22 20:25:00 +00:00
|
|
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
2016-09-26 11:56:24 +00:00
|
|
|
if (1)
|
|
|
|
{
|
|
|
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
|
|
|
{
|
|
|
|
static size_t idx = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", idx), this->bounding_box());
|
2016-09-26 11:56:24 +00:00
|
|
|
for (int n = (int)idx_layer - layerm->region()->config.bottom_solid_layers + 1; n < (int)idx_layer + layerm->region()->config.top_solid_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) {
|
2016-10-21 08:18:01 +00:00
|
|
|
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]));
|
2016-09-26 11:56:24 +00:00
|
|
|
svg.draw(expolys[i]);
|
|
|
|
svg.draw_outline(expolys[i].contour, "black", scale_(0.05));
|
|
|
|
svg.draw_outline(expolys[i].holes, "blue", scale_(0.05));
|
|
|
|
svg.Close();
|
|
|
|
|
|
|
|
svg_cummulative.draw(expolys[i]);
|
|
|
|
svg_cummulative.draw_outline(expolys[i].contour, "black", scale_(0.05));
|
|
|
|
svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++ idx;
|
|
|
|
}
|
|
|
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
|
|
|
for (int n = (int)idx_layer - layerm->region()->config.bottom_solid_layers + 1; n < (int)idx_layer + layerm->region()->config.top_solid_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) {
|
|
|
|
shell.push_back(expolys[i].contour);
|
|
|
|
shell.insert(shell.end(), expolys[i].holes.begin(), expolys[i].holes.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
|
|
|
{
|
|
|
|
static size_t idx = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", idx ++), get_extents(shell));
|
2016-09-26 11:56:24 +00:00
|
|
|
svg.draw(shell);
|
|
|
|
svg.draw_outline(shell, "black", scale_(0.05));
|
|
|
|
svg.Close();
|
|
|
|
}
|
|
|
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
|
|
|
shell = union_(shell, true);
|
2016-10-22 20:25:00 +00:00
|
|
|
if (! shell.empty()) {
|
|
|
|
// These regions will be filled by a rectilinear full infill. Currently this type of infill
|
|
|
|
// will only fill regions, which will fit at least a single line. To avoid gaps in the sparse infill,
|
|
|
|
// make sure that this region does not contain narrow parts.
|
|
|
|
coord_t min_perimeter_infill_spacing = coord_t(double(infill_line_spacing) * (1. - INSET_OVERLAP_TOLERANCE));
|
|
|
|
shell = offset2(shell, -min_perimeter_infill_spacing/2, min_perimeter_infill_spacing/2);
|
|
|
|
}
|
2016-09-26 11:56:24 +00:00
|
|
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
|
|
|
shell_ex = union_ex(shell, true);
|
|
|
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
|
|
|
}
|
|
|
|
|
2016-10-22 20:25:00 +00:00
|
|
|
if (shell.empty())
|
|
|
|
continue;
|
|
|
|
|
2016-09-26 11:56:24 +00:00
|
|
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
|
|
|
{
|
|
|
|
static size_t idx = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", idx ++), get_extents(shell));
|
2016-09-26 11:56:24 +00:00
|
|
|
svg.draw(shell_ex);
|
|
|
|
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
|
|
|
|
svg.Close();
|
|
|
|
}
|
|
|
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
|
|
|
|
|
|
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
|
|
|
{
|
|
|
|
static size_t idx = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", idx ++), get_extents(shell));
|
2016-09-26 11:56:24 +00:00
|
|
|
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);
|
|
|
|
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
|
|
|
|
svg.Close();
|
|
|
|
}
|
|
|
|
{
|
|
|
|
static size_t idx = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell));
|
2016-09-26 11:56:24 +00:00
|
|
|
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);
|
|
|
|
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
|
|
|
|
svg.Close();
|
|
|
|
}
|
|
|
|
{
|
|
|
|
static size_t idx = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell));
|
2016-09-26 11:56:24 +00:00
|
|
|
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);
|
|
|
|
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
|
|
|
|
svg.Close();
|
|
|
|
}
|
|
|
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
|
|
|
|
|
|
|
// Trim the internal & internalvoid by the $shell.
|
|
|
|
Slic3r::ExPolygons new_internal = diff_ex(
|
|
|
|
to_polygons(layerm->fill_surfaces.filter_by_type(stInternal)),
|
|
|
|
shell,
|
|
|
|
false
|
|
|
|
);
|
|
|
|
Slic3r::ExPolygons new_internal_void = diff_ex(
|
|
|
|
to_polygons(layerm->fill_surfaces.filter_by_type(stInternalVoid)),
|
|
|
|
shell,
|
|
|
|
false
|
|
|
|
);
|
|
|
|
// Add shells tstInternalVoido internal & internalvoid.
|
|
|
|
const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid };
|
|
|
|
Slic3r::ExPolygons new_internal_solid = intersection_ex(
|
|
|
|
to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 2)),
|
|
|
|
shell,
|
|
|
|
true
|
|
|
|
);
|
|
|
|
|
|
|
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
|
|
|
{
|
|
|
|
static size_t idx = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
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));
|
2016-09-26 11:56:24 +00:00
|
|
|
++ idx;
|
|
|
|
}
|
|
|
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
|
|
|
|
|
|
|
// Assign resulting internal surfaces to layer.
|
|
|
|
const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge, stInternalSolid };
|
|
|
|
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);
|
|
|
|
|
|
|
|
#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");
|
|
|
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
|
|
|
} // for each layer
|
|
|
|
} // for each region
|
|
|
|
}
|
|
|
|
|
2015-10-26 22:23:03 +00:00
|
|
|
/* This method applies bridge flow to the first internal solid layer above
|
|
|
|
sparse infill */
|
2014-12-24 09:20:55 +00:00
|
|
|
void
|
|
|
|
PrintObject::bridge_over_infill()
|
|
|
|
{
|
|
|
|
FOREACH_REGION(this->_print, region) {
|
|
|
|
size_t region_id = region - this->_print->regions.begin();
|
|
|
|
|
2015-10-26 22:23:03 +00:00
|
|
|
// skip bridging in case there are no voids
|
|
|
|
if ((*region)->config.fill_density.value == 100) continue;
|
2014-12-24 09:20:55 +00:00
|
|
|
|
2015-06-13 17:48:46 +00:00
|
|
|
// get bridge flow
|
|
|
|
Flow bridge_flow = (*region)->flow(
|
|
|
|
frSolidInfill,
|
|
|
|
-1, // layer height, not relevant for bridge flow
|
|
|
|
true, // bridge
|
|
|
|
false, // first layer
|
|
|
|
-1, // custom width, not relevant for bridge flow
|
|
|
|
*this
|
|
|
|
);
|
|
|
|
|
2014-12-24 09:20:55 +00:00
|
|
|
FOREACH_LAYER(this, layer_it) {
|
2015-10-26 22:23:03 +00:00
|
|
|
// skip first layer
|
2014-12-24 09:20:55 +00:00
|
|
|
if (layer_it == this->layers.begin()) continue;
|
|
|
|
|
|
|
|
Layer* layer = *layer_it;
|
|
|
|
LayerRegion* layerm = layer->get_region(region_id);
|
|
|
|
|
2015-06-13 17:48:46 +00:00
|
|
|
// extract the stInternalSolid surfaces that might be transformed into bridges
|
|
|
|
Polygons internal_solid;
|
2014-12-24 09:20:55 +00:00
|
|
|
layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid);
|
|
|
|
|
2015-06-13 17:48:46 +00:00
|
|
|
// check whether the lower area is deep enough for absorbing the extra flow
|
|
|
|
// (for obvious physical reasons but also for preventing the bridge extrudates
|
|
|
|
// from overflowing in 3D preview)
|
2014-12-24 09:20:55 +00:00
|
|
|
ExPolygons to_bridge;
|
2015-06-13 17:48:46 +00:00
|
|
|
{
|
|
|
|
Polygons to_bridge_pp = internal_solid;
|
|
|
|
|
|
|
|
// iterate through lower layers spanned by bridge_flow
|
|
|
|
double bottom_z = layer->print_z - bridge_flow.height;
|
|
|
|
for (int i = (layer_it - this->layers.begin()) - 1; i >= 0; --i) {
|
2015-10-26 22:23:03 +00:00
|
|
|
const Layer* lower_layer = this->layers[i];
|
2015-06-13 17:48:46 +00:00
|
|
|
|
|
|
|
// stop iterating if layer is lower than bottom_z
|
|
|
|
if (lower_layer->print_z < bottom_z) break;
|
|
|
|
|
|
|
|
// iterate through regions and collect internal surfaces
|
|
|
|
Polygons lower_internal;
|
|
|
|
FOREACH_LAYERREGION(lower_layer, lower_layerm_it)
|
|
|
|
(*lower_layerm_it)->fill_surfaces.filter_by_type(stInternal, &lower_internal);
|
|
|
|
|
|
|
|
// intersect such lower internal surfaces with the candidate solid surfaces
|
2015-10-26 22:23:03 +00:00
|
|
|
to_bridge_pp = intersection(to_bridge_pp, lower_internal);
|
2015-06-13 17:48:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// there's no point in bridging too thin/short regions
|
2016-09-26 11:56:24 +00:00
|
|
|
//FIXME Vojtech: The offset2 function is not a geometric offset,
|
|
|
|
// therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour.
|
|
|
|
// The gaps will be filled by a separate region, which makes the infill less stable and it takes longer.
|
2015-06-13 17:48:46 +00:00
|
|
|
{
|
|
|
|
double min_width = bridge_flow.scaled_width() * 3;
|
2015-10-26 22:23:03 +00:00
|
|
|
to_bridge_pp = offset2(to_bridge_pp, -min_width, +min_width);
|
2015-06-13 17:48:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (to_bridge_pp.empty()) continue;
|
|
|
|
|
|
|
|
// convert into ExPolygons
|
2015-10-26 22:23:03 +00:00
|
|
|
to_bridge = union_ex(to_bridge_pp);
|
2015-06-13 17:48:46 +00:00
|
|
|
}
|
2014-12-24 09:20:55 +00:00
|
|
|
|
|
|
|
#ifdef SLIC3R_DEBUG
|
2015-06-13 17:48:46 +00:00
|
|
|
printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id());
|
2014-12-24 09:20:55 +00:00
|
|
|
#endif
|
|
|
|
|
2015-06-13 17:48:46 +00:00
|
|
|
// compute the remaning internal solid surfaces as difference
|
2015-10-26 22:23:03 +00:00
|
|
|
ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true);
|
2016-09-26 11:56:24 +00:00
|
|
|
to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true);
|
2015-06-13 17:48:46 +00:00
|
|
|
|
2014-12-24 09:20:55 +00:00
|
|
|
// build the new collection of fill_surfaces
|
|
|
|
{
|
2016-09-26 11:56:24 +00:00
|
|
|
layerm->fill_surfaces.remove_type(stInternalSolid);
|
|
|
|
|
2014-12-24 09:20:55 +00:00
|
|
|
for (ExPolygons::const_iterator ex = to_bridge.begin(); ex != to_bridge.end(); ++ex)
|
2016-09-26 11:56:24 +00:00
|
|
|
layerm->fill_surfaces.surfaces.push_back(Surface(stInternalBridge, *ex));
|
2014-12-24 09:20:55 +00:00
|
|
|
|
|
|
|
for (ExPolygons::const_iterator ex = not_to_bridge.begin(); ex != not_to_bridge.end(); ++ex)
|
2016-09-26 11:56:24 +00:00
|
|
|
layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, *ex));
|
2014-12-24 09:20:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
# exclude infill from the layers below if needed
|
|
|
|
# see discussion at https://github.com/alexrj/Slic3r/issues/240
|
|
|
|
# Update: do not exclude any infill. Sparse infill is able to absorb the excess material.
|
|
|
|
if (0) {
|
|
|
|
my $excess = $layerm->extruders->{infill}->bridge_flow->width - $layerm->height;
|
|
|
|
for (my $i = $layer_id-1; $excess >= $self->get_layer($i)->height; $i--) {
|
|
|
|
Slic3r::debugf " skipping infill below those areas at layer %d\n", $i;
|
|
|
|
foreach my $lower_layerm (@{$self->get_layer($i)->regions}) {
|
|
|
|
my @new_surfaces = ();
|
|
|
|
# subtract the area from all types of surfaces
|
|
|
|
foreach my $group (@{$lower_layerm->fill_surfaces->group}) {
|
|
|
|
push @new_surfaces, map $group->[0]->clone(expolygon => $_),
|
|
|
|
@{diff_ex(
|
|
|
|
[ map $_->p, @$group ],
|
|
|
|
[ map @$_, @$to_bridge ],
|
|
|
|
)};
|
|
|
|
push @new_surfaces, map Slic3r::Surface->new(
|
|
|
|
expolygon => $_,
|
|
|
|
surface_type => S_TYPE_INTERNALVOID,
|
|
|
|
), @{intersection_ex(
|
|
|
|
[ map $_->p, @$group ],
|
|
|
|
[ map @$_, @$to_bridge ],
|
|
|
|
)};
|
|
|
|
}
|
|
|
|
$lower_layerm->fill_surfaces->clear;
|
|
|
|
$lower_layerm->fill_surfaces->append($_) for @new_surfaces;
|
|
|
|
}
|
|
|
|
|
|
|
|
$excess -= $self->get_layer($i)->height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
2016-09-26 11:56:24 +00:00
|
|
|
|
|
|
|
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
|
|
|
layerm->export_region_slices_to_svg_debug("7_bridge_over_infill");
|
|
|
|
layerm->export_region_fill_surfaces_to_svg_debug("7_bridge_over_infill");
|
|
|
|
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
2014-12-24 09:20:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-03 17:28:40 +00:00
|
|
|
}
|