Fixed a bug in validation of the FDM Print to check for extruder
collisions against already printed objects: Rotation of an object was not being taken into account. Fixes #2450 PrusaSlicer 2.0 Sequential printing vs rotate object Fixes #3585 Always Sequential Printing Error (Regardless of setting, placement or object size) Fixed some collisions in sequential print just after arrangement by making the validation using a slightly lower extruder radius value than the arrangement. Refactored PrintObject coordinate system so that the PrintObject's coordinate system is always centered in respect to its ModelObject's geometries. This refactoring may improve path planning a bit and it may lower the requirements on bits representing Clipper coordinates by 1 bit.
This commit is contained in:
parent
e66632a595
commit
b8c898bf40
8 changed files with 135 additions and 116 deletions
|
@ -1091,7 +1091,7 @@ namespace DoExport {
|
||||||
static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(const Print &print)
|
static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(const Print &print)
|
||||||
{
|
{
|
||||||
std::vector<const PrintObject*> objects(print.objects().begin(), print.objects().end());
|
std::vector<const PrintObject*> objects(print.objects().begin(), print.objects().end());
|
||||||
std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size()(2) < po2->size()(2); });
|
std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->height() < po2->height(); });
|
||||||
std::vector<const PrintInstance*> instances;
|
std::vector<const PrintInstance*> instances;
|
||||||
instances.reserve(objects.size());
|
instances.reserve(objects.size());
|
||||||
for (const PrintObject *object : objects)
|
for (const PrintObject *object : objects)
|
||||||
|
@ -2616,8 +2616,9 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (seam_position == spRear) {
|
else if (seam_position == spRear) {
|
||||||
last_pos = m_layer->object()->bounding_box().center();
|
// Object is centered around (0,0) in its current coordinate system.
|
||||||
last_pos(1) += coord_t(3. * m_layer->object()->bounding_box().radius());
|
last_pos.x() = 0;
|
||||||
|
last_pos.y() += coord_t(3. * m_layer->object()->bounding_box().radius());
|
||||||
last_pos_weight = 5.f;
|
last_pos_weight = 5.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -462,7 +462,7 @@ static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object
|
||||||
if (model_instance->is_printable()) {
|
if (model_instance->is_printable()) {
|
||||||
trafo.trafo = model_instance->get_matrix();
|
trafo.trafo = model_instance->get_matrix();
|
||||||
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
|
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
|
||||||
// Set the Z axis of the transformation.
|
// Reset the XY axes of the transformation.
|
||||||
trafo.trafo.data()[12] = 0;
|
trafo.trafo.data()[12] = 0;
|
||||||
trafo.trafo.data()[13] = 0;
|
trafo.trafo.data()[13] = 0;
|
||||||
// Search or insert a trafo.
|
// Search or insert a trafo.
|
||||||
|
@ -930,8 +930,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||||
if (old.empty()) {
|
if (old.empty()) {
|
||||||
// Simple case, just generate new instances.
|
// Simple case, just generate new instances.
|
||||||
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
|
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
|
||||||
PrintObject *print_object = new PrintObject(this, model_object, false);
|
PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
|
||||||
print_object->set_trafo_and_instances(print_instances.trafo, std::move(print_instances.instances));
|
|
||||||
print_object->config_apply(config);
|
print_object->config_apply(config);
|
||||||
print_objects_new.emplace_back(print_object);
|
print_objects_new.emplace_back(print_object);
|
||||||
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
||||||
|
@ -948,8 +947,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||||
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
|
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
|
||||||
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
|
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
|
||||||
// This is a new instance (or a set of instances with the same trafo). Just add it.
|
// This is a new instance (or a set of instances with the same trafo). Just add it.
|
||||||
PrintObject *print_object = new PrintObject(this, model_object, false);
|
PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
|
||||||
print_object->set_trafo_and_instances(new_instances.trafo, std::move(new_instances.instances));
|
|
||||||
print_object->config_apply(config);
|
print_object->config_apply(config);
|
||||||
print_objects_new.emplace_back(print_object);
|
print_objects_new.emplace_back(print_object);
|
||||||
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
|
||||||
|
@ -1151,6 +1149,62 @@ bool Print::has_skirt() const
|
||||||
|| this->has_infinite_skirt();
|
|| this->has_infinite_skirt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool sequential_print_horizontal_clearance_valid(const Print &print)
|
||||||
|
{
|
||||||
|
Polygons convex_hulls_other;
|
||||||
|
std::map<ObjectID, Polygon> map_model_object_to_convex_hull;
|
||||||
|
for (const PrintObject *print_object : print.objects()) {
|
||||||
|
assert(! print_object->model_object()->instances.empty());
|
||||||
|
assert(! print_object->instances().empty());
|
||||||
|
ObjectID model_object_id = print_object->model_object()->id();
|
||||||
|
auto it_convex_hull = map_model_object_to_convex_hull.find(model_object_id);
|
||||||
|
// Get convex hull of all printable volumes assigned to this print object.
|
||||||
|
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
|
||||||
|
if (it_convex_hull == map_model_object_to_convex_hull.end()) {
|
||||||
|
// Calculate the convex hull of a printable object.
|
||||||
|
// Grow convex hull with the clearance margin.
|
||||||
|
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
|
||||||
|
// which causes that the warning will be showed after arrangement with the
|
||||||
|
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
|
||||||
|
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
|
||||||
|
offset(print_object->model_object()->convex_hull_2d(
|
||||||
|
Geometry::assemble_transform(Vec3d::Zero(), model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
||||||
|
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||||
|
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||||
|
float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)),
|
||||||
|
jtRound, float(scale_(0.1))).front());
|
||||||
|
}
|
||||||
|
// Make a copy, so it may be rotated for instances.
|
||||||
|
Polygon convex_hull0 = it_convex_hull->second;
|
||||||
|
double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation());
|
||||||
|
if (std::abs(z_diff) > EPSILON)
|
||||||
|
convex_hull0.rotate(z_diff);
|
||||||
|
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
|
||||||
|
for (const PrintInstance &instance : print_object->instances()) {
|
||||||
|
Polygon convex_hull = convex_hull0;
|
||||||
|
// instance.shift is a position of a centered object, while model object may not be centered.
|
||||||
|
// Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
|
||||||
|
convex_hull.translate(instance.shift - print_object->center_offset());
|
||||||
|
if (! intersection(convex_hulls_other, convex_hull).empty())
|
||||||
|
return false;
|
||||||
|
polygons_append(convex_hulls_other, convex_hull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool sequential_print_vertical_clearance_valid(const Print &print)
|
||||||
|
{
|
||||||
|
std::vector<const PrintInstance*> print_instances_ordered = sort_object_instances_by_model_order(print);
|
||||||
|
// Ignore the last instance printed.
|
||||||
|
print_instances_ordered.pop_back();
|
||||||
|
// Find the other highest instance.
|
||||||
|
auto it = std::max_element(print_instances_ordered.begin(), print_instances_ordered.end(), [](auto l, auto r) {
|
||||||
|
return l->print_object->height() < r->print_object->height();
|
||||||
|
});
|
||||||
|
return it == print_instances_ordered.end() || (*it)->print_object->height() < scale_(print.config().extruder_clearance_height.value);
|
||||||
|
}
|
||||||
|
|
||||||
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
||||||
std::string Print::validate() const
|
std::string Print::validate() const
|
||||||
{
|
{
|
||||||
|
@ -1161,48 +1215,11 @@ std::string Print::validate() const
|
||||||
return L("The supplied settings will cause an empty print.");
|
return L("The supplied settings will cause an empty print.");
|
||||||
|
|
||||||
if (m_config.complete_objects) {
|
if (m_config.complete_objects) {
|
||||||
// Check horizontal clearance.
|
if (! sequential_print_horizontal_clearance_valid(*this))
|
||||||
{
|
|
||||||
Polygons convex_hulls_other;
|
|
||||||
for (const PrintObject *print_object : m_objects) {
|
|
||||||
assert(! print_object->model_object()->instances.empty());
|
|
||||||
assert(! print_object->instances().empty());
|
|
||||||
// Get convex hull of all meshes assigned to this print object.
|
|
||||||
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
|
|
||||||
Vec3d rotation = model_instance0->get_rotation();
|
|
||||||
rotation.z() = 0.;
|
|
||||||
// Calculate the convex hull of a printable object centered around X=0,Y=0.
|
|
||||||
// Grow convex hull with the clearance margin.
|
|
||||||
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
|
|
||||||
// which causes that the warning will be showed after arrangement with the
|
|
||||||
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
|
|
||||||
Polygon convex_hull0 = offset(
|
|
||||||
print_object->model_object()->convex_hull_2d(
|
|
||||||
Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
|
||||||
float(scale_(0.5 * m_config.extruder_clearance_radius.value)), jtRound, float(scale_(0.1))).front();
|
|
||||||
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
|
|
||||||
for (const PrintInstance &instance : print_object->instances()) {
|
|
||||||
Polygon convex_hull = convex_hull0;
|
|
||||||
convex_hull.translate(instance.shift);
|
|
||||||
if (! intersection(convex_hulls_other, convex_hull).empty())
|
|
||||||
return L("Some objects are too close; your extruder will collide with them.");
|
return L("Some objects are too close; your extruder will collide with them.");
|
||||||
polygons_append(convex_hulls_other, convex_hull);
|
if (! sequential_print_vertical_clearance_valid(*this))
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check vertical clearance.
|
|
||||||
{
|
|
||||||
std::vector<coord_t> object_height;
|
|
||||||
for (const PrintObject *object : m_objects)
|
|
||||||
object_height.insert(object_height.end(), object->instances().size(), object->size()(2));
|
|
||||||
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_(m_config.extruder_clearance_height.value))
|
|
||||||
return L("Some objects are too tall and cannot be printed without extruder collisions.");
|
return L("Some objects are too tall and cannot be printed without extruder collisions.");
|
||||||
}
|
}
|
||||||
} // end if (m_config.complete_objects)
|
|
||||||
|
|
||||||
if (m_config.spiral_vase) {
|
if (m_config.spiral_vase) {
|
||||||
size_t total_copies_count = 0;
|
size_t total_copies_count = 0;
|
||||||
|
@ -1418,6 +1435,7 @@ std::string Print::validate() const
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
// the bounding box of objects placed in copies position
|
// the bounding box of objects placed in copies position
|
||||||
// (without taking skirt/brim/support material into account)
|
// (without taking skirt/brim/support material into account)
|
||||||
BoundingBox Print::bounding_box() const
|
BoundingBox Print::bounding_box() const
|
||||||
|
@ -1425,8 +1443,9 @@ BoundingBox Print::bounding_box() const
|
||||||
BoundingBox bb;
|
BoundingBox bb;
|
||||||
for (const PrintObject *object : m_objects)
|
for (const PrintObject *object : m_objects)
|
||||||
for (const PrintInstance &instance : object->instances()) {
|
for (const PrintInstance &instance : object->instances()) {
|
||||||
bb.merge(instance.shift);
|
BoundingBox bb2(object->bounding_box());
|
||||||
bb.merge(instance.shift + to_2d(object->size()));
|
bb.merge(bb2.min + instance.shift);
|
||||||
|
bb.merge(bb2.max + instance.shift);
|
||||||
}
|
}
|
||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
|
@ -1471,6 +1490,7 @@ BoundingBox Print::total_bounding_box() const
|
||||||
|
|
||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
double Print::skirt_first_layer_height() const
|
double Print::skirt_first_layer_height() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -84,7 +84,7 @@ private:
|
||||||
|
|
||||||
PrintRegion(Print* print) : m_refcnt(0), m_print(print) {}
|
PrintRegion(Print* print) : m_refcnt(0), m_print(print) {}
|
||||||
PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {}
|
PrintRegion(Print* print, const PrintRegionConfig &config) : m_refcnt(0), m_print(print), m_config(config) {}
|
||||||
~PrintRegion() {}
|
~PrintRegion() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ struct PrintInstance
|
||||||
PrintObject *print_object;
|
PrintObject *print_object;
|
||||||
// Source ModelInstance of a ModelObject, for which this print_object was created.
|
// Source ModelInstance of a ModelObject, for which this print_object was created.
|
||||||
const ModelInstance *model_instance;
|
const ModelInstance *model_instance;
|
||||||
// Shift of this instance towards its PrintObject
|
// Shift of this instance's center into the world coordinates.
|
||||||
Point shift;
|
Point shift;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -116,21 +116,22 @@ public:
|
||||||
// vector of (layer height ranges and vectors of volume ids), indexed by region_id
|
// vector of (layer height ranges and vectors of volume ids), indexed by region_id
|
||||||
std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
|
std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
|
||||||
|
|
||||||
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
// Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane.
|
||||||
// so that next call to make_perimeters() performs a union() before computing loops
|
|
||||||
bool typed_slices;
|
|
||||||
|
|
||||||
// XYZ in scaled coordinates
|
|
||||||
const Vec3crd& size() const { return m_size; }
|
const Vec3crd& size() const { return m_size; }
|
||||||
const PrintObjectConfig& config() const { return m_config; }
|
const PrintObjectConfig& config() const { return m_config; }
|
||||||
const LayerPtrs& layers() const { return m_layers; }
|
const LayerPtrs& layers() const { return m_layers; }
|
||||||
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
|
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
|
||||||
const Transform3d& trafo() const { return m_trafo; }
|
const Transform3d& trafo() const { return m_trafo; }
|
||||||
const PrintInstances& instances() const { return m_instances; }
|
const PrintInstances& instances() const { return m_instances; }
|
||||||
const Point instance_center(size_t idx) const { return m_instances[idx].shift + m_copies_shift + Point(this->size().x() / 2, this->size().y() / 2); }
|
|
||||||
|
|
||||||
// since the object is aligned to origin, bounding box coincides with size
|
// Bounding box is used to align the object infill patterns, and to calculate attractor for the rear seam.
|
||||||
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size())); }
|
// The bounding box may not be quite snug.
|
||||||
|
BoundingBox bounding_box() const { return BoundingBox(Point(- m_size.x() / 2, - m_size.y() / 2), Point(m_size.x() / 2, m_size.y() / 2)); }
|
||||||
|
// Height is used for slicing, for sorting the objects by height for sequential printing and for checking vertical clearence in sequential print mode.
|
||||||
|
// The height is snug.
|
||||||
|
coord_t height() const { return m_size.z(); }
|
||||||
|
// Centering offset of the sliced mesh from the scaled and rotated mesh of the model.
|
||||||
|
const Point& center_offset() const { return m_center_offset; }
|
||||||
|
|
||||||
// adds region_id, too, if necessary
|
// adds region_id, too, if necessary
|
||||||
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
|
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
|
||||||
|
@ -196,14 +197,12 @@ private:
|
||||||
// to be called from Print only.
|
// to be called from Print only.
|
||||||
friend class Print;
|
friend class Print;
|
||||||
|
|
||||||
PrintObject(Print* print, ModelObject* model_object, bool add_instances = true);
|
PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances);
|
||||||
~PrintObject() {}
|
~PrintObject() = default;
|
||||||
|
|
||||||
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
|
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
|
||||||
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
|
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
|
||||||
void set_trafo(const Transform3d& trafo) { m_trafo = trafo; }
|
|
||||||
PrintBase::ApplyStatus set_instances(PrintInstances &&instances);
|
PrintBase::ApplyStatus set_instances(PrintInstances &&instances);
|
||||||
void set_trafo_and_instances(const Transform3d& trafo, PrintInstances &&instances) { this->set_trafo(trafo); this->set_instances(std::move(instances)); }
|
|
||||||
// Invalidates the step, and its depending steps in PrintObject and Print.
|
// Invalidates the step, and its depending steps in PrintObject and Print.
|
||||||
bool invalidate_step(PrintObjectStep step);
|
bool invalidate_step(PrintObjectStep step);
|
||||||
// Invalidates all PrintObject and Print steps.
|
// Invalidates all PrintObject and Print steps.
|
||||||
|
@ -242,15 +241,18 @@ private:
|
||||||
Transform3d m_trafo = Transform3d::Identity();
|
Transform3d m_trafo = Transform3d::Identity();
|
||||||
// Slic3r::Point objects in scaled G-code coordinates
|
// Slic3r::Point objects in scaled G-code coordinates
|
||||||
std::vector<PrintInstance> m_instances;
|
std::vector<PrintInstance> m_instances;
|
||||||
// scaled coordinates to add to copies (to compensate for the alignment
|
// The mesh is being centered before thrown to Clipper, so that the Clipper's fixed coordinates require less bits.
|
||||||
// operated when creating the object but still preserving a coherent API
|
// This is the adjustment of the the Object's coordinate system towards PrintObject's coordinate system.
|
||||||
// for external callers)
|
Point m_center_offset;
|
||||||
Point m_copies_shift;
|
|
||||||
|
|
||||||
SlicingParameters m_slicing_params;
|
SlicingParameters m_slicing_params;
|
||||||
LayerPtrs m_layers;
|
LayerPtrs m_layers;
|
||||||
SupportLayerPtrs m_support_layers;
|
SupportLayerPtrs m_support_layers;
|
||||||
|
|
||||||
|
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
||||||
|
// so that next call to make_perimeters() performs a union() before computing loops
|
||||||
|
bool m_typed_slices = false;
|
||||||
|
|
||||||
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z) const;
|
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z) const;
|
||||||
std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const;
|
std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const;
|
||||||
std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
|
std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
|
||||||
|
@ -343,7 +345,7 @@ private: // Prevents erroneous use by other classes.
|
||||||
typedef PrintBaseWithState<PrintStep, psCount> Inherited;
|
typedef PrintBaseWithState<PrintStep, psCount> Inherited;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Print() {}
|
Print() = default;
|
||||||
virtual ~Print() { this->clear(); }
|
virtual ~Print() { this->clear(); }
|
||||||
|
|
||||||
PrinterTechnology technology() const noexcept { return ptFFF; }
|
PrinterTechnology technology() const noexcept { return ptFFF; }
|
||||||
|
@ -379,8 +381,6 @@ public:
|
||||||
|
|
||||||
// Returns an empty string if valid, otherwise returns an error message.
|
// Returns an empty string if valid, otherwise returns an error message.
|
||||||
std::string validate() const override;
|
std::string validate() const override;
|
||||||
BoundingBox bounding_box() const;
|
|
||||||
BoundingBox total_bounding_box() const;
|
|
||||||
double skirt_first_layer_height() const;
|
double skirt_first_layer_height() const;
|
||||||
Flow brim_flow() const;
|
Flow brim_flow() const;
|
||||||
Flow skirt_flow() const;
|
Flow skirt_flow() const;
|
||||||
|
|
|
@ -40,41 +40,41 @@
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) :
|
// Constructor is called from the main thread, therefore all Model / ModelObject / ModelIntance data are valid.
|
||||||
|
PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances) :
|
||||||
PrintObjectBaseWithState(print, model_object),
|
PrintObjectBaseWithState(print, model_object),
|
||||||
typed_slices(false),
|
m_trafo(trafo)
|
||||||
m_size(Vec3crd::Zero())
|
|
||||||
{
|
{
|
||||||
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
|
// Compute centering offet to be applied to our meshes so that we work with smaller coordinates
|
||||||
{
|
// requiring less bits to represent Clipper coordinates.
|
||||||
// Translate meshes so that our toolpath generation algorithms work with smaller
|
|
||||||
// XY coordinates; this translation is an optimization and not strictly required.
|
// Snug bounding box of a rotated and scaled object by the 1st instantion, without the instance translation applied.
|
||||||
// A cloned mesh will be aligned to 0 before slicing in slice_region() since we
|
// All the instances share the transformation matrix with the exception of translation in XY and rotation by Z,
|
||||||
// don't assume it's already aligned and we don't alter the original position in model.
|
// therefore a bounding box from 1st instance of a ModelObject is good enough for calculating the object center,
|
||||||
// We store the XY translation so that we can place copies correctly in the output G-code
|
// snug height and an approximate bounding box in XY.
|
||||||
// (copies are expressed in G-code coordinates and this translation is not publicly exposed).
|
BoundingBoxf3 bbox = model_object->raw_bounding_box();
|
||||||
const BoundingBoxf3 modobj_bbox = model_object->raw_bounding_box();
|
Vec3d bbox_center = bbox.center();
|
||||||
m_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1));
|
// We may need to rotate the bbox / bbox_center from the original instance to the current instance.
|
||||||
// Scale the object size and store it
|
double z_diff = Geometry::rotation_diff_z(model_object->instances.front()->get_rotation(), instances.front().model_instance->get_rotation());
|
||||||
this->m_size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
|
if (std::abs(z_diff) > EPSILON) {
|
||||||
|
auto z_rot = Eigen::AngleAxisd(z_diff, Vec3d::UnitZ());
|
||||||
|
bbox = bbox.transformed(Transform3d(z_rot));
|
||||||
|
bbox_center = (z_rot * bbox_center).eval();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (add_instances) {
|
// Center of the transformed mesh (without translation).
|
||||||
PrintInstances instances;
|
m_center_offset = Point::new_scale(bbox_center.x(), bbox_center.y());
|
||||||
instances.reserve(m_model_object->instances.size());
|
// Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z.
|
||||||
for (const ModelInstance *mi : m_model_object->instances) {
|
m_size = (bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
|
||||||
assert(mi->is_printable());
|
|
||||||
const Vec3d &offset = mi->get_offset();
|
|
||||||
instances.emplace_back(PrintInstance{ nullptr, mi, Point::new_scale(offset(0), offset(1)) });
|
|
||||||
}
|
|
||||||
this->set_instances(std::move(instances));
|
this->set_instances(std::move(instances));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
|
PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
|
||||||
{
|
{
|
||||||
for (PrintInstance &i : instances)
|
for (PrintInstance &i : instances)
|
||||||
i.shift += m_copies_shift;
|
// Add the center offset, which will be subtracted from the mesh when slicing.
|
||||||
|
i.shift += m_center_offset;
|
||||||
// Invalidate and set copies.
|
// Invalidate and set copies.
|
||||||
PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED;
|
PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED;
|
||||||
bool equal_length = instances.size() == m_instances.size();
|
bool equal_length = instances.size() == m_instances.size();
|
||||||
|
@ -153,12 +153,12 @@ void PrintObject::make_perimeters()
|
||||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
|
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
|
||||||
|
|
||||||
// merge slices if they were split into types
|
// merge slices if they were split into types
|
||||||
if (this->typed_slices) {
|
if (m_typed_slices) {
|
||||||
for (Layer *layer : m_layers) {
|
for (Layer *layer : m_layers) {
|
||||||
layer->merge_slices();
|
layer->merge_slices();
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
}
|
}
|
||||||
this->typed_slices = false;
|
m_typed_slices = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare each layer to the one below, and mark those slices needing
|
// compare each layer to the one below, and mark those slices needing
|
||||||
|
@ -822,7 +822,7 @@ void PrintObject::detect_surfaces_type()
|
||||||
} // for each this->print->region_count
|
} // for each this->print->region_count
|
||||||
|
|
||||||
// Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
|
// Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
|
||||||
this->typed_slices = true;
|
m_typed_slices = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintObject::process_external_surfaces()
|
void PrintObject::process_external_surfaces()
|
||||||
|
@ -1481,7 +1481,7 @@ void PrintObject::update_slicing_parameters()
|
||||||
{
|
{
|
||||||
if (! m_slicing_params.valid)
|
if (! m_slicing_params.valid)
|
||||||
m_slicing_params = SlicingParameters::create_from_config(
|
m_slicing_params = SlicingParameters::create_from_config(
|
||||||
this->print()->config(), m_config, unscale<double>(this->size()(2)), this->object_extruders());
|
this->print()->config(), m_config, unscale<double>(this->height()), this->object_extruders());
|
||||||
}
|
}
|
||||||
|
|
||||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
|
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
|
||||||
|
@ -1568,7 +1568,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
|
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
|
||||||
|
|
||||||
this->typed_slices = false;
|
m_typed_slices = false;
|
||||||
|
|
||||||
#ifdef SLIC3R_PROFILE
|
#ifdef SLIC3R_PROFILE
|
||||||
// Disable parallelization so the Shiny profiler works
|
// Disable parallelization so the Shiny profiler works
|
||||||
|
@ -2030,7 +2030,7 @@ std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z,
|
||||||
if (mesh.stl.stats.number_of_facets > 0) {
|
if (mesh.stl.stats.number_of_facets > 0) {
|
||||||
mesh.transform(m_trafo, true);
|
mesh.transform(m_trafo, true);
|
||||||
// apply XY shift
|
// apply XY shift
|
||||||
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
|
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
|
||||||
// perform actual slicing
|
// perform actual slicing
|
||||||
const Print *print = this->print();
|
const Print *print = this->print();
|
||||||
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
|
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
|
||||||
|
@ -2060,7 +2060,7 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, c
|
||||||
if (mesh.stl.stats.number_of_facets > 0) {
|
if (mesh.stl.stats.number_of_facets > 0) {
|
||||||
mesh.transform(m_trafo, true);
|
mesh.transform(m_trafo, true);
|
||||||
// apply XY shift
|
// apply XY shift
|
||||||
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
|
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
|
||||||
// perform actual slicing
|
// perform actual slicing
|
||||||
TriangleMeshSlicer mslicer;
|
TriangleMeshSlicer mslicer;
|
||||||
const Print *print = this->print();
|
const Print *print = this->print();
|
||||||
|
|
|
@ -544,8 +544,8 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o
|
||||||
}
|
}
|
||||||
|
|
||||||
sl::rotate(poly, double(instances[i].rotation));
|
sl::rotate(poly, double(instances[i].rotation));
|
||||||
sl::translate(poly, ClipperPoint{instances[i].shift(X),
|
sl::translate(poly, ClipperPoint{instances[i].shift.x(),
|
||||||
instances[i].shift(Y)});
|
instances[i].shift.y()});
|
||||||
|
|
||||||
polygons.emplace_back(std::move(poly));
|
polygons.emplace_back(std::move(poly));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1954,7 +1954,8 @@ std::vector<const PrintInstance*> chain_print_object_instances(const Print &prin
|
||||||
for (size_t i = 0; i < print.objects().size(); ++ i) {
|
for (size_t i = 0; i < print.objects().size(); ++ i) {
|
||||||
const PrintObject &object = *print.objects()[i];
|
const PrintObject &object = *print.objects()[i];
|
||||||
for (size_t j = 0; j < object.instances().size(); ++ j) {
|
for (size_t j = 0; j < object.instances().size(); ++ j) {
|
||||||
object_reference_points.emplace_back(object.instance_center(j));
|
// Sliced PrintObjects are centered, object.instances()[j].shift is the center of the PrintObject in G-code coordinates.
|
||||||
|
object_reference_points.emplace_back(object.instances()[j].shift);
|
||||||
instances.emplace_back(i, j);
|
instances.emplace_back(i, j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6425,7 +6425,7 @@ void GLCanvas3D::_load_sla_shells()
|
||||||
v.indexed_vertex_array.finalize_geometry(this->m_initialized);
|
v.indexed_vertex_array.finalize_geometry(this->m_initialized);
|
||||||
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
|
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
|
||||||
v.composite_id.volume_id = volume_id;
|
v.composite_id.volume_id = volume_id;
|
||||||
v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0));
|
v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0));
|
||||||
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
|
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
|
||||||
v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.);
|
v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.);
|
||||||
v.set_convex_hull(mesh.convex_hull_3d());
|
v.set_convex_hull(mesh.convex_hull_3d());
|
||||||
|
|
|
@ -148,9 +148,6 @@ _constant()
|
||||||
croak("Configuration is not valid: %s\n", err.c_str());
|
croak("Configuration is not valid: %s\n", err.c_str());
|
||||||
RETVAL = 1;
|
RETVAL = 1;
|
||||||
%};
|
%};
|
||||||
Clone<BoundingBox> bounding_box();
|
|
||||||
Clone<BoundingBox> total_bounding_box();
|
|
||||||
Clone<Point> size() %code%{ RETVAL = THIS->bounding_box().size(); %};
|
|
||||||
|
|
||||||
void set_callback_event(int evt) %code%{
|
void set_callback_event(int evt) %code%{
|
||||||
%};
|
%};
|
||||||
|
|
Loading…
Reference in a new issue