Browse Source

Reworked the Perl unit / integration tests to use the same Print
interface that the application is using. Old interface used just
for the integration tests was removed.

bubnikv 5 years ago
parent
commit
ac6969c992
10 changed files with 67 additions and 357 deletions
  1. 0 1
      lib/Slic3r.pm
  2. 0 104
      lib/Slic3r/Print/Simple.pm
  3. 44 38
      lib/Slic3r/Test.pm
  4. 2 2
      src/libslic3r/Model.hpp
  5. 1 193
      src/libslic3r/Print.cpp
  6. 0 4
      src/libslic3r/Print.hpp
  7. 1 1
      t/combineinfill.t
  8. 15 12
      t/print.t
  9. 3 1
      t/skirt_brim.t
  10. 1 1
      xs/xsp/Config.xsp

+ 0 - 1
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";
 

+ 0 - 104
lib/Slic3r/Print/Simple.pm

@@ -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;

+ 44 - 38
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;

+ 2 - 2
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; }

+ 1 - 193
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<const ConfigOptionFloats*>(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<ConfigOptionVectorBase*>(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<PrintRegionConfig> other_region_configs;
-        for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) {
-            PrintRegion       &region = *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<t_layer_height_range, int> &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 &copy : print_object->m_copies) {
+                for (const Point &copy : print_object->copies()) {
                     Polygon convex_hull = convex_hull0;
                     convex_hull.translate(copy);
                     if (! intersection(convex_hulls_other, convex_hull).empty())

+ 0 - 4
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).

+ 1 - 1
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 }

+ 15 - 12
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';
 }

+ 3 - 1
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 = ();

+ 1 - 1
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();

Some files were not shown because too many files changed in this diff