2013-12-20 00:36:42 +00:00
|
|
|
|
#include "Print.hpp"
|
2014-05-06 08:07:18 +00:00
|
|
|
|
#include "BoundingBox.hpp"
|
2014-11-09 14:27:34 +00:00
|
|
|
|
#include "ClipperUtils.hpp"
|
2014-11-11 20:17:02 +00:00
|
|
|
|
#include "Geometry.hpp"
|
2014-08-03 16:41:09 +00:00
|
|
|
|
#include <algorithm>
|
2013-12-20 00:36:42 +00:00
|
|
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
|
|
2014-06-10 22:15:02 +00:00
|
|
|
|
template <class StepClass>
|
2013-12-20 00:36:42 +00:00
|
|
|
|
bool
|
2014-06-13 09:19:53 +00:00
|
|
|
|
PrintState<StepClass>::is_started(StepClass step) const
|
2013-12-20 00:36:42 +00:00
|
|
|
|
{
|
2014-06-13 09:19:53 +00:00
|
|
|
|
return this->started.find(step) != this->started.end();
|
2013-12-20 00:36:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-10 22:15:02 +00:00
|
|
|
|
template <class StepClass>
|
2013-12-20 00:36:42 +00:00
|
|
|
|
bool
|
2014-06-13 09:19:53 +00:00
|
|
|
|
PrintState<StepClass>::is_done(StepClass step) const
|
2013-12-20 00:36:42 +00:00
|
|
|
|
{
|
2014-06-13 09:19:53 +00:00
|
|
|
|
return this->done.find(step) != this->done.end();
|
2013-12-20 00:36:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-10 22:15:02 +00:00
|
|
|
|
template <class StepClass>
|
2013-12-20 00:36:42 +00:00
|
|
|
|
void
|
2014-06-10 22:15:02 +00:00
|
|
|
|
PrintState<StepClass>::set_started(StepClass step)
|
2013-12-20 00:36:42 +00:00
|
|
|
|
{
|
2014-06-13 09:19:53 +00:00
|
|
|
|
this->started.insert(step);
|
2013-12-20 00:36:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-10 22:15:02 +00:00
|
|
|
|
template <class StepClass>
|
2013-12-20 00:36:42 +00:00
|
|
|
|
void
|
2014-06-10 22:15:02 +00:00
|
|
|
|
PrintState<StepClass>::set_done(StepClass step)
|
2013-12-20 00:36:42 +00:00
|
|
|
|
{
|
2014-06-13 09:19:53 +00:00
|
|
|
|
this->done.insert(step);
|
2013-12-20 00:36:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-10 22:15:02 +00:00
|
|
|
|
template <class StepClass>
|
2014-06-12 07:29:26 +00:00
|
|
|
|
bool
|
2014-06-13 09:19:53 +00:00
|
|
|
|
PrintState<StepClass>::invalidate(StepClass step)
|
2014-01-02 21:06:58 +00:00
|
|
|
|
{
|
2014-06-13 09:19:53 +00:00
|
|
|
|
bool invalidated = this->started.erase(step) > 0;
|
|
|
|
|
this->done.erase(step);
|
|
|
|
|
return invalidated;
|
2014-01-02 21:06:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-10 22:15:02 +00:00
|
|
|
|
template class PrintState<PrintStep>;
|
|
|
|
|
template class PrintState<PrintObjectStep>;
|
2014-05-06 08:07:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Print::Print()
|
|
|
|
|
: total_used_filament(0),
|
|
|
|
|
total_extruded_volume(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Print::~Print()
|
|
|
|
|
{
|
|
|
|
|
clear_objects();
|
|
|
|
|
clear_regions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Print::clear_objects()
|
|
|
|
|
{
|
|
|
|
|
for (int i = this->objects.size()-1; i >= 0; --i)
|
|
|
|
|
this->delete_object(i);
|
|
|
|
|
|
|
|
|
|
this->clear_regions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PrintObject*
|
2014-06-10 14:01:57 +00:00
|
|
|
|
Print::get_object(size_t idx)
|
2014-05-06 08:07:18 +00:00
|
|
|
|
{
|
|
|
|
|
return objects.at(idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PrintObject*
|
2014-06-13 18:05:18 +00:00
|
|
|
|
Print::add_object(ModelObject *model_object, const BoundingBoxf3 &modobj_bbox)
|
2014-05-06 08:07:18 +00:00
|
|
|
|
{
|
2014-06-10 14:01:57 +00:00
|
|
|
|
PrintObject *object = new PrintObject(this, model_object, modobj_bbox);
|
2014-05-06 08:07:18 +00:00
|
|
|
|
objects.push_back(object);
|
2014-06-13 18:05:18 +00:00
|
|
|
|
|
|
|
|
|
// invalidate steps
|
|
|
|
|
this->invalidate_step(psSkirt);
|
|
|
|
|
this->invalidate_step(psBrim);
|
|
|
|
|
|
2014-05-06 08:07:18 +00:00
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PrintObject*
|
2014-06-10 14:01:57 +00:00
|
|
|
|
Print::set_new_object(size_t idx, ModelObject *model_object, const BoundingBoxf3 &modobj_bbox)
|
2014-05-06 08:07:18 +00:00
|
|
|
|
{
|
2014-06-10 22:15:02 +00:00
|
|
|
|
if (idx >= this->objects.size()) throw "bad idx";
|
2014-05-06 08:07:18 +00:00
|
|
|
|
|
|
|
|
|
PrintObjectPtrs::iterator old_it = this->objects.begin() + idx;
|
2014-06-13 18:05:18 +00:00
|
|
|
|
// before deleting object, invalidate all of its steps in order to
|
|
|
|
|
// invalidate all of the dependent ones in Print
|
|
|
|
|
(*old_it)->invalidate_all_steps();
|
2014-05-06 08:07:18 +00:00
|
|
|
|
delete *old_it;
|
|
|
|
|
|
2014-06-10 14:01:57 +00:00
|
|
|
|
PrintObject *object = new PrintObject(this, model_object, modobj_bbox);
|
2014-05-06 08:07:18 +00:00
|
|
|
|
this->objects[idx] = object;
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2014-06-10 14:01:57 +00:00
|
|
|
|
Print::delete_object(size_t idx)
|
2014-05-06 08:07:18 +00:00
|
|
|
|
{
|
|
|
|
|
PrintObjectPtrs::iterator i = this->objects.begin() + idx;
|
2014-06-10 14:01:57 +00:00
|
|
|
|
delete *i;
|
2014-05-06 08:07:18 +00:00
|
|
|
|
this->objects.erase(i);
|
|
|
|
|
|
|
|
|
|
// TODO: purge unused regions
|
|
|
|
|
|
2014-06-10 22:15:02 +00:00
|
|
|
|
this->state.invalidate(psSkirt);
|
|
|
|
|
this->state.invalidate(psBrim);
|
2014-05-06 08:07:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Print::clear_regions()
|
|
|
|
|
{
|
|
|
|
|
for (int i = this->regions.size()-1; i >= 0; --i)
|
|
|
|
|
this->delete_region(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PrintRegion*
|
2014-06-10 14:01:57 +00:00
|
|
|
|
Print::get_region(size_t idx)
|
2014-05-06 08:07:18 +00:00
|
|
|
|
{
|
|
|
|
|
return regions.at(idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PrintRegion*
|
|
|
|
|
Print::add_region()
|
|
|
|
|
{
|
|
|
|
|
PrintRegion *region = new PrintRegion(this);
|
|
|
|
|
regions.push_back(region);
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2014-06-10 14:01:57 +00:00
|
|
|
|
Print::delete_region(size_t idx)
|
2014-05-06 08:07:18 +00:00
|
|
|
|
{
|
|
|
|
|
PrintRegionPtrs::iterator i = this->regions.begin() + idx;
|
2014-06-10 14:01:57 +00:00
|
|
|
|
delete *i;
|
2014-05-06 08:07:18 +00:00
|
|
|
|
this->regions.erase(i);
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-10 22:15:02 +00:00
|
|
|
|
bool
|
|
|
|
|
Print::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
|
|
|
|
{
|
|
|
|
|
std::set<PrintStep> steps;
|
|
|
|
|
|
|
|
|
|
// this method only accepts PrintConfig 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 == "skirts"
|
|
|
|
|
|| *opt_key == "skirt_height"
|
|
|
|
|
|| *opt_key == "skirt_distance"
|
|
|
|
|
|| *opt_key == "min_skirt_length") {
|
|
|
|
|
steps.insert(psSkirt);
|
|
|
|
|
} else if (*opt_key == "brim_width") {
|
|
|
|
|
steps.insert(psBrim);
|
2014-07-24 21:43:19 +00:00
|
|
|
|
steps.insert(psSkirt);
|
2014-06-13 21:07:46 +00:00
|
|
|
|
} else if (*opt_key == "nozzle_diameter") {
|
|
|
|
|
steps.insert(psInitExtruders);
|
|
|
|
|
} else if (*opt_key == "avoid_crossing_perimeters"
|
2014-06-16 12:05:22 +00:00
|
|
|
|
|| *opt_key == "bed_shape"
|
2014-06-13 21:07:46 +00:00
|
|
|
|
|| *opt_key == "bed_temperature"
|
|
|
|
|
|| *opt_key == "bridge_acceleration"
|
|
|
|
|
|| *opt_key == "bridge_fan_speed"
|
|
|
|
|
|| *opt_key == "complete_objects"
|
|
|
|
|
|| *opt_key == "cooling"
|
|
|
|
|
|| *opt_key == "default_acceleration"
|
|
|
|
|
|| *opt_key == "disable_fan_first_layers"
|
|
|
|
|
|| *opt_key == "duplicate_distance"
|
|
|
|
|
|| *opt_key == "end_gcode"
|
|
|
|
|
|| *opt_key == "extruder_clearance_height"
|
|
|
|
|
|| *opt_key == "extruder_clearance_radius"
|
|
|
|
|
|| *opt_key == "extruder_offset"
|
|
|
|
|
|| *opt_key == "extrusion_axis"
|
|
|
|
|
|| *opt_key == "extrusion_multiplier"
|
|
|
|
|
|| *opt_key == "fan_always_on"
|
|
|
|
|
|| *opt_key == "fan_below_layer_time"
|
|
|
|
|
|| *opt_key == "filament_diameter"
|
|
|
|
|
|| *opt_key == "first_layer_acceleration"
|
|
|
|
|
|| *opt_key == "first_layer_bed_temperature"
|
|
|
|
|
|| *opt_key == "first_layer_speed"
|
|
|
|
|
|| *opt_key == "first_layer_temperature"
|
|
|
|
|
|| *opt_key == "gcode_arcs"
|
|
|
|
|
|| *opt_key == "gcode_comments"
|
|
|
|
|
|| *opt_key == "gcode_flavor"
|
|
|
|
|
|| *opt_key == "infill_acceleration"
|
|
|
|
|
|| *opt_key == "infill_first"
|
|
|
|
|
|| *opt_key == "layer_gcode"
|
|
|
|
|
|| *opt_key == "min_fan_speed"
|
|
|
|
|
|| *opt_key == "max_fan_speed"
|
|
|
|
|
|| *opt_key == "min_print_speed"
|
|
|
|
|
|| *opt_key == "notes"
|
|
|
|
|
|| *opt_key == "only_retract_when_crossing_perimeters"
|
|
|
|
|
|| *opt_key == "output_filename_format"
|
|
|
|
|
|| *opt_key == "perimeter_acceleration"
|
|
|
|
|
|| *opt_key == "post_process"
|
|
|
|
|
|| *opt_key == "retract_before_travel"
|
|
|
|
|
|| *opt_key == "retract_layer_change"
|
|
|
|
|
|| *opt_key == "retract_length"
|
|
|
|
|
|| *opt_key == "retract_length_toolchange"
|
|
|
|
|
|| *opt_key == "retract_lift"
|
|
|
|
|
|| *opt_key == "retract_restart_extra"
|
|
|
|
|
|| *opt_key == "retract_restart_extra_toolchange"
|
|
|
|
|
|| *opt_key == "retract_speed"
|
|
|
|
|
|| *opt_key == "slowdown_below_layer_time"
|
|
|
|
|
|| *opt_key == "spiral_vase"
|
|
|
|
|
|| *opt_key == "standby_temperature_delta"
|
|
|
|
|
|| *opt_key == "start_gcode"
|
|
|
|
|
|| *opt_key == "temperature"
|
|
|
|
|
|| *opt_key == "threads"
|
|
|
|
|
|| *opt_key == "toolchange_gcode"
|
|
|
|
|
|| *opt_key == "travel_speed"
|
|
|
|
|
|| *opt_key == "use_firmware_retraction"
|
|
|
|
|
|| *opt_key == "use_relative_e_distances"
|
|
|
|
|
|| *opt_key == "vibration_limit"
|
|
|
|
|
|| *opt_key == "wipe"
|
|
|
|
|
|| *opt_key == "z_offset") {
|
|
|
|
|
// these options only affect G-code export, so nothing to invalidate
|
2014-06-10 22:15:02 +00:00
|
|
|
|
} else {
|
2014-06-13 09:19:53 +00:00
|
|
|
|
// for legacy, if we can't handle this option let's invalidate all steps
|
|
|
|
|
return this->invalidate_all_steps();
|
2014-06-10 22:15:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-13 09:19:53 +00:00
|
|
|
|
bool invalidated = false;
|
|
|
|
|
for (std::set<PrintStep>::const_iterator step = steps.begin(); step != steps.end(); ++step) {
|
|
|
|
|
if (this->invalidate_step(*step)) invalidated = true;
|
|
|
|
|
}
|
2014-06-10 22:15:02 +00:00
|
|
|
|
|
2014-06-13 09:19:53 +00:00
|
|
|
|
return invalidated;
|
2014-06-10 22:15:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-13 09:19:53 +00:00
|
|
|
|
bool
|
2014-06-10 22:15:02 +00:00
|
|
|
|
Print::invalidate_step(PrintStep step)
|
|
|
|
|
{
|
2014-06-13 09:19:53 +00:00
|
|
|
|
bool invalidated = this->state.invalidate(step);
|
2014-06-10 22:15:02 +00:00
|
|
|
|
|
|
|
|
|
// propagate to dependent steps
|
|
|
|
|
if (step == psSkirt) {
|
|
|
|
|
this->invalidate_step(psBrim);
|
|
|
|
|
} else if (step == psInitExtruders) {
|
2014-08-03 16:41:09 +00:00
|
|
|
|
FOREACH_OBJECT(this, object) {
|
2014-06-10 22:15:02 +00:00
|
|
|
|
(*object)->invalidate_step(posPerimeters);
|
|
|
|
|
(*object)->invalidate_step(posSupportMaterial);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-06-13 09:19:53 +00:00
|
|
|
|
|
|
|
|
|
return invalidated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Print::invalidate_all_steps()
|
|
|
|
|
{
|
|
|
|
|
// make a copy because when invalidating steps the iterators are not working anymore
|
|
|
|
|
std::set<PrintStep> steps = this->state.started;
|
|
|
|
|
|
|
|
|
|
bool invalidated = false;
|
|
|
|
|
for (std::set<PrintStep>::const_iterator step = steps.begin(); step != steps.end(); ++step) {
|
|
|
|
|
if (this->invalidate_step(*step)) invalidated = true;
|
|
|
|
|
}
|
|
|
|
|
return invalidated;
|
2014-06-10 22:15:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-03 16:41:09 +00:00
|
|
|
|
// returns 0-based indices of used extruders
|
|
|
|
|
std::set<size_t>
|
|
|
|
|
Print::extruders() const
|
|
|
|
|
{
|
|
|
|
|
std::set<size_t> extruders;
|
|
|
|
|
|
|
|
|
|
FOREACH_REGION(this, region) {
|
|
|
|
|
extruders.insert((*region)->config.perimeter_extruder - 1);
|
|
|
|
|
extruders.insert((*region)->config.infill_extruder - 1);
|
|
|
|
|
}
|
|
|
|
|
FOREACH_OBJECT(this, object) {
|
|
|
|
|
extruders.insert((*object)->config.support_material_extruder - 1);
|
|
|
|
|
extruders.insert((*object)->config.support_material_interface_extruder - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return extruders;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Print::_simplify_slices(double distance)
|
|
|
|
|
{
|
|
|
|
|
FOREACH_OBJECT(this, object) {
|
|
|
|
|
FOREACH_LAYER(*object, layer) {
|
|
|
|
|
(*layer)->slices.simplify(distance);
|
|
|
|
|
FOREACH_LAYERREGION(*layer, layerm) {
|
|
|
|
|
(*layerm)->slices.simplify(distance);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
|
Print::max_allowed_layer_height() const
|
|
|
|
|
{
|
|
|
|
|
std::vector<double> nozzle_diameter;
|
|
|
|
|
|
|
|
|
|
std::set<size_t> extruders = this->extruders();
|
|
|
|
|
for (std::set<size_t>::const_iterator e = extruders.begin(); e != extruders.end(); ++e) {
|
|
|
|
|
nozzle_diameter.push_back(this->config.nozzle_diameter.get_at(*e));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return *std::max_element(nozzle_diameter.begin(), nozzle_diameter.end());
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-09 11:25:59 +00:00
|
|
|
|
/* Caller is responsible for supplying models whose objects don't collide
|
|
|
|
|
and have explicit instance positions */
|
|
|
|
|
void
|
|
|
|
|
Print::add_model_object(ModelObject* model_object, int idx)
|
|
|
|
|
{
|
|
|
|
|
DynamicPrintConfig object_config = model_object->config; // clone
|
|
|
|
|
object_config.normalize();
|
|
|
|
|
|
|
|
|
|
// initialize print object and store it at the given position
|
|
|
|
|
PrintObject* o;
|
|
|
|
|
{
|
|
|
|
|
BoundingBoxf3 bb;
|
|
|
|
|
model_object->raw_bounding_box(&bb);
|
|
|
|
|
o = (idx != -1)
|
|
|
|
|
? this->set_new_object(idx, model_object, bb)
|
|
|
|
|
: this->add_object(model_object, bb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
Points copies;
|
|
|
|
|
for (ModelInstancePtrs::const_iterator i = model_object->instances.begin(); i != model_object->instances.end(); ++i) {
|
|
|
|
|
copies.push_back(Point::new_scale((*i)->offset.x, (*i)->offset.y));
|
|
|
|
|
}
|
|
|
|
|
o->set_copies(copies);
|
|
|
|
|
}
|
|
|
|
|
o->layer_height_ranges = model_object->layer_height_ranges;
|
|
|
|
|
|
|
|
|
|
for (ModelVolumePtrs::const_iterator v_i = model_object->volumes.begin(); v_i != model_object->volumes.end(); ++v_i) {
|
|
|
|
|
size_t volume_id = v_i - model_object->volumes.begin();
|
|
|
|
|
ModelVolume* volume = *v_i;
|
|
|
|
|
|
|
|
|
|
// get the config applied to this volume
|
|
|
|
|
PrintRegionConfig config = this->_region_config_from_model_volume(*volume);
|
|
|
|
|
|
|
|
|
|
// find an existing print region with the same config
|
|
|
|
|
int region_id = -1;
|
|
|
|
|
for (PrintRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region) {
|
|
|
|
|
if (config.equals((*region)->config)) {
|
|
|
|
|
region_id = region - this->regions.begin();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if no region exists with the same config, create a new one
|
|
|
|
|
if (region_id == -1) {
|
|
|
|
|
PrintRegion* r = this->add_region();
|
|
|
|
|
r->config.apply(config);
|
|
|
|
|
region_id = this->regions.size() - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// assign volume to region
|
|
|
|
|
o->add_region_volume(region_id, volume_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// apply config to print object
|
|
|
|
|
o->config.apply(this->default_object_config);
|
|
|
|
|
o->config.apply(object_config, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Print::apply_config(DynamicPrintConfig config)
|
|
|
|
|
{
|
|
|
|
|
// we get a copy of the config object so we can modify it safely
|
|
|
|
|
config.normalize();
|
|
|
|
|
|
|
|
|
|
// apply variables to placeholder parser
|
|
|
|
|
this->placeholder_parser.apply_config(config);
|
|
|
|
|
|
|
|
|
|
bool invalidated = false;
|
|
|
|
|
|
|
|
|
|
// handle changes to print config
|
|
|
|
|
t_config_option_keys print_diff = this->config.diff(config);
|
|
|
|
|
if (!print_diff.empty()) {
|
|
|
|
|
this->config.apply(config, true);
|
|
|
|
|
|
|
|
|
|
if (this->invalidate_state_by_config_options(print_diff))
|
|
|
|
|
invalidated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handle changes to object config defaults
|
|
|
|
|
this->default_object_config.apply(config, true);
|
|
|
|
|
FOREACH_OBJECT(this, obj_ptr) {
|
|
|
|
|
// we don't assume that config contains a full ObjectConfig,
|
|
|
|
|
// so we base it on the current print-wise default
|
|
|
|
|
PrintObjectConfig new_config = this->default_object_config;
|
|
|
|
|
new_config.apply(config, true);
|
|
|
|
|
|
|
|
|
|
// we override the new config with object-specific options
|
|
|
|
|
{
|
|
|
|
|
DynamicPrintConfig model_object_config = (*obj_ptr)->model_object()->config;
|
|
|
|
|
model_object_config.normalize();
|
|
|
|
|
new_config.apply(model_object_config, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check whether the new config is different from the current one
|
|
|
|
|
t_config_option_keys diff = (*obj_ptr)->config.diff(new_config);
|
|
|
|
|
if (!diff.empty()) {
|
|
|
|
|
(*obj_ptr)->config.apply(new_config, true);
|
|
|
|
|
|
|
|
|
|
if ((*obj_ptr)->invalidate_state_by_config_options(diff))
|
|
|
|
|
invalidated = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handle changes to regions config defaults
|
|
|
|
|
this->default_region_config.apply(config, true);
|
|
|
|
|
|
|
|
|
|
// All regions now have distinct settings.
|
|
|
|
|
// Check whether applying the new region config defaults we'd get different regions.
|
|
|
|
|
bool rearrange_regions = false;
|
|
|
|
|
std::vector<PrintRegionConfig> other_region_configs;
|
|
|
|
|
FOREACH_REGION(this, it_r) {
|
|
|
|
|
size_t region_id = it_r - this->regions.begin();
|
|
|
|
|
PrintRegion* region = *it_r;
|
|
|
|
|
|
|
|
|
|
std::vector<PrintRegionConfig> this_region_configs;
|
|
|
|
|
FOREACH_OBJECT(this, it_o) {
|
|
|
|
|
PrintObject* object = *it_o;
|
|
|
|
|
|
|
|
|
|
std::vector<int> ®ion_volumes = object->region_volumes[region_id];
|
|
|
|
|
for (std::vector<int>::const_iterator volume_id = region_volumes.begin(); volume_id != region_volumes.end(); ++volume_id) {
|
|
|
|
|
ModelVolume* volume = object->model_object()->volumes[*volume_id];
|
|
|
|
|
|
|
|
|
|
PrintRegionConfig new_config = this->_region_config_from_model_volume(*volume);
|
|
|
|
|
|
|
|
|
|
for (std::vector<PrintRegionConfig>::iterator it = this_region_configs.begin(); it != this_region_configs.end(); ++it) {
|
|
|
|
|
// if the new config for this volume differs from the other
|
|
|
|
|
// volume configs currently associated to this region, it means
|
|
|
|
|
// the region subdivision does not make sense anymore
|
|
|
|
|
if (!it->equals(new_config)) {
|
|
|
|
|
rearrange_regions = true;
|
|
|
|
|
goto NEXT_REGION;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this_region_configs.push_back(new_config);
|
|
|
|
|
|
|
|
|
|
for (std::vector<PrintRegionConfig>::iterator it = other_region_configs.begin(); it != other_region_configs.end(); ++it) {
|
|
|
|
|
// if the new config for this volume equals any of the other
|
|
|
|
|
// volume configs that are not currently associated to this
|
|
|
|
|
// region, it means the region subdivision does not make
|
|
|
|
|
// sense anymore
|
|
|
|
|
if (it->equals(new_config)) {
|
|
|
|
|
rearrange_regions = true;
|
|
|
|
|
goto NEXT_REGION;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if we're here and the new region config is different from the old
|
|
|
|
|
// one, we need to apply the new config and invalidate all objects
|
|
|
|
|
// (possible optimization: only invalidate objects using this region)
|
|
|
|
|
t_config_option_keys region_config_diff = region->config.diff(new_config);
|
|
|
|
|
if (!region_config_diff.empty()) {
|
|
|
|
|
region->config.apply(new_config);
|
|
|
|
|
FOREACH_OBJECT(this, o) {
|
|
|
|
|
if ((*o)->invalidate_state_by_config_options(region_config_diff))
|
|
|
|
|
invalidated = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
other_region_configs.insert(other_region_configs.end(), this_region_configs.begin(), this_region_configs.end());
|
|
|
|
|
|
|
|
|
|
NEXT_REGION:
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rearrange_regions) {
|
|
|
|
|
// the current subdivision of regions does not make sense anymore.
|
|
|
|
|
// we need to remove all objects and re-add them
|
|
|
|
|
ModelObjectPtrs model_objects;
|
|
|
|
|
FOREACH_OBJECT(this, o) {
|
|
|
|
|
model_objects.push_back((*o)->model_object());
|
|
|
|
|
}
|
|
|
|
|
this->clear_objects();
|
|
|
|
|
for (ModelObjectPtrs::iterator it = model_objects.begin(); it != model_objects.end(); ++it) {
|
|
|
|
|
this->add_model_object(*it);
|
|
|
|
|
}
|
|
|
|
|
invalidated = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return invalidated;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-03 16:41:09 +00:00
|
|
|
|
void
|
|
|
|
|
Print::init_extruders()
|
|
|
|
|
{
|
|
|
|
|
if (this->state.is_done(psInitExtruders)) return;
|
|
|
|
|
this->state.set_done(psInitExtruders);
|
|
|
|
|
|
|
|
|
|
// enforce tall skirt if using ooze_prevention
|
|
|
|
|
// FIXME: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings)
|
|
|
|
|
if (this->config.ooze_prevention && this->extruders().size() > 1) {
|
|
|
|
|
this->config.skirt_height.value = -1;
|
|
|
|
|
if (this->config.skirts == 0) this->config.skirts.value = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->state.set_done(psInitExtruders);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-09 14:27:34 +00:00
|
|
|
|
void
|
|
|
|
|
Print::validate() const
|
|
|
|
|
{
|
|
|
|
|
if (this->config.complete_objects) {
|
|
|
|
|
// check horizontal clearance
|
|
|
|
|
{
|
|
|
|
|
Polygons a;
|
|
|
|
|
FOREACH_OBJECT(this, i_object) {
|
|
|
|
|
PrintObject* object = *i_object;
|
|
|
|
|
|
|
|
|
|
// get convex hulls of all meshes assigned to this print object
|
|
|
|
|
Polygons mesh_convex_hulls;
|
|
|
|
|
for (size_t i = 0; i < this->regions.size(); ++i) {
|
|
|
|
|
for (std::vector<int>::const_iterator it = object->region_volumes[i].begin(); it != object->region_volumes[i].end(); ++it) {
|
|
|
|
|
Polygon hull;
|
|
|
|
|
object->model_object()->volumes[*it]->mesh.convex_hull(&hull);
|
|
|
|
|
mesh_convex_hulls.push_back(hull);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make a single convex hull for all of them
|
|
|
|
|
Polygon convex_hull;
|
|
|
|
|
Slic3r::Geometry::convex_hull(mesh_convex_hulls, &convex_hull);
|
|
|
|
|
|
|
|
|
|
// apply the same transformations we apply to the actual meshes when slicing them
|
|
|
|
|
object->model_object()->instances.front()->transform_polygon(&convex_hull);
|
|
|
|
|
|
|
|
|
|
// align object to Z = 0 and apply XY shift
|
|
|
|
|
convex_hull.translate(object->_copies_shift);
|
|
|
|
|
|
|
|
|
|
// 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));
|
|
|
|
|
convex_hull = grown_hull.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))
|
|
|
|
|
throw PrintValidationException("Some objects are too close; your extruder will collide with them.");
|
|
|
|
|
|
|
|
|
|
union_(a, p, a);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check vertical clearance
|
|
|
|
|
{
|
|
|
|
|
std::vector<coord_t> object_height;
|
|
|
|
|
FOREACH_OBJECT(this, i_object) {
|
|
|
|
|
PrintObject* object = *i_object;
|
|
|
|
|
object_height.insert(object_height.end(), object->copies().size(), object->size.z);
|
|
|
|
|
}
|
|
|
|
|
std::sort(object_height.begin(), object_height.end());
|
|
|
|
|
// ignore the tallest *copy* (this is why we repeat height for all of them):
|
|
|
|
|
// it will be printed as last one so its height doesn't matter
|
|
|
|
|
object_height.pop_back();
|
|
|
|
|
if (!object_height.empty() && object_height.back() > scale_(this->config.extruder_clearance_height.value))
|
|
|
|
|
throw PrintValidationException("Some objects are too tall and cannot be printed without extruder collisions.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this->config.spiral_vase) {
|
|
|
|
|
size_t total_copies_count = 0;
|
|
|
|
|
FOREACH_OBJECT(this, i_object) total_copies_count += (*i_object)->copies().size();
|
|
|
|
|
if (total_copies_count > 1)
|
|
|
|
|
throw PrintValidationException("The Spiral Vase option can only be used when printing a single object.");
|
|
|
|
|
if (this->regions.size() > 1)
|
|
|
|
|
throw PrintValidationException("The Spiral Vase option can only be used when printing single material objects.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::vector<double> layer_heights;
|
|
|
|
|
FOREACH_OBJECT(this, i_object) {
|
|
|
|
|
PrintObject* object = *i_object;
|
|
|
|
|
layer_heights.push_back(object->config.layer_height);
|
|
|
|
|
layer_heights.push_back(object->config.get_abs_value("first_layer_height"));
|
|
|
|
|
}
|
|
|
|
|
double max_layer_height = *std::max_element(layer_heights.begin(), layer_heights.end());
|
|
|
|
|
|
|
|
|
|
std::set<size_t> extruders = this->extruders();
|
|
|
|
|
for (std::set<size_t>::iterator it = extruders.begin(); it != extruders.end(); ++it) {
|
|
|
|
|
if (max_layer_height > this->config.nozzle_diameter.get_at(*it))
|
|
|
|
|
throw PrintValidationException("Layer height can't be greater than nozzle diameter");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-09 11:25:59 +00:00
|
|
|
|
PrintRegionConfig
|
|
|
|
|
Print::_region_config_from_model_volume(const ModelVolume &volume)
|
|
|
|
|
{
|
|
|
|
|
PrintRegionConfig config = this->default_region_config;
|
|
|
|
|
{
|
|
|
|
|
DynamicPrintConfig other_config = volume.get_object()->config;
|
|
|
|
|
other_config.normalize();
|
|
|
|
|
config.apply(other_config, true);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
DynamicPrintConfig other_config = volume.config;
|
|
|
|
|
other_config.normalize();
|
|
|
|
|
config.apply(other_config, true);
|
|
|
|
|
}
|
|
|
|
|
if (!volume.material_id().empty()) {
|
|
|
|
|
DynamicPrintConfig material_config = volume.material()->config;
|
|
|
|
|
material_config.normalize();
|
|
|
|
|
config.apply(material_config, true);
|
|
|
|
|
}
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-03 16:41:09 +00:00
|
|
|
|
bool
|
|
|
|
|
Print::has_support_material() const
|
|
|
|
|
{
|
|
|
|
|
FOREACH_OBJECT(this, object) {
|
|
|
|
|
PrintObjectConfig &config = (*object)->config;
|
|
|
|
|
if (config.support_material || config.raft_layers > 0 || config.support_material_enforce_layers > 0)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-06 08:07:18 +00:00
|
|
|
|
|
|
|
|
|
#ifdef SLIC3RXS
|
|
|
|
|
REGISTER_CLASS(Print, "Print");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2013-12-20 00:36:42 +00:00
|
|
|
|
}
|