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 f15c8911d1..59fa49659d 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -782,7 +782,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; @@ -807,7 +807,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)); } @@ -1677,13 +1689,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; @@ -1698,6 +1711,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. @@ -2252,6 +2267,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/package/osx/startup_script.sh b/package/osx/startup_script.sh index b98fa5bdc4..4f665ac644 100644 --- a/package/osx/startup_script.sh +++ b/package/osx/startup_script.sh @@ -1,4 +1,4 @@ #!/bin/bash DIR=$(dirname "$0") -$DIR/perl-local -I$DIR/local-lib/lib/perl5 $DIR/slic3r.pl $@ +"$DIR/perl-local" -I"$DIR/local-lib/lib/perl5" "$DIR/slic3r.pl" $@ 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..9182cf56b9 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -92,9 +92,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 @@ -467,9 +466,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 +506,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 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