diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp index 1116d2dcb..c16d92f4e 100644 --- a/xs/src/libslic3r/BoundingBox.cpp +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -68,6 +68,26 @@ BoundingBox::polygon() const return p; } +BoundingBox BoundingBox::rotated(double angle) const +{ + BoundingBox out; + out.merge(this->min.rotated(angle)); + out.merge(this->max.rotated(angle)); + out.merge(Point(this->min.x, this->max.y).rotated(angle)); + out.merge(Point(this->max.x, this->min.y).rotated(angle)); + return out; +} + +BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const +{ + BoundingBox out; + out.merge(this->min.rotated(angle, center)); + out.merge(this->max.rotated(angle, center)); + out.merge(Point(this->min.x, this->max.y).rotated(angle, center)); + out.merge(Point(this->max.x, this->min.y).rotated(angle, center)); + return out; +} + template void BoundingBoxBase::scale(double factor) { @@ -163,6 +183,26 @@ BoundingBox3Base::size() const } template Pointf3 BoundingBox3Base::size() const; +template double +BoundingBoxBase::radius() const +{ + double x = this->max.x - this->min.x; + double y = this->max.y - this->min.y; + return 0.5 * sqrt(x*x+y*y); +} +template double BoundingBoxBase::radius() const; +template double BoundingBoxBase::radius() const; + +template double +BoundingBox3Base::radius() const +{ + double x = this->max.x - this->min.x; + double y = this->max.y - this->min.y; + double z = this->max.z - this->min.z; + return 0.5 * sqrt(x*x+y*y+z*z); +} +template double BoundingBox3Base::radius() const; + template void BoundingBoxBase::translate(coordf_t x, coordf_t y) { diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index 136ba1f8c..b4f101976 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -28,6 +28,7 @@ class BoundingBoxBase void merge(const BoundingBoxBase &bb); void scale(double factor); PointClass size() const; + double radius() const; void translate(coordf_t x, coordf_t y); void offset(coordf_t delta); PointClass center() const; @@ -44,6 +45,7 @@ class BoundingBox3Base : public BoundingBoxBase void merge(const std::vector &points); void merge(const BoundingBox3Base &bb); PointClass size() const; + double radius() const; void translate(coordf_t x, coordf_t y, coordf_t z); void offset(coordf_t delta); PointClass center() const; @@ -54,6 +56,10 @@ class BoundingBox : public BoundingBoxBase public: void polygon(Polygon* polygon) const; Polygon polygon() const; + BoundingBox rotated(double angle) const; + BoundingBox rotated(double angle, const Point ¢er) const; + void rotate(double angle) { (*this) = this->rotated(angle); } + void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); } BoundingBox() : BoundingBoxBase() {}; BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {}; @@ -100,7 +106,9 @@ inline bool empty(const BoundingBoxBase &bb) template inline bool empty(const BoundingBox3Base &bb) { - return bb.min.x > bb.max.x || bb.min.y > bb.max.y || bb.min.z > bb.max.z;} + return bb.min.x > bb.max.x || bb.min.y > bb.max.y || bb.min.z > bb.max.z; } +} // namespace Slic3r + #endif diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index a1297e574..af4070f58 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -1,6 +1,12 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" +// #define CLIPPER_UTILS_DEBUG + +#ifdef CLIPPER_UTILS_DEBUG +#include "SVG.hpp" +#endif /* CLIPPER_UTILS_DEBUG */ + namespace Slic3r { //----------------------------------------------------------- @@ -226,6 +232,19 @@ void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) { +#ifdef CLIPPER_UTILS_DEBUG + BoundingBox bbox = get_extents(polygons); + coordf_t stroke_width = scale_(0.005); + static int iRun = 0; + ++ iRun; + char path[2048]; + sprintf(path, "out\\offset2-%d.svg", iRun); + bool flipY = false; + SVG svg(path, bbox, scale_(1.), flipY); + for (Slic3r::Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it) + svg.draw(it->lines(), "gray", stroke_width); +#endif /* CLIPPER_UTILS_DEBUG */ + // read input ClipperLib::Paths input; Slic3rMultiPoints_to_ClipperPaths(polygons, &input); @@ -245,12 +264,18 @@ offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float ClipperLib::Paths output1; co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); co.Execute(output1, (delta1*scale)); +#ifdef CLIPPER_UTILS_DEBUG + svg.draw(output1, 1./CLIPPER_OFFSET_SCALE, "red", stroke_width); +#endif /* CLIPPER_UTILS_DEBUG */ // perform second offset co.Clear(); co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon); co.Execute(*retval, (delta2*scale)); - +#ifdef CLIPPER_UTILS_DEBUG + svg.draw(*retval, 1./CLIPPER_OFFSET_SCALE, "green", stroke_width); +#endif /* CLIPPER_UTILS_DEBUG */ + // unscale output scaleClipperPolygons(*retval, 1/scale); } diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index e7396af76..ccdf75588 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -14,6 +14,11 @@ using ClipperLib::jtSquare; namespace Slic3r { +// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library. +//FIXME Vojtech: Better to use a power of 2 coefficient and to use bit shifts for scaling. +// 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 //----------------------------------------------------------- diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 9194548c7..b26a519bc 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -39,8 +39,8 @@ ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type) const ConfigOptionDef* ConfigDef::get(const t_config_option_key &opt_key) const { - if (this->options.count(opt_key) == 0) return NULL; - return &const_cast(this)->options[opt_key]; + t_optiondef_map::iterator it = const_cast(this)->options.find(opt_key); + return (it == this->options.end()) ? NULL : &it->second; } bool @@ -113,6 +113,8 @@ ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str) return opt->deserialize(str); } +// Return an absolute value of a possibly relative config variable. +// For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height. double ConfigBase::get_abs_value(const t_config_option_key &opt_key) { ConfigOption* opt = this->option(opt_key, false); @@ -130,6 +132,8 @@ ConfigBase::get_abs_value(const t_config_option_key &opt_key) { } } +// Return an absolute value of a possibly relative config variable. +// For example, return absolute infill extrusion width, either from an absolute value, or relative to a provided value. double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) { // get stored option value @@ -180,6 +184,7 @@ DynamicConfig& DynamicConfig::operator= (DynamicConfig other) void DynamicConfig::swap(DynamicConfig &other) { + std::swap(this->def, other.def); std::swap(this->options, other.options); } @@ -197,7 +202,8 @@ DynamicConfig::DynamicConfig (const DynamicConfig& other) { ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) { - if (this->options.count(opt_key) == 0) { + t_options_map::iterator it = options.find(opt_key); + if (it == options.end()) { if (create) { const ConfigOptionDef* optdef = this->def->get(opt_key); assert(optdef != NULL); @@ -239,7 +245,7 @@ DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) { return NULL; } } - return this->options[opt_key]; + return it->second; } template @@ -273,7 +279,6 @@ StaticConfig::set_defaults() t_config_option_keys keys = this->keys(); for (t_config_option_keys::const_iterator it = keys.begin(); it != keys.end(); ++it) { const ConfigOptionDef* def = this->def->get(*it); - if (def->default_value != NULL) this->option(*it)->set(*def->default_value); } diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 99cc02fb0..ac6b03a49 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -14,9 +14,11 @@ namespace Slic3r { +// Name of the configuration option. typedef std::string t_config_option_key; typedef std::vector t_config_option_keys; +// A generic value of a configuration option. class ConfigOption { public: virtual ~ConfigOption() {}; @@ -31,6 +33,7 @@ class ConfigOption { friend bool operator!= (const ConfigOption &a, const ConfigOption &b); }; +// Value of a single valued option (bool, int, float, string, point, enum) template class ConfigOptionSingle : public ConfigOption { public: @@ -44,12 +47,14 @@ class ConfigOptionSingle : public ConfigOption { }; }; +// Value of a vector valued option (bools, ints, floats, strings, points) class ConfigOptionVectorBase : public ConfigOption { public: virtual ~ConfigOptionVectorBase() {}; virtual std::vector vserialize() const = 0; }; +// Value of a vector valued option (bools, ints, floats, strings, points), template template class ConfigOptionVector : public ConfigOptionVectorBase { @@ -436,6 +441,7 @@ class ConfigOptionBools : public ConfigOptionVector }; }; +// Map from an enum name to an enum integer value. typedef std::map t_config_enum_values; template @@ -461,11 +467,14 @@ class ConfigOptionEnum : public ConfigOptionSingle return true; }; + // Map from an enum name to an enum integer value. + //FIXME The map is called often, it shall be initialized statically. static t_config_enum_values get_enum_values(); }; -/* We use this one in DynamicConfig objects, otherwise it's better to use - the specialized ConfigOptionEnum containers. */ +// Generic enum configuration value. +// We use this one in DynamicConfig objects when creating a config value object for ConfigOptionType == coEnum. +// In the StaticConfig, it is better to use the specialized ConfigOptionEnum containers. class ConfigOptionEnumGeneric : public ConfigOptionInt { public: @@ -485,57 +494,117 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt }; }; +// Type of a configuration value. enum ConfigOptionType { coNone, + // single float coFloat, + // vector of floats coFloats, + // single int coInt, + // vector of ints coInts, + // single string coString, + // vector of strings coStrings, + // percent value. Currently only used for infill. coPercent, + // a fraction or an absolute value coFloatOrPercent, + // single 2d point. Currently not used. coPoint, + // vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets. coPoints, + // single boolean value coBool, + // vector of boolean values coBools, + // a generic enum coEnum, }; +// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling. class ConfigOptionDef { public: + // What type? bool, int, string etc. ConfigOptionType type; + // Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor. ConfigOption* default_value; + + // Usually empty. + // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection, + // "select_open" - to open a selection dialog (currently only a serial port selection). std::string gui_type; + // Usually empty. Otherwise "serialized" or "show_value" + // The flags may be combined. + // "serialized" - vector valued option is entered in a single edit field. Values are separated by a semicolon. + // "show_value" - even if enum_values / enum_labels are set, still display the value, not the enum label. std::string gui_flags; + // Label of the GUI input field. + // In case the GUI input fields are grouped in some views, the label defines a short label of a grouped value, + // while full_label contains a label of a stand-alone field. + // The full label is shown, when adding an override parameter for an object or a modified object. std::string label; std::string full_label; + // Category of a configuration field, from the GUI perspective. + // One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width" std::string category; + // A tooltip text shown in the GUI. std::string tooltip; + // Text right from the input field, usually a unit of measurement. std::string sidetext; + // Format of this parameter on a command line. std::string cli; + // Set for type == coFloatOrPercent. + // It provides a link to a configuration value, of which this option provides a ratio. + // For example, + // For example external_perimeter_speed may be defined as a fraction of perimeter_speed. t_config_option_key ratio_over; + // True for multiline strings. bool multiline; + // For text input: If true, the GUI text box spans the complete page width. bool full_width; + // Not editable. Currently only used for the display of the number of threads. bool readonly; + // Height of a multiline GUI text box. int height; + // Optional width of an input field. int width; + // limit of a numeric input. + // If not set, the is set to + // By setting min=0, only nonnegative input is allowed. int min; int max; + // Legacy names for this configuration option. + // Used when parsing legacy configuration file. std::vector aliases; + // Sometimes a single value may well define multiple values in a "beginner" mode. + // Currently used for aliasing "solid_layers" to "top_solid_layers", "bottom_solid_layers". std::vector shortcut; + // Definition of values / labels for a combo box. + // Mostly used for enums (when type == coEnum), but may be used for ints resp. floats, if gui_type is set to "i_enum_open" resp. "f_enum_open". std::vector enum_values; std::vector enum_labels; + // For enums (when type == coEnum). Maps enum_values to enums. + // Initialized by ConfigOptionEnum::get_enum_values() t_config_enum_values enum_keys_map; - + ConfigOptionDef() : type(coNone), default_value(NULL), multiline(false), full_width(false), readonly(false), height(-1), width(-1), min(INT_MIN), max(INT_MAX) {}; }; +// Map from a config option name to its definition. +// The definition does not carry an actual value of the config option, only its constant default value. +// t_config_option_key is std::string typedef std::map t_optiondef_map; +// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling. +// The configuration definition is static: It does not carry the actual configuration values, +// but it carries the defaults of the configuration values. class ConfigDef { public: @@ -545,9 +614,14 @@ class ConfigDef const ConfigOptionDef* get(const t_config_option_key &opt_key) const; }; +// An abstract configuration store. class ConfigBase { public: + // Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling. + // The configuration definition is static: It does not carry the actual configuration values, + // but it carries the defaults of the configuration values. + // ConfigBase does not own ConfigDef, it only references it. const ConfigDef* def; ConfigBase() : def(NULL) {}; @@ -562,11 +636,14 @@ class ConfigBase t_config_option_keys diff(ConfigBase &other); std::string serialize(const t_config_option_key &opt_key) const; bool set_deserialize(const t_config_option_key &opt_key, std::string str); + double get_abs_value(const t_config_option_key &opt_key); double get_abs_value(const t_config_option_key &opt_key, double ratio_over); void setenv_(); }; +// Configuration store with dynamic number of configuration values. +// In Slic3r, the dynamic config is mostly used at the user interface layer. class DynamicConfig : public virtual ConfigBase { public: @@ -585,13 +662,20 @@ class DynamicConfig : public virtual ConfigBase t_options_map options; }; +// Configuration store with a static definition of configuration values. +// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons, +// because the configuration values could be accessed directly. class StaticConfig : public virtual ConfigBase { public: StaticConfig() : ConfigBase() {}; + // Gets list of config option names for each config option of this->def, which has a static counter-part defined by the derived object + // and which could be resolved by this->optptr(key) call. t_config_option_keys keys() const; - //virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; + // Set all statically defined config options to their defaults defined by this->def. void set_defaults(); + // The derived class has to implement optptr to resolve a static configuration value. + // virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; }; } diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 1347f4a94..2220772b8 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -514,4 +514,20 @@ ExPolygon::dump_perl() const return ret.str(); } +BoundingBox get_extents(const ExPolygon &expolygon) +{ + return get_extents(expolygon.contour); } + +BoundingBox get_extents(const ExPolygons &expolygons) +{ + BoundingBox bbox; + if (! expolygons.empty()) { + bbox = get_extents(expolygons.front()); + for (size_t i = 1; i < expolygons.size(); ++ i) + bbox.merge(get_extents(expolygons[i])); + } + return bbox; +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 53a3fce1d..4bb8cf8e4 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -72,7 +72,10 @@ inline Polygons to_polygons(ExPolygons &&src) } #endif -} +extern BoundingBox get_extents(const ExPolygon &expolygon); +extern BoundingBox get_extents(const ExPolygons &expolygons); + +} // namespace Slic3r // start Boost #include diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp index 10e036210..eb5cceb0e 100644 --- a/xs/src/libslic3r/ExPolygonCollection.cpp +++ b/xs/src/libslic3r/ExPolygonCollection.cpp @@ -128,4 +128,9 @@ ExPolygonCollection::append(const ExPolygons &expp) this->expolygons.insert(this->expolygons.end(), expp.begin(), expp.end()); } +BoundingBox get_extents(const ExPolygonCollection &expolygon) +{ + return get_extents(expolygon.expolygons); +} + } diff --git a/xs/src/libslic3r/ExPolygonCollection.hpp b/xs/src/libslic3r/ExPolygonCollection.hpp index ec3cb9522..4c181cd6a 100644 --- a/xs/src/libslic3r/ExPolygonCollection.hpp +++ b/xs/src/libslic3r/ExPolygonCollection.hpp @@ -34,6 +34,8 @@ class ExPolygonCollection void append(const ExPolygons &expolygons); }; +extern BoundingBox get_extents(const ExPolygonCollection &expolygon); + } #endif diff --git a/xs/src/libslic3r/ExtrusionSimulator.cpp b/xs/src/libslic3r/ExtrusionSimulator.cpp index 2dbef9a25..83422cdee 100644 --- a/xs/src/libslic3r/ExtrusionSimulator.cpp +++ b/xs/src/libslic3r/ExtrusionSimulator.cpp @@ -1,7 +1,7 @@ // Optimize the extrusion simulator to the bones. -#pragma GCC optimize ("O3") -#undef SLIC3R_DEBUG -#define NDEBUG +//#pragma GCC optimize ("O3") +//#undef SLIC3R_DEBUG +//#define NDEBUG #include #include @@ -640,7 +640,7 @@ struct Cell // Height of the covered part in excess to the expected layer height. float excess_height; - bool operator<(const Cell &c2) { + bool operator<(const Cell &c2) const { return this->excess_height < c2.excess_height; } }; diff --git a/xs/src/libslic3r/ExtrusionSimulator.hpp b/xs/src/libslic3r/ExtrusionSimulator.hpp index f64a3b7d3..8b956ec11 100644 --- a/xs/src/libslic3r/ExtrusionSimulator.hpp +++ b/xs/src/libslic3r/ExtrusionSimulator.hpp @@ -2,7 +2,6 @@ #define slic3r_ExtrusionSimulator_hpp_ #include "libslic3r.h" -#include "Config.hpp" #include "ExtrusionEntity.hpp" #include "BoundingBox.hpp" diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp index 4e741ab08..b521cc696 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp @@ -23,6 +23,10 @@ // We want our version of assert. #include "../libslic3r.h" +#ifndef myassert +#define myassert assert +#endif + namespace Slic3r { #ifndef clamp diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index d6d661b5c..afd3051bd 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -78,6 +78,9 @@ class GCode { Wipe wipe; AvoidCrossingPerimeters avoid_crossing_perimeters; bool enable_loop_clipping; + // If enabled, the G-code generator will put following comments at the ends + // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END + // Those comments are received and consumed (removed from the G-code) by the CoolingBuffer.pm Perl module. bool enable_cooling_markers; // Markers for the Pressure Equalizer to recognize the extrusion type. // The Pressure Equalizer removes the markers from the final G-code. @@ -89,6 +92,10 @@ class GCode { // Distance Field structure to EdgeGrid::Grid *_lower_layer_edge_grid; bool first_layer; // this flag triggers first layer speeds + // Used by the CoolingBuffer.pm Perl module to calculate time spent per layer change. + // This value is not quite precise. First it only accouts for extrusion moves and travel moves, + // it does not account for wipe, retract / unretract moves. + // second it does not account for the velocity profiles of the printer. float elapsed_time; // seconds double volumetric_speed; // Support for the extrusion role markers. Which marker is active? diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index dc931923c..a392f732e 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -162,6 +162,10 @@ Layer::any_bottom_region_slice_contains(const T &item) const } template bool Layer::any_bottom_region_slice_contains(const Polyline &item) const; + +// Here the perimeters are created cummulatively for all layer regions sharing the same parameters influencing the perimeters. +// The perimeter paths and the thin fills (ExtrusionEntityCollection) are assigned to the first compatible layer region. +// The resulting fill surface is split back among the originating regions. void Layer::make_perimeters() { diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 50f6ebbea..d713b7c1f 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -77,6 +77,7 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* ); if (this->layer()->lower_layer != NULL) + // Cummulative sum of polygons over all the regions. g.lower_slices = &this->layer()->lower_layer->slices; g.layer_id = this->layer()->id(); diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 9eaf5e2f7..adc464bf7 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -772,7 +772,7 @@ ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const void ModelInstance::transform_polygon(Polygon* polygon) const { - polygon->rotate(this->rotation, Point(0,0)); // rotate around polygon origin + polygon->rotate(this->rotation); // rotate around polygon origin polygon->scale(this->scaling_factor); // scale around polygon origin } diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index f10707457..cb0ab05f2 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -27,10 +27,18 @@ typedef std::vector ModelObjectPtrs; typedef std::vector ModelVolumePtrs; typedef std::vector ModelInstancePtrs; +// The print bed content. +// Description of a triangular model with multiple materials, multiple instances with various affine transformations +// and with multiple modifier meshes. +// A model groups multiple objects, each object having possibly multiple instances, +// all objects may share mutliple materials. class Model { - public: +public: + // Materials are owned by a model and referenced by objects through t_model_material_id. + // Single material may be shared by multiple models. ModelMaterialMap materials; + // Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation). ModelObjectPtrs objects; Model(); @@ -63,34 +71,48 @@ class Model void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); }; +// Material, which may be shared across multiple ModelObjects of a single Model. class ModelMaterial { friend class Model; - public: +public: + // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose. t_model_material_attributes attributes; + // Dynamic configuration storage for the object specific configuration values, overriding the global configuration. DynamicPrintConfig config; Model* get_model() const { return this->model; }; void apply(const t_model_material_attributes &attributes); - private: +private: + // Parent, owning this material. Model* model; ModelMaterial(Model *model); ModelMaterial(Model *model, const ModelMaterial &other); }; +// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), +// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. +// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, +// different rotation and different uniform scaling. class ModelObject { friend class Model; - public: +public: std::string name; std::string input_file; + // Instances of this ModelObject. Each instance defines a shift on the print bed, rotation around the Z axis and a uniform scaling. + // Instances are owned by this ModelObject. ModelInstancePtrs instances; + // Printable and modifier volumes, each with its material ID and a set of override parameters. + // ModelVolumes are owned by this ModelObject. ModelVolumePtrs volumes; + // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. DynamicPrintConfig config; + // Variation of a layer thickness for spans of Z coordinates. t_layer_height_ranges layer_height_ranges; - + /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation to new volumes before adding them to this object in order to preserve alignment @@ -134,7 +156,8 @@ class ModelObject void split(ModelObjectPtrs* new_objects); void update_bounding_box(); // this is a private method but we expose it until we need to expose it via XS - private: +private: + // Parent object, owning this ModelObject. Model* model; ModelObject(Model *model); @@ -144,15 +167,22 @@ class ModelObject ~ModelObject(); }; +// An object STL, or a modifier volume, over which a different set of parameters shall be applied. +// ModelVolume instances are owned by a ModelObject. class ModelVolume { friend class ModelObject; - public: +public: std::string name; + // The triangular model. TriangleMesh mesh; + // Configuration parameters specific to an object model geometry or a modifier volume, + // overriding the global Slic3r settings and the ModelObject settings. DynamicPrintConfig config; + // Is it an object to be printed, or a modifier volume? bool modifier; + // A parent object owning this modifier volume. ModelObject* get_object() const { return this->object; }; t_model_material_id material_id() const; void material_id(t_model_material_id material_id); @@ -161,7 +191,8 @@ class ModelVolume ModelMaterial* assign_unique_material(); - private: +private: + // Parent object owning this ModelVolume. ModelObject* object; t_model_material_id _material_id; @@ -169,19 +200,25 @@ class ModelVolume ModelVolume(ModelObject *object, const ModelVolume &other); }; +// A single instance of a ModelObject. +// Knows the affine transformation of an object. class ModelInstance { friend class ModelObject; - public: - double rotation; // in radians around mesh center point +public: + double rotation; // Rotation around the Z axis, in radians around mesh center point double scaling_factor; Pointf offset; // in unscaled coordinates ModelObject* get_object() const { return this->object; }; + + // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; + // To be called on an external polygon. It does not translate the polygon, only rotates and scales. void transform_polygon(Polygon* polygon) const; - private: +private: + // Parent object, owning this instance. ModelObject* object; ModelInstance(ModelObject *object); diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 5be53e5da..e253f9bba 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -33,8 +33,8 @@ MultiPoint::translate(const Point &vector) void MultiPoint::rotate(double angle) { - double s = sin(angle); - double c = cos(angle); + double s = sin(angle); + double c = cos(angle); for (Points::iterator it = points.begin(); it != points.end(); ++it) { double cur_x = (double)it->x; double cur_y = (double)it->y; @@ -46,8 +46,13 @@ MultiPoint::rotate(double angle) void MultiPoint::rotate(double angle, const Point ¢er) { + double s = sin(angle); + double c = cos(angle); for (Points::iterator it = points.begin(); it != points.end(); ++it) { - (*it).rotate(angle, center); + double dx = double(it->x - center.x); + double dy = double(it->y - center.y); + it->x = (coord_t)round(double(center.x) + c * dx - s * dy); + it->y = (coord_t)round(double(center.y) + c * dy + s * dx); } } @@ -202,4 +207,9 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance) return results; } +BoundingBox get_extents(const MultiPoint &mp) +{ + return mp.bounding_box(); +} + } diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 7cf41a870..66b36d720 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -47,6 +47,8 @@ class MultiPoint static Points _douglas_peucker(const Points &points, const double tolerance); }; -} +extern BoundingBox get_extents(const MultiPoint &mp); + +} // namespace Slic3r #endif diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index e9153923f..f1108fcbd 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -533,12 +533,6 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo return coll; } -bool -PerimeterGeneratorLoop::is_external() const -{ - return this->depth == 0; -} - bool PerimeterGeneratorLoop::is_internal_contour() const { diff --git a/xs/src/libslic3r/PerimeterGenerator.hpp b/xs/src/libslic3r/PerimeterGenerator.hpp index eb3fa0fcb..0e7fbd3e4 100644 --- a/xs/src/libslic3r/PerimeterGenerator.hpp +++ b/xs/src/libslic3r/PerimeterGenerator.hpp @@ -11,25 +11,33 @@ namespace Slic3r { -class PerimeterGeneratorLoop; -typedef std::vector PerimeterGeneratorLoops; - +// Hierarchy of perimeters. class PerimeterGeneratorLoop { - public: +public: + // Polygon of this contour. Polygon polygon; + // Is it a contour or a hole? + // Contours are CCW oriented, holes are CW oriented. bool is_contour; + // Depth in the hierarchy. External perimeter has depth = 0. An external perimeter could be both a contour and a hole. unsigned short depth; + // Children contour, may be both CCW and CW oriented (outer contours or holes). std::vector children; PerimeterGeneratorLoop(Polygon polygon, unsigned short depth) : polygon(polygon), is_contour(false), depth(depth) {}; - bool is_external() const; + // External perimeter. It may be CCW or CW oriented (outer contour or hole contour). + bool is_external() const { return this->depth == 0; } + // An island, which may have holes, but it does not have another internal island. bool is_internal_contour() const; }; +typedef std::vector PerimeterGeneratorLoops; + class PerimeterGenerator { - public: +public: + // Inputs: const SurfaceCollection* slices; const ExPolygonCollection* lower_slices; double layer_height; @@ -41,14 +49,26 @@ class PerimeterGenerator { PrintRegionConfig* config; PrintObjectConfig* object_config; PrintConfig* print_config; + // Outputs: ExtrusionEntityCollection* loops; ExtrusionEntityCollection* gap_fill; SurfaceCollection* fill_surfaces; - PerimeterGenerator(const SurfaceCollection* slices, double layer_height, Flow flow, - PrintRegionConfig* config, PrintObjectConfig* object_config, - PrintConfig* print_config, ExtrusionEntityCollection* loops, - ExtrusionEntityCollection* gap_fill, SurfaceCollection* fill_surfaces) + PerimeterGenerator( + // Input: + const SurfaceCollection* slices, + double layer_height, + Flow flow, + PrintRegionConfig* config, + PrintObjectConfig* object_config, + PrintConfig* print_config, + // Output: + // Loops with the external thin walls + ExtrusionEntityCollection* loops, + // Gaps without the thin walls + ExtrusionEntityCollection* gap_fill, + // Infills without the gap fills + SurfaceCollection* fill_surfaces) : slices(slices), lower_slices(NULL), layer_height(layer_height), layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), overhang_flow(flow), solid_infill_flow(flow), diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp index 2a1668950..6dbcc6f00 100644 --- a/xs/src/libslic3r/Point.cpp +++ b/xs/src/libslic3r/Point.cpp @@ -94,6 +94,12 @@ Point::nearest_point_index(const Points &points) const return this->nearest_point_index(p); } +template +inline T sqr(const T x) +{ + return x * x; +} + int Point::nearest_point_index(const PointConstPtrs &points) const { @@ -103,12 +109,12 @@ Point::nearest_point_index(const PointConstPtrs &points) const for (PointConstPtrs::const_iterator it = points.begin(); it != points.end(); ++it) { /* If the X distance of the candidate is > than the total distance of the best previous candidate, we know we don't want it */ - double d = pow(this->x - (*it)->x, 2); + double d = sqr(this->x - (*it)->x); if (distance != -1 && d > distance) continue; /* If the Y distance of the candidate is > than the total distance of the best previous candidate, we know we don't want it */ - d += pow(this->y - (*it)->y, 2); + d += sqr(this->y - (*it)->y); if (distance != -1 && d > distance) continue; idx = it - points.begin(); @@ -129,10 +135,10 @@ Point::nearest_waypoint_index(const Points &points, const Point &dest) const for (Points::const_iterator p = points.begin(); p != points.end(); ++p) { // distance from this to candidate - double d = pow(this->x - p->x, 2) + pow(this->y - p->y, 2); + double d = sqr(this->x - p->x) + sqr(this->y - p->y); // distance from candidate to dest - d += pow(p->x - dest.x, 2) + pow(p->y - dest.y, 2); + d += sqr(p->x - dest.x) + sqr(p->y - dest.y); // if the total distance is greater than current min distance, ignore it if (distance != -1 && d > distance) continue; @@ -278,8 +284,10 @@ Point::projection_onto(const Line &line) const If theta is outside the interval [0,1], then one of the Line_Segment's endpoints must be closest to calling Point. */ - double theta = ( (double)(line.b.x - this->x)*(double)(line.b.x - line.a.x) + (double)(line.b.y- this->y)*(double)(line.b.y - line.a.y) ) - / ( (double)pow(line.b.x - line.a.x, 2) + (double)pow(line.b.y - line.a.y, 2) ); + double lx = (double)(line.b.x - line.a.x); + double ly = (double)(line.b.y - line.a.y); + double theta = ( (double)(line.b.x - this->x)*lx + (double)(line.b.y- this->y)*ly ) + / ( sqr(lx) + sqr(ly) ); if (0.0 <= theta && theta <= 1.0) return theta * line.a + (1.0-theta) * line.b; diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 9c23252cb..58747690a 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -44,6 +44,8 @@ class Point void translate(const Vector &vector); void rotate(double angle); void rotate(double angle, const Point ¢er); + Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; } + Point rotated(double angle, const Point ¢er) const { Point res(*this); res.rotate(angle, center); return res; } bool coincides_with(const Point &point) const { return this->x == point.x && this->y == point.y; } bool coincides_with_epsilon(const Point &point) const; int nearest_point_index(const Points &points) const; diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp index 102838809..ac5185a0b 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/xs/src/libslic3r/Polygon.cpp @@ -1,3 +1,4 @@ +#include "BoundingBox.hpp" #include "ClipperUtils.hpp" #include "Polygon.hpp" #include "Polyline.hpp" @@ -59,6 +60,7 @@ Polygon::split_at_vertex(const Point &point) const return Polyline(); } +// Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline Polygon::split_at_index(int index) const { @@ -71,6 +73,7 @@ Polygon::split_at_index(int index) const return polyline; } +// Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline Polygon::split_at_first_point() const { @@ -131,6 +134,8 @@ Polygon::is_valid() const return this->points.size() >= 3; } +// Does an unoriented polygon contain a point? +// Tested by counting intersections along a horizontal line. bool Polygon::contains(const Point &point) const { @@ -139,9 +144,20 @@ Polygon::contains(const Point &point) const Points::const_iterator i = this->points.begin(); Points::const_iterator j = this->points.end() - 1; for (; i != this->points.end(); j = i++) { + //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point.y well. + // Does the ray with y == point.y intersect this line segment? +#if 1 if ( ((i->y > point.y) != (j->y > point.y)) && ((double)point.x < (double)(j->x - i->x) * (double)(point.y - i->y) / (double)(j->y - i->y) + (double)i->x) ) result = !result; +#else + if ((i->y > point.y) != (j->y > point.y)) { + // Orientation predicated relative to i-th point. + double orient = (double)(point.x - i->x) * (double)(j->y - i->y) - (double)(point.y - i->y) * (double)(j->x - i->x); + if ((i->y > j->y) ? (orient > 0.) : (orient < 0.)) + result = !result; + } +#endif } return result; } @@ -265,4 +281,20 @@ Polygon::convex_points(double angle) const return points; } +BoundingBox get_extents(const Polygon &poly) +{ + return poly.bounding_box(); +} + +BoundingBox get_extents(const Polygons &polygons) +{ + BoundingBox bb; + if (! polygons.empty()) { + bb = polygons.front().bounding_box(); + for (size_t i = 1; i < polygons.size(); ++ i) + bb.merge(polygons[i]); + } + return bb; +} + } diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index ccde4a740..9f0bc6028 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -25,7 +25,9 @@ class Polygon : public MultiPoint { Point last_point() const; virtual Lines lines() const; Polyline split_at_vertex(const Point &point) const; + // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_index(int index) const; + // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_first_point() const; Points equally_spaced_points(double distance) const; double area() const; @@ -34,6 +36,8 @@ class Polygon : public MultiPoint { bool make_counter_clockwise(); bool make_clockwise(); bool is_valid() const; + // Does an unoriented polygon contain a point? + // Tested by counting intersections along a horizontal line. bool contains(const Point &point) const; Polygons simplify(double tolerance) const; void simplify(double tolerance, Polygons &polygons) const; @@ -44,6 +48,9 @@ class Polygon : public MultiPoint { Points convex_points(double angle = PI) const; }; +extern BoundingBox get_extents(const Polygon &poly); +extern BoundingBox get_extents(const Polygons &polygons); + } // start Boost diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 8d114f6e2..185c59aad 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -639,7 +639,7 @@ Print::validate() const 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."); } - } + } // end if (this->config.complete_objects) if (this->config.spiral_vase) { size_t total_copies_count = 0; @@ -837,6 +837,7 @@ Print::auto_assign_extruders(ModelObject* model_object) const size_t extruders = this->config.nozzle_diameter.values.size(); for (ModelVolumePtrs::const_iterator v = model_object->volumes.begin(); v != model_object->volumes.end(); ++v) { if (!(*v)->material_id().empty()) { + //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. size_t extruder_id = (v - model_object->volumes.begin()) + 1; if (!(*v)->config.has("extruder")) (*v)->config.opt("extruder", true)->value = extruder_id; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index fcf6d0a79..a2a5ba7b4 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -20,7 +20,7 @@ class Print; class PrintObject; class ModelObject; - +// Print step IDs for keeping track of the print state. enum PrintStep { psSkirt, psBrim, }; @@ -34,6 +34,7 @@ class PrintValidationException : public std::runtime_error { PrintValidationException(const std::string &error) : std::runtime_error(error) {}; }; +// To be instantiated over PrintStep or PrintObjectStep enums. template class PrintState { @@ -120,6 +121,7 @@ class PrintObject size_t layer_count() const; void clear_layers(); Layer* get_layer(int idx); + // print_z: top of the layer; slice_z: center of the layer. Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); void delete_layer(int idx); @@ -152,6 +154,7 @@ class PrintObject typedef std::vector PrintObjectPtrs; typedef std::vector PrintRegionPtrs; +// The complete print tray with possibly multiple objects. class Print { public: diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 47609aaff..e3bc4bad9 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -885,11 +885,37 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("random"); def->enum_values.push_back("nearest"); def->enum_values.push_back("aligned"); +// def->enum_values.push_back("preferred"); def->enum_labels.push_back("Random"); def->enum_labels.push_back("Nearest"); def->enum_labels.push_back("Aligned"); +// def->enum_labels.push_back("Preferred Direction"); def->default_value = new ConfigOptionEnum(spAligned); +#if 0 + def = this->add("seam_preferred_direction", coFloat); +// def->gui_type = "slider"; + def->label = "Direction"; + def->sidetext = "°"; + def->full_label = "Preferred direction of the seam"; + def->tooltip = "Seam preferred direction"; + def->cli = "seam-preferred-direction=f"; + def->min = 0; + def->max = 360; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("seam_preferred_direction_jitter", coFloat); +// def->gui_type = "slider"; + def->label = "Jitter"; + def->sidetext = "°"; + def->full_label = "Seam preferred direction jitter"; + def->tooltip = "Preferred direction of the seam - jitter"; + def->cli = "seam-preferred-direction-jitter=f"; + def->min = 0; + def->max = 360; + def->default_value = new ConfigOptionFloat(30); +#endif + def = this->add("serial_port", coString); def->gui_type = "select_open"; def->label = ""; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 3f6eb8e06..d8c57cb57 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -1,3 +1,20 @@ +// Configuration store of Slic3r. +// +// The configuration store is either static or dynamic. +// DynamicPrintConfig is used mainly at the user interface. while the StaticPrintConfig is used +// during the slicing and the g-code generation. +// +// The classes derived from StaticPrintConfig form a following hierarchy. +// Virtual inheritance is used for some of the parent objects. +// +// FullPrintConfig +// PrintObjectConfig +// PrintRegionConfig +// PrintConfig +// GCodeConfig +// HostConfig +// + #ifndef slic3r_PrintConfig_hpp_ #define slic3r_PrintConfig_hpp_ @@ -22,7 +39,7 @@ enum SupportMaterialPattern { }; enum SeamPosition { - spRandom, spNearest, spAligned + spRandom, spNearest, spAligned //, spPreferred }; template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { @@ -65,17 +82,23 @@ template<> inline t_config_enum_values ConfigOptionEnum::get_enum_ keys_map["random"] = spRandom; keys_map["nearest"] = spNearest; keys_map["aligned"] = spAligned; +// keys_map["preferred"] = spPreferred; return keys_map; } +// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs. +// Does not store the actual values, but defines default values. class PrintConfigDef : public ConfigDef { public: PrintConfigDef(); }; +// The one and only global definition of SLic3r configuration options. +// This definition is constant. extern PrintConfigDef print_config_def; +// Slic3r configuration storage with print_config_def assigned. class PrintConfigBase : public virtual ConfigBase { public: @@ -86,6 +109,12 @@ class PrintConfigBase : public virtual ConfigBase double min_object_distance() const; }; +// Slic3r dynamic configuration, used to override the configuration +// per object, per modification volume or per printing material. +// The dynamic configuration is also used to store user modifications of the print global parameters, +// so the modified configuration values may be diffed against the active configuration +// to invalidate the proper slicing resp. g-code generation processing steps. +// This object is mapped to Perl as Slic3r::Config. class DynamicPrintConfig : public PrintConfigBase, public DynamicConfig { public: @@ -93,12 +122,14 @@ class DynamicPrintConfig : public PrintConfigBase, public DynamicConfig void normalize(); }; + class StaticPrintConfig : public PrintConfigBase, public StaticConfig { public: StaticPrintConfig() : PrintConfigBase(), StaticConfig() {}; }; +// This object is mapped to Perl as Slic3r::Config::PrintObject. class PrintObjectConfig : public virtual StaticPrintConfig { public: @@ -110,6 +141,8 @@ class PrintObjectConfig : public virtual StaticPrintConfig ConfigOptionFloat layer_height; ConfigOptionInt raft_layers; ConfigOptionEnum seam_position; +// ConfigOptionFloat seam_preferred_direction; +// ConfigOptionFloat seam_preferred_direction_jitter; ConfigOptionBool support_material; ConfigOptionInt support_material_angle; ConfigOptionFloat support_material_contact_distance; @@ -130,7 +163,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(dont_support_bridges); OPT_PTR(extrusion_width); @@ -140,6 +173,8 @@ class PrintObjectConfig : public virtual StaticPrintConfig OPT_PTR(layer_height); OPT_PTR(raft_layers); OPT_PTR(seam_position); +// OPT_PTR(seam_preferred_direction); +// OPT_PTR(seam_preferred_direction_jitter); OPT_PTR(support_material); OPT_PTR(support_material_angle); OPT_PTR(support_material_contact_distance); @@ -160,6 +195,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig }; }; +// This object is mapped to Perl as Slic3r::Config::PrintRegion. class PrintRegionConfig : public virtual StaticPrintConfig { public: @@ -200,7 +236,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(bottom_solid_layers); OPT_PTR(bridge_flow_ratio); @@ -239,6 +275,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig }; }; +// This object is mapped to Perl as Slic3r::Config::GCode. class GCodeConfig : public virtual StaticPrintConfig { public: @@ -315,6 +352,7 @@ class GCodeConfig : public virtual StaticPrintConfig }; }; +// This object is mapped to Perl as Slic3r::Config::Print. class PrintConfig : public GCodeConfig { public: @@ -373,7 +411,7 @@ class PrintConfig : public GCodeConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(avoid_crossing_perimeters); OPT_PTR(bed_shape); @@ -446,7 +484,7 @@ class HostConfig : public virtual StaticPrintConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(octoprint_host); OPT_PTR(octoprint_apikey); @@ -457,6 +495,7 @@ class HostConfig : public virtual StaticPrintConfig }; }; +// This object is mapped to Perl as Slic3r::Config::Full. class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig, public HostConfig { diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 23d9e74e1..016403059 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -270,6 +270,8 @@ PrintObject::invalidate_state_by_config_options(const std::vector -#define COORD(x) ((float)unscale(x)*10) +#define COORD(x) ((float)unscale((x))*10) namespace Slic3r { SVG::SVG(const char* filename) - : arrows(false), fill("grey"), stroke("black"), filename(filename) + : arrows(false), fill("grey"), stroke("black"), filename(filename), flipY(false) { this->f = fopen(filename, "w"); fprintf(this->f, @@ -19,12 +19,12 @@ SVG::SVG(const char* filename) ); } -SVG::SVG(const char* filename, const BoundingBox &bbox) - : arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min) +SVG::SVG(const char* filename, const BoundingBox &bbox, const coord_t bbox_offset, bool aflipY) + : arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min - Point(bbox_offset, bbox_offset)), flipY(aflipY) { this->f = fopen(filename, "w"); - float w = COORD(bbox.max.x - bbox.min.x); - float h = COORD(bbox.max.y - bbox.min.y); + float w = COORD(bbox.max.x - bbox.min.x + 2 * bbox_offset); + float h = COORD(bbox.max.y - bbox.min.y + 2 * bbox_offset); fprintf(this->f, "\n" "\n" @@ -36,7 +36,7 @@ SVG::SVG(const char* filename, const BoundingBox &bbox) } void -SVG::draw(const Line &line, std::string stroke, coord_t stroke_width) +SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width) { fprintf(this->f, " f, "/>\n"); } -void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width) +void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coordf_t stroke_width) { Pointf dir(line.b.x-line.a.x, line.b.y-line.a.y); Pointf perp(-dir.y, dir.x); @@ -68,10 +68,10 @@ void SVG::draw(const ThickLine &line, const std::string &fill, const std::string } void -SVG::draw(const Lines &lines, std::string stroke) +SVG::draw(const Lines &lines, std::string stroke, coordf_t stroke_width) { for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) - this->draw(*it, stroke); + this->draw(*it, stroke, stroke_width); } void @@ -91,7 +91,7 @@ SVG::draw(const ExPolygon &expolygon, std::string fill) for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { d += this->get_path_d(*p, true) + " "; } - this->path(d, true); + this->path(d, true, 0); } void @@ -105,7 +105,7 @@ void SVG::draw(const Polygon &polygon, std::string fill) { this->fill = fill; - this->path(this->get_path_d(polygon, true), !fill.empty()); + this->path(this->get_path_d(polygon, true), !fill.empty(), 0); } void @@ -116,34 +116,34 @@ SVG::draw(const Polygons &polygons, std::string fill) } void -SVG::draw(const Polyline &polyline, std::string stroke, coord_t stroke_width) +SVG::draw(const Polyline &polyline, std::string stroke, coordf_t stroke_width) { this->stroke = stroke; this->path(this->get_path_d(polyline, false), false, stroke_width); } void -SVG::draw(const Polylines &polylines, std::string stroke, coord_t stroke_width) +SVG::draw(const Polylines &polylines, std::string stroke, coordf_t stroke_width) { for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) this->draw(*it, fill, stroke_width); } -void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std::string &stroke, coord_t stroke_width) +void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std::string &stroke, coordf_t stroke_width) { for (ThickLines::const_iterator it = thicklines.begin(); it != thicklines.end(); ++it) this->draw(*it, fill, stroke, stroke_width); } void -SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coord_t stroke_width) +SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coordf_t stroke_width) { for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) this->draw((Polyline)*it, stroke, stroke_width); } void -SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coord_t stroke_width) +SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coordf_t stroke_width) { for (ThickPolylines::const_iterator it = thickpolylines.begin(); it != thickpolylines.end(); ++ it) draw(it->thicklines(), fill, stroke, stroke_width); @@ -168,8 +168,36 @@ SVG::draw(const Points &points, std::string fill, coord_t radius) this->draw(*it, fill, radius); } +void +SVG::draw(const ClipperLib::Path &polygon, double scale, std::string stroke, coordf_t stroke_width) +{ + this->stroke = stroke; + this->path(this->get_path_d(polygon, scale, true), false, stroke_width); +} + +void +SVG::draw(const ClipperLib::Paths &polygons, double scale, std::string stroke, coordf_t stroke_width) +{ + for (ClipperLib::Paths::const_iterator it = polygons.begin(); it != polygons.end(); ++ it) + draw(*it, scale, stroke, stroke_width); +} + +void +SVG::draw_outline(const Polygon &polygon, std::string stroke, coordf_t stroke_width) +{ + this->stroke = stroke; + this->path(this->get_path_d(polygon, true), false, stroke_width); +} + +void +SVG::draw_outline(const Polygons &polygons, std::string stroke, coordf_t stroke_width) +{ + for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it) + draw_outline(*it, stroke, stroke_width); +} + void -SVG::path(const std::string &d, bool fill, coord_t stroke_width) +SVG::path(const std::string &d, bool fill, coordf_t stroke_width) { float lineWidth = 0.f; if (! fill) @@ -199,6 +227,19 @@ SVG::get_path_d(const MultiPoint &mp, bool closed) const return d.str(); } +std::string +SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const +{ + std::ostringstream d; + d << "M "; + for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) { + d << COORD(scale * p->X - origin.x) << " "; + d << COORD(scale * p->Y - origin.y) << " "; + } + if (closed) d << "z"; + return d.str(); +} + void SVG::Close() { diff --git a/xs/src/libslic3r/SVG.hpp b/xs/src/libslic3r/SVG.hpp index aa08c0e20..5388b1b03 100644 --- a/xs/src/libslic3r/SVG.hpp +++ b/xs/src/libslic3r/SVG.hpp @@ -2,6 +2,7 @@ #define slic3r_SVG_hpp_ #include "libslic3r.h" +#include "clipper.hpp" #include "ExPolygon.hpp" #include "Line.hpp" #include "TriangleMesh.hpp" @@ -14,34 +15,43 @@ class SVG bool arrows; std::string fill, stroke; Point origin; + bool flipY; SVG(const char* filename); - SVG(const char* filename, const BoundingBox &bbox); + SVG(const char* filename, const BoundingBox &bbox, const coord_t bbox_offset = scale_(1.), bool flipY = false); ~SVG() { if (f != NULL) Close(); } - void draw(const Line &line, std::string stroke = "black", coord_t stroke_width = 0); - void draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width = 0); - void draw(const Lines &lines, std::string stroke = "black"); + void draw(const Line &line, std::string stroke = "black", coordf_t stroke_width = 0); + void draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coordf_t stroke_width = 0); + void draw(const Lines &lines, std::string stroke = "black", coordf_t stroke_width = 0); void draw(const IntersectionLines &lines, std::string stroke = "black"); void draw(const ExPolygon &expolygon, std::string fill = "grey"); void draw(const ExPolygons &expolygons, std::string fill = "grey"); void draw(const Polygon &polygon, std::string fill = "grey"); + void draw_outline(const Polygon &polygon, std::string stroke = "black", coordf_t stroke_width = 0); void draw(const Polygons &polygons, std::string fill = "grey"); - void draw(const Polyline &polyline, std::string stroke = "black", coord_t stroke_width = 0); - void draw(const Polylines &polylines, std::string stroke = "black", coord_t stroke_width = 0); - void draw(const ThickLines &thicklines, const std::string &fill = "lime", const std::string &stroke = "black", coord_t stroke_width = 0); - void draw(const ThickPolylines &polylines, const std::string &stroke = "black", coord_t stroke_width = 0); - void draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coord_t stroke_width); + void draw_outline(const Polygons &polygons, std::string stroke = "black", coordf_t stroke_width = 0); + void draw(const Polyline &polyline, std::string stroke = "black", coordf_t stroke_width = 0); + void draw(const Polylines &polylines, std::string stroke = "black", coordf_t stroke_width = 0); + void draw(const ThickLines &thicklines, const std::string &fill = "lime", const std::string &stroke = "black", coordf_t stroke_width = 0); + void draw(const ThickPolylines &polylines, const std::string &stroke = "black", coordf_t stroke_width = 0); + void draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coordf_t stroke_width); void draw(const Point &point, std::string fill = "black", coord_t radius = 0); void draw(const Points &points, std::string fill = "black", coord_t radius = 0); + + // Support for rendering the ClipperLib paths + void draw(const ClipperLib::Path &polygon, double scale, std::string fill = "grey", coordf_t stroke_width = 0); + void draw(const ClipperLib::Paths &polygons, double scale, std::string fill = "grey", coordf_t stroke_width = 0); + void Close(); private: std::string filename; FILE* f; - void path(const std::string &d, bool fill, coord_t stroke_width = 0); + void path(const std::string &d, bool fill, coordf_t stroke_width); std::string get_path_d(const MultiPoint &mp, bool closed = false) const; + std::string get_path_d(const ClipperLib::Path &mp, double scale, bool closed = false) const; }; } diff --git a/xs/src/libslic3r/Surface.cpp b/xs/src/libslic3r/Surface.cpp index 4d2234e4d..5df2dd99a 100644 --- a/xs/src/libslic3r/Surface.cpp +++ b/xs/src/libslic3r/Surface.cpp @@ -1,3 +1,4 @@ +#include "BoundingBox.hpp" #include "Surface.hpp" namespace Slic3r { @@ -54,4 +55,31 @@ Surface::is_bridge() const || this->surface_type == stInternalBridge; } +BoundingBox get_extents(const Surface &surface) +{ + return get_extents(surface.expolygon.contour); +} + +BoundingBox get_extents(const Surfaces &surfaces) +{ + BoundingBox bbox; + if (! surfaces.empty()) { + bbox = get_extents(surfaces.front()); + for (size_t i = 1; i < surfaces.size(); ++ i) + bbox.merge(get_extents(surfaces[i])); + } + return bbox; +} + +BoundingBox get_extents(const SurfacesPtr &surfaces) +{ + BoundingBox bbox; + if (! surfaces.empty()) { + bbox = get_extents(*surfaces.front()); + for (size_t i = 1; i < surfaces.size(); ++ i) + bbox.merge(get_extents(*surfaces[i])); + } + return bbox; +} + } diff --git a/xs/src/libslic3r/Surface.hpp b/xs/src/libslic3r/Surface.hpp index 21395bdc6..00286c207 100644 --- a/xs/src/libslic3r/Surface.hpp +++ b/xs/src/libslic3r/Surface.hpp @@ -34,6 +34,10 @@ class Surface typedef std::vector Surfaces; typedef std::vector SurfacesPtr; +extern BoundingBox get_extents(const Surface &surface); +extern BoundingBox get_extents(const Surfaces &surfaces); +extern BoundingBox get_extents(const SurfacesPtr &surfaces); + } #endif diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index a087455cf..680716bb8 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -8,12 +8,22 @@ #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 diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp index db424ff29..948042eaf 100644 --- a/xs/xsp/BoundingBox.xsp +++ b/xs/xsp/BoundingBox.xsp @@ -19,13 +19,15 @@ Clone polygon(); Clone size(); Clone center(); + double radius(); Clone min_point() %code{% RETVAL = THIS->min; %}; Clone max_point() %code{% RETVAL = THIS->max; %}; long x_min() %code{% RETVAL = THIS->min.x; %}; long x_max() %code{% RETVAL = THIS->max.x; %}; long y_min() %code{% RETVAL = THIS->min.y; %}; long y_max() %code{% RETVAL = THIS->max.y; %}; - + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%d,%d;%d,%d", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; + %{ BoundingBox* @@ -51,6 +53,7 @@ new_from_points(CLASS, points) void translate(double x, double y); Clone size(); Clone center(); + double radius(); Clone min_point() %code{% RETVAL = THIS->min; %}; Clone max_point() %code{% RETVAL = THIS->max; %}; double x_min() %code{% RETVAL = THIS->min.x; %}; @@ -61,7 +64,8 @@ new_from_points(CLASS, points) void set_x_max(double val) %code{% THIS->max.x = val; %}; void set_y_min(double val) %code{% THIS->min.y = val; %}; void set_y_max(double val) %code{% THIS->max.y = val; %}; - + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; + %{ BoundingBoxf* @@ -87,6 +91,7 @@ new_from_points(CLASS, points) void translate(double x, double y, double z); Clone size(); Clone center(); + double radius(); Clone min_point() %code{% RETVAL = THIS->min; %}; Clone max_point() %code{% RETVAL = THIS->max; %}; double x_min() %code{% RETVAL = THIS->min.x; %}; @@ -95,4 +100,5 @@ new_from_points(CLASS, points) double y_max() %code{% RETVAL = THIS->max.y; %}; double z_min() %code{% RETVAL = THIS->min.z; %}; double z_max() %code{% RETVAL = THIS->max.z; %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min.x, THIS->min.y, THIS->min.z, THIS->max.x, THIS->max.y, THIS->max.z); RETVAL = buf; %}; }; diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index 60a4864e3..e1467143c 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -49,6 +49,7 @@ %code{% RETVAL = new Point(THIS->negative()); %}; bool coincides_with_epsilon(Point* point) %code{% RETVAL = THIS->coincides_with_epsilon(*point); %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%d,%d", THIS->x, THIS->y); RETVAL = buf; %}; %{ @@ -86,6 +87,7 @@ Point::coincides_with(point_sv) %code{% RETVAL = THIS->y; %}; long z() %code{% RETVAL = THIS->z; %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%d,%d,%d", THIS->x, THIS->y, THIS->z); RETVAL = buf; %}; }; %name{Slic3r::Pointf} class Pointf { @@ -113,6 +115,7 @@ Point::coincides_with(point_sv) %code{% RETVAL = THIS->negative(); %}; Clone vector_to(Pointf* point) %code{% RETVAL = THIS->vector_to(*point); %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf", THIS->x, THIS->y); RETVAL = buf; %}; }; %name{Slic3r::Pointf3} class Pointf3 { @@ -140,4 +143,5 @@ Point::coincides_with(point_sv) %code{% RETVAL = THIS->negative(); %}; Clone vector_to(Pointf3* point) %code{% RETVAL = THIS->vector_to(*point); %}; + std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf", THIS->x, THIS->y, THIS->z); RETVAL = buf; %}; };