diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 58fbe0ca8..83290cb2b 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -48,81 +48,6 @@ sub reload_object { $self->add_model_object($_) for @models_objects; } -sub validate { - my $self = shift; - - if ($self->config->complete_objects) { - # check horizontal clearance - { - my @a = (); - foreach my $object (@{$self->objects}) { - # get convex hulls of all meshes assigned to this print object - my @mesh_convex_hulls = map $object->model_object->volumes->[$_]->mesh->convex_hull, - map @$_, - grep defined $_, - @{$object->region_volumes}; - - # make a single convex hull for all of them - my $convex_hull = convex_hull([ map @$_, @mesh_convex_hulls ]); - - # apply the same transformations we apply to the actual meshes when slicing them - $object->model_object->instances->[0]->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 - ($convex_hull) = @{offset([$convex_hull], scale $self->config->extruder_clearance_radius / 2, 1, JT_ROUND, scale(0.1))}; - - # now we need that no instance of $convex_hull does not intersect any of the previously checked object instances - for my $copy (@{$object->_shifted_copies}) { - my $p = $convex_hull->clone; - - $p->translate(@$copy); - if (@{ intersection(\@a, [$p]) }) { - die "Some objects are too close; your extruder will collide with them.\n"; - } - @a = @{union([@a, $p])}; - } - } - } - - # check vertical clearance - { - my @object_height = (); - foreach my $object (@{$self->objects}) { - my $height = $object->size->z; - push @object_height, $height for @{$object->copies}; - } - @object_height = sort { $a <=> $b } @object_height; - # 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 - pop @object_height; - if (@object_height && max(@object_height) > scale $self->config->extruder_clearance_height) { - die "Some objects are too tall and cannot be printed without extruder collisions.\n"; - } - } - } - - if ($self->config->spiral_vase) { - if ((map @{$_->copies}, @{$self->objects}) > 1) { - die "The Spiral Vase option can only be used when printing a single object.\n"; - } - if (@{$self->regions} > 1) { - die "The Spiral Vase option can only be used when printing single material objects.\n"; - } - } - - { - my $max_layer_height = max( - map { $_->config->layer_height, $_->config->get_value('first_layer_height') } @{$self->objects}, - ); - my $extruders = $self->extruders; - die "Layer height can't be greater than nozzle diameter\n" - if grep { $max_layer_height > $self->config->get_at('nozzle_diameter', $_) } @$extruders; - } -} - # this value is not supposed to be compared with $layer->id # since they have different semantics sub total_layer_count { diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 240cf3b68..cc77e19ef 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -417,6 +417,16 @@ template void intersection(const Slic3r::Pol template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); template void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); +template +bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_) +{ + SubjectType retval; + intersection(subject, clip, retval, safety_offset_); + return !retval.empty(); +} +template bool intersects(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_); +template bool intersects(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_); + void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_) { @@ -432,6 +442,13 @@ void union_(const Slic3r::Polygons &subject, T &retval, bool safety_offset_) template void union_(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool safety_offset_); template void union_(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_); +void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons &retval, bool safety_offset) +{ + Polygons pp = subject1; + pp.insert(pp.end(), subject2.begin(), subject2.end()); + union_(pp, retval, safety_offset); +} + void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree &retval, bool safety_offset_) { Slic3r::Polygons clip; diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 2ab3ff775..795c092be 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -84,12 +84,17 @@ void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType & template void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false); +template +bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); + void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_ = false); template void union_(const Slic3r::Polygons &subject, T &retval, bool safety_offset_ = false); +void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons &retval, bool safety_offset = false); + void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree &retval, bool safety_offset_ = false); void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_ = false); static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons &retval); diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index 002f11f46..8c2664aa1 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -52,6 +52,16 @@ convex_hull(Points points, Polygon* hull) hull->points.pop_back(); } +void +convex_hull(const Polygons &polygons, Polygon* hull) +{ + Points pp; + for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) { + pp.insert(pp.end(), p->points.begin(), p->points.end()); + } + convex_hull(pp, hull); +} + /* accepts an arrayref of points and returns a list of indices according to a nearest-neighbor walk */ void diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index 31e29816d..8ec192dcc 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -12,6 +12,7 @@ using boost::polygon::voronoi_diagram; namespace Slic3r { namespace Geometry { void convex_hull(Points points, Polygon* hull); +void convex_hull(const Polygons &polygons, Polygon* hull); void chained_path(const Points &points, std::vector &retval, Point start_near); void chained_path(const Points &points, std::vector &retval); template void chained_path_items(Points &points, T &items, T &retval); diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 5da3cb499..24f210a99 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -24,6 +24,12 @@ MultiPoint::translate(double x, double y) } } +void +MultiPoint::translate(const Point &vector) +{ + this->translate(vector.x, vector.y); +} + void MultiPoint::rotate(double angle, const Point ¢er) { diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 075b0dbdb..ba3c72e50 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -19,6 +19,7 @@ class MultiPoint operator Points() const; void scale(double factor); void translate(double x, double y); + void translate(const Point &vector); void rotate(double angle, const Point ¢er); void reverse(); Point first_point() const; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 54ad73bcf..79519a8a5 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -1,5 +1,7 @@ #include "Print.hpp" #include "BoundingBox.hpp" +#include "ClipperUtils.hpp" +#include "Geometry.hpp"; #include namespace Slic3r { @@ -507,6 +509,97 @@ Print::init_extruders() this->state.set_done(psInitExtruders); } +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::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 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 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 extruders = this->extruders(); + for (std::set::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"); + } + } +} + PrintRegionConfig Print::_region_config_from_model_volume(const ModelVolume &volume) { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 9436d590e..febf8ea4c 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "Flow.hpp" #include "PrintConfig.hpp" #include "Point.hpp" @@ -27,6 +28,11 @@ enum PrintObjectStep { posInfill, posSupportMaterial, }; +class PrintValidationException : public std::runtime_error { + public: + PrintValidationException(const std::string &error) : std::runtime_error(error) {}; +}; + template class PrintState { @@ -171,6 +177,7 @@ class Print void add_model_object(ModelObject* model_object, int idx = -1); bool apply_config(DynamicPrintConfig config); void init_extruders(); + void validate() const; std::set extruders() const; void _simplify_slices(double distance); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 4a32255fc..3d2d2ad4f 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -171,6 +171,14 @@ _constant() bool apply_config(DynamicPrintConfig* config) %code%{ RETVAL = THIS->apply_config(*config); %}; void init_extruders(); + void validate() + %code%{ + try { + THIS->validate(); + } catch (PrintValidationException &e) { + croak(e.what()); + } + %}; %{ double