mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 16:21:24 -06:00
Merge branch 'master' into sender
Conflicts: Build.PL lib/Slic3r.pm xs/MANIFEST xs/src/libslic3r/PrintConfig.hpp
This commit is contained in:
commit
9b21ac877a
93 changed files with 3339 additions and 2050 deletions
|
@ -185,16 +185,12 @@ BridgeDetector::detect_angle()
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BridgeDetector::coverage(Polygons* coverage) const
|
||||
{
|
||||
if (this->angle == -1) return;
|
||||
return this->coverage(angle, coverage);
|
||||
}
|
||||
|
||||
void
|
||||
BridgeDetector::coverage(double angle, Polygons* coverage) const
|
||||
{
|
||||
if (angle == -1) angle = this->angle;
|
||||
if (angle == -1) return;
|
||||
|
||||
// Clone our expolygon and rotate it so that we work with vertical lines.
|
||||
ExPolygon expolygon = this->expolygon;
|
||||
expolygon.rotate(PI/2.0 - angle, Point(0,0));
|
||||
|
@ -263,19 +259,23 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const
|
|||
*/
|
||||
}
|
||||
|
||||
Polygons
|
||||
BridgeDetector::coverage(double angle) const
|
||||
{
|
||||
Polygons pp;
|
||||
this->coverage(angle, &pp);
|
||||
return pp;
|
||||
}
|
||||
|
||||
/* This method returns the bridge edges (as polylines) that are not supported
|
||||
but would allow the entire bridge area to be bridged with detected angle
|
||||
if supported too */
|
||||
void
|
||||
BridgeDetector::unsupported_edges(Polylines* unsupported) const
|
||||
{
|
||||
if (this->angle == -1) return;
|
||||
return this->unsupported_edges(this->angle, unsupported);
|
||||
}
|
||||
|
||||
void
|
||||
BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
|
||||
{
|
||||
if (angle == -1) angle = this->angle;
|
||||
if (angle == -1) return;
|
||||
|
||||
// get bridge edges (both contour and holes)
|
||||
Polylines bridge_edges;
|
||||
{
|
||||
|
@ -319,6 +319,14 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
|
|||
*/
|
||||
}
|
||||
|
||||
Polylines
|
||||
BridgeDetector::unsupported_edges(double angle) const
|
||||
{
|
||||
Polylines pp;
|
||||
this->unsupported_edges(angle, &pp);
|
||||
return pp;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(BridgeDetector, "BridgeDetector");
|
||||
#endif
|
||||
|
|
|
@ -18,10 +18,10 @@ class BridgeDetector {
|
|||
|
||||
BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
|
||||
bool detect_angle();
|
||||
void coverage(Polygons* coverage) const;
|
||||
void coverage(double angle, Polygons* coverage) const;
|
||||
void unsupported_edges(Polylines* unsupported) const;
|
||||
Polygons coverage(double angle = -1) const;
|
||||
void unsupported_edges(double angle, Polylines* unsupported) const;
|
||||
Polylines unsupported_edges(double angle = -1) const;
|
||||
|
||||
private:
|
||||
Polylines _edges; // representing the supporting edges
|
||||
|
|
|
@ -136,6 +136,15 @@ offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float d
|
|||
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
||||
}
|
||||
|
||||
Slic3r::Polygons
|
||||
offset(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
Slic3r::Polygons pp;
|
||||
offset(polygons, &pp, delta, scale, joinType, miterLimit);
|
||||
return pp;
|
||||
}
|
||||
|
||||
void
|
||||
offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
|
@ -203,6 +212,15 @@ offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float
|
|||
ClipperPaths_to_Slic3rExPolygons(output, retval);
|
||||
}
|
||||
|
||||
Slic3r::ExPolygons
|
||||
offset_ex(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit)
|
||||
{
|
||||
Slic3r::ExPolygons expp;
|
||||
offset(polygons, &expp, delta, scale, joinType, miterLimit);
|
||||
return expp;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -248,6 +266,15 @@ offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float
|
|||
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
||||
}
|
||||
|
||||
Slic3r::Polygons
|
||||
offset2(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
{
|
||||
Slic3r::Polygons pp;
|
||||
offset2(polygons, &pp, delta1, delta2, scale, joinType, miterLimit);
|
||||
return pp;
|
||||
}
|
||||
|
||||
void
|
||||
offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
|
||||
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
|
@ -260,6 +287,15 @@ offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const floa
|
|||
ClipperPaths_to_Slic3rExPolygons(output, retval);
|
||||
}
|
||||
|
||||
Slic3r::ExPolygons
|
||||
offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
|
||||
{
|
||||
Slic3r::ExPolygons expp;
|
||||
offset2(polygons, &expp, delta1, delta2, scale, joinType, miterLimit);
|
||||
return expp;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||
const Slic3r::Polygons &clip, T* retval, const ClipperLib::PolyFillType fillType, const bool safety_offset_)
|
||||
|
@ -437,6 +473,25 @@ void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType
|
|||
}
|
||||
template void diff<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_);
|
||||
|
||||
Slic3r::Polygons
|
||||
diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_)
|
||||
{
|
||||
Slic3r::Polygons pp;
|
||||
diff(subject, clip, &pp, safety_offset_);
|
||||
return pp;
|
||||
}
|
||||
|
||||
template <class SubjectType, class ClipType>
|
||||
Slic3r::ExPolygons
|
||||
diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_)
|
||||
{
|
||||
Slic3r::ExPolygons expp;
|
||||
diff(subject, clip, &expp, safety_offset_);
|
||||
return expp;
|
||||
}
|
||||
template Slic3r::ExPolygons diff_ex<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_);
|
||||
template Slic3r::ExPolygons diff_ex<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_);
|
||||
|
||||
template <class SubjectType, class ResultType>
|
||||
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_)
|
||||
{
|
||||
|
@ -448,6 +503,30 @@ template void intersection<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Po
|
|||
template void intersection<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_);
|
||||
template void intersection<Slic3r::Lines, Slic3r::Lines>(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_);
|
||||
|
||||
Slic3r::Polygons
|
||||
intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_)
|
||||
{
|
||||
Slic3r::Polygons pp;
|
||||
intersection(subject, clip, &pp, safety_offset_);
|
||||
return pp;
|
||||
}
|
||||
|
||||
Slic3r::Polylines
|
||||
intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_)
|
||||
{
|
||||
Slic3r::Polylines pp;
|
||||
intersection(subject, clip, &pp, safety_offset_);
|
||||
return pp;
|
||||
}
|
||||
|
||||
Slic3r::ExPolygons
|
||||
intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_)
|
||||
{
|
||||
Slic3r::ExPolygons expp;
|
||||
intersection(subject, clip, &expp, safety_offset_);
|
||||
return expp;
|
||||
}
|
||||
|
||||
template <class SubjectType>
|
||||
bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_)
|
||||
{
|
||||
|
@ -474,6 +553,22 @@ void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_)
|
|||
template void union_<Slic3r::ExPolygons>(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool safety_offset_);
|
||||
template void union_<Slic3r::Polygons>(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_);
|
||||
|
||||
Slic3r::Polygons
|
||||
union_(const Slic3r::Polygons &subject, bool safety_offset)
|
||||
{
|
||||
Polygons pp;
|
||||
union_(subject, &pp, safety_offset);
|
||||
return pp;
|
||||
}
|
||||
|
||||
Slic3r::ExPolygons
|
||||
union_ex(const Slic3r::Polygons &subject, bool safety_offset)
|
||||
{
|
||||
ExPolygons expp;
|
||||
union_(subject, &expp, safety_offset);
|
||||
return expp;
|
||||
}
|
||||
|
||||
void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset)
|
||||
{
|
||||
Polygons pp = subject1;
|
||||
|
|
|
@ -40,6 +40,9 @@ void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const f
|
|||
void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta,
|
||||
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
|
||||
// offset Polylines
|
||||
void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
|
||||
|
@ -55,6 +58,9 @@ void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const floa
|
|||
void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta,
|
||||
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
|
||||
void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
|
||||
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
|
@ -62,9 +68,15 @@ void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const
|
|||
void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1,
|
||||
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
|
||||
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
|
||||
template <class T>
|
||||
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||
|
@ -86,9 +98,18 @@ void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType*
|
|||
template <class SubjectType, class ResultType>
|
||||
void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false);
|
||||
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
|
||||
template <class SubjectType, class ClipType>
|
||||
Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false);
|
||||
|
||||
template <class SubjectType, class ResultType>
|
||||
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false);
|
||||
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::Polylines intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
|
||||
template <class SubjectType>
|
||||
bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
|
||||
|
@ -98,6 +119,9 @@ void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r:
|
|||
template <class T>
|
||||
void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false);
|
||||
|
||||
Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset = false);
|
||||
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false);
|
||||
|
||||
void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset = false);
|
||||
|
||||
void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_ = false);
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
#include "Config.hpp"
|
||||
#include <stdlib.h> // for setenv()
|
||||
|
||||
#ifdef _WIN32
|
||||
#define setenv(k, v, o) _putenv_s(k, v)
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -10,8 +15,7 @@ ConfigBase::has(const t_config_option_key opt_key) {
|
|||
void
|
||||
ConfigBase::apply(const ConfigBase &other, bool ignore_nonexistent) {
|
||||
// get list of option keys to apply
|
||||
t_config_option_keys opt_keys;
|
||||
other.keys(&opt_keys);
|
||||
t_config_option_keys opt_keys = other.keys();
|
||||
|
||||
// loop through options and apply them
|
||||
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
|
||||
|
@ -37,8 +41,7 @@ t_config_option_keys
|
|||
ConfigBase::diff(ConfigBase &other) {
|
||||
t_config_option_keys diff;
|
||||
|
||||
t_config_option_keys my_keys;
|
||||
this->keys(&my_keys);
|
||||
t_config_option_keys my_keys = this->keys();
|
||||
for (t_config_option_keys::const_iterator opt_key = my_keys.begin(); opt_key != my_keys.end(); ++opt_key) {
|
||||
if (other.has(*opt_key) && other.serialize(*opt_key) != this->serialize(*opt_key)) {
|
||||
diff.push_back(*opt_key);
|
||||
|
@ -98,14 +101,31 @@ ConfigBase::get_abs_value(const t_config_option_key opt_key, double ratio_over)
|
|||
return opt->get_abs_value(ratio_over);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigBase::setenv_()
|
||||
{
|
||||
t_config_option_keys opt_keys = this->keys();
|
||||
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
|
||||
// prepend the SLIC3R_ prefix
|
||||
std::ostringstream ss;
|
||||
ss << "SLIC3R_";
|
||||
ss << *it;
|
||||
std::string envname = ss.str();
|
||||
|
||||
// capitalize environment variable name
|
||||
for (size_t i = 0; i < envname.size(); ++i)
|
||||
envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
|
||||
|
||||
setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
SV*
|
||||
ConfigBase::as_hash() {
|
||||
HV* hv = newHV();
|
||||
|
||||
t_config_option_keys opt_keys;
|
||||
this->keys(&opt_keys);
|
||||
|
||||
t_config_option_keys opt_keys = this->keys();
|
||||
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it)
|
||||
(void)hv_store( hv, it->c_str(), it->length(), this->get(*it), 0 );
|
||||
|
||||
|
@ -368,10 +388,12 @@ DynamicConfig::option(const t_config_option_key opt_key) const {
|
|||
return const_cast<DynamicConfig*>(this)->option(opt_key, false);
|
||||
}
|
||||
|
||||
void
|
||||
DynamicConfig::keys(t_config_option_keys *keys) const {
|
||||
t_config_option_keys
|
||||
DynamicConfig::keys() const {
|
||||
t_config_option_keys keys;
|
||||
for (t_options_map::const_iterator it = this->options.begin(); it != this->options.end(); ++it)
|
||||
keys->push_back(it->first);
|
||||
keys.push_back(it->first);
|
||||
return keys;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -379,12 +401,14 @@ DynamicConfig::erase(const t_config_option_key opt_key) {
|
|||
this->options.erase(opt_key);
|
||||
}
|
||||
|
||||
void
|
||||
StaticConfig::keys(t_config_option_keys *keys) const {
|
||||
t_config_option_keys
|
||||
StaticConfig::keys() const {
|
||||
t_config_option_keys keys;
|
||||
for (t_optiondef_map::const_iterator it = this->def->begin(); it != this->def->end(); ++it) {
|
||||
const ConfigOption* opt = this->option(it->first);
|
||||
if (opt != NULL) keys->push_back(it->first);
|
||||
if (opt != NULL) keys.push_back(it->first);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
const ConfigOption*
|
||||
|
|
|
@ -515,7 +515,7 @@ class ConfigBase
|
|||
bool has(const t_config_option_key opt_key);
|
||||
virtual ConfigOption* option(const t_config_option_key opt_key, bool create = false) = 0;
|
||||
virtual const ConfigOption* option(const t_config_option_key opt_key) const = 0;
|
||||
virtual void keys(t_config_option_keys *keys) const = 0;
|
||||
virtual t_config_option_keys keys() const = 0;
|
||||
void apply(const ConfigBase &other, bool ignore_nonexistent = false);
|
||||
bool equals(ConfigBase &other);
|
||||
t_config_option_keys diff(ConfigBase &other);
|
||||
|
@ -524,6 +524,7 @@ class ConfigBase
|
|||
void set_ifndef(t_config_option_key opt_key, SV* value, bool deserialize = false);
|
||||
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_();
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
SV* as_hash();
|
||||
|
@ -545,7 +546,7 @@ class DynamicConfig : public ConfigBase
|
|||
template<class T> T* opt(const t_config_option_key opt_key, bool create = false);
|
||||
ConfigOption* option(const t_config_option_key opt_key, bool create = false);
|
||||
const ConfigOption* option(const t_config_option_key opt_key) const;
|
||||
void keys(t_config_option_keys *keys) const;
|
||||
t_config_option_keys keys() const;
|
||||
void erase(const t_config_option_key opt_key);
|
||||
|
||||
private:
|
||||
|
@ -556,7 +557,7 @@ class DynamicConfig : public ConfigBase
|
|||
class StaticConfig : public ConfigBase
|
||||
{
|
||||
public:
|
||||
void keys(t_config_option_keys *keys) const;
|
||||
t_config_option_keys keys() const;
|
||||
virtual ConfigOption* option(const t_config_option_key opt_key, bool create = false) = 0;
|
||||
const ConfigOption* option(const t_config_option_key opt_key) const;
|
||||
|
||||
|
|
|
@ -122,6 +122,13 @@ ExPolygon::has_boundary_point(const Point &point) const
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::simplify_p(double tolerance, Polygons* polygons) const
|
||||
{
|
||||
Polygons pp = this->simplify_p(tolerance);
|
||||
polygons->insert(polygons->end(), pp.begin(), pp.end());
|
||||
}
|
||||
|
||||
Polygons
|
||||
ExPolygon::simplify_p(double tolerance) const
|
||||
{
|
||||
|
@ -180,16 +187,17 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
|
|||
}
|
||||
|
||||
// compute the Voronoi diagram
|
||||
ma.build(polylines);
|
||||
Polylines pp;
|
||||
ma.build(&pp);
|
||||
|
||||
// clip segments to our expolygon area
|
||||
// (do this before extending endpoints as external segments coule be extended into
|
||||
// expolygon, this leaving wrong things inside)
|
||||
intersection(*polylines, *this, polylines);
|
||||
pp = intersection(pp, *this);
|
||||
|
||||
// extend initial and final segments of each polyline (they will be clipped)
|
||||
// unless they represent closed loops
|
||||
for (Polylines::iterator polyline = polylines->begin(); polyline != polylines->end(); ++polyline) {
|
||||
for (Polylines::iterator polyline = pp.begin(); polyline != pp.end(); ++polyline) {
|
||||
if (polyline->points.front().distance_to(polyline->points.back()) < min_width) continue;
|
||||
// TODO: we should *not* extend endpoints where other polylines start/end
|
||||
// (such as T joints, which are returned as three polylines by MedialAxis)
|
||||
|
@ -198,18 +206,20 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
|
|||
}
|
||||
|
||||
// clip again after extending endpoints to prevent them from exceeding the expolygon boundaries
|
||||
intersection(*polylines, *this, polylines);
|
||||
pp = intersection(pp, *this);
|
||||
|
||||
// remove too short polylines
|
||||
// (we can't do this check before endpoints extension and clipping because we don't
|
||||
// know how long will the endpoints be extended since it depends on polygon thickness
|
||||
// which is variable - extension will be <= max_width/2 on each side)
|
||||
for (size_t i = 0; i < polylines->size(); ++i) {
|
||||
if ((*polylines)[i].length() < max_width) {
|
||||
polylines->erase(polylines->begin() + i);
|
||||
for (size_t i = 0; i < pp.size(); ++i) {
|
||||
if (pp[i].length() < max_width) {
|
||||
pp.erase(pp.begin() + i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
polylines->insert(polylines->end(), pp.begin(), pp.end());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -27,6 +27,7 @@ class ExPolygon
|
|||
bool contains(const Point &point) const;
|
||||
bool contains_b(const Point &point) const;
|
||||
bool has_boundary_point(const Point &point) const;
|
||||
void simplify_p(double tolerance, Polygons* polygons) const;
|
||||
Polygons simplify_p(double tolerance) const;
|
||||
ExPolygons simplify(double tolerance) const;
|
||||
void simplify(double tolerance, ExPolygons &expolygons) const;
|
||||
|
|
|
@ -122,6 +122,12 @@ ExPolygonCollection::contours() const
|
|||
return contours;
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygonCollection::append(const ExPolygons &expp)
|
||||
{
|
||||
this->expolygons.insert(this->expolygons.end(), expp.begin(), expp.end());
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(ExPolygonCollection, "ExPolygon::Collection");
|
||||
#endif
|
||||
|
|
|
@ -31,6 +31,7 @@ class ExPolygonCollection
|
|||
Polygon convex_hull() const;
|
||||
Lines lines() const;
|
||||
Polygons contours() const;
|
||||
void append(const ExPolygons &expolygons);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "ExPolygonCollection.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Extruder.hpp"
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -113,57 +114,6 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo
|
|||
REGISTER_CLASS(ExtrusionPath, "ExtrusionPath");
|
||||
#endif
|
||||
|
||||
std::string
|
||||
ExtrusionPath::gcode(Extruder* extruder, double e, double F,
|
||||
double xofs, double yofs, std::string extrusion_axis,
|
||||
std::string gcode_line_suffix) const
|
||||
{
|
||||
dSP;
|
||||
|
||||
std::stringstream stream;
|
||||
stream.setf(std::ios::fixed);
|
||||
|
||||
double local_F = F;
|
||||
|
||||
Lines lines = this->polyline.lines();
|
||||
for (Lines::const_iterator line_it = lines.begin();
|
||||
line_it != lines.end(); ++line_it)
|
||||
{
|
||||
const double line_length = line_it->length() * SCALING_FACTOR;
|
||||
|
||||
// calculate extrusion length for this line
|
||||
double E = 0;
|
||||
if (e > 0) {
|
||||
extruder->extrude(e * line_length);
|
||||
E = extruder->E;
|
||||
}
|
||||
|
||||
// compose G-code line
|
||||
|
||||
Point point = line_it->b;
|
||||
const double x = point.x * SCALING_FACTOR + xofs;
|
||||
const double y = point.y * SCALING_FACTOR + yofs;
|
||||
stream.precision(3);
|
||||
stream << "G1 X" << x << " Y" << y;
|
||||
|
||||
if (E != 0) {
|
||||
stream.precision(5);
|
||||
stream << " " << extrusion_axis << E;
|
||||
}
|
||||
|
||||
if (local_F != 0) {
|
||||
stream.precision(3);
|
||||
stream << " F" << local_F;
|
||||
local_F = 0;
|
||||
}
|
||||
|
||||
stream << gcode_line_suffix;
|
||||
stream << "\n";
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
Polygons
|
||||
ExtrusionPath::grow() const
|
||||
{
|
||||
|
@ -375,6 +325,20 @@ ExtrusionLoop::grow() const
|
|||
return pp;
|
||||
}
|
||||
|
||||
double
|
||||
ExtrusionLoop::min_mm3_per_mm() const
|
||||
{
|
||||
double min_mm3_per_mm = 0;
|
||||
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
|
||||
if (min_mm3_per_mm == 0) {
|
||||
min_mm3_per_mm = path->mm3_per_mm;
|
||||
} else {
|
||||
min_mm3_per_mm = fmin(min_mm3_per_mm, path->mm3_per_mm);
|
||||
}
|
||||
}
|
||||
return min_mm3_per_mm;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
|
||||
#endif
|
||||
|
|
|
@ -13,6 +13,7 @@ class Extruder;
|
|||
|
||||
/* Each ExtrusionRole value identifies a distinct set of { extruder, speed } */
|
||||
enum ExtrusionRole {
|
||||
erNone,
|
||||
erPerimeter,
|
||||
erExternalPerimeter,
|
||||
erOverhangPerimeter,
|
||||
|
@ -50,6 +51,8 @@ class ExtrusionEntity
|
|||
virtual Point first_point() const = 0;
|
||||
virtual Point last_point() const = 0;
|
||||
virtual Polygons grow() const = 0;
|
||||
virtual double min_mm3_per_mm() const = 0;
|
||||
virtual Polyline as_polyline() const = 0;
|
||||
};
|
||||
|
||||
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
||||
|
@ -77,10 +80,13 @@ class ExtrusionPath : public ExtrusionEntity
|
|||
bool is_infill() const;
|
||||
bool is_solid_infill() const;
|
||||
bool is_bridge() const;
|
||||
std::string gcode(Extruder* extruder, double e, double F,
|
||||
double xofs, double yofs, std::string extrusion_axis,
|
||||
std::string gcode_line_suffix) const;
|
||||
Polygons grow() const;
|
||||
double min_mm3_per_mm() const {
|
||||
return this->mm3_per_mm;
|
||||
};
|
||||
Polyline as_polyline() const {
|
||||
return this->polyline;
|
||||
};
|
||||
|
||||
private:
|
||||
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
||||
|
@ -95,6 +101,8 @@ class ExtrusionLoop : public ExtrusionEntity
|
|||
ExtrusionLoopRole role;
|
||||
|
||||
ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : role(role) {};
|
||||
ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault)
|
||||
: paths(paths), role(role) {};
|
||||
bool is_loop() const {
|
||||
return true;
|
||||
};
|
||||
|
@ -117,6 +125,10 @@ class ExtrusionLoop : public ExtrusionEntity
|
|||
bool is_infill() const;
|
||||
bool is_solid_infill() const;
|
||||
Polygons grow() const;
|
||||
double min_mm3_per_mm() const;
|
||||
Polyline as_polyline() const {
|
||||
return this->polygon().split_at_first_point();
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "ExtrusionEntityCollection.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -7,9 +8,13 @@ namespace Slic3r {
|
|||
ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionEntityCollection& collection)
|
||||
: no_sort(collection.no_sort), orig_indices(collection.orig_indices)
|
||||
{
|
||||
this->entities.reserve(collection.entities.size());
|
||||
for (ExtrusionEntitiesPtr::const_iterator it = collection.entities.begin(); it != collection.entities.end(); ++it)
|
||||
this->entities.push_back((*it)->clone());
|
||||
this->append(collection.entities);
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths)
|
||||
: no_sort(false)
|
||||
{
|
||||
this->append(paths);
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const ExtrusionEntityCollection &other)
|
||||
|
@ -27,10 +32,30 @@ ExtrusionEntityCollection::swap (ExtrusionEntityCollection &c)
|
|||
std::swap(this->no_sort, c.no_sort);
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection::~ExtrusionEntityCollection()
|
||||
{
|
||||
for (ExtrusionEntitiesPtr::iterator it = this->entities.begin(); it != this->entities.end(); ++it)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection::operator ExtrusionPaths() const
|
||||
{
|
||||
ExtrusionPaths paths;
|
||||
for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
|
||||
if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(*it))
|
||||
paths.push_back(*path);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection*
|
||||
ExtrusionEntityCollection::clone() const
|
||||
{
|
||||
return new ExtrusionEntityCollection(*this);
|
||||
ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(*this);
|
||||
for (size_t i = 0; i < coll->entities.size(); ++i) {
|
||||
coll->entities[i] = this->entities[i]->clone();
|
||||
}
|
||||
return coll;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -56,6 +81,34 @@ ExtrusionEntityCollection::last_point() const
|
|||
return this->entities.back()->last_point();
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionEntityCollection::append(const ExtrusionEntity &entity)
|
||||
{
|
||||
this->entities.push_back(entity.clone());
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionEntityCollection::append(const ExtrusionEntitiesPtr &entities)
|
||||
{
|
||||
for (ExtrusionEntitiesPtr::const_iterator ptr = entities.begin(); ptr != entities.end(); ++ptr)
|
||||
this->append(**ptr);
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionEntityCollection::append(const ExtrusionPaths &paths)
|
||||
{
|
||||
for (ExtrusionPaths::const_iterator path = paths.begin(); path != paths.end(); ++path)
|
||||
this->append(*path);
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection
|
||||
ExtrusionEntityCollection::chained_path(bool no_reverse, std::vector<size_t>* orig_indices) const
|
||||
{
|
||||
ExtrusionEntityCollection coll;
|
||||
this->chained_path(&coll, no_reverse, orig_indices);
|
||||
return coll;
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, std::vector<size_t>* orig_indices) const
|
||||
{
|
||||
|
@ -137,6 +190,43 @@ ExtrusionEntityCollection::items_count() const
|
|||
return count;
|
||||
}
|
||||
|
||||
/* Returns a single vector of pointers to all non-collection items contained in this one */
|
||||
void
|
||||
ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval) const
|
||||
{
|
||||
for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
|
||||
if ((*it)->is_collection()) {
|
||||
ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(*it);
|
||||
retval->append(collection->flatten().entities);
|
||||
} else {
|
||||
retval->append(**it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection
|
||||
ExtrusionEntityCollection::flatten() const
|
||||
{
|
||||
ExtrusionEntityCollection coll;
|
||||
this->flatten(&coll);
|
||||
return coll;
|
||||
}
|
||||
|
||||
double
|
||||
ExtrusionEntityCollection::min_mm3_per_mm() const
|
||||
{
|
||||
double min_mm3_per_mm = 0;
|
||||
for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
|
||||
double mm3_per_mm = (*it)->min_mm3_per_mm();
|
||||
if (min_mm3_per_mm == 0) {
|
||||
min_mm3_per_mm = mm3_per_mm;
|
||||
} else {
|
||||
min_mm3_per_mm = fmin(min_mm3_per_mm, mm3_per_mm);
|
||||
}
|
||||
}
|
||||
return min_mm3_per_mm;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
// there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection
|
||||
REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection");
|
||||
|
|
|
@ -10,19 +10,33 @@ class ExtrusionEntityCollection : public ExtrusionEntity
|
|||
{
|
||||
public:
|
||||
ExtrusionEntityCollection* clone() const;
|
||||
ExtrusionEntitiesPtr entities;
|
||||
ExtrusionEntitiesPtr entities; // we own these entities
|
||||
std::vector<size_t> orig_indices; // handy for XS
|
||||
bool no_sort;
|
||||
ExtrusionEntityCollection(): no_sort(false) {};
|
||||
ExtrusionEntityCollection(const ExtrusionEntityCollection &collection);
|
||||
ExtrusionEntityCollection(const ExtrusionPaths &paths);
|
||||
ExtrusionEntityCollection& operator= (const ExtrusionEntityCollection &other);
|
||||
~ExtrusionEntityCollection();
|
||||
operator ExtrusionPaths() const;
|
||||
|
||||
bool is_collection() const {
|
||||
return true;
|
||||
};
|
||||
bool can_reverse() const {
|
||||
return !this->no_sort;
|
||||
};
|
||||
bool empty() const {
|
||||
return this->entities.empty();
|
||||
};
|
||||
void clear() {
|
||||
this->entities.clear();
|
||||
};
|
||||
void swap (ExtrusionEntityCollection &c);
|
||||
void append(const ExtrusionEntity &entity);
|
||||
void append(const ExtrusionEntitiesPtr &entities);
|
||||
void append(const ExtrusionPaths &paths);
|
||||
ExtrusionEntityCollection chained_path(bool no_reverse = false, std::vector<size_t>* orig_indices = NULL) const;
|
||||
void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector<size_t>* orig_indices = NULL) const;
|
||||
void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector<size_t>* orig_indices = NULL) const;
|
||||
void reverse();
|
||||
|
@ -30,6 +44,13 @@ class ExtrusionEntityCollection : public ExtrusionEntity
|
|||
Point last_point() const;
|
||||
Polygons grow() const;
|
||||
size_t items_count() const;
|
||||
void flatten(ExtrusionEntityCollection* retval) const;
|
||||
ExtrusionEntityCollection flatten() const;
|
||||
double min_mm3_per_mm() const;
|
||||
Polyline as_polyline() const {
|
||||
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
|
||||
return Polyline();
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
774
xs/src/libslic3r/GCode.cpp
Normal file
774
xs/src/libslic3r/GCode.cpp
Normal file
|
@ -0,0 +1,774 @@
|
|||
#include "GCode.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
AvoidCrossingPerimeters::AvoidCrossingPerimeters()
|
||||
: use_external_mp(false), use_external_mp_once(false), disable_once(true),
|
||||
_external_mp(NULL), _layer_mp(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
AvoidCrossingPerimeters::~AvoidCrossingPerimeters()
|
||||
{
|
||||
if (this->_external_mp != NULL)
|
||||
delete this->_external_mp;
|
||||
|
||||
if (this->_layer_mp != NULL)
|
||||
delete this->_layer_mp;
|
||||
}
|
||||
|
||||
void
|
||||
AvoidCrossingPerimeters::init_external_mp(const ExPolygons &islands)
|
||||
{
|
||||
if (this->_external_mp != NULL)
|
||||
delete this->_external_mp;
|
||||
|
||||
this->_external_mp = new MotionPlanner(islands);
|
||||
}
|
||||
|
||||
void
|
||||
AvoidCrossingPerimeters::init_layer_mp(const ExPolygons &islands)
|
||||
{
|
||||
if (this->_layer_mp != NULL)
|
||||
delete this->_layer_mp;
|
||||
|
||||
this->_layer_mp = new MotionPlanner(islands);
|
||||
}
|
||||
|
||||
Polyline
|
||||
AvoidCrossingPerimeters::travel_to(GCode &gcodegen, Point point)
|
||||
{
|
||||
if (this->use_external_mp || this->use_external_mp_once) {
|
||||
// get current origin set in gcodegen
|
||||
// (the one that will be used to translate the G-code coordinates by)
|
||||
Point scaled_origin = Point::new_scale(gcodegen.origin.x, gcodegen.origin.y);
|
||||
|
||||
// represent last_pos in absolute G-code coordinates
|
||||
Point last_pos = gcodegen.last_pos();
|
||||
last_pos.translate(scaled_origin);
|
||||
|
||||
// represent point in absolute G-code coordinates
|
||||
point.translate(scaled_origin);
|
||||
|
||||
// calculate path
|
||||
Polyline travel = this->_external_mp->shortest_path(last_pos, point);
|
||||
|
||||
// translate the path back into the shifted coordinate system that gcodegen
|
||||
// is currently using for writing coordinates
|
||||
travel.translate(scaled_origin.negative());
|
||||
return travel;
|
||||
} else {
|
||||
return this->_layer_mp->shortest_path(gcodegen.last_pos(), point);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(AvoidCrossingPerimeters, "GCode::AvoidCrossingPerimeters");
|
||||
#endif
|
||||
|
||||
OozePrevention::OozePrevention()
|
||||
: enable(false)
|
||||
{
|
||||
}
|
||||
|
||||
std::string
|
||||
OozePrevention::pre_toolchange(GCode &gcodegen)
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
// move to the nearest standby point
|
||||
if (!this->standby_points.empty()) {
|
||||
// get current position in print coordinates
|
||||
Pointf3 writer_pos = gcodegen.writer.get_position();
|
||||
Point pos = Point::new_scale(writer_pos.x, writer_pos.y);
|
||||
|
||||
// find standby point
|
||||
Point standby_point;
|
||||
pos.nearest_point(this->standby_points, &standby_point);
|
||||
|
||||
/* We don't call gcodegen.travel_to() because we don't need retraction (it was already
|
||||
triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates
|
||||
of the destination point must not be transformed by origin nor current extruder offset. */
|
||||
gcode += gcodegen.writer.travel_to_xy(Pointf::new_unscale(standby_point),
|
||||
"move to standby position");
|
||||
}
|
||||
|
||||
if (gcodegen.config.standby_temperature_delta.value != 0) {
|
||||
// we assume that heating is always slower than cooling, so no need to block
|
||||
gcode += gcodegen.writer.set_temperature
|
||||
(this->_get_temp(gcodegen) + gcodegen.config.standby_temperature_delta.value, false);
|
||||
}
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string
|
||||
OozePrevention::post_toolchange(GCode &gcodegen)
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
if (gcodegen.config.standby_temperature_delta.value != 0) {
|
||||
gcode += gcodegen.writer.set_temperature(this->_get_temp(gcodegen), true);
|
||||
}
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
int
|
||||
OozePrevention::_get_temp(GCode &gcodegen)
|
||||
{
|
||||
return (gcodegen.layer != NULL && gcodegen.layer->id() == 0)
|
||||
? gcodegen.config.first_layer_temperature.get_at(gcodegen.writer.extruder()->id)
|
||||
: gcodegen.config.temperature.get_at(gcodegen.writer.extruder()->id);
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(OozePrevention, "GCode::OozePrevention");
|
||||
#endif
|
||||
|
||||
Wipe::Wipe()
|
||||
: enable(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
Wipe::has_path()
|
||||
{
|
||||
return !this->path.points.empty();
|
||||
}
|
||||
|
||||
void
|
||||
Wipe::reset_path()
|
||||
{
|
||||
this->path = Polyline();
|
||||
}
|
||||
|
||||
std::string
|
||||
Wipe::wipe(GCode &gcodegen, bool toolchange)
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
/* Reduce feedrate a bit; travel speed is often too high to move on existing material.
|
||||
Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */
|
||||
double wipe_speed = gcodegen.writer.config.travel_speed.value * 0.8;
|
||||
|
||||
// get the retraction length
|
||||
double length = toolchange
|
||||
? gcodegen.writer.extruder()->retract_length_toolchange()
|
||||
: gcodegen.writer.extruder()->retract_length();
|
||||
|
||||
if (length > 0) {
|
||||
/* Calculate how long we need to travel in order to consume the required
|
||||
amount of retraction. In other words, how far do we move in XY at wipe_speed
|
||||
for the time needed to consume retract_length at retract_speed? */
|
||||
double wipe_dist = scale_(length / gcodegen.writer.extruder()->retract_speed() * wipe_speed);
|
||||
|
||||
/* Take the stored wipe path and replace first point with the current actual position
|
||||
(they might be different, for example, in case of loop clipping). */
|
||||
Polyline wipe_path;
|
||||
wipe_path.append(gcodegen.last_pos());
|
||||
wipe_path.append(
|
||||
this->path.points.begin() + 1,
|
||||
this->path.points.end()
|
||||
);
|
||||
|
||||
wipe_path.clip_end(wipe_path.length() - wipe_dist);
|
||||
|
||||
// subdivide the retraction in segments
|
||||
double retracted = 0;
|
||||
Lines lines = wipe_path.lines();
|
||||
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
|
||||
double segment_length = line->length();
|
||||
/* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
|
||||
due to rounding (TODO: test and/or better math for this) */
|
||||
double dE = length * (segment_length / wipe_dist) * 0.95;
|
||||
gcode += gcodegen.writer.set_speed(wipe_speed*60);
|
||||
gcode += gcodegen.writer.extrude_to_xy(
|
||||
gcodegen.point_to_gcode(line->b),
|
||||
-dE,
|
||||
(std::string)"wipe and retract" + (gcodegen.enable_cooling_markers ? ";_WIPE" : "")
|
||||
);
|
||||
retracted += dE;
|
||||
}
|
||||
gcodegen.writer.extruder()->retracted += retracted;
|
||||
|
||||
// prevent wiping again on same path
|
||||
this->reset_path();
|
||||
}
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(Wipe, "GCode::Wipe");
|
||||
#endif
|
||||
|
||||
#define EXTRUDER_CONFIG(OPT) this->config.OPT.get_at(this->writer.extruder()->id)
|
||||
|
||||
GCode::GCode()
|
||||
: enable_loop_clipping(true), enable_cooling_markers(false), layer_count(0),
|
||||
layer_index(-1), first_layer(false), elapsed_time(0), volumetric_speed(0),
|
||||
_last_pos_defined(false), layer(NULL), placeholder_parser(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
Point&
|
||||
GCode::last_pos()
|
||||
{
|
||||
return this->_last_pos;
|
||||
}
|
||||
|
||||
void
|
||||
GCode::set_last_pos(const Point &pos)
|
||||
{
|
||||
this->_last_pos = pos;
|
||||
this->_last_pos_defined = true;
|
||||
}
|
||||
|
||||
bool
|
||||
GCode::last_pos_defined() const
|
||||
{
|
||||
return this->_last_pos_defined;
|
||||
}
|
||||
|
||||
void
|
||||
GCode::apply_print_config(const PrintConfig &print_config)
|
||||
{
|
||||
this->writer.apply_print_config(print_config);
|
||||
this->config.apply(print_config);
|
||||
}
|
||||
|
||||
void
|
||||
GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
|
||||
{
|
||||
this->writer.set_extruders(extruder_ids);
|
||||
|
||||
// enable wipe path generation if any extruder has wipe enabled
|
||||
this->wipe.enable = false;
|
||||
for (std::vector<unsigned int>::const_iterator it = extruder_ids.begin();
|
||||
it != extruder_ids.end(); ++it) {
|
||||
if (this->config.wipe.get_at(*it)) {
|
||||
this->wipe.enable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCode::set_origin(const Pointf &pointf)
|
||||
{
|
||||
// if origin increases (goes towards right), last_pos decreases because it goes towards left
|
||||
Point translate(
|
||||
scale_(this->origin.x - pointf.x),
|
||||
scale_(this->origin.y - pointf.y)
|
||||
);
|
||||
this->_last_pos.translate(translate);
|
||||
this->wipe.path.translate(translate);
|
||||
|
||||
this->origin = pointf;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::preamble()
|
||||
{
|
||||
std::string gcode = this->writer.preamble();
|
||||
|
||||
/* Perform a *silent* move to z_offset: we need this to initialize the Z
|
||||
position of our writer object so that any initial lift taking place
|
||||
before the first layer change will raise the extruder from the correct
|
||||
initial Z instead of 0. */
|
||||
this->writer.travel_to_z(this->config.z_offset.value);
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::change_layer(const Layer &layer)
|
||||
{
|
||||
this->layer = &layer;
|
||||
this->layer_index++;
|
||||
this->first_layer = (layer.id() == 0);
|
||||
|
||||
// avoid computing islands and overhangs if they're not needed
|
||||
if (this->config.avoid_crossing_perimeters) {
|
||||
ExPolygons islands;
|
||||
union_(layer.slices, &islands, true);
|
||||
this->avoid_crossing_perimeters.init_layer_mp(islands);
|
||||
}
|
||||
|
||||
std::string gcode;
|
||||
if (this->layer_count > 0) {
|
||||
gcode += this->writer.update_progress(this->layer_index, this->layer_count);
|
||||
}
|
||||
|
||||
coordf_t z = layer.print_z + this->config.z_offset.value; // in unscaled coordinates
|
||||
if (EXTRUDER_CONFIG(retract_layer_change) && this->writer.will_move_z(z)) {
|
||||
gcode += this->retract();
|
||||
}
|
||||
{
|
||||
std::ostringstream comment;
|
||||
comment << "move to next layer (" << this->layer_index << ")";
|
||||
gcode += this->writer.travel_to_z(z, comment.str());
|
||||
}
|
||||
|
||||
// forget last wiping path as wiping after raising Z is pointless
|
||||
this->wipe.reset_path();
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
|
||||
{
|
||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||
// next copies (if any) would not detect the correct orientation
|
||||
|
||||
// extrude all loops ccw
|
||||
bool was_clockwise = loop.make_counter_clockwise();
|
||||
|
||||
// find the point of the loop that is closest to the current extruder position
|
||||
// or randomize if requested
|
||||
Point last_pos = this->last_pos();
|
||||
if (this->config.spiral_vase) {
|
||||
loop.split_at(last_pos);
|
||||
} else if (this->config.seam_position == spNearest || this->config.seam_position == spAligned) {
|
||||
Polygon polygon = loop.polygon();
|
||||
|
||||
// simplify polygon in order to skip false positives in concave/convex detection
|
||||
// (loop is always ccw as polygon.simplify() only works on ccw polygons)
|
||||
Polygons simplified = polygon.simplify(scale_(EXTRUDER_CONFIG(nozzle_diameter))/2);
|
||||
|
||||
// restore original winding order so that concave and convex detection always happens
|
||||
// on the right/outer side of the polygon
|
||||
if (was_clockwise) {
|
||||
for (Polygons::iterator p = simplified.begin(); p != simplified.end(); ++p)
|
||||
p->reverse();
|
||||
}
|
||||
|
||||
// concave vertices have priority
|
||||
Points candidates;
|
||||
for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) {
|
||||
Points concave = p->concave_points(PI*4/3);
|
||||
candidates.insert(candidates.end(), concave.begin(), concave.end());
|
||||
}
|
||||
|
||||
// if no concave points were found, look for convex vertices
|
||||
if (candidates.empty()) {
|
||||
for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) {
|
||||
Points convex = p->convex_points(PI*2/3);
|
||||
candidates.insert(candidates.end(), convex.begin(), convex.end());
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve the last start position for this object
|
||||
if (this->layer != NULL && this->_seam_position.count(this->layer->object()) > 0) {
|
||||
last_pos = this->_seam_position[this->layer->object()];
|
||||
}
|
||||
|
||||
Point point;
|
||||
if (this->config.seam_position == spNearest) {
|
||||
if (candidates.empty()) candidates = polygon.points;
|
||||
last_pos.nearest_point(candidates, &point);
|
||||
|
||||
// On 32-bit Linux, Clipper will change some point coordinates by 1 unit
|
||||
// while performing simplify_polygons(), thus split_at_vertex() won't
|
||||
// find them anymore.
|
||||
if (!loop.split_at_vertex(point)) loop.split_at(point);
|
||||
} else if (!candidates.empty()) {
|
||||
Points non_overhang;
|
||||
for (Points::const_iterator p = candidates.begin(); p != candidates.end(); ++p) {
|
||||
if (!loop.has_overhang_point(*p))
|
||||
non_overhang.push_back(*p);
|
||||
}
|
||||
|
||||
if (!non_overhang.empty())
|
||||
candidates = non_overhang;
|
||||
|
||||
last_pos.nearest_point(candidates, &point);
|
||||
if (!loop.split_at_vertex(point)) loop.split_at(point); // see note above
|
||||
} else {
|
||||
point = last_pos.projection_onto(polygon);
|
||||
loop.split_at(point);
|
||||
}
|
||||
if (this->layer != NULL)
|
||||
this->_seam_position[this->layer->object()] = point;
|
||||
} else if (this->config.seam_position == spRandom) {
|
||||
if (loop.role == elrContourInternalPerimeter) {
|
||||
Polygon polygon = loop.polygon();
|
||||
Point centroid = polygon.centroid();
|
||||
last_pos = Point(polygon.bounding_box().max.x, centroid.y);
|
||||
last_pos.rotate(rand() % 2*PI, centroid);
|
||||
}
|
||||
loop.split_at(last_pos);
|
||||
}
|
||||
|
||||
// clip the path to avoid the extruder to get exactly on the first point of the loop;
|
||||
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||
// we discard it in that case
|
||||
double clip_length = this->enable_loop_clipping
|
||||
? scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER
|
||||
: 0;
|
||||
|
||||
// get paths
|
||||
ExtrusionPaths paths;
|
||||
loop.clip_end(clip_length, &paths);
|
||||
if (paths.empty()) return "";
|
||||
|
||||
// apply the small perimeter speed
|
||||
if (paths.front().is_perimeter() && loop.length() <= SMALL_PERIMETER_LENGTH) {
|
||||
if (speed == -1) speed = this->config.get_abs_value("small_perimeter_speed");
|
||||
}
|
||||
|
||||
// extrude along the path
|
||||
std::string gcode;
|
||||
for (ExtrusionPaths::const_iterator path = paths.begin(); path != paths.end(); ++path)
|
||||
gcode += this->_extrude(*path, description, speed);
|
||||
|
||||
// reset acceleration
|
||||
gcode += this->writer.set_acceleration(this->config.default_acceleration.value);
|
||||
|
||||
if (this->wipe.enable)
|
||||
this->wipe.path = paths.front().polyline; // TODO: don't limit wipe to last path
|
||||
|
||||
// make a little move inwards before leaving loop
|
||||
if (paths.back().role == erExternalPerimeter && this->layer != NULL && this->config.perimeters > 1) {
|
||||
Polyline &last_path_polyline = paths.back().polyline;
|
||||
// detect angle between last and first segment
|
||||
// the side depends on the original winding order of the polygon (left for contours, right for holes)
|
||||
Point a = paths.front().polyline.points[1]; // second point
|
||||
Point b = *(paths.back().polyline.points.end()-3); // second to last point
|
||||
if (was_clockwise) {
|
||||
// swap points
|
||||
Point c = a; a = b; b = c;
|
||||
}
|
||||
|
||||
double angle = paths.front().first_point().ccw_angle(a, b) / 3;
|
||||
|
||||
// turn left if contour, turn right if hole
|
||||
if (was_clockwise) angle *= -1;
|
||||
|
||||
// create the destination point along the first segment and rotate it
|
||||
// we make sure we don't exceed the segment length because we don't know
|
||||
// the rotation of the second segment so we might cross the object boundary
|
||||
Line first_segment(
|
||||
paths.front().polyline.points[0],
|
||||
paths.front().polyline.points[1]
|
||||
);
|
||||
double distance = std::min(
|
||||
scale_(EXTRUDER_CONFIG(nozzle_diameter)),
|
||||
first_segment.length()
|
||||
);
|
||||
Point point = first_segment.point_at(distance);
|
||||
point.rotate(angle, first_segment.a);
|
||||
|
||||
// generate the travel move
|
||||
gcode += this->writer.travel_to_xy(this->point_to_gcode(point), "move inwards before travel");
|
||||
}
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::extrude(const ExtrusionEntity &entity, std::string description, double speed)
|
||||
{
|
||||
if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity)) {
|
||||
return this->extrude(*path, description, speed);
|
||||
} else if (const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(&entity)) {
|
||||
return this->extrude(*loop, description, speed);
|
||||
} else {
|
||||
CONFESS("Invalid argument supplied to extrude()");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::extrude(const ExtrusionPath &path, std::string description, double speed)
|
||||
{
|
||||
std::string gcode = this->_extrude(path, description, speed);
|
||||
|
||||
// reset acceleration
|
||||
gcode += this->writer.set_acceleration(this->config.default_acceleration.value);
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::_extrude(ExtrusionPath path, std::string description, double speed)
|
||||
{
|
||||
path.simplify(SCALED_RESOLUTION);
|
||||
|
||||
std::string gcode;
|
||||
|
||||
// go to first point of extrusion path
|
||||
if (!this->_last_pos_defined || !this->_last_pos.coincides_with(path.first_point())) {
|
||||
gcode += this->travel_to(
|
||||
path.first_point(),
|
||||
path.role,
|
||||
"move to first " + description + " point"
|
||||
);
|
||||
}
|
||||
|
||||
// compensate retraction
|
||||
gcode += this->unretract();
|
||||
|
||||
// adjust acceleration
|
||||
{
|
||||
double acceleration;
|
||||
if (this->config.first_layer_acceleration.value > 0 && this->first_layer) {
|
||||
acceleration = this->config.first_layer_acceleration.value;
|
||||
} else if (this->config.perimeter_acceleration.value > 0 && path.is_perimeter()) {
|
||||
acceleration = this->config.perimeter_acceleration.value;
|
||||
} else if (this->config.bridge_acceleration.value > 0 && path.is_bridge()) {
|
||||
acceleration = this->config.bridge_acceleration.value;
|
||||
} else if (this->config.infill_acceleration.value > 0 && path.is_infill()) {
|
||||
acceleration = this->config.infill_acceleration.value;
|
||||
} else {
|
||||
acceleration = this->config.default_acceleration.value;
|
||||
}
|
||||
gcode += this->writer.set_acceleration(acceleration);
|
||||
}
|
||||
|
||||
// calculate extrusion length per distance unit
|
||||
double e_per_mm = this->writer.extruder()->e_per_mm3 * path.mm3_per_mm;
|
||||
if (this->writer.extrusion_axis().empty()) e_per_mm = 0;
|
||||
|
||||
// set speed
|
||||
if (speed == -1) {
|
||||
if (path.role == erPerimeter) {
|
||||
speed = this->config.get_abs_value("perimeter_speed");
|
||||
} else if (path.role == erExternalPerimeter) {
|
||||
speed = this->config.get_abs_value("external_perimeter_speed");
|
||||
} else if (path.role == erOverhangPerimeter || path.role == erBridgeInfill) {
|
||||
speed = this->config.get_abs_value("bridge_speed");
|
||||
} else if (path.role == erInternalInfill) {
|
||||
speed = this->config.get_abs_value("infill_speed");
|
||||
} else if (path.role == erSolidInfill) {
|
||||
speed = this->config.get_abs_value("solid_infill_speed");
|
||||
} else if (path.role == erTopSolidInfill) {
|
||||
speed = this->config.get_abs_value("top_solid_infill_speed");
|
||||
} else if (path.role == erGapFill) {
|
||||
speed = this->config.get_abs_value("gap_fill_speed");
|
||||
} else {
|
||||
CONFESS("Invalid speed");
|
||||
}
|
||||
}
|
||||
if (this->first_layer) {
|
||||
speed = this->config.get_abs_value("first_layer_speed", speed);
|
||||
}
|
||||
if (this->volumetric_speed != 0 && speed == 0) {
|
||||
speed = this->volumetric_speed / path.mm3_per_mm;
|
||||
}
|
||||
if (this->config.max_volumetric_speed.value > 0) {
|
||||
// cap speed with max_volumetric_speed anyway (even if user is not using autospeed)
|
||||
speed = std::min(
|
||||
speed,
|
||||
this->config.max_volumetric_speed.value / path.mm3_per_mm
|
||||
);
|
||||
}
|
||||
double F = speed * 60; // convert mm/sec to mm/min
|
||||
|
||||
// extrude arc or line
|
||||
if (path.is_bridge() && this->enable_cooling_markers)
|
||||
gcode += ";_BRIDGE_FAN_START\n";
|
||||
gcode += this->writer.set_speed(F);
|
||||
double path_length = 0;
|
||||
{
|
||||
std::string comment = this->config.gcode_comments ? (" ; " + description) : "";
|
||||
Lines lines = path.polyline.lines();
|
||||
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
|
||||
const double line_length = line->length() * SCALING_FACTOR;
|
||||
path_length += line_length;
|
||||
|
||||
gcode += this->writer.extrude_to_xy(
|
||||
this->point_to_gcode(line->b),
|
||||
e_per_mm * line_length,
|
||||
comment
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this->wipe.enable) {
|
||||
this->wipe.path = path.polyline;
|
||||
this->wipe.path.reverse();
|
||||
}
|
||||
if (path.is_bridge() && this->enable_cooling_markers)
|
||||
gcode += ";_BRIDGE_FAN_END\n";
|
||||
|
||||
this->set_last_pos(path.last_point());
|
||||
|
||||
if (this->config.cooling)
|
||||
this->elapsed_time += path_length / F * 60;
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
// This method accepts &point in print coordinates.
|
||||
std::string
|
||||
GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment)
|
||||
{
|
||||
/* Define the travel move as a line between current position and the taget point.
|
||||
This is expressed in print coordinates, so it will need to be translated by
|
||||
this->origin in order to get G-code coordinates. */
|
||||
Polyline travel;
|
||||
travel.append(this->last_pos());
|
||||
travel.append(point);
|
||||
|
||||
// check whether a straight travel move would need retraction
|
||||
bool needs_retraction = this->needs_retraction(travel, role);
|
||||
|
||||
// if a retraction would be needed, try to use avoid_crossing_perimeters to plan a
|
||||
// multi-hop travel path inside the configuration space
|
||||
if (needs_retraction
|
||||
&& this->config.avoid_crossing_perimeters
|
||||
&& !this->avoid_crossing_perimeters.disable_once) {
|
||||
travel = this->avoid_crossing_perimeters.travel_to(*this, point);
|
||||
|
||||
// check again whether the new travel path still needs a retraction
|
||||
needs_retraction = this->needs_retraction(travel, role);
|
||||
}
|
||||
|
||||
// Re-allow avoid_crossing_perimeters for the next travel moves
|
||||
this->avoid_crossing_perimeters.disable_once = false;
|
||||
this->avoid_crossing_perimeters.use_external_mp_once = false;
|
||||
|
||||
// generate G-code for the travel move
|
||||
std::string gcode;
|
||||
if (needs_retraction) gcode += this->retract();
|
||||
|
||||
// use G1 because we rely on paths being straight (G0 may make round paths)
|
||||
Lines lines = travel.lines();
|
||||
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
|
||||
gcode += this->writer.travel_to_xy(this->point_to_gcode(line->b), comment);
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
bool
|
||||
GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
|
||||
{
|
||||
if (travel.length() < scale_(EXTRUDER_CONFIG(retract_before_travel))) {
|
||||
// skip retraction if the move is shorter than the configured threshold
|
||||
return false;
|
||||
}
|
||||
|
||||
if (role == erSupportMaterial) {
|
||||
const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(this->layer);
|
||||
if (support_layer != NULL && support_layer->support_islands.contains(travel)) {
|
||||
// skip retraction if this is a travel move inside a support material island
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->config.only_retract_when_crossing_perimeters && this->layer != NULL) {
|
||||
if (this->config.fill_density.value > 0
|
||||
&& this->layer->any_internal_region_slice_contains(travel)) {
|
||||
/* skip retraction if travel is contained in an internal slice *and*
|
||||
internal infill is enabled (so that stringing is entirely not visible) */
|
||||
return false;
|
||||
} else if (this->layer->any_bottom_region_slice_contains(travel)
|
||||
&& this->layer->upper_layer != NULL
|
||||
&& this->layer->upper_layer->slices.contains(travel)
|
||||
&& (this->config.bottom_solid_layers.value >= 2 || this->config.fill_density.value > 0)) {
|
||||
/* skip retraction if travel is contained in an *infilled* bottom slice
|
||||
but only if it's also covered by an *infilled* upper layer's slice
|
||||
so that it's not visible from above (a bottom surface might not have an
|
||||
upper slice in case of a thin membrane) */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// retract if only_retract_when_crossing_perimeters is disabled or doesn't apply
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::retract(bool toolchange)
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
if (this->writer.extruder() == NULL)
|
||||
return gcode;
|
||||
|
||||
// wipe (if it's enabled for this extruder and we have a stored wipe path)
|
||||
if (EXTRUDER_CONFIG(wipe) && this->wipe.has_path()) {
|
||||
gcode += this->wipe.wipe(*this, toolchange);
|
||||
}
|
||||
|
||||
/* The parent class will decide whether we need to perform an actual retraction
|
||||
(the extruder might be already retracted fully or partially). We call these
|
||||
methods even if we performed wipe, since this will ensure the entire retraction
|
||||
length is honored in case wipe path was too short. */
|
||||
gcode += toolchange ? this->writer.retract_for_toolchange() : this->writer.retract();
|
||||
|
||||
gcode += this->writer.reset_e();
|
||||
if (this->writer.extruder()->retract_length() > 0 || this->config.use_firmware_retraction)
|
||||
gcode += this->writer.lift();
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::unretract()
|
||||
{
|
||||
std::string gcode;
|
||||
gcode += this->writer.unlift();
|
||||
gcode += this->writer.unretract();
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::set_extruder(unsigned int extruder_id)
|
||||
{
|
||||
this->placeholder_parser->set("current_extruder", extruder_id);
|
||||
if (!this->writer.need_toolchange(extruder_id))
|
||||
return "";
|
||||
|
||||
// if we are running a single-extruder setup, just set the extruder and return nothing
|
||||
if (!this->writer.multiple_extruders) {
|
||||
return this->writer.toolchange(extruder_id);
|
||||
}
|
||||
|
||||
// prepend retraction on the current extruder
|
||||
std::string gcode = this->retract(true);
|
||||
|
||||
// append custom toolchange G-code
|
||||
if (this->writer.extruder() != NULL && !this->config.toolchange_gcode.value.empty()) {
|
||||
PlaceholderParser pp = *this->placeholder_parser;
|
||||
pp.set("previous_extruder", this->writer.extruder()->id);
|
||||
pp.set("next_extruder", extruder_id);
|
||||
gcode += pp.process(this->config.toolchange_gcode.value) + '\n';
|
||||
}
|
||||
|
||||
// if ooze prevention is enabled, park current extruder in the nearest
|
||||
// standby point and set it to the standby temperature
|
||||
if (this->ooze_prevention.enable && this->writer.extruder() != NULL)
|
||||
gcode += this->ooze_prevention.pre_toolchange(*this);
|
||||
|
||||
// append the toolchange command
|
||||
gcode += this->writer.toolchange(extruder_id);
|
||||
|
||||
// set the new extruder to the operating temperature
|
||||
if (this->ooze_prevention.enable)
|
||||
gcode += this->ooze_prevention.post_toolchange(*this);
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
// convert a model-space scaled point into G-code coordinates
|
||||
Pointf
|
||||
GCode::point_to_gcode(const Point &point)
|
||||
{
|
||||
Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset);
|
||||
return Pointf(
|
||||
unscale(point.x) + this->origin.x - extruder_offset.x,
|
||||
unscale(point.y) + this->origin.y - extruder_offset.y
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(GCode, "GCode");
|
||||
#endif
|
||||
|
||||
}
|
|
@ -2,32 +2,112 @@
|
|||
#define slic3r_GCode_hpp_
|
||||
|
||||
#include <myinit.h>
|
||||
#include "ExPolygon.hpp"
|
||||
#include "GCodeWriter.hpp"
|
||||
#include "Layer.hpp"
|
||||
#include "MotionPlanner.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "PlaceholderParser.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// draft for a binary representation of a G-code line
|
||||
class GCode;
|
||||
|
||||
enum GCodeCmdType {
|
||||
gcctSyncMotion,
|
||||
gcctExtrude,
|
||||
gcctResetE,
|
||||
gcctSetTemp,
|
||||
gcctSetTempWait,
|
||||
gcctToolchange,
|
||||
gcctCustom
|
||||
class AvoidCrossingPerimeters {
|
||||
public:
|
||||
|
||||
// this flag triggers the use of the external configuration space
|
||||
bool use_external_mp;
|
||||
bool use_external_mp_once; // just for the next travel move
|
||||
|
||||
// this flag disables avoid_crossing_perimeters just for the next travel move
|
||||
// we enable it by default for the first travel move in print
|
||||
bool disable_once;
|
||||
|
||||
AvoidCrossingPerimeters();
|
||||
~AvoidCrossingPerimeters();
|
||||
void init_external_mp(const ExPolygons &islands);
|
||||
void init_layer_mp(const ExPolygons &islands);
|
||||
Polyline travel_to(GCode &gcodegen, Point point);
|
||||
|
||||
private:
|
||||
MotionPlanner* _external_mp;
|
||||
MotionPlanner* _layer_mp;
|
||||
};
|
||||
|
||||
class GCodeCmd {
|
||||
class OozePrevention {
|
||||
public:
|
||||
GCodeCmdType type;
|
||||
float X, Y, Z, E, F;
|
||||
unsigned short T, S;
|
||||
std::string custom, comment;
|
||||
float xy_dist; // cache
|
||||
bool enable;
|
||||
Points standby_points;
|
||||
|
||||
GCodeCmd(GCodeCmdType type)
|
||||
: type(type), X(0), Y(0), Z(0), E(0), F(0), T(-1), S(0), xy_dist(-1) {};
|
||||
OozePrevention();
|
||||
std::string pre_toolchange(GCode &gcodegen);
|
||||
std::string post_toolchange(GCode &gcodegen);
|
||||
|
||||
private:
|
||||
int _get_temp(GCode &gcodegen);
|
||||
};
|
||||
|
||||
class Wipe {
|
||||
public:
|
||||
bool enable;
|
||||
Polyline path;
|
||||
|
||||
Wipe();
|
||||
bool has_path();
|
||||
void reset_path();
|
||||
std::string wipe(GCode &gcodegen, bool toolchange = false);
|
||||
};
|
||||
|
||||
class GCode {
|
||||
public:
|
||||
|
||||
/* Origin of print coordinates expressed in unscaled G-code coordinates.
|
||||
This affects the input arguments supplied to the extrude*() and travel_to()
|
||||
methods. */
|
||||
Pointf origin;
|
||||
FullPrintConfig config;
|
||||
GCodeWriter writer;
|
||||
PlaceholderParser* placeholder_parser;
|
||||
OozePrevention ooze_prevention;
|
||||
Wipe wipe;
|
||||
AvoidCrossingPerimeters avoid_crossing_perimeters;
|
||||
bool enable_loop_clipping;
|
||||
bool enable_cooling_markers;
|
||||
size_t layer_count;
|
||||
int layer_index; // just a counter
|
||||
const Layer* layer;
|
||||
std::map<const PrintObject*,Point> _seam_position;
|
||||
bool first_layer; // this flag triggers first layer speeds
|
||||
unsigned int elapsed_time; // seconds
|
||||
double volumetric_speed;
|
||||
|
||||
GCode();
|
||||
Point& last_pos();
|
||||
void set_last_pos(const Point &pos);
|
||||
bool last_pos_defined() const;
|
||||
void apply_print_config(const PrintConfig &print_config);
|
||||
void set_extruders(const std::vector<unsigned int> &extruder_ids);
|
||||
void set_origin(const Pointf &pointf);
|
||||
std::string preamble();
|
||||
std::string change_layer(const Layer &layer);
|
||||
std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1);
|
||||
std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1);
|
||||
std::string extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
|
||||
std::string travel_to(const Point &point, ExtrusionRole role, std::string comment);
|
||||
bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone);
|
||||
std::string retract(bool toolchange = false);
|
||||
std::string unretract();
|
||||
std::string set_extruder(unsigned int extruder_id);
|
||||
Pointf point_to_gcode(const Point &point);
|
||||
|
||||
private:
|
||||
Point _last_pos;
|
||||
bool _last_pos_defined;
|
||||
std::string _extrude(ExtrusionPath path, std::string description = "", double speed = -1);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -504,7 +504,7 @@ MedialAxis::is_valid_edge(const VD::edge_type& edge) const
|
|||
//printf(" => too thin, skipping\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,12 @@ Layer::object()
|
|||
return this->_object;
|
||||
}
|
||||
|
||||
const PrintObject*
|
||||
Layer::object() const
|
||||
{
|
||||
return this->_object;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
Layer::region_count()
|
||||
|
|
|
@ -40,8 +40,7 @@ class LayerRegion
|
|||
|
||||
// collection of expolygons representing the bridged areas (thus not
|
||||
// needing support material)
|
||||
// (this could be just a Polygons object)
|
||||
ExPolygonCollection bridged;
|
||||
Polygons bridged;
|
||||
|
||||
// collection of polylines representing the unsupported bridge edges
|
||||
PolylineCollection unsupported_bridge_edges;
|
||||
|
@ -57,6 +56,9 @@ class LayerRegion
|
|||
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
|
||||
void merge_slices();
|
||||
void prepare_fill_surfaces();
|
||||
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
|
||||
void process_external_surfaces(const Layer* lower_layer);
|
||||
double infill_area_threshold() const;
|
||||
|
||||
private:
|
||||
Layer *_layer;
|
||||
|
@ -76,6 +78,7 @@ class Layer {
|
|||
size_t id() const;
|
||||
void set_id(size_t id);
|
||||
PrintObject* object();
|
||||
const PrintObject* object() const;
|
||||
|
||||
Layer *upper_layer;
|
||||
Layer *lower_layer;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "Layer.hpp"
|
||||
#include "BridgeDetector.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "PerimeterGenerator.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "Surface.hpp"
|
||||
|
||||
|
@ -53,6 +55,178 @@ LayerRegion::merge_slices()
|
|||
this->slices.surfaces.push_back(Surface(stInternal, *expoly));
|
||||
}
|
||||
|
||||
void
|
||||
LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
|
||||
{
|
||||
this->perimeters.clear();
|
||||
this->thin_fills.clear();
|
||||
|
||||
PerimeterGenerator g(
|
||||
// input:
|
||||
&slices,
|
||||
this->layer()->height,
|
||||
this->flow(frPerimeter),
|
||||
&this->region()->config,
|
||||
&this->layer()->object()->config,
|
||||
&this->layer()->object()->print()->config,
|
||||
|
||||
// output:
|
||||
&this->perimeters,
|
||||
&this->thin_fills,
|
||||
fill_surfaces
|
||||
);
|
||||
|
||||
if (this->layer()->lower_layer != NULL)
|
||||
g.lower_slices = &this->layer()->lower_layer->slices;
|
||||
|
||||
g.layer_id = this->layer()->id();
|
||||
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
|
||||
g.overhang_flow = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object());
|
||||
g.solid_infill_flow = this->flow(frSolidInfill);
|
||||
|
||||
g.process();
|
||||
}
|
||||
|
||||
void
|
||||
LayerRegion::process_external_surfaces(const Layer* lower_layer)
|
||||
{
|
||||
const Surfaces &surfaces = this->fill_surfaces.surfaces;
|
||||
const double margin = scale_(EXTERNAL_INFILL_MARGIN);
|
||||
|
||||
SurfaceCollection bottom;
|
||||
for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
|
||||
if (!surface->is_bottom()) continue;
|
||||
|
||||
ExPolygons grown = offset_ex(surface->expolygon, +margin);
|
||||
|
||||
/* detect bridge direction before merging grown surfaces otherwise adjacent bridges
|
||||
would get merged into a single one while they need different directions
|
||||
also, supply the original expolygon instead of the grown one, because in case
|
||||
of very thin (but still working) anchors, the grown expolygon would go beyond them */
|
||||
double angle = -1;
|
||||
if (lower_layer != NULL) {
|
||||
BridgeDetector bd(
|
||||
surface->expolygon,
|
||||
lower_layer->slices,
|
||||
this->flow(frInfill, this->layer()->height, true).scaled_width()
|
||||
);
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("Processing bridge at layer %zu:\n", this->layer()->id();
|
||||
#endif
|
||||
|
||||
if (bd.detect_angle() && this->layer()->object()->config.support_material) {
|
||||
angle = bd.angle;
|
||||
|
||||
Polygons coverage = bd.coverage();
|
||||
this->bridged.insert(this->bridged.end(), coverage.begin(), coverage.end());
|
||||
this->unsupported_bridge_edges.append(bd.unsupported_edges());
|
||||
}
|
||||
}
|
||||
|
||||
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) {
|
||||
Surface s = *surface;
|
||||
s.expolygon = *it;
|
||||
s.bridge_angle = angle;
|
||||
bottom.surfaces.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
SurfaceCollection top;
|
||||
for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
|
||||
if (surface->surface_type != stTop) continue;
|
||||
|
||||
// give priority to bottom surfaces
|
||||
ExPolygons grown = diff_ex(
|
||||
offset(surface->expolygon, +margin),
|
||||
(Polygons)bottom
|
||||
);
|
||||
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) {
|
||||
Surface s = *surface;
|
||||
s.expolygon = *it;
|
||||
top.surfaces.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* if we're slicing with no infill, we can't extend external surfaces
|
||||
over non-existent infill */
|
||||
SurfaceCollection fill_boundaries;
|
||||
if (this->region()->config.fill_density.value > 0) {
|
||||
fill_boundaries = SurfaceCollection(surfaces);
|
||||
} else {
|
||||
for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it) {
|
||||
if (it->surface_type != stInternal)
|
||||
fill_boundaries.surfaces.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
// intersect the grown surfaces with the actual fill boundaries
|
||||
SurfaceCollection new_surfaces;
|
||||
{
|
||||
// merge top and bottom in a single collection
|
||||
SurfaceCollection tb = top;
|
||||
tb.surfaces.insert(tb.surfaces.end(), bottom.surfaces.begin(), bottom.surfaces.end());
|
||||
|
||||
// group surfaces
|
||||
std::vector<SurfacesPtr> groups;
|
||||
tb.group(&groups);
|
||||
|
||||
for (std::vector<SurfacesPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
|
||||
Polygons subject;
|
||||
for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) {
|
||||
Polygons pp = **s;
|
||||
subject.insert(subject.end(), pp.begin(), pp.end());
|
||||
}
|
||||
|
||||
ExPolygons expp = intersection_ex(
|
||||
subject,
|
||||
(Polygons)fill_boundaries,
|
||||
true // to ensure adjacent expolygons are unified
|
||||
);
|
||||
|
||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
|
||||
Surface s = *g->front();
|
||||
s.expolygon = *ex;
|
||||
new_surfaces.surfaces.push_back(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* subtract the new top surfaces from the other non-top surfaces and re-add them */
|
||||
{
|
||||
SurfaceCollection other;
|
||||
for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
|
||||
if (s->surface_type != stTop && !s->is_bottom())
|
||||
other.surfaces.push_back(*s);
|
||||
}
|
||||
|
||||
// group surfaces
|
||||
std::vector<SurfacesPtr> groups;
|
||||
other.group(&groups);
|
||||
|
||||
for (std::vector<SurfacesPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
|
||||
Polygons subject;
|
||||
for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) {
|
||||
Polygons pp = **s;
|
||||
subject.insert(subject.end(), pp.begin(), pp.end());
|
||||
}
|
||||
|
||||
ExPolygons expp = diff_ex(
|
||||
subject,
|
||||
(Polygons)new_surfaces
|
||||
);
|
||||
|
||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
|
||||
Surface s = *g->front();
|
||||
s.expolygon = *ex;
|
||||
new_surfaces.surfaces.push_back(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->fill_surfaces = new_surfaces;
|
||||
}
|
||||
|
||||
void
|
||||
LayerRegion::prepare_fill_surfaces()
|
||||
{
|
||||
|
@ -90,6 +264,13 @@ LayerRegion::prepare_fill_surfaces()
|
|||
}
|
||||
}
|
||||
|
||||
double
|
||||
LayerRegion::infill_area_threshold() const
|
||||
{
|
||||
double ss = this->flow(frSolidInfill).scaled_spacing();
|
||||
return ss*ss;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(LayerRegion, "Layer::Region");
|
||||
#endif
|
||||
|
|
|
@ -690,7 +690,8 @@ ModelVolume::assign_unique_material()
|
|||
{
|
||||
Model* model = this->get_object()->get_model();
|
||||
|
||||
this->_material_id = 1 + model->materials.size();
|
||||
// as material-id "0" is reserved by the AMF spec we start from 1
|
||||
this->_material_id = 1 + model->materials.size(); // watchout for implicit cast
|
||||
return model->add_material(this->_material_id);
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,24 @@ MultiPoint::remove_duplicate_points()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MultiPoint::append(const Point &point)
|
||||
{
|
||||
this->points.push_back(point);
|
||||
}
|
||||
|
||||
void
|
||||
MultiPoint::append(const Points &points)
|
||||
{
|
||||
this->append(points.begin(), points.end());
|
||||
}
|
||||
|
||||
void
|
||||
MultiPoint::append(const Points::const_iterator &begin, const Points::const_iterator &end)
|
||||
{
|
||||
this->points.insert(this->points.end(), begin, end);
|
||||
}
|
||||
|
||||
Points
|
||||
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
|
||||
{
|
||||
|
|
|
@ -33,6 +33,9 @@ class MultiPoint
|
|||
bool has_boundary_point(const Point &point) const;
|
||||
BoundingBox bounding_box() const;
|
||||
void remove_duplicate_points();
|
||||
void append(const Point &point);
|
||||
void append(const Points &points);
|
||||
void append(const Points::const_iterator &begin, const Points::const_iterator &end);
|
||||
|
||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||
|
||||
|
|
525
xs/src/libslic3r/PerimeterGenerator.cpp
Normal file
525
xs/src/libslic3r/PerimeterGenerator.cpp
Normal file
|
@ -0,0 +1,525 @@
|
|||
#include "PerimeterGenerator.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "ExtrusionEntityCollection.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void
|
||||
PerimeterGenerator::process()
|
||||
{
|
||||
// other perimeters
|
||||
this->_mm3_per_mm = this->perimeter_flow.mm3_per_mm();
|
||||
coord_t pwidth = this->perimeter_flow.scaled_width();
|
||||
coord_t pspacing = this->perimeter_flow.scaled_spacing();
|
||||
|
||||
// external perimeters
|
||||
this->_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm();
|
||||
coord_t ext_pwidth = this->ext_perimeter_flow.scaled_width();
|
||||
coord_t ext_pspacing = scale_(this->ext_perimeter_flow.spacing(this->perimeter_flow));
|
||||
|
||||
// overhang perimeters
|
||||
this->_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm();
|
||||
|
||||
// solid infill
|
||||
coord_t ispacing = this->solid_infill_flow.scaled_spacing();
|
||||
coord_t gap_area_threshold = pwidth * pwidth;
|
||||
|
||||
// Calculate the minimum required spacing between two adjacent traces.
|
||||
// This should be equal to the nominal flow spacing but we experiment
|
||||
// with some tolerance in order to avoid triggering medial axis when
|
||||
// some squishing might work. Loops are still spaced by the entire
|
||||
// flow spacing; this only applies to collapsing parts.
|
||||
coord_t min_spacing = pspacing * (1 - INSET_OVERLAP_TOLERANCE);
|
||||
coord_t ext_min_spacing = ext_pspacing * (1 - INSET_OVERLAP_TOLERANCE);
|
||||
|
||||
// prepare grown lower layer slices for overhang detection
|
||||
if (this->lower_slices != NULL && this->config->overhangs) {
|
||||
// We consider overhang any part where the entire nozzle diameter is not supported by the
|
||||
// lower layer, so we take lower slices and offset them by half the nozzle diameter used
|
||||
// in the current layer
|
||||
double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1);
|
||||
|
||||
this->_lower_slices_p = offset(*this->lower_slices, scale_(+nozzle_diameter/2));
|
||||
}
|
||||
|
||||
// we need to process each island separately because we might have different
|
||||
// extra perimeters for each one
|
||||
for (Surfaces::const_iterator surface = this->slices->surfaces.begin();
|
||||
surface != this->slices->surfaces.end(); ++surface) {
|
||||
// detect how many perimeters must be generated for this island
|
||||
signed short loop_number = this->config->perimeters + surface->extra_perimeters;
|
||||
loop_number--; // 0-indexed loops
|
||||
|
||||
Polygons gaps;
|
||||
|
||||
Polygons last = surface->expolygon.simplify_p(SCALED_RESOLUTION);
|
||||
if (loop_number >= 0) { // no loops = -1
|
||||
|
||||
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
|
||||
std::vector<PerimeterGeneratorLoops> holes(loop_number+1); // depth => loops
|
||||
Polylines thin_walls;
|
||||
|
||||
// we loop one time more than needed in order to find gaps after the last perimeter was applied
|
||||
for (signed short i = 0; i <= loop_number+1; ++i) { // outer loop is 0
|
||||
Polygons offsets;
|
||||
if (i == 0) {
|
||||
// the minimum thickness of a single loop is:
|
||||
// ext_width/2 + ext_spacing/2 + spacing/2 + width/2
|
||||
if (this->config->thin_walls) {
|
||||
offsets = offset2(
|
||||
last,
|
||||
-(0.5*ext_pwidth + 0.5*ext_min_spacing - 1),
|
||||
+(0.5*ext_min_spacing - 1)
|
||||
);
|
||||
} else {
|
||||
offsets = offset(last, -0.5*ext_pwidth);
|
||||
}
|
||||
|
||||
// look for thin walls
|
||||
if (this->config->thin_walls) {
|
||||
Polygons diffpp = diff(
|
||||
last,
|
||||
offset(offsets, +0.5*ext_pwidth),
|
||||
true // medial axis requires non-overlapping geometry
|
||||
);
|
||||
|
||||
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
||||
// (actually, something larger than that still may exist due to mitering or other causes)
|
||||
coord_t min_width = ext_pwidth / 2;
|
||||
ExPolygons expp = offset2_ex(diffpp, -min_width/2, +min_width/2);
|
||||
|
||||
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
|
||||
Polylines pp;
|
||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
|
||||
ex->medial_axis(ext_pwidth + ext_pspacing, min_width, &pp);
|
||||
|
||||
double threshold = ext_pwidth * ext_pwidth;
|
||||
for (Polylines::const_iterator p = pp.begin(); p != pp.end(); ++p) {
|
||||
if (p->length() > threshold) {
|
||||
thin_walls.push_back(*p);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf(" %zu thin walls detected\n", thin_walls.size());
|
||||
#endif
|
||||
|
||||
/*
|
||||
if (false) {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"medial_axis.svg",
|
||||
no_arrows => 1,
|
||||
#expolygons => \@expp,
|
||||
polylines => \@thin_walls,
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
coord_t distance = (i == 1) ? ext_pspacing : pspacing;
|
||||
|
||||
if (this->config->thin_walls) {
|
||||
offsets = offset2(
|
||||
last,
|
||||
-(distance + 0.5*min_spacing - 1),
|
||||
+(0.5*min_spacing - 1)
|
||||
);
|
||||
} else {
|
||||
offsets = offset(
|
||||
last,
|
||||
-distance
|
||||
);
|
||||
}
|
||||
|
||||
// look for gaps
|
||||
if (this->config->gap_fill_speed.value > 0 && this->config->fill_density.value > 0) {
|
||||
// not using safety offset here would "detect" very narrow gaps
|
||||
// (but still long enough to escape the area threshold) that gap fill
|
||||
// won't be able to fill but we'd still remove from infill area
|
||||
ExPolygons diff_expp = diff_ex(
|
||||
offset(last, -0.5*distance),
|
||||
offset(offsets, +0.5*distance + 10) // safety offset
|
||||
);
|
||||
for (ExPolygons::const_iterator ex = diff_expp.begin(); ex != diff_expp.end(); ++ex) {
|
||||
if (fabs(ex->area()) >= gap_area_threshold) {
|
||||
Polygons pp = *ex;
|
||||
gaps.insert(gaps.end(), pp.begin(), pp.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (offsets.empty()) break;
|
||||
if (i > loop_number) break; // we were only looking for gaps this time
|
||||
|
||||
last = offsets;
|
||||
for (Polygons::const_iterator polygon = offsets.begin(); polygon != offsets.end(); ++polygon) {
|
||||
PerimeterGeneratorLoop loop(*polygon, i);
|
||||
loop.is_contour = polygon->is_counter_clockwise();
|
||||
if (loop.is_contour) {
|
||||
contours[i].push_back(loop);
|
||||
} else {
|
||||
holes[i].push_back(loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nest loops: holes first
|
||||
for (signed short d = 0; d <= loop_number; ++d) {
|
||||
PerimeterGeneratorLoops &holes_d = holes[d];
|
||||
|
||||
// loop through all holes having depth == d
|
||||
for (signed short i = 0; i < holes_d.size(); ++i) {
|
||||
const PerimeterGeneratorLoop &loop = holes_d[i];
|
||||
|
||||
// find the hole loop that contains this one, if any
|
||||
for (signed short t = d+1; t <= loop_number; ++t) {
|
||||
for (signed short j = 0; j < holes[t].size(); ++j) {
|
||||
PerimeterGeneratorLoop &candidate_parent = holes[t][j];
|
||||
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
|
||||
candidate_parent.children.push_back(loop);
|
||||
holes_d.erase(holes_d.begin() + i);
|
||||
--i;
|
||||
goto NEXT_LOOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no hole contains this hole, find the contour loop that contains it
|
||||
for (signed short t = loop_number; t >= 0; --t) {
|
||||
for (signed short j = 0; j < contours[t].size(); ++j) {
|
||||
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
|
||||
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
|
||||
candidate_parent.children.push_back(loop);
|
||||
holes_d.erase(holes_d.begin() + i);
|
||||
--i;
|
||||
goto NEXT_LOOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
NEXT_LOOP: ;
|
||||
}
|
||||
}
|
||||
|
||||
// nest contour loops
|
||||
for (signed short d = loop_number; d >= 1; --d) {
|
||||
PerimeterGeneratorLoops &contours_d = contours[d];
|
||||
|
||||
// loop through all contours having depth == d
|
||||
for (signed short i = 0; i < contours_d.size(); ++i) {
|
||||
const PerimeterGeneratorLoop &loop = contours_d[i];
|
||||
|
||||
// find the contour loop that contains it
|
||||
for (signed short t = d-1; t >= 0; --t) {
|
||||
for (signed short j = 0; j < contours[t].size(); ++j) {
|
||||
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
|
||||
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
|
||||
candidate_parent.children.push_back(loop);
|
||||
contours_d.erase(contours_d.begin() + i);
|
||||
--i;
|
||||
goto NEXT_CONTOUR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NEXT_CONTOUR: ;
|
||||
}
|
||||
}
|
||||
|
||||
// at this point, all loops should be in contours[0]
|
||||
|
||||
ExtrusionEntityCollection entities = this->_traverse_loops(contours.front(), thin_walls);
|
||||
|
||||
// if brim will be printed, reverse the order of perimeters so that
|
||||
// we continue inwards after having finished the brim
|
||||
// TODO: add test for perimeter order
|
||||
if (this->config->external_perimeters_first
|
||||
|| (this->layer_id == 0 && this->print_config->brim_width.value > 0))
|
||||
entities.reverse();
|
||||
|
||||
// append perimeters for this slice as a collection
|
||||
if (!entities.empty())
|
||||
this->loops->append(entities);
|
||||
}
|
||||
|
||||
// fill gaps
|
||||
if (!gaps.empty()) {
|
||||
/*
|
||||
if (false) {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"gaps.svg",
|
||||
expolygons => union_ex(\@gaps),
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
// where $pwidth < thickness < 2*$pspacing, infill with width = 2*$pwidth
|
||||
// where 0.1*$pwidth < thickness < $pwidth, infill with width = 1*$pwidth
|
||||
std::vector<PerimeterGeneratorGapSize> gap_sizes;
|
||||
gap_sizes.push_back(PerimeterGeneratorGapSize(pwidth, 2*pspacing, unscale(2*pwidth)));
|
||||
gap_sizes.push_back(PerimeterGeneratorGapSize(0.1*pwidth, pwidth, unscale(1*pwidth)));
|
||||
|
||||
for (std::vector<PerimeterGeneratorGapSize>::const_iterator gap_size = gap_sizes.begin();
|
||||
gap_size != gap_sizes.end(); ++gap_size) {
|
||||
ExtrusionEntityCollection gap_fill = this->_fill_gaps(gap_size->min,
|
||||
gap_size->max, gap_size->width, gaps);
|
||||
this->gap_fill->append(gap_fill.entities);
|
||||
|
||||
// Make sure we don't infill narrow parts that are already gap-filled
|
||||
// (we only consider this surface's gaps to reduce the diff() complexity).
|
||||
// Growing actual extrusions ensures that gaps not filled by medial axis
|
||||
// are not subtracted from fill surfaces (they might be too short gaps
|
||||
// that medial axis skips but infill might join with other infill regions
|
||||
// and use zigzag).
|
||||
double dist = scale_(gap_size->width/2);
|
||||
Polygons filled;
|
||||
for (ExtrusionEntitiesPtr::const_iterator it = gap_fill.entities.begin();
|
||||
it != gap_fill.entities.end(); ++it)
|
||||
offset((*it)->as_polyline(), &filled, dist);
|
||||
|
||||
last = diff(last, filled);
|
||||
gaps = diff(gaps, filled); // prevent more gap fill here
|
||||
}
|
||||
}
|
||||
|
||||
// create one more offset to be used as boundary for fill
|
||||
// we offset by half the perimeter spacing (to get to the actual infill boundary)
|
||||
// and then we offset back and forth by half the infill spacing to only consider the
|
||||
// non-collapsing regions
|
||||
coord_t inset = 0;
|
||||
if (loop_number == 0) {
|
||||
// one loop
|
||||
inset += ext_pspacing/2;
|
||||
} else if (loop_number > 0) {
|
||||
// two or more loops
|
||||
inset += pspacing/2;
|
||||
}
|
||||
|
||||
// only apply infill overlap if we actually have one perimeter
|
||||
if (inset > 0)
|
||||
inset -= this->config->get_abs_value("infill_overlap", inset + ispacing/2);
|
||||
|
||||
{
|
||||
ExPolygons expp = union_ex(last);
|
||||
|
||||
// simplify infill contours according to resolution
|
||||
Polygons pp;
|
||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
|
||||
ex->simplify_p(SCALED_RESOLUTION, &pp);
|
||||
|
||||
// collapse too narrow infill areas
|
||||
coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE);
|
||||
expp = offset2_ex(
|
||||
pp,
|
||||
-inset -min_perimeter_infill_spacing/2,
|
||||
+min_perimeter_infill_spacing/2
|
||||
);
|
||||
|
||||
// append infill areas to fill_surfaces
|
||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
|
||||
this->fill_surfaces->surfaces.push_back(Surface(stInternal, *ex)); // use a bogus surface type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection
|
||||
PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
|
||||
Polylines &thin_walls) const
|
||||
{
|
||||
// loops is an arrayref of ::Loop objects
|
||||
// turn each one into an ExtrusionLoop object
|
||||
ExtrusionEntityCollection coll;
|
||||
for (PerimeterGeneratorLoops::const_iterator loop = loops.begin();
|
||||
loop != loops.end(); ++loop) {
|
||||
bool is_external = loop->is_external();
|
||||
|
||||
ExtrusionRole role;
|
||||
ExtrusionLoopRole loop_role;
|
||||
role = is_external ? erExternalPerimeter : erPerimeter;
|
||||
if (loop->is_internal_contour()) {
|
||||
// Note that we set loop role to ContourInternalPerimeter
|
||||
// also when loop is both internal and external (i.e.
|
||||
// there's only one contour loop).
|
||||
loop_role = elrContourInternalPerimeter;
|
||||
} else {
|
||||
loop_role = elrDefault;
|
||||
}
|
||||
|
||||
// detect overhanging/bridging perimeters
|
||||
ExtrusionPaths paths;
|
||||
if (this->config->overhangs && this->layer_id > 0
|
||||
&& !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) {
|
||||
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||
{
|
||||
Polylines polylines;
|
||||
intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
|
||||
|
||||
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
|
||||
ExtrusionPath path(role);
|
||||
path.polyline = *polyline;
|
||||
path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm;
|
||||
path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width;
|
||||
path.height = this->layer_height;
|
||||
paths.push_back(path);
|
||||
}
|
||||
}
|
||||
|
||||
// get overhang paths by checking what parts of this loop fall
|
||||
// outside the grown lower slices (thus where the distance between
|
||||
// the loop centerline and original lower slices is >= half nozzle diameter
|
||||
{
|
||||
Polylines polylines;
|
||||
diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
|
||||
|
||||
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
|
||||
ExtrusionPath path(erOverhangPerimeter);
|
||||
path.polyline = *polyline;
|
||||
path.mm3_per_mm = this->_mm3_per_mm_overhang;
|
||||
path.width = this->overhang_flow.width;
|
||||
path.height = this->overhang_flow.height;
|
||||
paths.push_back(path);
|
||||
}
|
||||
}
|
||||
|
||||
// reapply the nearest point search for starting point
|
||||
// We allow polyline reversal because Clipper may have randomly
|
||||
// reversed polylines during clipping.
|
||||
paths = ExtrusionEntityCollection(paths).chained_path();
|
||||
} else {
|
||||
ExtrusionPath path(role);
|
||||
path.polyline = loop->polygon.split_at_first_point();
|
||||
path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm;
|
||||
path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width;
|
||||
path.height = this->layer_height;
|
||||
paths.push_back(path);
|
||||
}
|
||||
|
||||
coll.append(ExtrusionLoop(paths, loop_role));
|
||||
}
|
||||
|
||||
// append thin walls to the nearest-neighbor search (only for first iteration)
|
||||
if (!thin_walls.empty()) {
|
||||
for (Polylines::const_iterator polyline = thin_walls.begin(); polyline != thin_walls.end(); ++polyline) {
|
||||
ExtrusionPath path(erExternalPerimeter);
|
||||
path.polyline = *polyline;
|
||||
path.mm3_per_mm = this->_mm3_per_mm;
|
||||
path.width = this->perimeter_flow.width;
|
||||
path.height = this->layer_height;
|
||||
coll.append(path);
|
||||
}
|
||||
|
||||
thin_walls.clear();
|
||||
}
|
||||
|
||||
// sort entities
|
||||
ExtrusionEntityCollection sorted_coll;
|
||||
coll.chained_path(&sorted_coll, false, &sorted_coll.orig_indices);
|
||||
|
||||
// traverse children
|
||||
ExtrusionEntityCollection entities;
|
||||
for (unsigned short i = 0; i < sorted_coll.orig_indices.size(); ++i) {
|
||||
size_t idx = sorted_coll.orig_indices[i];
|
||||
if (idx >= loops.size()) {
|
||||
// this is a thin wall
|
||||
// let's get it from the sorted collection as it might have been reversed
|
||||
entities.append(*sorted_coll.entities[i]);
|
||||
} else {
|
||||
const PerimeterGeneratorLoop &loop = loops[i];
|
||||
ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[idx]);
|
||||
|
||||
ExtrusionEntityCollection children = this->_traverse_loops(loop.children, thin_walls);
|
||||
if (loop.is_contour) {
|
||||
eloop.make_counter_clockwise();
|
||||
entities.append(children.entities);
|
||||
entities.append(eloop);
|
||||
} else {
|
||||
eloop.make_clockwise();
|
||||
entities.append(eloop);
|
||||
entities.append(children.entities);
|
||||
}
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection
|
||||
PerimeterGenerator::_fill_gaps(double min, double max, double w,
|
||||
const Polygons &gaps) const
|
||||
{
|
||||
ExtrusionEntityCollection coll;
|
||||
|
||||
min *= (1 - INSET_OVERLAP_TOLERANCE);
|
||||
|
||||
ExPolygons curr = diff_ex(
|
||||
offset2(gaps, -min/2, +min/2),
|
||||
offset2(gaps, -max/2, +max/2),
|
||||
true
|
||||
);
|
||||
|
||||
Polylines polylines;
|
||||
for (ExPolygons::const_iterator ex = curr.begin(); ex != curr.end(); ++ex)
|
||||
ex->medial_axis(max, min/2, &polylines);
|
||||
if (polylines.empty())
|
||||
return coll;
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
if (!curr.empty())
|
||||
printf(" %zu gaps filled with extrusion width = %f\n", curr.size(), w);
|
||||
#endif
|
||||
|
||||
//my $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 0, $w);
|
||||
Flow flow(
|
||||
w, this->layer_height, this->solid_infill_flow.nozzle_diameter
|
||||
);
|
||||
|
||||
double mm3_per_mm = flow.mm3_per_mm();
|
||||
|
||||
for (Polylines::const_iterator p = polylines.begin(); p != polylines.end(); ++p) {
|
||||
ExtrusionPath path(erGapFill);
|
||||
path.polyline = *p;
|
||||
path.mm3_per_mm = mm3_per_mm;
|
||||
path.width = flow.width;
|
||||
path.height = this->layer_height;
|
||||
|
||||
if (p->is_valid() && p->first_point().coincides_with(p->last_point())) {
|
||||
// since medial_axis() now returns only Polyline objects, detect loops here
|
||||
ExtrusionLoop loop;
|
||||
loop.paths.push_back(path);
|
||||
coll.append(loop);
|
||||
} else {
|
||||
coll.append(path);
|
||||
}
|
||||
}
|
||||
|
||||
return coll;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(PerimeterGenerator, "Layer::PerimeterGenerator");
|
||||
#endif
|
||||
|
||||
bool
|
||||
PerimeterGeneratorLoop::is_external() const
|
||||
{
|
||||
return this->depth == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
PerimeterGeneratorLoop::is_internal_contour() const
|
||||
{
|
||||
if (this->is_contour) {
|
||||
// an internal contour is a contour containing no other contours
|
||||
for (std::vector<PerimeterGeneratorLoop>::const_iterator loop = this->children.begin();
|
||||
loop != this->children.end(); ++loop) {
|
||||
if (loop->is_contour) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
84
xs/src/libslic3r/PerimeterGenerator.hpp
Normal file
84
xs/src/libslic3r/PerimeterGenerator.hpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
#ifndef slic3r_PerimeterGenerator_hpp_
|
||||
#define slic3r_PerimeterGenerator_hpp_
|
||||
|
||||
#include <myinit.h>
|
||||
#include <vector>
|
||||
#include "ExPolygonCollection.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "SurfaceCollection.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PerimeterGeneratorLoop;
|
||||
typedef std::vector<PerimeterGeneratorLoop> PerimeterGeneratorLoops;
|
||||
|
||||
class PerimeterGeneratorLoop {
|
||||
public:
|
||||
Polygon polygon;
|
||||
bool is_contour;
|
||||
unsigned short depth;
|
||||
std::vector<PerimeterGeneratorLoop> children;
|
||||
|
||||
PerimeterGeneratorLoop(Polygon polygon, unsigned short depth)
|
||||
: polygon(polygon), depth(depth), is_contour(false)
|
||||
{};
|
||||
bool is_external() const;
|
||||
bool is_internal_contour() const;
|
||||
};
|
||||
|
||||
class PerimeterGenerator {
|
||||
public:
|
||||
const SurfaceCollection* slices;
|
||||
const ExPolygonCollection* lower_slices;
|
||||
double layer_height;
|
||||
int layer_id;
|
||||
Flow perimeter_flow;
|
||||
Flow ext_perimeter_flow;
|
||||
Flow overhang_flow;
|
||||
Flow solid_infill_flow;
|
||||
PrintRegionConfig* config;
|
||||
PrintObjectConfig* object_config;
|
||||
PrintConfig* print_config;
|
||||
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)
|
||||
: slices(slices), lower_slices(NULL), layer_height(layer_height),
|
||||
perimeter_flow(flow), ext_perimeter_flow(flow), overhang_flow(flow),
|
||||
solid_infill_flow(flow), layer_id(-1),
|
||||
config(config), object_config(object_config), print_config(print_config),
|
||||
loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces),
|
||||
_ext_mm3_per_mm(-1), _mm3_per_mm(-1), _mm3_per_mm_overhang(-1)
|
||||
{};
|
||||
void process();
|
||||
|
||||
private:
|
||||
double _ext_mm3_per_mm;
|
||||
double _mm3_per_mm;
|
||||
double _mm3_per_mm_overhang;
|
||||
Polygons _lower_slices_p;
|
||||
|
||||
ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops,
|
||||
Polylines &thin_walls) const;
|
||||
ExtrusionEntityCollection _fill_gaps(double min, double max, double w,
|
||||
const Polygons &gaps) const;
|
||||
};
|
||||
|
||||
class PerimeterGeneratorGapSize {
|
||||
public:
|
||||
coord_t min;
|
||||
coord_t max;
|
||||
coord_t width;
|
||||
PerimeterGeneratorGapSize(coord_t min, coord_t max, coord_t width)
|
||||
: min(min), max(max), width(width) {};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -2,14 +2,16 @@
|
|||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <unistd.h> // provides **environ
|
||||
|
||||
extern char **environ;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
PlaceholderParser::PlaceholderParser()
|
||||
{
|
||||
this->_single["version"] = SLIC3R_VERSION;
|
||||
// TODO: port these methods to C++, then call them here
|
||||
// this->apply_env_variables();
|
||||
this->set("version", SLIC3R_VERSION);
|
||||
this->apply_env_variables();
|
||||
this->update_timestamp();
|
||||
}
|
||||
|
||||
|
@ -62,16 +64,32 @@ void PlaceholderParser::apply_config(DynamicPrintConfig &config)
|
|||
|
||||
if (const ConfigOptionVectorBase* optv = dynamic_cast<const ConfigOptionVectorBase*>(opt)) {
|
||||
// set placeholders for options with multiple values
|
||||
// TODO: treat [bed_shape] as single, not multiple
|
||||
this->set(key, optv->vserialize());
|
||||
} else if (const ConfigOptionPoint* optp = dynamic_cast<const ConfigOptionPoint*>(opt)) {
|
||||
this->_single[key] = optp->serialize();
|
||||
this->set(key, optp->serialize());
|
||||
|
||||
Pointf val = *optp;
|
||||
this->_multiple[key + "_X"] = val.x;
|
||||
this->_multiple[key + "_Y"] = val.y;
|
||||
this->set(key + "_X", val.x);
|
||||
this->set(key + "_Y", val.y);
|
||||
} else {
|
||||
// set single-value placeholders
|
||||
this->_single[key] = opt->serialize();
|
||||
this->set(key, opt->serialize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PlaceholderParser::apply_env_variables()
|
||||
{
|
||||
for (char** env = environ; *env; env++) {
|
||||
if (strncmp(*env, "SLIC3R_", 7) == 0) {
|
||||
std::stringstream ss(*env);
|
||||
std::string key, value;
|
||||
std::getline(ss, key, '=');
|
||||
ss >> value;
|
||||
|
||||
this->set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,18 +110,57 @@ PlaceholderParser::set(const std::string &key, int value)
|
|||
}
|
||||
|
||||
void
|
||||
PlaceholderParser::set(const std::string &key, const std::vector<std::string> &values)
|
||||
PlaceholderParser::set(const std::string &key, std::vector<std::string> values)
|
||||
{
|
||||
for (std::vector<std::string>::const_iterator v = values.begin(); v != values.end(); ++v) {
|
||||
if (values.empty()) {
|
||||
this->_multiple.erase(key);
|
||||
this->_single.erase(key);
|
||||
} else {
|
||||
this->_multiple[key] = values;
|
||||
this->_single[key] = values.front();
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
PlaceholderParser::process(std::string str) const
|
||||
{
|
||||
// replace single options, like [foo]
|
||||
for (t_strstr_map::const_iterator it = this->_single.begin(); it != this->_single.end(); ++it) {
|
||||
std::stringstream ss;
|
||||
ss << key << "_" << (v - values.begin());
|
||||
|
||||
this->_multiple[ ss.str() ] = *v;
|
||||
if (v == values.begin()) {
|
||||
this->_multiple[key] = *v;
|
||||
ss << '[' << it->first << ']';
|
||||
this->find_and_replace(str, ss.str(), it->second);
|
||||
}
|
||||
|
||||
// replace multiple options like [foo_0] by looping until we have enough values
|
||||
// or until a previous match was found (this handles non-existing indices reasonably
|
||||
// without a regex)
|
||||
for (t_strstrs_map::const_iterator it = this->_multiple.begin(); it != this->_multiple.end(); ++it) {
|
||||
const std::vector<std::string> &values = it->second;
|
||||
bool found = false;
|
||||
for (size_t i = 0; (i < values.size()) || found; ++i) {
|
||||
std::stringstream ss;
|
||||
ss << '[' << it->first << '_' << i << ']';
|
||||
if (i < values.size()) {
|
||||
found = this->find_and_replace(str, ss.str(), values[i]);
|
||||
} else {
|
||||
found = this->find_and_replace(str, ss.str(), values.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
this->_single.erase(key);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
bool
|
||||
PlaceholderParser::find_and_replace(std::string &source, std::string const &find, std::string const &replace) const
|
||||
{
|
||||
bool found = false;
|
||||
for (std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos; ) {
|
||||
source.replace(i, find.length(), replace);
|
||||
i += replace.length();
|
||||
found = true;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
|
|
|
@ -11,18 +11,26 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
typedef std::map<std::string, std::string> t_strstr_map;
|
||||
typedef std::map<std::string, std::vector<std::string> > t_strstrs_map;
|
||||
|
||||
class PlaceholderParser
|
||||
{
|
||||
public:
|
||||
std::map<std::string, std::string> _single;
|
||||
std::map<std::string, std::string> _multiple;
|
||||
|
||||
t_strstr_map _single;
|
||||
t_strstrs_map _multiple;
|
||||
|
||||
PlaceholderParser();
|
||||
void update_timestamp();
|
||||
void apply_config(DynamicPrintConfig &config);
|
||||
void apply_env_variables();
|
||||
void set(const std::string &key, const std::string &value);
|
||||
void set(const std::string &key, int value);
|
||||
void set(const std::string &key, const std::vector<std::string> &values);
|
||||
void set(const std::string &key, std::vector<std::string> values);
|
||||
std::string process(std::string str) const;
|
||||
|
||||
private:
|
||||
bool find_and_replace(std::string &source, std::string const &find, std::string const &replace) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -218,7 +218,8 @@ Polygon::wkt() const
|
|||
return wkt.str();
|
||||
}
|
||||
|
||||
// find all concave vertices (i.e. having an internal angle greater than the supplied angle) */
|
||||
// find all concave vertices (i.e. having an internal angle greater than the supplied angle)
|
||||
// (external = right side, thus we consider ccw orientation)
|
||||
Points
|
||||
Polygon::concave_points(double angle) const
|
||||
{
|
||||
|
@ -241,7 +242,8 @@ Polygon::concave_points(double angle) const
|
|||
return points;
|
||||
}
|
||||
|
||||
// find all convex vertices (i.e. having an internal angle smaller than the supplied angle) */
|
||||
// find all convex vertices (i.e. having an internal angle smaller than the supplied angle)
|
||||
// (external = right side, thus we consider ccw orientation)
|
||||
Points
|
||||
Polygon::convex_points(double angle) const
|
||||
{
|
||||
|
|
|
@ -50,6 +50,12 @@ PolylineCollection::leftmost_point() const
|
|||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
PolylineCollection::append(const Polylines &pp)
|
||||
{
|
||||
this->polylines.insert(this->polylines.end(), pp.begin(), pp.end());
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(PolylineCollection, "Polyline::Collection");
|
||||
#endif
|
||||
|
|
|
@ -13,6 +13,7 @@ class PolylineCollection
|
|||
void chained_path(PolylineCollection* retval, bool no_reverse = false) const;
|
||||
void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const;
|
||||
Point leftmost_point() const;
|
||||
void append(const Polylines &polylines);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ class PrintRegion
|
|||
|
||||
private:
|
||||
Print* _print;
|
||||
|
||||
|
||||
PrintRegion(Print* print);
|
||||
~PrintRegion();
|
||||
};
|
||||
|
@ -135,6 +135,7 @@ class PrintObject
|
|||
bool invalidate_all_steps();
|
||||
|
||||
bool has_support_material() const;
|
||||
void process_external_surfaces();
|
||||
void bridge_over_infill();
|
||||
|
||||
private:
|
||||
|
|
|
@ -151,10 +151,11 @@ PrintConfigDef::build_def() {
|
|||
Options["external_perimeter_speed"].type = coFloatOrPercent;
|
||||
Options["external_perimeter_speed"].label = "External perimeters";
|
||||
Options["external_perimeter_speed"].category = "Speed";
|
||||
Options["external_perimeter_speed"].tooltip = "This separate setting will affect the speed of external perimeters (the visible ones). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above.";
|
||||
Options["external_perimeter_speed"].tooltip = "This separate setting will affect the speed of external perimeters (the visible ones). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above. Set to zero for auto.";
|
||||
Options["external_perimeter_speed"].sidetext = "mm/s or %";
|
||||
Options["external_perimeter_speed"].cli = "external-perimeter-speed=s";
|
||||
Options["external_perimeter_speed"].ratio_over = "perimeter_speed";
|
||||
Options["external_perimeter_speed"].min = 0;
|
||||
|
||||
Options["external_perimeters_first"].type = coBool;
|
||||
Options["external_perimeters_first"].label = "External perimeters first";
|
||||
|
@ -300,6 +301,7 @@ PrintConfigDef::build_def() {
|
|||
Options["fill_pattern"].cli = "fill-pattern=s";
|
||||
Options["fill_pattern"].enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
Options["fill_pattern"].enum_values.push_back("rectilinear");
|
||||
Options["fill_pattern"].enum_values.push_back("grid");
|
||||
Options["fill_pattern"].enum_values.push_back("line");
|
||||
Options["fill_pattern"].enum_values.push_back("concentric");
|
||||
Options["fill_pattern"].enum_values.push_back("honeycomb");
|
||||
|
@ -308,6 +310,7 @@ PrintConfigDef::build_def() {
|
|||
Options["fill_pattern"].enum_values.push_back("archimedeanchords");
|
||||
Options["fill_pattern"].enum_values.push_back("octagramspiral");
|
||||
Options["fill_pattern"].enum_labels.push_back("Rectilinear");
|
||||
Options["fill_pattern"].enum_labels.push_back("Grid");
|
||||
Options["fill_pattern"].enum_labels.push_back("Line");
|
||||
Options["fill_pattern"].enum_labels.push_back("Concentric");
|
||||
Options["fill_pattern"].enum_labels.push_back("Honeycomb");
|
||||
|
@ -350,6 +353,7 @@ PrintConfigDef::build_def() {
|
|||
Options["first_layer_speed"].tooltip = "If expressed as absolute value in mm/s, this speed will be applied to all the print moves of the first layer, regardless of their type. If expressed as a percentage (for example: 40%) it will scale the default speeds.";
|
||||
Options["first_layer_speed"].sidetext = "mm/s or %";
|
||||
Options["first_layer_speed"].cli = "first-layer-speed=s";
|
||||
Options["first_layer_speed"].min = 0;
|
||||
|
||||
Options["first_layer_temperature"].type = coInts;
|
||||
Options["first_layer_temperature"].label = "First layer";
|
||||
|
@ -448,7 +452,7 @@ PrintConfigDef::build_def() {
|
|||
Options["infill_speed"].type = coFloat;
|
||||
Options["infill_speed"].label = "Infill";
|
||||
Options["infill_speed"].category = "Speed";
|
||||
Options["infill_speed"].tooltip = "Speed for printing the internal fill.";
|
||||
Options["infill_speed"].tooltip = "Speed for printing the internal fill. Set to zero for auto.";
|
||||
Options["infill_speed"].sidetext = "mm/s";
|
||||
Options["infill_speed"].cli = "infill-speed=f";
|
||||
Options["infill_speed"].aliases.push_back("print_feed_rate");
|
||||
|
@ -499,7 +503,20 @@ PrintConfigDef::build_def() {
|
|||
Options["min_print_speed"].sidetext = "mm/s";
|
||||
Options["min_print_speed"].cli = "min-print-speed=f";
|
||||
Options["min_print_speed"].min = 0;
|
||||
Options["min_print_speed"].max = 1000;
|
||||
|
||||
Options["max_print_speed"].type = coFloat;
|
||||
Options["max_print_speed"].label = "Max print speed";
|
||||
Options["max_print_speed"].tooltip = "When setting other speed settings to 0 Slic3r will autocalculate the optimal speed in order to keep constant extruder pressure. This experimental setting is used to set the highest print speed you want to allow.";
|
||||
Options["max_print_speed"].sidetext = "mm/s";
|
||||
Options["max_print_speed"].cli = "max-print-speed=f";
|
||||
Options["max_print_speed"].min = 1;
|
||||
|
||||
Options["max_volumetric_speed"].type = coFloat;
|
||||
Options["max_volumetric_speed"].label = "Max volumetric speed";
|
||||
Options["max_volumetric_speed"].tooltip = "This experimental setting is used to set the maximum volumetric speed your extruder supports.";
|
||||
Options["max_volumetric_speed"].sidetext = "mm³/s";
|
||||
Options["max_volumetric_speed"].cli = "max-volumetric-speed=f";
|
||||
Options["max_volumetric_speed"].min = 0;
|
||||
|
||||
Options["min_skirt_length"].type = coFloat;
|
||||
Options["min_skirt_length"].label = "Minimum extrusion length";
|
||||
|
@ -579,7 +596,7 @@ PrintConfigDef::build_def() {
|
|||
Options["perimeter_speed"].type = coFloat;
|
||||
Options["perimeter_speed"].label = "Perimeters";
|
||||
Options["perimeter_speed"].category = "Speed";
|
||||
Options["perimeter_speed"].tooltip = "Speed for perimeters (contours, aka vertical shells).";
|
||||
Options["perimeter_speed"].tooltip = "Speed for perimeters (contours, aka vertical shells). Set to zero for auto.";
|
||||
Options["perimeter_speed"].sidetext = "mm/s";
|
||||
Options["perimeter_speed"].cli = "perimeter-speed=f";
|
||||
Options["perimeter_speed"].aliases.push_back("perimeter_feed_rate");
|
||||
|
@ -637,12 +654,14 @@ PrintConfigDef::build_def() {
|
|||
|
||||
Options["retract_length"].type = coFloats;
|
||||
Options["retract_length"].label = "Length";
|
||||
Options["retract_length"].full_label = "Retraction Length";
|
||||
Options["retract_length"].tooltip = "When retraction is triggered, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder).";
|
||||
Options["retract_length"].sidetext = "mm (zero to disable)";
|
||||
Options["retract_length"].cli = "retract-length=f@";
|
||||
|
||||
Options["retract_length_toolchange"].type = coFloats;
|
||||
Options["retract_length_toolchange"].label = "Length";
|
||||
Options["retract_length_toolchange"].full_label = "Retraction Length (Toolchange)";
|
||||
Options["retract_length_toolchange"].tooltip = "When retraction is triggered before changing tool, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder).";
|
||||
Options["retract_length_toolchange"].sidetext = "mm (zero to disable)";
|
||||
Options["retract_length_toolchange"].cli = "retract-length-toolchange=f@";
|
||||
|
@ -667,10 +686,10 @@ PrintConfigDef::build_def() {
|
|||
|
||||
Options["retract_speed"].type = coInts;
|
||||
Options["retract_speed"].label = "Speed";
|
||||
Options["retract_speed"].full_label = "Retraction Speed";
|
||||
Options["retract_speed"].tooltip = "The speed for retractions (it only applies to the extruder motor).";
|
||||
Options["retract_speed"].sidetext = "mm/s";
|
||||
Options["retract_speed"].cli = "retract-speed=f@";
|
||||
Options["retract_speed"].max = 1000;
|
||||
|
||||
Options["seam_position"].type = coEnum;
|
||||
Options["seam_position"].label = "Seam position";
|
||||
|
@ -719,6 +738,7 @@ PrintConfigDef::build_def() {
|
|||
|
||||
Options["skirts"].type = coInt;
|
||||
Options["skirts"].label = "Loops (minimum)";
|
||||
Options["skirts"].full_label = "Skirt Loops";
|
||||
Options["skirts"].tooltip = "Number of loops for the skirt. If the Minimum Extrusion Length option is set, the number of loops might be greater than the one configured here. Set this to zero to disable skirt completely.";
|
||||
Options["skirts"].cli = "skirts=i";
|
||||
Options["skirts"].min = 0;
|
||||
|
@ -735,10 +755,11 @@ PrintConfigDef::build_def() {
|
|||
Options["small_perimeter_speed"].type = coFloatOrPercent;
|
||||
Options["small_perimeter_speed"].label = "Small perimeters";
|
||||
Options["small_perimeter_speed"].category = "Speed";
|
||||
Options["small_perimeter_speed"].tooltip = "This separate setting will affect the speed of perimeters having radius <= 6.5mm (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above.";
|
||||
Options["small_perimeter_speed"].tooltip = "This separate setting will affect the speed of perimeters having radius <= 6.5mm (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above. Set to zero for auto.";
|
||||
Options["small_perimeter_speed"].sidetext = "mm/s or %";
|
||||
Options["small_perimeter_speed"].cli = "small-perimeter-speed=s";
|
||||
Options["small_perimeter_speed"].ratio_over = "perimeter_speed";
|
||||
Options["small_perimeter_speed"].min = 0;
|
||||
|
||||
Options["solid_infill_below_area"].type = coFloat;
|
||||
Options["solid_infill_below_area"].label = "Solid infill threshold area";
|
||||
|
@ -773,11 +794,12 @@ PrintConfigDef::build_def() {
|
|||
Options["solid_infill_speed"].type = coFloatOrPercent;
|
||||
Options["solid_infill_speed"].label = "Solid infill";
|
||||
Options["solid_infill_speed"].category = "Speed";
|
||||
Options["solid_infill_speed"].tooltip = "Speed for printing solid regions (top/bottom/internal horizontal shells). This can be expressed as a percentage (for example: 80%) over the default infill speed above.";
|
||||
Options["solid_infill_speed"].tooltip = "Speed for printing solid regions (top/bottom/internal horizontal shells). This can be expressed as a percentage (for example: 80%) over the default infill speed above. Set to zero for auto.";
|
||||
Options["solid_infill_speed"].sidetext = "mm/s or %";
|
||||
Options["solid_infill_speed"].cli = "solid-infill-speed=s";
|
||||
Options["solid_infill_speed"].ratio_over = "infill_speed";
|
||||
Options["solid_infill_speed"].aliases.push_back("solid_infill_feed_rate");
|
||||
Options["solid_infill_speed"].min = 0;
|
||||
|
||||
Options["solid_layers"].type = coInt;
|
||||
Options["solid_layers"].label = "Solid layers";
|
||||
|
@ -889,6 +911,7 @@ PrintConfigDef::build_def() {
|
|||
Options["support_material_interface_speed"].sidetext = "mm/s or %";
|
||||
Options["support_material_interface_speed"].cli = "support-material-interface-speed=s";
|
||||
Options["support_material_interface_speed"].ratio_over = "support_material_speed";
|
||||
Options["support_material_interface_speed"].min = 0;
|
||||
|
||||
Options["support_material_pattern"].type = coEnum;
|
||||
Options["support_material_pattern"].label = "Pattern";
|
||||
|
@ -970,10 +993,11 @@ PrintConfigDef::build_def() {
|
|||
Options["top_solid_infill_speed"].type = coFloatOrPercent;
|
||||
Options["top_solid_infill_speed"].label = "Top solid infill";
|
||||
Options["top_solid_infill_speed"].category = "Speed";
|
||||
Options["top_solid_infill_speed"].tooltip = "Speed for printing top solid layers (it only applies to the uppermost external layers and not to their internal solid layers). You may want to slow down this to get a nicer surface finish. This can be expressed as a percentage (for example: 80%) over the solid infill speed above.";
|
||||
Options["top_solid_infill_speed"].tooltip = "Speed for printing top solid layers (it only applies to the uppermost external layers and not to their internal solid layers). You may want to slow down this to get a nicer surface finish. This can be expressed as a percentage (for example: 80%) over the solid infill speed above. Set to zero for auto.";
|
||||
Options["top_solid_infill_speed"].sidetext = "mm/s or %";
|
||||
Options["top_solid_infill_speed"].cli = "top-solid-infill-speed=s";
|
||||
Options["top_solid_infill_speed"].ratio_over = "solid_infill_speed";
|
||||
Options["top_solid_infill_speed"].min = 0;
|
||||
|
||||
Options["top_solid_layers"].type = coInt;
|
||||
Options["top_solid_layers"].label = "Top";
|
||||
|
@ -989,7 +1013,7 @@ PrintConfigDef::build_def() {
|
|||
Options["travel_speed"].sidetext = "mm/s";
|
||||
Options["travel_speed"].cli = "travel-speed=f";
|
||||
Options["travel_speed"].aliases.push_back("travel_feed_rate");
|
||||
Options["travel_speed"].min = 0;
|
||||
Options["travel_speed"].min = 1;
|
||||
|
||||
Options["use_firmware_retraction"].type = coBool;
|
||||
Options["use_firmware_retraction"].label = "Use firmware retraction";
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "Config.hpp"
|
||||
|
||||
#define OPT_PTR(KEY) if (opt_key == #KEY) return &this->KEY
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum GCodeFlavor {
|
||||
|
@ -10,7 +12,7 @@ enum GCodeFlavor {
|
|||
};
|
||||
|
||||
enum InfillPattern {
|
||||
ipRectilinear, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipRectilinear, ipGrid, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
|
||||
};
|
||||
|
||||
|
@ -37,6 +39,7 @@ template<> inline t_config_enum_values ConfigOptionEnum<GCodeFlavor>::get_enum_v
|
|||
template<> inline t_config_enum_values ConfigOptionEnum<InfillPattern>::get_enum_values() {
|
||||
t_config_enum_values keys_map;
|
||||
keys_map["rectilinear"] = ipRectilinear;
|
||||
keys_map["grid"] = ipGrid;
|
||||
keys_map["line"] = ipLine;
|
||||
keys_map["concentric"] = ipConcentric;
|
||||
keys_map["honeycomb"] = ipHoneycomb;
|
||||
|
@ -148,29 +151,29 @@ class PrintObjectConfig : public virtual StaticPrintConfig
|
|||
};
|
||||
|
||||
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
|
||||
if (opt_key == "dont_support_bridges") return &this->dont_support_bridges;
|
||||
if (opt_key == "extrusion_width") return &this->extrusion_width;
|
||||
if (opt_key == "first_layer_height") return &this->first_layer_height;
|
||||
if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed;
|
||||
if (opt_key == "interface_shells") return &this->interface_shells;
|
||||
if (opt_key == "layer_height") return &this->layer_height;
|
||||
if (opt_key == "raft_layers") return &this->raft_layers;
|
||||
if (opt_key == "seam_position") return &this->seam_position;
|
||||
if (opt_key == "support_material") return &this->support_material;
|
||||
if (opt_key == "support_material_angle") return &this->support_material_angle;
|
||||
if (opt_key == "support_material_contact_distance") return &this->support_material_contact_distance;
|
||||
if (opt_key == "support_material_enforce_layers") return &this->support_material_enforce_layers;
|
||||
if (opt_key == "support_material_extruder") return &this->support_material_extruder;
|
||||
if (opt_key == "support_material_extrusion_width") return &this->support_material_extrusion_width;
|
||||
if (opt_key == "support_material_interface_extruder") return &this->support_material_interface_extruder;
|
||||
if (opt_key == "support_material_interface_layers") return &this->support_material_interface_layers;
|
||||
if (opt_key == "support_material_interface_spacing") return &this->support_material_interface_spacing;
|
||||
if (opt_key == "support_material_interface_speed") return &this->support_material_interface_speed;
|
||||
if (opt_key == "support_material_pattern") return &this->support_material_pattern;
|
||||
if (opt_key == "support_material_spacing") return &this->support_material_spacing;
|
||||
if (opt_key == "support_material_speed") return &this->support_material_speed;
|
||||
if (opt_key == "support_material_threshold") return &this->support_material_threshold;
|
||||
if (opt_key == "xy_size_compensation") return &this->xy_size_compensation;
|
||||
OPT_PTR(dont_support_bridges);
|
||||
OPT_PTR(extrusion_width);
|
||||
OPT_PTR(first_layer_height);
|
||||
OPT_PTR(infill_only_where_needed);
|
||||
OPT_PTR(interface_shells);
|
||||
OPT_PTR(layer_height);
|
||||
OPT_PTR(raft_layers);
|
||||
OPT_PTR(seam_position);
|
||||
OPT_PTR(support_material);
|
||||
OPT_PTR(support_material_angle);
|
||||
OPT_PTR(support_material_contact_distance);
|
||||
OPT_PTR(support_material_enforce_layers);
|
||||
OPT_PTR(support_material_extruder);
|
||||
OPT_PTR(support_material_extrusion_width);
|
||||
OPT_PTR(support_material_interface_extruder);
|
||||
OPT_PTR(support_material_interface_layers);
|
||||
OPT_PTR(support_material_interface_spacing);
|
||||
OPT_PTR(support_material_interface_speed);
|
||||
OPT_PTR(support_material_pattern);
|
||||
OPT_PTR(support_material_spacing);
|
||||
OPT_PTR(support_material_speed);
|
||||
OPT_PTR(support_material_threshold);
|
||||
OPT_PTR(xy_size_compensation);
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
@ -258,38 +261,38 @@ class PrintRegionConfig : public virtual StaticPrintConfig
|
|||
};
|
||||
|
||||
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
|
||||
if (opt_key == "bottom_solid_layers") return &this->bottom_solid_layers;
|
||||
if (opt_key == "bridge_flow_ratio") return &this->bridge_flow_ratio;
|
||||
if (opt_key == "bridge_speed") return &this->bridge_speed;
|
||||
if (opt_key == "external_fill_pattern") return &this->external_fill_pattern;
|
||||
if (opt_key == "external_perimeter_extrusion_width") return &this->external_perimeter_extrusion_width;
|
||||
if (opt_key == "external_perimeter_speed") return &this->external_perimeter_speed;
|
||||
if (opt_key == "external_perimeters_first") return &this->external_perimeters_first;
|
||||
if (opt_key == "extra_perimeters") return &this->extra_perimeters;
|
||||
if (opt_key == "fill_angle") return &this->fill_angle;
|
||||
if (opt_key == "fill_density") return &this->fill_density;
|
||||
if (opt_key == "fill_pattern") return &this->fill_pattern;
|
||||
if (opt_key == "gap_fill_speed") return &this->gap_fill_speed;
|
||||
if (opt_key == "infill_extruder") return &this->infill_extruder;
|
||||
if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width;
|
||||
if (opt_key == "infill_every_layers") return &this->infill_every_layers;
|
||||
if (opt_key == "infill_overlap") return &this->infill_overlap;
|
||||
if (opt_key == "infill_speed") return &this->infill_speed;
|
||||
if (opt_key == "overhangs") return &this->overhangs;
|
||||
if (opt_key == "perimeter_extruder") return &this->perimeter_extruder;
|
||||
if (opt_key == "perimeter_extrusion_width") return &this->perimeter_extrusion_width;
|
||||
if (opt_key == "perimeter_speed") return &this->perimeter_speed;
|
||||
if (opt_key == "perimeters") return &this->perimeters;
|
||||
if (opt_key == "small_perimeter_speed") return &this->small_perimeter_speed;
|
||||
if (opt_key == "solid_infill_below_area") return &this->solid_infill_below_area;
|
||||
if (opt_key == "solid_infill_extruder") return &this->solid_infill_extruder;
|
||||
if (opt_key == "solid_infill_extrusion_width") return &this->solid_infill_extrusion_width;
|
||||
if (opt_key == "solid_infill_every_layers") return &this->solid_infill_every_layers;
|
||||
if (opt_key == "solid_infill_speed") return &this->solid_infill_speed;
|
||||
if (opt_key == "thin_walls") return &this->thin_walls;
|
||||
if (opt_key == "top_infill_extrusion_width") return &this->top_infill_extrusion_width;
|
||||
if (opt_key == "top_solid_infill_speed") return &this->top_solid_infill_speed;
|
||||
if (opt_key == "top_solid_layers") return &this->top_solid_layers;
|
||||
OPT_PTR(bottom_solid_layers);
|
||||
OPT_PTR(bridge_flow_ratio);
|
||||
OPT_PTR(bridge_speed);
|
||||
OPT_PTR(external_fill_pattern);
|
||||
OPT_PTR(external_perimeter_extrusion_width);
|
||||
OPT_PTR(external_perimeter_speed);
|
||||
OPT_PTR(external_perimeters_first);
|
||||
OPT_PTR(extra_perimeters);
|
||||
OPT_PTR(fill_angle);
|
||||
OPT_PTR(fill_density);
|
||||
OPT_PTR(fill_pattern);
|
||||
OPT_PTR(gap_fill_speed);
|
||||
OPT_PTR(infill_extruder);
|
||||
OPT_PTR(infill_extrusion_width);
|
||||
OPT_PTR(infill_every_layers);
|
||||
OPT_PTR(infill_overlap);
|
||||
OPT_PTR(infill_speed);
|
||||
OPT_PTR(overhangs);
|
||||
OPT_PTR(perimeter_extruder);
|
||||
OPT_PTR(perimeter_extrusion_width);
|
||||
OPT_PTR(perimeter_speed);
|
||||
OPT_PTR(perimeters);
|
||||
OPT_PTR(small_perimeter_speed);
|
||||
OPT_PTR(solid_infill_below_area);
|
||||
OPT_PTR(solid_infill_extruder);
|
||||
OPT_PTR(solid_infill_extrusion_width);
|
||||
OPT_PTR(solid_infill_every_layers);
|
||||
OPT_PTR(solid_infill_speed);
|
||||
OPT_PTR(thin_walls);
|
||||
OPT_PTR(top_infill_extrusion_width);
|
||||
OPT_PTR(top_solid_infill_speed);
|
||||
OPT_PTR(top_solid_layers);
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
@ -306,6 +309,8 @@ class GCodeConfig : public virtual StaticPrintConfig
|
|||
ConfigOptionBool gcode_comments;
|
||||
ConfigOptionEnum<GCodeFlavor> gcode_flavor;
|
||||
ConfigOptionString layer_gcode;
|
||||
ConfigOptionFloat max_print_speed;
|
||||
ConfigOptionFloat max_volumetric_speed;
|
||||
ConfigOptionFloat pressure_advance;
|
||||
ConfigOptionFloats retract_length;
|
||||
ConfigOptionFloats retract_length_toolchange;
|
||||
|
@ -331,6 +336,8 @@ class GCodeConfig : public virtual StaticPrintConfig
|
|||
this->gcode_comments.value = false;
|
||||
this->gcode_flavor.value = gcfRepRap;
|
||||
this->layer_gcode.value = "";
|
||||
this->max_print_speed.value = 80;
|
||||
this->max_volumetric_speed.value = 0;
|
||||
this->pressure_advance.value = 0;
|
||||
this->retract_length.values.resize(1);
|
||||
this->retract_length.values[0] = 2;
|
||||
|
@ -353,27 +360,29 @@ class GCodeConfig : public virtual StaticPrintConfig
|
|||
};
|
||||
|
||||
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
|
||||
if (opt_key == "before_layer_gcode") return &this->before_layer_gcode;
|
||||
if (opt_key == "end_gcode") return &this->end_gcode;
|
||||
if (opt_key == "extrusion_axis") return &this->extrusion_axis;
|
||||
if (opt_key == "extrusion_multiplier") return &this->extrusion_multiplier;
|
||||
if (opt_key == "filament_diameter") return &this->filament_diameter;
|
||||
if (opt_key == "gcode_comments") return &this->gcode_comments;
|
||||
if (opt_key == "gcode_flavor") return &this->gcode_flavor;
|
||||
if (opt_key == "layer_gcode") return &this->layer_gcode;
|
||||
if (opt_key == "pressure_advance") return &this->pressure_advance;
|
||||
if (opt_key == "retract_length") return &this->retract_length;
|
||||
if (opt_key == "retract_length_toolchange") return &this->retract_length_toolchange;
|
||||
if (opt_key == "retract_lift") return &this->retract_lift;
|
||||
if (opt_key == "retract_restart_extra") return &this->retract_restart_extra;
|
||||
if (opt_key == "retract_restart_extra_toolchange") return &this->retract_restart_extra_toolchange;
|
||||
if (opt_key == "retract_speed") return &this->retract_speed;
|
||||
if (opt_key == "start_gcode") return &this->start_gcode;
|
||||
if (opt_key == "toolchange_gcode") return &this->toolchange_gcode;
|
||||
if (opt_key == "travel_speed") return &this->travel_speed;
|
||||
if (opt_key == "use_firmware_retraction") return &this->use_firmware_retraction;
|
||||
if (opt_key == "use_relative_e_distances") return &this->use_relative_e_distances;
|
||||
if (opt_key == "use_volumetric_e") return &this->use_volumetric_e;
|
||||
OPT_PTR(before_layer_gcode);
|
||||
OPT_PTR(end_gcode);
|
||||
OPT_PTR(extrusion_axis);
|
||||
OPT_PTR(extrusion_multiplier);
|
||||
OPT_PTR(filament_diameter);
|
||||
OPT_PTR(gcode_comments);
|
||||
OPT_PTR(gcode_flavor);
|
||||
OPT_PTR(layer_gcode);
|
||||
OPT_PTR(max_print_speed);
|
||||
OPT_PTR(max_volumetric_speed);
|
||||
OPT_PTR(pressure_advance);
|
||||
OPT_PTR(retract_length);
|
||||
OPT_PTR(retract_length_toolchange);
|
||||
OPT_PTR(retract_lift);
|
||||
OPT_PTR(retract_restart_extra);
|
||||
OPT_PTR(retract_restart_extra_toolchange);
|
||||
OPT_PTR(retract_speed);
|
||||
OPT_PTR(start_gcode);
|
||||
OPT_PTR(toolchange_gcode);
|
||||
OPT_PTR(travel_speed);
|
||||
OPT_PTR(use_firmware_retraction);
|
||||
OPT_PTR(use_relative_e_distances);
|
||||
OPT_PTR(use_volumetric_e);
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
@ -510,56 +519,56 @@ class PrintConfig : public GCodeConfig
|
|||
};
|
||||
|
||||
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
|
||||
if (opt_key == "avoid_crossing_perimeters") return &this->avoid_crossing_perimeters;
|
||||
if (opt_key == "bed_shape") return &this->bed_shape;
|
||||
if (opt_key == "bed_temperature") return &this->bed_temperature;
|
||||
if (opt_key == "bridge_acceleration") return &this->bridge_acceleration;
|
||||
if (opt_key == "bridge_fan_speed") return &this->bridge_fan_speed;
|
||||
if (opt_key == "brim_width") return &this->brim_width;
|
||||
if (opt_key == "complete_objects") return &this->complete_objects;
|
||||
if (opt_key == "cooling") return &this->cooling;
|
||||
if (opt_key == "default_acceleration") return &this->default_acceleration;
|
||||
if (opt_key == "disable_fan_first_layers") return &this->disable_fan_first_layers;
|
||||
if (opt_key == "duplicate_distance") return &this->duplicate_distance;
|
||||
if (opt_key == "extruder_clearance_height") return &this->extruder_clearance_height;
|
||||
if (opt_key == "extruder_clearance_radius") return &this->extruder_clearance_radius;
|
||||
if (opt_key == "extruder_offset") return &this->extruder_offset;
|
||||
if (opt_key == "fan_always_on") return &this->fan_always_on;
|
||||
if (opt_key == "fan_below_layer_time") return &this->fan_below_layer_time;
|
||||
if (opt_key == "filament_colour") return &this->filament_colour;
|
||||
if (opt_key == "first_layer_acceleration") return &this->first_layer_acceleration;
|
||||
if (opt_key == "first_layer_bed_temperature") return &this->first_layer_bed_temperature;
|
||||
if (opt_key == "first_layer_extrusion_width") return &this->first_layer_extrusion_width;
|
||||
if (opt_key == "first_layer_speed") return &this->first_layer_speed;
|
||||
if (opt_key == "first_layer_temperature") return &this->first_layer_temperature;
|
||||
if (opt_key == "gcode_arcs") return &this->gcode_arcs;
|
||||
if (opt_key == "infill_acceleration") return &this->infill_acceleration;
|
||||
if (opt_key == "infill_first") return &this->infill_first;
|
||||
if (opt_key == "max_fan_speed") return &this->max_fan_speed;
|
||||
if (opt_key == "min_fan_speed") return &this->min_fan_speed;
|
||||
if (opt_key == "min_print_speed") return &this->min_print_speed;
|
||||
if (opt_key == "min_skirt_length") return &this->min_skirt_length;
|
||||
if (opt_key == "notes") return &this->notes;
|
||||
if (opt_key == "nozzle_diameter") return &this->nozzle_diameter;
|
||||
if (opt_key == "only_retract_when_crossing_perimeters") return &this->only_retract_when_crossing_perimeters;
|
||||
if (opt_key == "ooze_prevention") return &this->ooze_prevention;
|
||||
if (opt_key == "output_filename_format") return &this->output_filename_format;
|
||||
if (opt_key == "perimeter_acceleration") return &this->perimeter_acceleration;
|
||||
if (opt_key == "post_process") return &this->post_process;
|
||||
if (opt_key == "resolution") return &this->resolution;
|
||||
if (opt_key == "retract_before_travel") return &this->retract_before_travel;
|
||||
if (opt_key == "retract_layer_change") return &this->retract_layer_change;
|
||||
if (opt_key == "skirt_distance") return &this->skirt_distance;
|
||||
if (opt_key == "skirt_height") return &this->skirt_height;
|
||||
if (opt_key == "skirts") return &this->skirts;
|
||||
if (opt_key == "slowdown_below_layer_time") return &this->slowdown_below_layer_time;
|
||||
if (opt_key == "spiral_vase") return &this->spiral_vase;
|
||||
if (opt_key == "standby_temperature_delta") return &this->standby_temperature_delta;
|
||||
if (opt_key == "temperature") return &this->temperature;
|
||||
if (opt_key == "threads") return &this->threads;
|
||||
if (opt_key == "vibration_limit") return &this->vibration_limit;
|
||||
if (opt_key == "wipe") return &this->wipe;
|
||||
if (opt_key == "z_offset") return &this->z_offset;
|
||||
OPT_PTR(avoid_crossing_perimeters);
|
||||
OPT_PTR(bed_shape);
|
||||
OPT_PTR(bed_temperature);
|
||||
OPT_PTR(bridge_acceleration);
|
||||
OPT_PTR(bridge_fan_speed);
|
||||
OPT_PTR(brim_width);
|
||||
OPT_PTR(complete_objects);
|
||||
OPT_PTR(cooling);
|
||||
OPT_PTR(default_acceleration);
|
||||
OPT_PTR(disable_fan_first_layers);
|
||||
OPT_PTR(duplicate_distance);
|
||||
OPT_PTR(extruder_clearance_height);
|
||||
OPT_PTR(extruder_clearance_radius);
|
||||
OPT_PTR(extruder_offset);
|
||||
OPT_PTR(fan_always_on);
|
||||
OPT_PTR(fan_below_layer_time);
|
||||
OPT_PTR(filament_colour);
|
||||
OPT_PTR(first_layer_acceleration);
|
||||
OPT_PTR(first_layer_bed_temperature);
|
||||
OPT_PTR(first_layer_extrusion_width);
|
||||
OPT_PTR(first_layer_speed);
|
||||
OPT_PTR(first_layer_temperature);
|
||||
OPT_PTR(gcode_arcs);
|
||||
OPT_PTR(infill_acceleration);
|
||||
OPT_PTR(infill_first);
|
||||
OPT_PTR(max_fan_speed);
|
||||
OPT_PTR(min_fan_speed);
|
||||
OPT_PTR(min_print_speed);
|
||||
OPT_PTR(min_skirt_length);
|
||||
OPT_PTR(notes);
|
||||
OPT_PTR(nozzle_diameter);
|
||||
OPT_PTR(only_retract_when_crossing_perimeters);
|
||||
OPT_PTR(ooze_prevention);
|
||||
OPT_PTR(output_filename_format);
|
||||
OPT_PTR(perimeter_acceleration);
|
||||
OPT_PTR(post_process);
|
||||
OPT_PTR(resolution);
|
||||
OPT_PTR(retract_before_travel);
|
||||
OPT_PTR(retract_layer_change);
|
||||
OPT_PTR(skirt_distance);
|
||||
OPT_PTR(skirt_height);
|
||||
OPT_PTR(skirts);
|
||||
OPT_PTR(slowdown_below_layer_time);
|
||||
OPT_PTR(spiral_vase);
|
||||
OPT_PTR(standby_temperature_delta);
|
||||
OPT_PTR(temperature);
|
||||
OPT_PTR(threads);
|
||||
OPT_PTR(vibration_limit);
|
||||
OPT_PTR(wipe);
|
||||
OPT_PTR(z_offset);
|
||||
|
||||
// look in parent class
|
||||
ConfigOption* opt;
|
||||
|
@ -585,10 +594,10 @@ class HostConfig : public virtual StaticPrintConfig
|
|||
};
|
||||
|
||||
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
|
||||
if (opt_key == "octoprint_host") return &this->octoprint_host;
|
||||
if (opt_key == "octoprint_apikey") return &this->octoprint_apikey;
|
||||
if (opt_key == "serial_port") return &this->serial_port;
|
||||
if (opt_key == "serial_speed") return &this->serial_speed;
|
||||
OPT_PTR(octoprint_host);
|
||||
OPT_PTR(octoprint_apikey);
|
||||
OPT_PTR(serial_port);
|
||||
OPT_PTR(serial_speed);
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
|
|
@ -340,39 +340,97 @@ PrintObject::has_support_material() const
|
|||
|| this->config.support_material_enforce_layers > 0;
|
||||
}
|
||||
|
||||
void
|
||||
PrintObject::process_external_surfaces()
|
||||
{
|
||||
FOREACH_REGION(this->_print, region) {
|
||||
size_t region_id = region - this->_print->regions.begin();
|
||||
|
||||
FOREACH_LAYER(this, layer_it) {
|
||||
const Layer* lower_layer = (layer_it == this->layers.begin())
|
||||
? NULL
|
||||
: *(layer_it-1);
|
||||
|
||||
(*layer_it)->get_region(region_id)->process_external_surfaces(lower_layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This method applies bridge flow to the first internal solid layer above
|
||||
sparse infill */
|
||||
void
|
||||
PrintObject::bridge_over_infill()
|
||||
{
|
||||
FOREACH_REGION(this->_print, region) {
|
||||
size_t region_id = region - this->_print->regions.begin();
|
||||
|
||||
double fill_density = (*region)->config.fill_density.value;
|
||||
if (fill_density == 100) continue;
|
||||
// skip bridging in case there are no voids
|
||||
if ((*region)->config.fill_density.value == 100) continue;
|
||||
|
||||
// get bridge flow
|
||||
Flow bridge_flow = (*region)->flow(
|
||||
frSolidInfill,
|
||||
-1, // layer height, not relevant for bridge flow
|
||||
true, // bridge
|
||||
false, // first layer
|
||||
-1, // custom width, not relevant for bridge flow
|
||||
*this
|
||||
);
|
||||
|
||||
FOREACH_LAYER(this, layer_it) {
|
||||
// skip first layer
|
||||
if (layer_it == this->layers.begin()) continue;
|
||||
|
||||
Layer* layer = *layer_it;
|
||||
Layer* lower_layer = *(layer_it - 1);
|
||||
LayerRegion* layerm = layer->get_region(region_id);
|
||||
|
||||
// compute the areas needing bridge math
|
||||
Polygons internal_solid, lower_internal;
|
||||
// extract the stInternalSolid surfaces that might be transformed into bridges
|
||||
Polygons internal_solid;
|
||||
layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid);
|
||||
FOREACH_LAYERREGION(lower_layer, lower_layerm_it)
|
||||
(*lower_layerm_it)->fill_surfaces.filter_by_type(stInternal, &lower_internal);
|
||||
|
||||
// check whether the lower area is deep enough for absorbing the extra flow
|
||||
// (for obvious physical reasons but also for preventing the bridge extrudates
|
||||
// from overflowing in 3D preview)
|
||||
ExPolygons to_bridge;
|
||||
intersection(internal_solid, lower_internal, &to_bridge);
|
||||
if (to_bridge.empty()) continue;
|
||||
|
||||
ExPolygons not_to_bridge;
|
||||
diff(internal_solid, to_bridge, ¬_to_bridge, true);
|
||||
{
|
||||
Polygons to_bridge_pp = internal_solid;
|
||||
|
||||
// iterate through lower layers spanned by bridge_flow
|
||||
double bottom_z = layer->print_z - bridge_flow.height;
|
||||
for (int i = (layer_it - this->layers.begin()) - 1; i >= 0; --i) {
|
||||
const Layer* lower_layer = this->layers[i];
|
||||
|
||||
// stop iterating if layer is lower than bottom_z
|
||||
if (lower_layer->print_z < bottom_z) break;
|
||||
|
||||
// iterate through regions and collect internal surfaces
|
||||
Polygons lower_internal;
|
||||
FOREACH_LAYERREGION(lower_layer, lower_layerm_it)
|
||||
(*lower_layerm_it)->fill_surfaces.filter_by_type(stInternal, &lower_internal);
|
||||
|
||||
// intersect such lower internal surfaces with the candidate solid surfaces
|
||||
to_bridge_pp = intersection(to_bridge_pp, lower_internal);
|
||||
}
|
||||
|
||||
// there's no point in bridging too thin/short regions
|
||||
{
|
||||
double min_width = bridge_flow.scaled_width() * 3;
|
||||
to_bridge_pp = offset2(to_bridge_pp, -min_width, +min_width);
|
||||
}
|
||||
|
||||
if (to_bridge_pp.empty()) continue;
|
||||
|
||||
// convert into ExPolygons
|
||||
to_bridge = union_ex(to_bridge_pp);
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("Bridging %zu internal areas at layer %d\n", to_bridge.size(), layer->id());
|
||||
printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id());
|
||||
#endif
|
||||
|
||||
// compute the remaning internal solid surfaces as difference
|
||||
ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true);
|
||||
|
||||
// build the new collection of fill_surfaces
|
||||
{
|
||||
Surfaces new_surfaces;
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
Surface::operator Polygons() const
|
||||
{
|
||||
return this->expolygon;
|
||||
}
|
||||
|
||||
double
|
||||
Surface::area() const
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@ class Surface
|
|||
: surface_type(_surface_type), expolygon(_expolygon),
|
||||
thickness(-1), thickness_layers(1), bridge_angle(-1), extra_perimeters(0)
|
||||
{};
|
||||
operator Polygons() const;
|
||||
double area() const;
|
||||
bool is_solid() const;
|
||||
bool is_external() const;
|
||||
|
|
|
@ -111,6 +111,12 @@ SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
SurfaceCollection::append(const SurfaceCollection &coll)
|
||||
{
|
||||
this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end());
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(SurfaceCollection, "Surface::Collection");
|
||||
#endif
|
||||
|
|
|
@ -11,6 +11,9 @@ class SurfaceCollection
|
|||
public:
|
||||
Surfaces surfaces;
|
||||
|
||||
SurfaceCollection() {};
|
||||
SurfaceCollection(const Surfaces &_surfaces)
|
||||
: surfaces(_surfaces) {};
|
||||
operator Polygons() const;
|
||||
operator ExPolygons() const;
|
||||
void simplify(double tolerance);
|
||||
|
@ -19,6 +22,7 @@ class SurfaceCollection
|
|||
template <class T> bool any_bottom_contains(const T &item) const;
|
||||
SurfacesPtr filter_by_type(SurfaceType type);
|
||||
void filter_by_type(SurfaceType type, Polygons* polygons);
|
||||
void append(const SurfaceCollection &coll);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -6,11 +6,17 @@
|
|||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#define SLIC3R_VERSION "1.2.8-dev"
|
||||
#define SLIC3R_VERSION "1.2.10-dev"
|
||||
|
||||
#define EPSILON 1e-4
|
||||
#define SCALING_FACTOR 0.000001
|
||||
#define RESOLUTION 0.0125
|
||||
#define SCALED_RESOLUTION (RESOLUTION / SCALING_FACTOR)
|
||||
#define PI 3.141592653589793238
|
||||
#define LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER 0.15
|
||||
#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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue