diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm index d2cf76ce69..920d4cec11 100644 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm @@ -548,11 +548,16 @@ sub BUILD { EVT_TEXT($self->parent, $textctrl, sub { my $value = $textctrl->GetValue; if ($value =~ /^-?\d+(\.\d*)?$/) { - $self->set_value($value); + # Update the slider without re-updating the text field being modified. + $self->disable_change_event(1); + $self->slider->SetValue($value*$self->scale); + $self->disable_change_event(0); + $self->_on_change($self->option->opt_id); } }); EVT_KILL_FOCUS($textctrl, sub { + $self->_update_textctrl; $self->_on_kill_focus($self->option->opt_id, @_); }); } @@ -573,7 +578,9 @@ sub get_value { sub _update_textctrl { my ($self) = @_; - $self->textctrl->SetLabel($self->get_value); + + $self->textctrl->ChangeValue($self->get_value); + $self->textctrl->SetInsertionPointEnd; } sub enable { diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index fe00e0260c..d43f1cf528 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -256,43 +256,10 @@ sub new { EVT_LEFT_UP($self->{btn_send_gcode}, sub { my (undef, $e) = @_; - my $filename = basename($self->{print}->output_filepath($main::opt{output} // '')); - - if (!$e->AltDown) { - # When the alt key is pressed, bypass the dialog. - my $dlg = Slic3r::GUI::Plater::OctoPrintSpoolDialog->new($self, $filename); - return unless $dlg->ShowModal == wxID_OK; - $filename = $dlg->{filename}; - } - - if (!$Slic3r::GUI::Settings->{octoprint}{overwrite}) { - my $progress = Wx::ProgressDialog->new('Querying OctoPrint…', - "Checking whether file already exists…", 100, $self, 0); - $progress->Pulse; - - my $ua = LWP::UserAgent->new; - $ua->timeout(5); - my $res = $ua->get("http://" . $self->{config}->octoprint_host . "/api/files/local"); - $progress->Destroy; - if ($res->is_success) { - if ($res->decoded_content =~ /"name":\s*"\Q$filename\E"/) { - my $dialog = Wx::MessageDialog->new($self, - "It looks like a file with the same name already exists in the server. " - . "Shall I overwrite it?", - 'OctoPrint', wxICON_WARNING | wxYES | wxNO); - if ($dialog->ShowModal() == wxID_NO) { - return; - } - } - } else { - my $message = "Error while connecting to the OctoPrint server: " . $res->status_line; - Slic3r::GUI::show_error($self, $message); - return; - } - } - - $self->{send_gcode_file_print} = $Slic3r::GUI::Settings->{octoprint}{start}; - $self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir() . "/" . $filename); + my $alt = $e->AltDown; + wxTheApp->CallAfter(sub { + $self->prepare_send($alt); + }); }); EVT_BUTTON($self, $self->{btn_export_stl}, \&export_stl); @@ -632,7 +599,7 @@ sub add_tin { sub load_file { my $self = shift; - my ($input_file) = @_; + my ($input_file, $obj_idx) = @_; $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file); wxTheApp->save_settings; @@ -657,7 +624,19 @@ sub load_file { $model->convert_multipart_object; } } - @obj_idx = $self->load_model_objects(@{$model->objects}); + + if (defined $obj_idx) { + return () if $obj_idx >= $model->objects_count; + @obj_idx = $self->load_model_objects($model->get_object($obj_idx)); + } else { + @obj_idx = $self->load_model_objects(@{$model->objects}); + } + + my $i = 0; + foreach my $obj_idx (@obj_idx) { + $self->{objects}[$obj_idx]->input_file($input_file); + $self->{objects}[$obj_idx]->input_file_obj_idx($i++); + } $self->statusbar->SetStatusText("Loaded " . basename($input_file)); } @@ -692,6 +671,9 @@ sub load_model_objects { # add a default instance and center object around origin $o->center_around_origin; # also aligns object to Z = 0 $o->add_instance(offset => $bed_centerf); + } else { + # if object has defined positions we still need to ensure it's aligned to Z = 0 + $o->align_to_ground; } { @@ -861,6 +843,7 @@ sub set_number_of_copies { # prompt user my $copies = Wx::GetNumberFromUser("", "Enter the number of copies of the selected object:", "Copies", $model_object->instances_count, 0, 1000, $self); + return if $copies == -1; my $diff = $copies - $model_object->instances_count; if ($diff == 0) { # no variation @@ -1440,6 +1423,52 @@ sub do_print { $self->GetFrame->select_tab(1); } +sub prepare_send { + my ($self, $skip_dialog) = @_; + + return if !$self->{btn_send_gcode}->IsEnabled; + my $filename = basename($self->{print}->output_filepath($main::opt{output} // '')); + + if (!$skip_dialog) { + # When the alt key is pressed, bypass the dialog. + my $dlg = Slic3r::GUI::Plater::OctoPrintSpoolDialog->new($self, $filename); + return unless $dlg->ShowModal == wxID_OK; + $filename = $dlg->{filename}; + } + + if (!$Slic3r::GUI::Settings->{octoprint}{overwrite}) { + my $progress = Wx::ProgressDialog->new('Querying OctoPrint…', + "Checking whether file already exists…", 100, $self, 0); + $progress->Pulse; + + my $ua = LWP::UserAgent->new; + $ua->timeout(5); + my $res = $ua->get( + "http://" . $self->{config}->octoprint_host . "/api/files/local", + 'X-Api-Key' => $self->{config}->octoprint_apikey, + ); + $progress->Destroy; + if ($res->is_success) { + if ($res->decoded_content =~ /"name":\s*"\Q$filename\E"/) { + my $dialog = Wx::MessageDialog->new($self, + "It looks like a file with the same name already exists in the server. " + . "Shall I overwrite it?", + 'OctoPrint', wxICON_WARNING | wxYES | wxNO); + if ($dialog->ShowModal() == wxID_NO) { + return; + } + } + } else { + my $message = "Error while connecting to the OctoPrint server: " . $res->status_line; + Slic3r::GUI::show_error($self, $message); + return; + } + } + + $self->{send_gcode_file_print} = $Slic3r::GUI::Settings->{octoprint}{start}; + $self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir() . "/" . $filename); +} + sub send_gcode { my ($self) = @_; @@ -1488,13 +1517,14 @@ sub reload_from_disk { my ($obj_idx, $object) = $self->selected_object; return if !defined $obj_idx; - my $model_object = $self->{model}->objects->[$obj_idx]; - return if !$model_object->input_file - || !-e $model_object->input_file; + return if !$object->input_file + || !-e $object->input_file; - my @new_obj_idx = $self->load_file($model_object->input_file); + # Only reload the selected object and not all objects from the input file. + my @new_obj_idx = $self->load_file($object->input_file, $object->input_file_obj_idx); return if !@new_obj_idx; + my $model_object = $self->{model}->objects->[$obj_idx]; foreach my $new_obj_idx (@new_obj_idx) { my $o = $self->{model}->objects->[$new_obj_idx]; $o->clear_instances; @@ -1509,6 +1539,8 @@ sub reload_from_disk { $self->remove($obj_idx); + # TODO: refresh object list which contains wrong count and scale + # Trigger thumbnail generation again, because the remove() method altered # object indexes before background thumbnail generation called its completion # event, so the on_thumbnail_made callback is called with the wrong $obj_idx. @@ -2127,6 +2159,8 @@ use List::Util qw(first); use Slic3r::Geometry qw(X Y Z MIN MAX deg2rad); has 'name' => (is => 'rw', required => 1); +has 'input_file' => (is => 'rw'); +has 'input_file_obj_idx' => (is => 'rw'); has 'thumbnail' => (is => 'rw'); # ExPolygon::Collection in scaled model units with no transforms has 'transformed_thumbnail' => (is => 'rw'); has 'instance_thumbnails' => (is => 'ro', default => sub { [] }); # array of ExPolygon::Collection objects, each one representing the actual placed thumbnail of each instance in pixel units diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 99c69cd4a3..cba64323a4 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -52,33 +52,6 @@ sub set_material { return $material; } -sub looks_like_multipart_object { - my ($self) = @_; - - return 0 if $self->objects_count == 1; - return 0 if any { $_->volumes_count > 1 } @{$self->objects}; - return 0 if any { @{$_->config->get_keys} > 1 } @{$self->objects}; - - my %heights = map { $_ => 1 } map $_->mesh->bounding_box->z_min, map @{$_->volumes}, @{$self->objects}; - return scalar(keys %heights) > 1; -} - -sub convert_multipart_object { - my ($self) = @_; - - my @objects = @{$self->objects}; - my $object = $self->add_object( - input_file => $objects[0]->input_file, - ); - foreach my $v (map @{$_->volumes}, @objects) { - my $volume = $object->add_volume($v); - $volume->set_name($v->object->name); - } - $object->add_instance($_) for map @{$_->instances}, @objects; - - $self->delete_object($_) for reverse 0..($self->objects_count-2); -} - # Extends C++ class Slic3r::ModelMaterial package Slic3r::Model::Material; diff --git a/package/osx/plist.sh b/package/osx/plist.sh index 898ae3be2d..38e03edbcb 100644 --- a/package/osx/plist.sh +++ b/package/osx/plist.sh @@ -91,6 +91,8 @@ cat << EOF >> $plistfile LSMinimumSystemVersion 10.7 + NSPrincipalClass + NSApplication EOF diff --git a/xs/Build.PL b/xs/Build.PL index 87ba0c1ef6..619cc879fa 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -39,6 +39,19 @@ if ($^O eq 'darwin') { # that prevents this from happening, not needed with newer Boost versions. # See here for more details: https://svn.boost.org/trac/boost/ticket/7671 push @cflags, qw(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE); + + # ExtUtils::CppGuess has a hard-coded -lstdc++, so we filter it out + { + no strict 'refs'; + no warnings 'redefine'; + my $func = "ExtUtils::CppGuess::_get_lflags"; + my $orig = *$func{CODE}; + *{$func} = sub { + my $lflags = $orig->(@_); + $lflags =~ s/\s*-lstdc\+\+//; + return $lflags; + }; + } } if ($mswin) { # In case windows.h is included, we don't want the min / max macros to be active. diff --git a/xs/MANIFEST b/xs/MANIFEST index a2ffbba5a7..61a97afede 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -68,6 +68,8 @@ src/libslic3r/GCode/CoolingBuffer.cpp src/libslic3r/GCode/CoolingBuffer.hpp src/libslic3r/GCode/SpiralVase.cpp src/libslic3r/GCode/SpiralVase.hpp +src/libslic3r/GCodeReader.cpp +src/libslic3r/GCodeReader.hpp src/libslic3r/GCodeSender.cpp src/libslic3r/GCodeSender.hpp src/libslic3r/GCodeWriter.cpp diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index df1f8137d4..66876905c2 100755 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -76,7 +76,7 @@ enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; #else typedef signed long long cInt; static cInt const loRange = 0x3FFFFFFF; - static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + constexpr cInt hiRange = 0x3FFFFFFFFFFFFFFFLL; typedef signed long long long64; //used by Int128 class typedef unsigned long long ulong64; diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 9e4e5e89ae..ddd551ca26 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -19,7 +19,8 @@ namespace Slic3r { // How about 2^17=131072? // By the way, is the scalling needed at all? Cura runs all the computation with a fixed point precision of 1um, while Slic3r scales to 1nm, // further scaling by 10e5 brings us to -#define CLIPPER_OFFSET_SCALE 100000.0 +constexpr float CLIPPER_OFFSET_SCALE = 100000.0; +constexpr auto MAX_COORD = ClipperLib::hiRange / CLIPPER_OFFSET_SCALE; //----------------------------------------------------------- // legacy code from Clipper documentation diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index ec02acc356..93fbba4676 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -514,4 +514,12 @@ ExPolygon::dump_perl() const return ret.str(); } +std::ostream& +operator <<(std::ostream &s, const ExPolygons &expolygons) +{ + for (const ExPolygon &e : expolygons) + s << e.dump_perl() << std::endl; + return s; +} + } diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 63cc560cd9..404a4385e3 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -4,6 +4,7 @@ #include "libslic3r.h" #include "Polygon.hpp" #include "Polyline.hpp" +#include #include namespace Slic3r { @@ -62,6 +63,8 @@ operator+(ExPolygons src1, const ExPolygons &src2) { return src1; }; +std::ostream& operator <<(std::ostream &s, const ExPolygons &expolygons); + } // start Boost diff --git a/xs/src/libslic3r/Flow.hpp b/xs/src/libslic3r/Flow.hpp index 2f041d03cd..fdfcac6950 100644 --- a/xs/src/libslic3r/Flow.hpp +++ b/xs/src/libslic3r/Flow.hpp @@ -7,8 +7,8 @@ namespace Slic3r { -#define BRIDGE_EXTRA_SPACING 0.05 -#define OVERLAP_FACTOR 1.0 +constexpr auto BRIDGE_EXTRA_SPACING = 0.05; +constexpr auto OVERLAP_FACTOR = 1.0; enum FlowRole { frExternalPerimeter, diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 39af871b27..f6ae8f75a4 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -453,8 +453,8 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) paths.front().polyline.points[0], paths.front().polyline.points[1] ); - double distance = std::min( - scale_(EXTRUDER_CONFIG(nozzle_diameter)), + const double distance = std::min( + (double)scale_(EXTRUDER_CONFIG(nozzle_diameter)), first_segment.length() ); Point point = first_segment.point_at(distance); diff --git a/xs/src/libslic3r/GCodeReader.cpp b/xs/src/libslic3r/GCodeReader.cpp index fcaaea9be8..1a0742864f 100644 --- a/xs/src/libslic3r/GCodeReader.cpp +++ b/xs/src/libslic3r/GCodeReader.cpp @@ -56,6 +56,9 @@ GCodeReader::parse(const std::string &gcode, callback_t callback) } } + if (gline.has('E') && this->_config.use_relative_e_distances) + this->E = 0; + if (callback) callback(*this, gline); // update coordinates diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp index 5fc33d9c90..c1568790ec 100644 --- a/xs/src/libslic3r/GCodeSender.cpp +++ b/xs/src/libslic3r/GCodeSender.cpp @@ -26,10 +26,10 @@ std::fstream fs; #endif -#define KEEP_SENT 20 - namespace Slic3r { +constexpr auto KEEP_SENT = 20; + namespace asio = boost::asio; GCodeSender::GCodeSender() diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index 0cc9a575c3..b8584000fa 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -609,6 +609,13 @@ MedialAxis::process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* pol bool MedialAxis::validate_edge(const VD::edge_type* edge) { + // prevent overflows and detect almost-infinite edges + if (std::abs(edge->vertex0()->x()) > (double)MAX_COORD + || std::abs(edge->vertex0()->y()) > (double)MAX_COORD + || std::abs(edge->vertex1()->x()) > (double)MAX_COORD + || std::abs(edge->vertex1()->y()) > (double)MAX_COORD) + return false; + // construct the line representing this edge of the Voronoi diagram const Line line( Point( edge->vertex0()->x(), edge->vertex0()->y() ), diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index b7d9058fe6..1baaa0bef5 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -231,12 +231,13 @@ LayerRegion::prepare_fill_surfaces() } // turn too small internal regions into solid regions according to the user setting - if (this->region()->config.fill_density.value > 0) { + const float &fill_density = this->region()->config.fill_density; + if (fill_density > 0 && fill_density < 100) { // scaling an area requires two calls! - double min_area = scale_(scale_(this->region()->config.solid_infill_below_area.value)); - for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { - if (surface->surface_type == stInternal && surface->area() <= min_area) - surface->surface_type = stInternalSolid; + const double min_area = scale_(scale_(this->region()->config.solid_infill_below_area.value)); + for (Surface &surface : this->fill_surfaces.surfaces) { + if (surface.surface_type == stInternal && surface.area() <= min_area) + surface.surface_type = stInternalSolid; } } } diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index ca9c0b2274..5243799caa 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -2,6 +2,7 @@ #include "Geometry.hpp" #include "IO.hpp" #include +#include #include #include @@ -92,9 +93,8 @@ Model::delete_object(size_t idx) void Model::clear_objects() { - // int instead of size_t because it can be -1 when vector is empty - for (int i = this->objects.size()-1; i >= 0; --i) - this->delete_object(i); + while (!this->objects.empty()) + this->delete_object(0); } void @@ -373,6 +373,43 @@ Model::print_info() const (*o)->print_info(); } +bool +Model::looks_like_multipart_object() const +{ + if (this->objects.size() == 1) return false; + for (const ModelObject* o : this->objects) { + if (o->volumes.size() > 1) return false; + if (o->config.keys().size() > 1) return false; + } + + std::set heights; + for (const ModelObject* o : this->objects) + for (const ModelVolume* v : o->volumes) + heights.insert(v->mesh.bounding_box().min.z); + return heights.size() > 1; +} + +void +Model::convert_multipart_object() +{ + if (this->objects.empty()) return; + + ModelObject* object = this->add_object(); + object->input_file = this->objects.front()->input_file; + + for (const ModelObject* o : this->objects) { + for (const ModelVolume* v : o->volumes) { + ModelVolume* v2 = object->add_volume(*v); + v2->name = o->name; + } + } + for (const ModelInstance* i : this->objects.front()->instances) + object->add_instance(*i); + + while (this->objects.size() > 1) + this->delete_object(0); +} + ModelMaterial::ModelMaterial(Model *model) : model(model) {} ModelMaterial::ModelMaterial(Model *model, const ModelMaterial &other) : attributes(other.attributes), config(other.config), model(model) @@ -467,9 +504,8 @@ ModelObject::delete_volume(size_t idx) void ModelObject::clear_volumes() { - // int instead of size_t because it can be -1 when vector is empty - for (int i = this->volumes.size()-1; i >= 0; --i) - this->delete_volume(i); + while (!this->volumes.empty()) + this->delete_volume(0); } ModelInstance* @@ -508,8 +544,8 @@ ModelObject::delete_last_instance() void ModelObject::clear_instances() { - for (size_t i = 0; i < this->instances.size(); ++i) - this->delete_instance(i); + while (!this->instances.empty()) + this->delete_last_instance(); } // this returns the bounding box of the *transformed* instances @@ -599,6 +635,20 @@ ModelObject::instance_bounding_box(size_t instance_idx) const return bb; } +void +ModelObject::align_to_ground() +{ + // calculate the displacements needed to + // center this object around the origin + BoundingBoxf3 bb; + for (const ModelVolume* v : this->volumes) + if (!v->modifier) + bb.merge(v->mesh.bounding_box()); + + this->translate(0, 0, -bb.min.z); + this->origin_translation.translate(0, 0, -bb.min.z); +} + void ModelObject::center_around_origin() { diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 9c8e0d1e20..36571ff5d7 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -74,6 +74,8 @@ class Model void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); void print_info() const; + bool looks_like_multipart_object() const; + void convert_multipart_object(); }; // Material, which may be shared across multiple ModelObjects of a single Model. @@ -149,6 +151,7 @@ class ModelObject TriangleMesh raw_mesh() const; BoundingBoxf3 raw_bounding_box() const; BoundingBoxf3 instance_bounding_box(size_t instance_idx) const; + void align_to_ground(); void center_around_origin(); void translate(const Vectorf3 &vector); void translate(coordf_t x, coordf_t y, coordf_t z); diff --git a/xs/src/libslic3r/MotionPlanner.hpp b/xs/src/libslic3r/MotionPlanner.hpp index 1fbdb4123f..80d61213ec 100644 --- a/xs/src/libslic3r/MotionPlanner.hpp +++ b/xs/src/libslic3r/MotionPlanner.hpp @@ -9,11 +9,11 @@ #include #include -#define MP_INNER_MARGIN scale_(1.0) -#define MP_OUTER_MARGIN scale_(2.0) - namespace Slic3r { +constexpr coord_t MP_INNER_MARGIN = scale_(1.0); +constexpr coord_t MP_OUTER_MARGIN = scale_(2.0); + class MotionPlanner; class MotionPlannerEnv diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index edea226959..03703aa40f 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -4,7 +4,7 @@ namespace Slic3r { // how much we extend support around the actual contact area -#define SUPPORT_MATERIAL_MARGIN 1.5 +constexpr coordf_t SUPPORT_MATERIAL_MARGIN = 1.5; } diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 6789d58ffc..50b4241de3 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -10,33 +10,6 @@ #include #include -#define SLIC3R_VERSION "1.3.0-dev" - -//FIXME This epsilon value is used for many non-related purposes: -// For a threshold of a squared Euclidean distance, -// for a trheshold in a difference of radians, -// for a threshold of a cross product of two non-normalized vectors etc. -#define EPSILON 1e-4 -// Scaling factor for a conversion from coord_t to coordf_t: 10e-6 -// This scaling generates a following fixed point representation with for a 32bit integer: -// 0..4294mm with 1nm resolution -#define SCALING_FACTOR 0.000001 -// RESOLUTION, SCALED_RESOLUTION: Used as an error threshold for a Douglas-Peucker polyline simplification algorithm. -#define RESOLUTION 0.0125 -#define SCALED_RESOLUTION (RESOLUTION / SCALING_FACTOR) -#define PI 3.141592653589793238 -// When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam. -#define LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER 0.15 -// Maximum perimeter length for the loop to apply the small perimeter speed. -#define SMALL_PERIMETER_LENGTH (6.5 / SCALING_FACTOR) * 2 * PI -#define INSET_OVERLAP_TOLERANCE 0.4 -#define EXTERNAL_INFILL_MARGIN 3 -#define scale_(val) ((val) / SCALING_FACTOR) -#define unscale(val) ((val) * SCALING_FACTOR) -#define SCALED_EPSILON scale_(EPSILON) -typedef long coord_t; -typedef double coordf_t; - /* Implementation of CONFESS("foo"): */ #ifdef _MSC_VER #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) @@ -61,6 +34,35 @@ void confess_at(const char *file, int line, const char *func, const char *pat, . namespace Slic3r { +constexpr auto SLIC3R_VERSION = "1.3.0-dev"; + +typedef long coord_t; +typedef double coordf_t; + +// Scaling factor for a conversion from coord_t to coordf_t: 10e-6 +// This scaling generates a following fixed point representation with for a 32bit integer: +// 0..4294mm with 1nm resolution +constexpr auto SCALING_FACTOR = 0.000001; +inline constexpr coord_t scale_(const coordf_t &val) { return val / SCALING_FACTOR; } +inline constexpr coordf_t unscale(const coord_t &val) { return val * SCALING_FACTOR; } + +//FIXME This epsilon value is used for many non-related purposes: +// For a threshold of a squared Euclidean distance, +// for a trheshold in a difference of radians, +// for a threshold of a cross product of two non-normalized vectors etc. +constexpr auto EPSILON = 1e-4; +constexpr auto SCALED_EPSILON = scale_(EPSILON); +// RESOLUTION, SCALED_RESOLUTION: Used as an error threshold for a Douglas-Peucker polyline simplification algorithm. +constexpr auto RESOLUTION = 0.0125; +constexpr auto SCALED_RESOLUTION = scale_(RESOLUTION); +constexpr auto PI = 3.141592653589793238; +// When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam. +constexpr auto LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER = 0.15; +// Maximum perimeter length for the loop to apply the small perimeter speed. +constexpr coord_t SMALL_PERIMETER_LENGTH = scale_(6.5) * 2 * PI; +constexpr coordf_t INSET_OVERLAP_TOLERANCE = 0.4; +constexpr coordf_t EXTERNAL_INFILL_MARGIN = 3; + enum Axis { X=0, Y, Z }; template diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 214baad9f2..f6efc8a5d7 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -286,6 +286,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo is_deeply $config->get('retract_speed'), [0.4, 0.5], 'read_cli(): floats array'; } { + no warnings 'qw'; my $config = $parse->(qw(--extruder-offset 0,0 --extruder-offset 10x5)); is_deeply [ map $_->pp, @{$config->get('extruder_offset')} ], [[0,0], [10,5]], 'read_cli(): points array'; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index c5c7006068..cb415ad140 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -95,6 +95,8 @@ void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL); void duplicate_objects_grid(unsigned int x, unsigned int y, double dist); void print_info(); + bool looks_like_multipart_object(); + void convert_multipart_object(); void repair(); }; @@ -207,6 +209,7 @@ ModelMaterial::attributes() bool needed_repair() const; int materials_count() const; int facets_count(); + void align_to_ground(); void center_around_origin(); void translate(double x, double y, double z); void scale_xyz(Pointf3* versor)