diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 44100db8c..94f0b5658 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -50,7 +50,6 @@ use Slic3r::Point; use Slic3r::Polygon; use Slic3r::Polyline; use Slic3r::Print::Object; -use Slic3r::Print::Simple; use Slic3r::Surface; our $build = eval "use Slic3r::Build; 1"; diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm deleted file mode 100644 index 2ab68f4d3..000000000 --- a/lib/Slic3r/Print/Simple.pm +++ /dev/null @@ -1,104 +0,0 @@ -# A simple wrapper to quickly print a single model without a GUI. -# Used by the command line slic3r.pl, by command line utilities pdf-slic3s.pl and view-toolpaths.pl, -# and by the quick slice menu of the Slic3r GUI. -# -# It creates and owns an instance of Slic3r::Print to perform the slicing -# and it accepts an instance of Slic3r::Model from the outside. - -package Slic3r::Print::Simple; -use Moo; - -use Slic3r::Geometry qw(X Y); - -has '_print' => ( - is => 'ro', - default => sub { Slic3r::Print->new }, - handles => [qw(apply_config_perl_tests_only extruders output_filepath - total_used_filament total_extruded_volume - placeholder_parser process)], -); - -has 'duplicate' => ( - is => 'rw', - default => sub { 1 }, -); - -has 'scale' => ( - is => 'rw', - default => sub { 1 }, -); - -has 'rotate' => ( - is => 'rw', - default => sub { 0 }, -); - -has 'duplicate_grid' => ( - is => 'rw', - default => sub { [1,1] }, -); - -has 'print_center' => ( - is => 'rw', - default => sub { Slic3r::Pointf->new(100,100) }, -); - -has 'dont_arrange' => ( - is => 'rw', - default => sub { 0 }, -); - -has 'output_file' => ( - is => 'rw', -); - -sub set_model { - # $model is of type Slic3r::Model - my ($self, $model) = @_; - - # make method idempotent so that the object is reusable - $self->_print->clear_objects; - - # make sure all objects have at least one defined instance - my $need_arrange = $model->add_default_instances && ! $self->dont_arrange; - - # apply scaling and rotation supplied from command line if any - foreach my $instance (map @{$_->instances}, @{$model->objects}) { - $instance->set_scaling_factor($instance->scaling_factor * $self->scale); - $instance->set_rotation($instance->rotation + $self->rotate); - } - - if ($self->duplicate_grid->[X] > 1 || $self->duplicate_grid->[Y] > 1) { - $model->duplicate_objects_grid($self->duplicate_grid->[X], $self->duplicate_grid->[Y], $self->_print->config->duplicate_distance); - } elsif ($need_arrange) { - $model->duplicate_objects($self->duplicate, $self->_print->config->min_object_distance); - } elsif ($self->duplicate > 1) { - # if all input objects have defined position(s) apply duplication to the whole model - $model->duplicate($self->duplicate, $self->_print->config->min_object_distance); - } - $_->translate(0,0,-$_->bounding_box->z_min) for @{$model->objects}; - $model->center_instances_around_point($self->print_center) if (! $self->dont_arrange); - - foreach my $model_object (@{$model->objects}) { - $self->_print->auto_assign_extruders($model_object); - $self->_print->add_model_object($model_object); - } -} - -sub export_gcode { - my ($self) = @_; - $self->_print->validate; - $self->_print->export_gcode($self->output_file // ''); -} - -sub export_png { - my ($self) = @_; - - $self->_before_export; - - $self->_print->export_png(output_file => $self->output_file); - - $self->_after_export; -} - -1; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index d1b99e48c..570bca41b 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -146,60 +146,66 @@ sub mesh { } sub model { - my ($model_name, %params) = @_; + my ($model_names, %params) = @_; + $model_names = [ $model_names ] if ! ref($model_names); - my $input_file = "${model_name}.stl"; - my $mesh = mesh($model_name, %params); -# $mesh->write_ascii("out/$input_file"); - my $model = Slic3r::Model->new; - my $object = $model->add_object(input_file => $input_file); - $model->set_material($model_name); - $object->add_volume(mesh => $mesh, material_id => $model_name); - $object->add_instance( - offset => Slic3r::Pointf->new(0,0), - # 3D full transform - rotation => Slic3r::Pointf3->new(0, 0, $params{rotation} // 0), - scaling_factor => Slic3r::Pointf3->new($params{scale} // 1, $params{scale} // 1, $params{scale} // 1), - # old transform -# rotation => $params{rotation} // 0, -# scaling_factor => $params{scale} // 1, - ); + + for my $model_name (@$model_names) { + my $input_file = "${model_name}.stl"; + my $mesh = mesh($model_name, %params); + # $mesh->write_ascii("out/$input_file"); + + my $object = $model->add_object(input_file => $input_file); + $model->set_material($model_name); + $object->add_volume(mesh => $mesh, material_id => $model_name); + $object->add_instance( + offset => Slic3r::Pointf->new(0,0), + # 3D full transform + rotation => Slic3r::Pointf3->new(0, 0, $params{rotation} // 0), + scaling_factor => Slic3r::Pointf3->new($params{scale} // 1, $params{scale} // 1, $params{scale} // 1), + # old transform + # rotation => $params{rotation} // 0, + # scaling_factor => $params{scale} // 1, + ); + } return $model; } sub init_print { my ($models, %params) = @_; + my $model; + if (ref($models) eq 'ARRAY') { + $model = model($models, %params); + } elsif (ref($models)) { + $model = $models; + } else { + $model = model([$models], %params); + } my $config = Slic3r::Config->new; $config->apply($params{config}) if $params{config}; $config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE}; my $print = Slic3r::Print->new; - $print->apply_config_perl_tests_only($config); - - $models = [$models] if ref($models) ne 'ARRAY'; - $models = [ map { ref($_) ? $_ : model($_, %params) } @$models ]; - for my $model (@$models) { - die "Unknown model in test" if !defined $model; - if (defined $params{duplicate} && $params{duplicate} > 1) { - $model->duplicate($params{duplicate} // 1, $print->config->min_object_distance); - } - $model->arrange_objects($print->config->min_object_distance); - $model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100)); - foreach my $model_object (@{$model->objects}) { - $print->auto_assign_extruders($model_object); - $print->add_model_object($model_object); - } + die "Unknown model in test" if !defined $model; + if (defined $params{duplicate} && $params{duplicate} > 1) { + $model->duplicate($params{duplicate} // 1, $config->min_object_distance); } - # Call apply_config_perl_tests_only one more time, so that the layer height profiles are updated over all PrintObjects. - $print->apply_config_perl_tests_only($config); + $model->arrange_objects($config->min_object_distance); + $model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100)); + foreach my $model_object (@{$model->objects}) { + $model_object->ensure_on_bed; + $print->auto_assign_extruders($model_object); + } + + $print->apply($model, $config); $print->validate; # We return a proxy object in order to keep $models alive as required by the Print API. return Slic3r::Test::Print->new( - print => $print, - models => $models, + print => $print, + model => $model, ); } @@ -250,7 +256,7 @@ sub add_facet { package Slic3r::Test::Print; use Moo; -has 'print' => (is => 'ro', required => 1, handles => [qw(process apply_config_perl_tests_only)]); -has 'models' => (is => 'ro', required => 1); +has 'print' => (is => 'ro', required => 1, handles => [qw(process apply)]); +has 'model' => (is => 'ro', required => 1); 1; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c1850df98..bac8ae507 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -597,8 +597,8 @@ public: Model() {} ~Model() { this->clear_objects(); this->clear_materials(); } - /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ - /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ + // To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" + // (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); } explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); } Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 5e4f25334..227123f72 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -328,198 +328,6 @@ double Print::max_allowed_layer_height() const return nozzle_diameter_max; } -// Caller is responsible for supplying models whose objects don't collide -// and have explicit instance positions. -void Print::add_model_object_perl_tests_only(ModelObject* model_object, int idx) -{ - tbb::mutex::scoped_lock lock(this->state_mutex()); - // Add a copy of this ModelObject to this Print. - m_model.objects.emplace_back(ModelObject::new_copy(*model_object)); - m_model.objects.back()->set_model(&m_model); - // Initialize a new print object and store it at the given position. - PrintObject *object = new PrintObject(this, model_object, true); - if (idx != -1) { - delete m_objects[idx]; - m_objects[idx] = object; - } else - m_objects.emplace_back(object); - // Invalidate all print steps. - this->invalidate_all_steps(); - - // Set the transformation matrix without translation from the first instance. - if (! model_object->instances.empty()) { - // Trafo and bounding box, both in world coordinate system. - Transform3d trafo = model_object->instances.front()->get_matrix(); - BoundingBoxf3 bbox = model_object->instance_bounding_box(0); - // Now shift the object up to align it with the print bed. - trafo.data()[14] -= bbox.min(2); - // and reset the XY translation. - trafo.data()[12] = 0; - trafo.data()[13] = 0; - object->set_trafo(trafo); - } - - int volume_id = 0; - for (const ModelVolume *volume : model_object->volumes) { - if (! volume->is_model_part() && ! volume->is_modifier()) - continue; - // Get the config applied to this volume. - PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, nullptr, *volume, 99999); - // Find an existing print region with the same config. - int region_id = -1; - for (int i = 0; i < (int)m_regions.size(); ++ i) - if (config.equals(m_regions[i]->config())) { - region_id = i; - break; - } - // If no region exists with the same config, create a new one. - if (region_id == -1) { - region_id = (int)m_regions.size(); - this->add_region(config); - } - // Assign volume to a region. - object->add_region_volume((unsigned int)region_id, volume_id, t_layer_height_range(0, DBL_MAX)); - ++ volume_id; - } - - // Apply config to print object. - object->config_apply(this->default_object_config()); - { - //normalize_and_apply_config(object->config(), model_object->config); - DynamicPrintConfig src_normalized(model_object->config); - src_normalized.normalize(); - object->config_apply(src_normalized, true); - } -} - -// This function is only called through the Perl-C++ binding from the unit tests, should be -// removed when unit tests are rewritten to C++. -bool Print::apply_config_perl_tests_only(DynamicPrintConfig config) -{ - tbb::mutex::scoped_lock lock(this->state_mutex()); - - - // Perl unit tests were failing in case the preset was not normalized (e.g. https://github.com/prusa3d/PrusaSlicer/issues/2288 was caused - // by too short max_layer_height vector. Calling the necessary function Preset::normalize(...) is not currently possible because there is no - // access to preset. This should be solved when the unit tests are rewritten to C++. For now we just copy-pasted code from Preset.cpp - // to make sure the unit tests pass (functions set_num_extruders and nozzle_options()). - auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter", true)); - assert(nozzle_diameter != nullptr); - const auto &defaults = FullPrintConfig::defaults(); - for (const std::string &key : { "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset", - "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed", - "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe", - "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour" }) - { - auto *opt = config.option(key, true); - assert(opt != nullptr); - assert(opt->is_vector()); - unsigned int num_extruders = (unsigned int)nozzle_diameter->values.size(); - static_cast(opt)->resize(num_extruders, defaults.option(key)); - } - - // 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); - - // handle changes to print config - t_config_option_keys print_diff = m_config.diff(config); - m_config.apply_only(config, print_diff, true); - bool invalidated = this->invalidate_state_by_config_options(print_diff); - - // handle changes to object config defaults - m_default_object_config.apply(config, true); - for (PrintObject *object : m_objects) { - // 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(); - // we override the new config with object-specific options - normalize_and_apply_config(new_config, object->model_object()->config); - // check whether the new config is different from the current one - t_config_option_keys diff = object->config().diff(new_config); - object->config_apply_only(new_config, diff, true); - invalidated |= object->invalidate_state_by_config_options(diff); - } - - // handle changes to regions config defaults - m_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; - { - // Collect the already visited region configs into other_region_configs, - // so one may check for duplicates. - std::vector other_region_configs; - for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) { - PrintRegion ®ion = *m_regions[region_id]; - PrintRegionConfig this_region_config; - bool this_region_config_set = false; - for (PrintObject *object : m_objects) { - if (region_id < object->region_volumes.size()) { - for (const std::pair &volume_and_range : object->region_volumes[region_id]) { - const ModelVolume &volume = *object->model_object()->volumes[volume_and_range.second]; - if (this_region_config_set) { - // 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 (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, nullptr, volume, 99999))) { - rearrange_regions = true; - goto exit_for_rearrange_regions; - } - } else { - this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, nullptr, volume, 99999); - this_region_config_set = true; - } - for (const PrintRegionConfig &cfg : other_region_configs) { - // 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 (cfg.equals(this_region_config)) { - rearrange_regions = true; - goto exit_for_rearrange_regions; - } - } - } - } - } - if (this_region_config_set) { - t_config_option_keys diff = region.config().diff(this_region_config); - if (! diff.empty()) { - region.config_apply_only(this_region_config, diff, false); - for (PrintObject *object : m_objects) - if (region_id < object->region_volumes.size() && ! object->region_volumes[region_id].empty()) - invalidated |= object->invalidate_state_by_config_options(diff); - } - other_region_configs.emplace_back(std::move(this_region_config)); - } - } - } - -exit_for_rearrange_regions: - - 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; - model_objects.reserve(m_objects.size()); - for (PrintObject *object : m_objects) - model_objects.push_back(object->model_object()); - this->clear(); - for (ModelObject *mo : model_objects) - this->add_model_object_perl_tests_only(mo); - invalidated = true; - } - - for (PrintObject *object : m_objects) - object->update_slicing_parameters(); - - return invalidated; -} - // Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new // in the exact order and with the same IDs. // It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order. @@ -1247,7 +1055,7 @@ std::string Print::validate() const 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 Point © : print_object->m_copies) { + for (const Point © : print_object->copies()) { Polygon convex_hull = convex_hull0; convex_hull.translate(copy); if (! intersection(convex_hulls_other, convex_hull).empty()) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index c024a3ad2..c3fa5e062 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -294,10 +294,6 @@ public: ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override; - // The following three methods are used by the Perl tests only. Get rid of them! - void add_model_object_perl_tests_only(ModelObject* model_object, int idx = -1); - bool apply_config_perl_tests_only(DynamicPrintConfig config); - void process() override; // Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file. // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r). diff --git a/t/combineinfill.t b/t/combineinfill.t index 8aa0ff5e3..282bf467a 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -89,7 +89,7 @@ plan tests => 8; # we disable combination after infill has been generated $config->set('infill_every_layers', 1); - $print->apply_config_perl_tests_only($config); + $print->apply($print->print->model->clone, $config); $print->process; ok !(defined first { @{$_->get_region(0)->fill_surfaces} == 0 } diff --git a/t/print.t b/t/print.t index e76d226af..2144e80c1 100644 --- a/t/print.t +++ b/t/print.t @@ -1,4 +1,4 @@ -use Test::More tests => 2; +use Test::More tests => 6; use strict; use warnings; @@ -31,30 +31,33 @@ use Slic3r::Test; ok abs(unscale($center->[Y]) - $print_center->[Y]) < 0.005, 'print is centered around print_center (Y)'; } -# This is really testing a path, which is no more used by the slicer, just by the test cases. -if (0) { # this represents the aggregate config from presets my $config = Slic3r::Config::new_from_defaults; + # Define 4 extruders. + $config->set('nozzle_diameter', [0.4, 0.4, 0.4, 0.4]); # user adds one object to the plater my $print = Slic3r::Test::init_print(my $model = Slic3r::Test::model('20mm_cube'), config => $config); # user sets a per-region option - $print->print->objects->[0]->model_object->config->set('fill_density', 100); -# $print->print->reload_object(0); + my $model2 = $model->clone; + $model2->get_object(0)->config->set('fill_density', 100); + $print->apply($model2, $config); + is $print->print->regions->[0]->config->fill_density, 100, 'region config inherits model object config'; # user exports G-code, thus the default config is reapplied - $print->print->apply_config_perl_tests_only($config); - - is $print->print->regions->[0]->config->fill_density, 100, 'apply_config() does not override per-object settings'; + $model2->get_object(0)->config->erase('fill_density'); + $print->apply($model2, $config); + + is $print->print->regions->[0]->config->fill_density, 20, 'region config is resetted'; # user assigns object extruders - $print->print->objects->[0]->model_object->config->set('extruder', 3); - $print->print->objects->[0]->model_object->config->set('perimeter_extruder', 2); -# $print->print->reload_object(0); - + $model2->get_object(0)->config->set('extruder', 3); + $model2->get_object(0)->config->set('perimeter_extruder', 2); + $print->apply($model2, $config); + is $print->print->regions->[0]->config->infill_extruder, 3, 'extruder setting is correctly expanded'; is $print->print->regions->[0]->config->perimeter_extruder, 2, 'extruder setting does not override explicitely specified extruders'; } diff --git a/t/skirt_brim.t b/t/skirt_brim.t index b05435784..beb3c94c5 100644 --- a/t/skirt_brim.t +++ b/t/skirt_brim.t @@ -91,6 +91,8 @@ use Slic3r::Test; { my $config = Slic3r::Config::new_from_defaults; + # Define 4 extruders. + $config->set('nozzle_diameter', [0.4, 0.4, 0.4, 0.4]); $config->set('layer_height', 0.4); $config->set('first_layer_height', 0.4); $config->set('skirts', 1); @@ -106,7 +108,7 @@ use Slic3r::Test; # we enable support material after skirt has been generated $config->set('support_material', 1); - $print->apply_config_perl_tests_only($config); + $print->apply($print->print->model->clone, $config); my $skirt_length = 0; my @extrusion_points = (); diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 4d48a2c6f..e193a2321 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -49,7 +49,7 @@ void erase(t_config_option_key opt_key); void normalize(); %name{setenv} void setenv_(); - double min_object_distance() %code{% RETVAL = PrintConfig::min_object_distance(THIS); %}; + double min_object_distance() %code{% PrintConfig cfg; cfg.apply(*THIS, true); RETVAL = cfg.min_object_distance(); %}; static DynamicPrintConfig* load(char *path) %code%{ auto config = new DynamicPrintConfig(); diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index af4cdce06..35b1c01ce 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -211,6 +211,7 @@ ModelMaterial::attributes() void set_origin_translation(Vec3d* point) %code%{ THIS->origin_translation = *point; %}; + void ensure_on_bed(); bool needed_repair() const; int materials_count() const; int facets_count(); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 70313ff5e..df5f48587 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -70,6 +70,8 @@ _constant() Print(); ~Print(); + Ref model() + %code%{ RETVAL = const_cast(&THIS->model()); %}; Ref config() %code%{ RETVAL = const_cast(static_cast(&THIS->config())); %}; Ref placeholder_parser() @@ -140,8 +142,8 @@ _constant() } %}; - bool apply_config_perl_tests_only(DynamicPrintConfig* config) - %code%{ RETVAL = THIS->apply_config_perl_tests_only(*config); %}; + bool apply(Model *model, DynamicPrintConfig* config) + %code%{ RETVAL = THIS->apply(*model, *config); %}; bool has_infinite_skirt(); std::vector extruders() const; int validate() %code%{