PrusaSlicer-NonPlainar/xs/src/libslic3r/PrintObject.cpp

493 lines
17 KiB
C++

#include "Print.hpp"
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
namespace Slic3r {
PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox)
: typed_slices(false),
_print(print),
_model_object(model_object)
{
// 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));
}
this->reload_model_instances();
this->layer_height_ranges = model_object->layer_height_ranges;
}
PrintObject::~PrintObject()
{
}
Print*
PrintObject::print()
{
return this->_print;
}
ModelObject*
PrintObject::model_object()
{
return this->_model_object;
}
Points
PrintObject::copies() const
{
return this->_copies;
}
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);
}
bool
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);
}
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);
}
BoundingBox
PrintObject::bounding_box() const
{
// 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);
return BoundingBox(pp);
}
void
PrintObject::add_region_volume(int region_id, int volume_id)
{
region_volumes[region_id].push_back(volume_id);
}
/* 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 */
size_t
PrintObject::total_layer_count() const
{
return this->layer_count() + this->support_layer_count();
}
size_t
PrintObject::layer_count() const
{
return this->layers.size();
}
void
PrintObject::clear_layers()
{
for (int i = this->layers.size()-1; i >= 0; --i)
this->delete_layer(i);
}
Layer*
PrintObject::get_layer(int idx)
{
return this->layers.at(idx);
}
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
PrintObject::support_layer_count() const
{
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*
PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z)
{
SupportLayer* layer = new SupportLayer(id, this, height, print_z, -1);
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"
|| *opt_key == "first_layer_extrusion_width"
|| *opt_key == "perimeter_extrusion_width"
|| *opt_key == "infill_overlap"
|| *opt_key == "thin_walls"
|| *opt_key == "external_perimeters_first") {
steps.insert(posPerimeters);
} else if (*opt_key == "layer_height"
|| *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"
|| *opt_key == "support_material_pattern"
|| *opt_key == "support_material_spacing"
|| *opt_key == "support_material_threshold"
|| *opt_key == "dont_support_bridges"
|| *opt_key == "first_layer_extrusion_width") {
steps.insert(posSupportMaterial);
} else if (*opt_key == "interface_shells"
|| *opt_key == "infill_only_where_needed"
|| *opt_key == "infill_every_layers"
|| *opt_key == "solid_infill_every_layers"
|| *opt_key == "bottom_solid_layers"
|| *opt_key == "top_solid_layers"
|| *opt_key == "solid_infill_below_area"
|| *opt_key == "infill_extruder"
|| *opt_key == "solid_infill_extruder"
|| *opt_key == "infill_extrusion_width") {
steps.insert(posPrepareInfill);
} else if (*opt_key == "external_fill_pattern"
|| *opt_key == "fill_angle"
|| *opt_key == "fill_pattern"
|| *opt_key == "top_infill_extrusion_width"
|| *opt_key == "first_layer_extrusion_width") {
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"
|| *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);
} else if (step == posSupportMaterial) {
this->_print->invalidate_step(psSkirt);
this->_print->invalidate_step(psBrim);
}
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;
}
bool
PrintObject::has_support_material() const
{
return this->config.support_material
|| this->config.raft_layers > 0
|| this->config.support_material_enforce_layers > 0;
}
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);
}
}
}
/* This method applies bridge flow to the first internal solid layer above
sparse infill */
void
PrintObject::bridge_over_infill()
{
FOREACH_REGION(this->_print, region) {
size_t region_id = region - this->_print->regions.begin();
// skip bridging in case there are no voids
if ((*region)->config.fill_density.value == 100) continue;
// 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
);
FOREACH_LAYER(this, layer_it) {
// skip first layer
if (layer_it == this->layers.begin()) continue;
Layer* layer = *layer_it;
LayerRegion* layerm = layer->get_region(region_id);
// extract the stInternalSolid surfaces that might be transformed into bridges
Polygons internal_solid;
layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid);
// 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)
ExPolygons to_bridge;
{
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) {
const Layer* lower_layer = this->layers[i];
// 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
to_bridge_pp = intersection(to_bridge_pp, lower_internal);
}
// there's no point in bridging too thin/short regions
{
double min_width = bridge_flow.scaled_width() * 3;
to_bridge_pp = offset2(to_bridge_pp, -min_width, +min_width);
}
if (to_bridge_pp.empty()) continue;
// convert into ExPolygons
to_bridge = union_ex(to_bridge_pp);
}
#ifdef SLIC3R_DEBUG
printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id());
#endif
// compute the remaning internal solid surfaces as difference
ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true);
// build the new collection of fill_surfaces
{
Surfaces new_surfaces;
for (Surfaces::const_iterator surface = layerm->fill_surfaces.surfaces.begin(); surface != layerm->fill_surfaces.surfaces.end(); ++surface) {
if (surface->surface_type != stInternalSolid)
new_surfaces.push_back(*surface);
}
for (ExPolygons::const_iterator ex = to_bridge.begin(); ex != to_bridge.end(); ++ex)
new_surfaces.push_back(Surface(stInternalBridge, *ex));
for (ExPolygons::const_iterator ex = not_to_bridge.begin(); ex != not_to_bridge.end(); ++ex)
new_surfaces.push_back(Surface(stInternalSolid, *ex));
layerm->fill_surfaces.surfaces = new_surfaces;
}
/*
# 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;
}
}
*/
}
}
}
#ifdef SLIC3RXS
REGISTER_CLASS(PrintObject, "Print::Object");
#endif
}