mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-29 19:53:44 -06:00
Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_splitted_vbuffer
This commit is contained in:
commit
676540279d
111 changed files with 12989 additions and 9870 deletions
|
|
@ -716,45 +716,33 @@ static void traverse_pt_noholes(const ClipperLib::PolyNodes &nodes, Polygons *ou
|
|||
});
|
||||
}
|
||||
|
||||
static void traverse_pt_old(ClipperLib::PolyNodes &nodes, Polygons* retval)
|
||||
static void traverse_pt_outside_in(const ClipperLib::PolyNodes &nodes, Polygons *retval)
|
||||
{
|
||||
/* use a nearest neighbor search to order these children
|
||||
TODO: supply start_near to chained_path() too? */
|
||||
|
||||
// collect ordering points
|
||||
Points ordering_points;
|
||||
ordering_points.reserve(nodes.size());
|
||||
for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
|
||||
Point p((*it)->Contour.front().X, (*it)->Contour.front().Y);
|
||||
ordering_points.push_back(p);
|
||||
}
|
||||
|
||||
// perform the ordering
|
||||
ClipperLib::PolyNodes ordered_nodes = chain_clipper_polynodes(ordering_points, nodes);
|
||||
|
||||
// push results recursively
|
||||
for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) {
|
||||
for (const ClipperLib::PolyNode *node : nodes)
|
||||
ordering_points.emplace_back(node->Contour.front().X, node->Contour.front().Y);
|
||||
|
||||
// Perform the ordering, push results recursively.
|
||||
//FIXME pass the last point to chain_clipper_polynodes?
|
||||
for (const ClipperLib::PolyNode *node : chain_clipper_polynodes(ordering_points, nodes)) {
|
||||
retval->emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour));
|
||||
if (node->IsHole())
|
||||
// Orient a hole, which is clockwise oriented, to CCW.
|
||||
retval->back().reverse();
|
||||
// traverse the next depth
|
||||
traverse_pt_old((*it)->Childs, retval);
|
||||
retval->push_back(ClipperPath_to_Slic3rPolygon((*it)->Contour));
|
||||
if ((*it)->IsHole()) retval->back().reverse(); // ccw
|
||||
traverse_pt_outside_in(node->Childs, retval);
|
||||
}
|
||||
}
|
||||
|
||||
Polygons union_pt_chained(const Polygons &subject, bool safety_offset_)
|
||||
Polygons union_pt_chained_outside_in(const Polygons &subject, bool safety_offset_)
|
||||
{
|
||||
ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_);
|
||||
|
||||
Polygons retval;
|
||||
traverse_pt_old(polytree.Childs, &retval);
|
||||
traverse_pt_outside_in(polytree.Childs, &retval);
|
||||
return retval;
|
||||
|
||||
// TODO: This needs to be tested:
|
||||
// ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_);
|
||||
|
||||
// Polygons retval;
|
||||
// traverse_pt_noholes(polytree.Childs, &retval);
|
||||
// return retval;
|
||||
}
|
||||
|
||||
Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear)
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject, bool safety_off
|
|||
ClipperLib::PolyTree union_pt(Slic3r::Polygons &&subject, bool safety_offset_ = false);
|
||||
ClipperLib::PolyTree union_pt(Slic3r::ExPolygons &&subject, bool safety_offset_ = false);
|
||||
|
||||
Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false);
|
||||
Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject, bool safety_offset_ = false);
|
||||
|
||||
ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes);
|
||||
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
|
|||
case coFloats: return new ConfigOptionFloatsNullable();
|
||||
case coInts: return new ConfigOptionIntsNullable();
|
||||
case coPercents: return new ConfigOptionPercentsNullable();
|
||||
case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable();
|
||||
case coBools: return new ConfigOptionBoolsNullable();
|
||||
default: throw Slic3r::RuntimeError(std::string("Unknown option type for nullable option ") + this->label);
|
||||
}
|
||||
|
|
@ -247,6 +248,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
|
|||
case coPercent: return new ConfigOptionPercent();
|
||||
case coPercents: return new ConfigOptionPercents();
|
||||
case coFloatOrPercent: return new ConfigOptionFloatOrPercent();
|
||||
case coFloatsOrPercents: return new ConfigOptionFloatsOrPercents();
|
||||
case coPoint: return new ConfigOptionPoint();
|
||||
case coPoints: return new ConfigOptionPoints();
|
||||
case coPoint3: return new ConfigOptionPoint3();
|
||||
|
|
@ -587,7 +589,7 @@ void ConfigBase::setenv_() const
|
|||
|
||||
void ConfigBase::load(const std::string &file)
|
||||
{
|
||||
if (boost::iends_with(file, ".gcode") || boost::iends_with(file, ".g"))
|
||||
if (is_gcode_file(file))
|
||||
this->load_from_gcode_file(file);
|
||||
else
|
||||
this->load_from_ini(file);
|
||||
|
|
@ -950,6 +952,8 @@ CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercent)
|
|||
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercents)
|
||||
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercentsNullable)
|
||||
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatOrPercent)
|
||||
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatsOrPercents)
|
||||
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatsOrPercentsNullable)
|
||||
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint)
|
||||
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoints)
|
||||
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint3)
|
||||
|
|
@ -984,6 +988,8 @@ CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloat, Slic3r::ConfigOp
|
|||
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloats, Slic3r::ConfigOptionPercents)
|
||||
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloats, Slic3r::ConfigOptionPercentsNullable)
|
||||
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionPercent, Slic3r::ConfigOptionFloatOrPercent)
|
||||
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::FloatOrPercent>, Slic3r::ConfigOptionFloatsOrPercents)
|
||||
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::FloatOrPercent>, Slic3r::ConfigOptionFloatsOrPercentsNullable)
|
||||
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec2d>, Slic3r::ConfigOptionPoint)
|
||||
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::Vec2d>, Slic3r::ConfigOptionPoints)
|
||||
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec3d>, Slic3r::ConfigOptionPoint3)
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ enum ConfigOptionType {
|
|||
coPercents = coPercent + coVectorType,
|
||||
// a fraction or an absolute value
|
||||
coFloatOrPercent = 5,
|
||||
// vector of the above
|
||||
coFloatsOrPercents = coFloatOrPercent + coVectorType,
|
||||
// single 2d point (Point2f). Currently not used.
|
||||
coPoint = 6,
|
||||
// vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets.
|
||||
|
|
@ -889,6 +891,143 @@ private:
|
|||
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); }
|
||||
};
|
||||
|
||||
|
||||
struct FloatOrPercent
|
||||
{
|
||||
double value;
|
||||
bool percent;
|
||||
|
||||
private:
|
||||
friend class cereal::access;
|
||||
template<class Archive> void serialize(Archive & ar) { ar(this->value); ar(this->percent); }
|
||||
};
|
||||
|
||||
inline bool operator==(const FloatOrPercent &l, const FloatOrPercent &r)
|
||||
{
|
||||
return l.value == r.value && l.percent == r.percent;
|
||||
}
|
||||
|
||||
inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r)
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
template<bool NULLABLE>
|
||||
class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector<FloatOrPercent>
|
||||
{
|
||||
public:
|
||||
ConfigOptionFloatsOrPercentsTempl() : ConfigOptionVector<FloatOrPercent>() {}
|
||||
explicit ConfigOptionFloatsOrPercentsTempl(size_t n, FloatOrPercent value) : ConfigOptionVector<FloatOrPercent>(n, value) {}
|
||||
explicit ConfigOptionFloatsOrPercentsTempl(std::initializer_list<FloatOrPercent> il) : ConfigOptionVector<FloatOrPercent>(std::move(il)) {}
|
||||
explicit ConfigOptionFloatsOrPercentsTempl(const std::vector<FloatOrPercent> &vec) : ConfigOptionVector<FloatOrPercent>(vec) {}
|
||||
explicit ConfigOptionFloatsOrPercentsTempl(std::vector<FloatOrPercent> &&vec) : ConfigOptionVector<FloatOrPercent>(std::move(vec)) {}
|
||||
|
||||
static ConfigOptionType static_type() { return coFloatsOrPercents; }
|
||||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionFloatsOrPercentsTempl(*this); }
|
||||
bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
|
||||
bool operator==(const ConfigOption &rhs) const override {
|
||||
if (rhs.type() != this->type())
|
||||
throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types");
|
||||
assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
|
||||
return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
|
||||
}
|
||||
// Could a special "nil" value be stored inside the vector, indicating undefined value?
|
||||
bool nullable() const override { return NULLABLE; }
|
||||
// Special "nil" value to be stored into the vector if this->supports_nil().
|
||||
static FloatOrPercent nil_value() { return { std::numeric_limits<double>::quiet_NaN(), false }; }
|
||||
// A scalar is nil, or all values of a vector are nil.
|
||||
bool is_nil() const override { for (auto v : this->values) if (! std::isnan(v.value)) return false; return true; }
|
||||
bool is_nil(size_t idx) const override { return std::isnan(this->values[idx].value); }
|
||||
|
||||
std::string serialize() const override
|
||||
{
|
||||
std::ostringstream ss;
|
||||
for (const FloatOrPercent &v : this->values) {
|
||||
if (&v != &this->values.front())
|
||||
ss << ",";
|
||||
serialize_single_value(ss, v);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::vector<std::string> vserialize() const override
|
||||
{
|
||||
std::vector<std::string> vv;
|
||||
vv.reserve(this->values.size());
|
||||
for (const FloatOrPercent &v : this->values) {
|
||||
std::ostringstream ss;
|
||||
serialize_single_value(ss, v);
|
||||
vv.push_back(ss.str());
|
||||
}
|
||||
return vv;
|
||||
}
|
||||
|
||||
bool deserialize(const std::string &str, bool append = false) override
|
||||
{
|
||||
if (! append)
|
||||
this->values.clear();
|
||||
std::istringstream is(str);
|
||||
std::string item_str;
|
||||
while (std::getline(is, item_str, ',')) {
|
||||
boost::trim(item_str);
|
||||
if (item_str == "nil") {
|
||||
if (NULLABLE)
|
||||
this->values.push_back(nil_value());
|
||||
else
|
||||
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
|
||||
} else {
|
||||
bool percent = item_str.find_first_of("%") != std::string::npos;
|
||||
std::istringstream iss(item_str);
|
||||
double value;
|
||||
iss >> value;
|
||||
this->values.push_back({ value, percent });
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ConfigOptionFloatsOrPercentsTempl& operator=(const ConfigOption *opt)
|
||||
{
|
||||
this->set(opt);
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
void serialize_single_value(std::ostringstream &ss, const FloatOrPercent &v) const {
|
||||
if (std::isfinite(v.value)) {
|
||||
ss << v.value;
|
||||
if (v.percent)
|
||||
ss << "%";
|
||||
} else if (std::isnan(v.value)) {
|
||||
if (NULLABLE)
|
||||
ss << "nil";
|
||||
else
|
||||
throw Slic3r::RuntimeError("Serializing NaN");
|
||||
} else
|
||||
throw Slic3r::RuntimeError("Serializing invalid number");
|
||||
}
|
||||
static bool vectors_equal(const std::vector<FloatOrPercent> &v1, const std::vector<FloatOrPercent> &v2) {
|
||||
if (NULLABLE) {
|
||||
if (v1.size() != v2.size())
|
||||
return false;
|
||||
for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end(); ++ it1, ++ it2)
|
||||
if (! ((std::isnan(it1->value) && std::isnan(it2->value)) || *it1 == *it2))
|
||||
return false;
|
||||
return true;
|
||||
} else
|
||||
// Not supporting nullable values, the default vector compare is cheaper.
|
||||
return v1 == v2;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class cereal::access;
|
||||
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<FloatOrPercent>>(this)); }
|
||||
};
|
||||
|
||||
using ConfigOptionFloatsOrPercents = ConfigOptionFloatsOrPercentsTempl<false>;
|
||||
using ConfigOptionFloatsOrPercentsNullable = ConfigOptionFloatsOrPercentsTempl<true>;
|
||||
|
||||
class ConfigOptionPoint : public ConfigOptionSingle<Vec2d>
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -297,15 +297,16 @@ std::pair<double, double> adaptive_fill_line_spacing(const PrintObject &print_ob
|
|||
double max_nozzle_diameter = *std::max_element(nozzle_diameters.begin(), nozzle_diameters.end());
|
||||
double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
|
||||
for (const PrintRegion *region : print_object.print()->regions()) {
|
||||
const PrintRegionConfig &config = region->config();
|
||||
bool nonempty = config.fill_density > 0;
|
||||
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
|
||||
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
|
||||
const PrintRegionConfig &config = region->config();
|
||||
bool nonempty = config.fill_density > 0;
|
||||
bool has_adaptive_infill = nonempty && config.fill_pattern == ipAdaptiveCubic;
|
||||
bool has_support_infill = nonempty && config.fill_pattern == ipSupportCubic;
|
||||
double infill_extrusion_width = config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * config.infill_extrusion_width : config.infill_extrusion_width;
|
||||
region_fill_data.push_back(RegionFillData({
|
||||
has_adaptive_infill ? Tristate::Maybe : Tristate::No,
|
||||
has_support_infill ? Tristate::Maybe : Tristate::No,
|
||||
config.fill_density,
|
||||
config.infill_extrusion_width != 0. ? config.infill_extrusion_width : default_infill_extrusion_width
|
||||
infill_extrusion_width != 0. ? infill_extrusion_width : default_infill_extrusion_width
|
||||
}));
|
||||
build_octree |= has_adaptive_infill || has_support_infill;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,11 @@ protected:
|
|||
const std::pair<float, Point> &direction,
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out) override;
|
||||
bool no_sort() const override { return true; }
|
||||
// Let the G-code export reoder the infill lines.
|
||||
//FIXME letting the G-code exporter to reorder infill lines of Adaptive Cubic Infill
|
||||
// may not be optimal as the internal infill lines may get extruded before the long infill
|
||||
// lines to which the short infill lines are supposed to anchor.
|
||||
bool no_sort() const override { return false; }
|
||||
};
|
||||
|
||||
}; // namespace FillAdaptive
|
||||
|
|
|
|||
|
|
@ -96,10 +96,10 @@ coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance)
|
|||
assert(width >= 0);
|
||||
assert(distance > 0);
|
||||
// floor(width / distance)
|
||||
coord_t number_of_intervals = (width - EPSILON) / distance;
|
||||
coord_t distance_new = (number_of_intervals == 0) ?
|
||||
const auto number_of_intervals = coord_t((width - EPSILON) / distance);
|
||||
coord_t distance_new = (number_of_intervals == 0) ?
|
||||
distance :
|
||||
((width - EPSILON) / number_of_intervals);
|
||||
coord_t((width - EPSILON) / number_of_intervals);
|
||||
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
|
||||
assert(factor > 1. - 1e-5);
|
||||
// How much could the extrusion width be increased? By 20%.
|
||||
|
|
@ -143,7 +143,7 @@ std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const
|
|||
#ifdef SLIC3R_DEBUG
|
||||
printf("Filling bridge with angle %f\n", surface->bridge_angle);
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
out_angle = surface->bridge_angle;
|
||||
out_angle = float(surface->bridge_angle);
|
||||
} else if (this->layer_id != size_t(-1)) {
|
||||
// alternate fill direction
|
||||
out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers);
|
||||
|
|
@ -161,15 +161,15 @@ struct ContourIntersectionPoint {
|
|||
size_t contour_idx;
|
||||
size_t point_idx;
|
||||
// Eucleidean parameter of point_idx along its contour.
|
||||
float param;
|
||||
double param;
|
||||
// Other intersection points along the same contour. If there is only a single T-joint on a contour
|
||||
// with an intersection line, then the prev_on_contour and next_on_contour remain nulls.
|
||||
ContourIntersectionPoint* prev_on_contour { nullptr };
|
||||
ContourIntersectionPoint* next_on_contour { nullptr };
|
||||
// Length of the contour not yet allocated to some extrusion path going back (clockwise), or masked out by some overlapping infill line.
|
||||
float contour_not_taken_length_prev { std::numeric_limits<float>::max() };
|
||||
double contour_not_taken_length_prev { std::numeric_limits<double>::max() };
|
||||
// Length of the contour not yet allocated to some extrusion path going forward (counter-clockwise), or masked out by some overlapping infill line.
|
||||
float contour_not_taken_length_next { std::numeric_limits<float>::max() };
|
||||
double contour_not_taken_length_next { std::numeric_limits<double>::max() };
|
||||
// End point is consumed if an infill line connected to this T-joint was already connected left or right along the contour,
|
||||
// or if the infill line was processed, but it was not possible to connect it left or right along the contour.
|
||||
bool consumed { false };
|
||||
|
|
@ -180,13 +180,13 @@ struct ContourIntersectionPoint {
|
|||
void consume_prev() { this->contour_not_taken_length_prev = 0.; this->prev_trimmed = true; this->consumed = true; }
|
||||
void consume_next() { this->contour_not_taken_length_next = 0.; this->next_trimmed = true; this->consumed = true; }
|
||||
|
||||
void trim_prev(const float new_len) {
|
||||
void trim_prev(const double new_len) {
|
||||
if (new_len < this->contour_not_taken_length_prev) {
|
||||
this->contour_not_taken_length_prev = new_len;
|
||||
this->prev_trimmed = true;
|
||||
}
|
||||
}
|
||||
void trim_next(const float new_len) {
|
||||
void trim_next(const double new_len) {
|
||||
if (new_len < this->contour_not_taken_length_next) {
|
||||
this->contour_not_taken_length_next = new_len;
|
||||
this->next_trimmed = true;
|
||||
|
|
@ -207,24 +207,24 @@ struct ContourIntersectionPoint {
|
|||
};
|
||||
|
||||
// Distance from param1 to param2 when going counter-clockwise.
|
||||
static inline float closed_contour_distance_ccw(float param1, float param2, float contour_length)
|
||||
static inline double closed_contour_distance_ccw(double param1, double param2, double contour_length)
|
||||
{
|
||||
assert(param1 >= 0.f && param1 <= contour_length);
|
||||
assert(param2 >= 0.f && param2 <= contour_length);
|
||||
float d = param2 - param1;
|
||||
if (d < 0.f)
|
||||
assert(param1 >= 0. && param1 <= contour_length);
|
||||
assert(param2 >= 0. && param2 <= contour_length);
|
||||
double d = param2 - param1;
|
||||
if (d < 0.)
|
||||
d += contour_length;
|
||||
return d;
|
||||
}
|
||||
|
||||
// Distance from param1 to param2 when going clockwise.
|
||||
static inline float closed_contour_distance_cw(float param1, float param2, float contour_length)
|
||||
static inline double closed_contour_distance_cw(double param1, double param2, double contour_length)
|
||||
{
|
||||
return closed_contour_distance_ccw(param2, param1, contour_length);
|
||||
}
|
||||
|
||||
// Length along the contour from cp1 to cp2 going counter-clockwise.
|
||||
float path_length_along_contour_ccw(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, float contour_length)
|
||||
double path_length_along_contour_ccw(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, double contour_length)
|
||||
{
|
||||
assert(cp1 != nullptr);
|
||||
assert(cp2 != nullptr);
|
||||
|
|
@ -234,13 +234,13 @@ float path_length_along_contour_ccw(const ContourIntersectionPoint *cp1, const C
|
|||
}
|
||||
|
||||
// Lengths along the contour from cp1 to cp2 going CCW and going CW.
|
||||
std::pair<float, float> path_lengths_along_contour(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, float contour_length)
|
||||
std::pair<double, double> path_lengths_along_contour(const ContourIntersectionPoint *cp1, const ContourIntersectionPoint *cp2, double contour_length)
|
||||
{
|
||||
// Zero'th param is the length of the contour.
|
||||
float param_lo = cp1->param;
|
||||
float param_hi = cp2->param;
|
||||
assert(param_lo >= 0.f && param_lo <= contour_length);
|
||||
assert(param_hi >= 0.f && param_hi <= contour_length);
|
||||
double param_lo = cp1->param;
|
||||
double param_hi = cp2->param;
|
||||
assert(param_lo >= 0. && param_lo <= contour_length);
|
||||
assert(param_hi >= 0. && param_hi <= contour_length);
|
||||
bool reversed = false;
|
||||
if (param_lo > param_hi) {
|
||||
std::swap(param_lo, param_hi);
|
||||
|
|
@ -267,25 +267,25 @@ static inline void take_cw_full(Polyline &pl, const Points& contour, size_t idx_
|
|||
}
|
||||
|
||||
// Add contour points from interval (idx_start, idx_end> to polyline, limited by the Eucleidean length taken.
|
||||
static inline float take_cw_limited(Polyline &pl, const Points &contour, const std::vector<float> ¶ms, size_t idx_start, size_t idx_end, float length_to_take)
|
||||
static inline double take_cw_limited(Polyline &pl, const Points &contour, const std::vector<double> ¶ms, size_t idx_start, size_t idx_end, double length_to_take)
|
||||
{
|
||||
// If appending to an infill line, then the start point of a perimeter line shall match the end point of an infill line.
|
||||
assert(pl.empty() || pl.points.back() == contour[idx_start]);
|
||||
assert(contour.size() + 1 == params.size());
|
||||
assert(length_to_take > SCALED_EPSILON);
|
||||
// Length of the contour.
|
||||
float length = params.back();
|
||||
double length = params.back();
|
||||
// Parameter (length from contour.front()) for the first point.
|
||||
float p0 = params[idx_start];
|
||||
double p0 = params[idx_start];
|
||||
// Current (2nd) point of the contour.
|
||||
size_t i = (idx_start == 0) ? contour.size() - 1 : idx_start - 1;
|
||||
// Previous point of the contour.
|
||||
size_t iprev = idx_start;
|
||||
// Length of the contour curve taken for iprev.
|
||||
float lprev = 0.f;
|
||||
double lprev = 0.;
|
||||
|
||||
for (;;) {
|
||||
float l = closed_contour_distance_cw(p0, params[i], length);
|
||||
double l = closed_contour_distance_cw(p0, params[i], length);
|
||||
if (l >= length_to_take) {
|
||||
// Trim the last segment.
|
||||
double t = double(length_to_take - lprev) / (l - lprev);
|
||||
|
|
@ -323,16 +323,16 @@ static inline void take_ccw_full(Polyline &pl, const Points &contour, size_t idx
|
|||
|
||||
// Add contour points from interval (idx_start, idx_end> to polyline, limited by the Eucleidean length taken.
|
||||
// Returns length of the contour taken.
|
||||
static inline float take_ccw_limited(Polyline &pl, const Points &contour, const std::vector<float> ¶ms, size_t idx_start, size_t idx_end, float length_to_take)
|
||||
static inline double take_ccw_limited(Polyline &pl, const Points &contour, const std::vector<double> ¶ms, size_t idx_start, size_t idx_end, double length_to_take)
|
||||
{
|
||||
// If appending to an infill line, then the start point of a perimeter line shall match the end point of an infill line.
|
||||
assert(pl.empty() || pl.points.back() == contour[idx_start]);
|
||||
assert(contour.size() + 1 == params.size());
|
||||
assert(length_to_take > SCALED_EPSILON);
|
||||
// Length of the contour.
|
||||
float length = params.back();
|
||||
double length = params.back();
|
||||
// Parameter (length from contour.front()) for the first point.
|
||||
float p0 = params[idx_start];
|
||||
double p0 = params[idx_start];
|
||||
// Current (2nd) point of the contour.
|
||||
size_t i = idx_start;
|
||||
if (++ i == contour.size())
|
||||
|
|
@ -340,9 +340,9 @@ static inline float take_ccw_limited(Polyline &pl, const Points &contour, const
|
|||
// Previous point of the contour.
|
||||
size_t iprev = idx_start;
|
||||
// Length of the contour curve taken at iprev.
|
||||
float lprev = 0.f;
|
||||
double lprev = 0;
|
||||
for (;;) {
|
||||
float l = closed_contour_distance_ccw(p0, params[i], length);
|
||||
double l = closed_contour_distance_ccw(p0, params[i], length);
|
||||
if (l >= length_to_take) {
|
||||
// Trim the last segment.
|
||||
double t = double(length_to_take - lprev) / (l - lprev);
|
||||
|
|
@ -411,8 +411,8 @@ static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, Cont
|
|||
}
|
||||
|
||||
static void take_limited(
|
||||
Polyline &pl1, const Points &contour, const std::vector<float> ¶ms,
|
||||
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, float take_max_length, float line_half_width)
|
||||
Polyline &pl1, const Points &contour, const std::vector<double> ¶ms,
|
||||
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, double take_max_length, double line_half_width)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
// This is a valid case, where a single infill line connect to two different contours (outer contour + hole or two holes).
|
||||
|
|
@ -445,11 +445,11 @@ static void take_limited(
|
|||
pl1.points.reserve(pl1.points.size() + pl_tmp.size() + size_t(new_points));
|
||||
}
|
||||
|
||||
float length = params.back();
|
||||
float length_to_go = take_max_length;
|
||||
double length = params.back();
|
||||
double length_to_go = take_max_length;
|
||||
cp_start->consumed = true;
|
||||
if (cp_start == cp_end) {
|
||||
length_to_go = std::max(0.f, std::min(length_to_go, length - line_half_width));
|
||||
length_to_go = std::max(0., std::min(length_to_go, length - line_half_width));
|
||||
length_to_go = std::min(length_to_go, clockwise ? cp_start->contour_not_taken_length_prev : cp_start->contour_not_taken_length_next);
|
||||
cp_start->consume_prev();
|
||||
cp_start->consume_next();
|
||||
|
|
@ -462,11 +462,11 @@ static void take_limited(
|
|||
assert(cp_start != cp_end);
|
||||
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->prev_on_contour) {
|
||||
// Length of the segment from cp to cp->prev_on_contour.
|
||||
float l = closed_contour_distance_cw(cp->param, cp->prev_on_contour->param, length);
|
||||
double l = closed_contour_distance_cw(cp->param, cp->prev_on_contour->param, length);
|
||||
length_to_go = std::min(length_to_go, cp->contour_not_taken_length_prev);
|
||||
//if (cp->prev_on_contour->consumed)
|
||||
// Don't overlap with an already extruded infill line.
|
||||
length_to_go = std::max(0.f, std::min(length_to_go, l - line_half_width));
|
||||
length_to_go = std::max(0., std::min(length_to_go, l - line_half_width));
|
||||
cp->consume_prev();
|
||||
if (l >= length_to_go) {
|
||||
if (length_to_go > SCALED_EPSILON) {
|
||||
|
|
@ -475,7 +475,7 @@ static void take_limited(
|
|||
}
|
||||
break;
|
||||
} else {
|
||||
cp->prev_on_contour->trim_next(0.f);
|
||||
cp->prev_on_contour->trim_next(0.);
|
||||
take_cw_full(pl1, contour, cp->point_idx, cp->prev_on_contour->point_idx);
|
||||
length_to_go -= l;
|
||||
}
|
||||
|
|
@ -483,11 +483,11 @@ static void take_limited(
|
|||
} else {
|
||||
assert(cp_start != cp_end);
|
||||
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->next_on_contour) {
|
||||
float l = closed_contour_distance_ccw(cp->param, cp->next_on_contour->param, length);
|
||||
double l = closed_contour_distance_ccw(cp->param, cp->next_on_contour->param, length);
|
||||
length_to_go = std::min(length_to_go, cp->contour_not_taken_length_next);
|
||||
//if (cp->next_on_contour->consumed)
|
||||
// Don't overlap with an already extruded infill line.
|
||||
length_to_go = std::max(0.f, std::min(length_to_go, l - line_half_width));
|
||||
length_to_go = std::max(0., std::min(length_to_go, l - line_half_width));
|
||||
cp->consume_next();
|
||||
if (l >= length_to_go) {
|
||||
if (length_to_go > SCALED_EPSILON) {
|
||||
|
|
@ -496,7 +496,7 @@ static void take_limited(
|
|||
}
|
||||
break;
|
||||
} else {
|
||||
cp->next_on_contour->trim_prev(0.f);
|
||||
cp->next_on_contour->trim_prev(0.);
|
||||
take_ccw_full(pl1, contour, cp->point_idx, cp->next_on_contour->point_idx);
|
||||
length_to_go -= l;
|
||||
}
|
||||
|
|
@ -678,19 +678,19 @@ static inline bool line_rounded_thick_segment_collision(
|
|||
return intersects;
|
||||
}
|
||||
|
||||
static inline bool inside_interval(float low, float high, float p)
|
||||
static inline bool inside_interval(double low, double high, double p)
|
||||
{
|
||||
return p >= low && p <= high;
|
||||
}
|
||||
|
||||
static inline bool interval_inside_interval(float outer_low, float outer_high, float inner_low, float inner_high, float epsilon)
|
||||
static inline bool interval_inside_interval(double outer_low, double outer_high, double inner_low, double inner_high, double epsilon)
|
||||
{
|
||||
outer_low -= epsilon;
|
||||
outer_high += epsilon;
|
||||
return inside_interval(outer_low, outer_high, inner_low) && inside_interval(outer_low, outer_high, inner_high);
|
||||
}
|
||||
|
||||
static inline bool cyclic_interval_inside_interval(float outer_low, float outer_high, float inner_low, float inner_high, float length)
|
||||
static inline bool cyclic_interval_inside_interval(double outer_low, double outer_high, double inner_low, double inner_high, double length)
|
||||
{
|
||||
if (outer_low > outer_high)
|
||||
outer_high += length;
|
||||
|
|
@ -700,7 +700,7 @@ static inline bool cyclic_interval_inside_interval(float outer_low, float outer_
|
|||
inner_low += length;
|
||||
inner_high += length;
|
||||
}
|
||||
return interval_inside_interval(outer_low, outer_high, inner_low, inner_high, float(SCALED_EPSILON));
|
||||
return interval_inside_interval(outer_low, outer_high, inner_low, inner_high, double(SCALED_EPSILON));
|
||||
}
|
||||
|
||||
// #define INFILL_DEBUG_OUTPUT
|
||||
|
|
@ -710,7 +710,7 @@ static void export_infill_to_svg(
|
|||
// Boundary contour, along which the perimeter extrusions will be drawn.
|
||||
const std::vector<Points> &boundary,
|
||||
// Parametrization of boundary with Euclidian length.
|
||||
const std::vector<std::vector<float>> &boundary_parameters,
|
||||
const std::vector<std::vector<double>> &boundary_parameters,
|
||||
// Intersections (T-joints) of the infill lines with the boundary.
|
||||
std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections,
|
||||
// Infill lines, either completely inside the boundary, or touching the boundary.
|
||||
|
|
@ -739,7 +739,7 @@ static void export_infill_to_svg(
|
|||
for (const std::vector<ContourIntersectionPoint*> &intersections : boundary_intersections) {
|
||||
const size_t boundary_idx = &intersections - boundary_intersections.data();
|
||||
const Points &contour = boundary[boundary_idx];
|
||||
const std::vector<float> &contour_param = boundary_parameters[boundary_idx];
|
||||
const std::vector<double> &contour_param = boundary_parameters[boundary_idx];
|
||||
for (const ContourIntersectionPoint *ip : intersections) {
|
||||
assert(ip->next_trimmed == ip->next_on_contour->prev_trimmed);
|
||||
assert(ip->prev_trimmed == ip->prev_on_contour->next_trimmed);
|
||||
|
|
@ -834,7 +834,7 @@ void mark_boundary_segments_touching_infill(
|
|||
// Boundary contour, along which the perimeter extrusions will be drawn.
|
||||
const std::vector<Points> &boundary,
|
||||
// Parametrization of boundary with Euclidian length.
|
||||
const std::vector<std::vector<float>> &boundary_parameters,
|
||||
const std::vector<std::vector<double>> &boundary_parameters,
|
||||
// Intersections (T-joints) of the infill lines with the boundary.
|
||||
std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections,
|
||||
// Bounding box around the boundary.
|
||||
|
|
@ -865,12 +865,12 @@ void mark_boundary_segments_touching_infill(
|
|||
// Make sure that the the grid is big enough for queries against the thick segment.
|
||||
grid.set_bbox(boundary_bbox.inflated(distance_colliding * 1.43));
|
||||
// Inflate the bounding box by a thick line width.
|
||||
grid.create(boundary, std::max(clip_distance, distance_colliding) + scale_(10.));
|
||||
grid.create(boundary, coord_t(std::max(clip_distance, distance_colliding) + scale_(10.)));
|
||||
|
||||
// Visitor for the EdgeGrid to trim boundary_intersections with existing infill lines.
|
||||
struct Visitor {
|
||||
Visitor(const EdgeGrid::Grid &grid,
|
||||
const std::vector<Points> &boundary, const std::vector<std::vector<float>> &boundary_parameters, std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections,
|
||||
const std::vector<Points> &boundary, const std::vector<std::vector<double>> &boundary_parameters, std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections,
|
||||
const double radius) :
|
||||
grid(grid), boundary(boundary), boundary_parameters(boundary_parameters), boundary_intersections(boundary_intersections), radius(radius), trim_l_threshold(0.5 * radius) {}
|
||||
|
||||
|
|
@ -907,10 +907,10 @@ void mark_boundary_segments_touching_infill(
|
|||
// The boundary segment intersects with the infill segment thickened by radius.
|
||||
// Interval is specified in Euclidian length from seg_pt1 to seg_pt2.
|
||||
// 1) Find the Euclidian parameters of seg_pt1 and seg_pt2 on its boundary contour.
|
||||
const std::vector<float> &contour_parameters = boundary_parameters[it_contour_and_segment->first];
|
||||
const float contour_length = contour_parameters.back();
|
||||
const float param_seg_pt1 = contour_parameters[it_contour_and_segment->second];
|
||||
const float param_seg_pt2 = contour_parameters[it_contour_and_segment->second + 1];
|
||||
const std::vector<double> &contour_parameters = boundary_parameters[it_contour_and_segment->first];
|
||||
const double contour_length = contour_parameters.back();
|
||||
const double param_seg_pt1 = contour_parameters[it_contour_and_segment->second];
|
||||
const double param_seg_pt2 = contour_parameters[it_contour_and_segment->second + 1];
|
||||
#ifdef INFILL_DEBUG_OUTPUT
|
||||
this->perimeter_overlaps.push_back({ Point((seg_pt1 + (seg_pt2 - seg_pt1).normalized() * interval.first).cast<coord_t>()),
|
||||
Point((seg_pt1 + (seg_pt2 - seg_pt1).normalized() * interval.second).cast<coord_t>()) });
|
||||
|
|
@ -918,8 +918,8 @@ void mark_boundary_segments_touching_infill(
|
|||
assert(interval.first >= 0.);
|
||||
assert(interval.second >= 0.);
|
||||
assert(interval.first <= interval.second);
|
||||
const auto param_overlap1 = std::min(param_seg_pt2, float(param_seg_pt1 + interval.first));
|
||||
const auto param_overlap2 = std::min(param_seg_pt2, float(param_seg_pt1 + interval.second));
|
||||
const auto param_overlap1 = std::min(param_seg_pt2, param_seg_pt1 + interval.first);
|
||||
const auto param_overlap2 = std::min(param_seg_pt2, param_seg_pt1 + interval.second);
|
||||
// 2) Find the ContourIntersectionPoints before param_overlap1 and after param_overlap2.
|
||||
// Find the span of ContourIntersectionPoints, that is trimmed by the interval (param_overlap1, param_overlap2).
|
||||
ContourIntersectionPoint *ip_low, *ip_high;
|
||||
|
|
@ -946,7 +946,7 @@ void mark_boundary_segments_touching_infill(
|
|||
ip->consume_next();
|
||||
}
|
||||
// Subtract the interval from the first and last segments.
|
||||
float trim_l = closed_contour_distance_ccw(ip_low->param, param_overlap1, contour_length);
|
||||
double trim_l = closed_contour_distance_ccw(ip_low->param, param_overlap1, contour_length);
|
||||
//if (trim_l > trim_l_threshold)
|
||||
ip_low->trim_next(trim_l);
|
||||
trim_l = closed_contour_distance_ccw(param_overlap2, ip_high->param, contour_length);
|
||||
|
|
@ -978,12 +978,12 @@ void mark_boundary_segments_touching_infill(
|
|||
|
||||
const EdgeGrid::Grid &grid;
|
||||
const std::vector<Points> &boundary;
|
||||
const std::vector<std::vector<float>> &boundary_parameters;
|
||||
const std::vector<std::vector<double>> &boundary_parameters;
|
||||
std::vector<std::vector<ContourIntersectionPoint*>> &boundary_intersections;
|
||||
// Maximum distance between the boundary and the infill line allowed to consider the boundary not touching the infill line.
|
||||
const double radius;
|
||||
// Region around the contour / infill line intersection point, where the intersections are ignored.
|
||||
const float trim_l_threshold;
|
||||
const double trim_l_threshold;
|
||||
|
||||
const Vec2d *infill_pt1;
|
||||
const Vec2d *infill_pt2;
|
||||
|
|
@ -1100,11 +1100,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const Polygons &boundary_s
|
|||
void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms)
|
||||
{
|
||||
assert(! infill_ordered.empty());
|
||||
assert(params.anchor_length >= 0.f);
|
||||
assert(params.anchor_length >= 0.);
|
||||
assert(params.anchor_length_max >= 0.01f);
|
||||
assert(params.anchor_length_max >= params.anchor_length);
|
||||
const auto anchor_length = float(scale_(params.anchor_length));
|
||||
const auto anchor_length_max = float(scale_(params.anchor_length_max));
|
||||
const double anchor_length = scale_(params.anchor_length);
|
||||
const double anchor_length_max = scale_(params.anchor_length_max);
|
||||
|
||||
#if 0
|
||||
append(polylines_out, infill_ordered);
|
||||
|
|
@ -1113,9 +1113,9 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
|
||||
// 1) Add the end points of infill_ordered to boundary_src.
|
||||
std::vector<Points> boundary;
|
||||
std::vector<std::vector<float>> boundary_params;
|
||||
std::vector<std::vector<double>> boundary_params;
|
||||
boundary.assign(boundary_src.size(), Points());
|
||||
boundary_params.assign(boundary_src.size(), std::vector<float>());
|
||||
boundary_params.assign(boundary_src.size(), std::vector<double>());
|
||||
// Mapping the infill_ordered end point to a (contour, point) of boundary.
|
||||
static constexpr auto boundary_idx_unconnected = std::numeric_limits<size_t>::max();
|
||||
std::vector<ContourIntersectionPoint> map_infill_end_point_to_boundary(infill_ordered.size() * 2, ContourIntersectionPoint{ boundary_idx_unconnected, boundary_idx_unconnected });
|
||||
|
|
@ -1125,11 +1125,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
{
|
||||
EdgeGrid::Grid grid;
|
||||
grid.set_bbox(bbox.inflated(SCALED_EPSILON));
|
||||
grid.create(boundary_src, scale_(10.));
|
||||
grid.create(boundary_src, coord_t(scale_(10.)));
|
||||
intersection_points.reserve(infill_ordered.size() * 2);
|
||||
for (const Polyline &pl : infill_ordered)
|
||||
for (const Point *pt : { &pl.points.front(), &pl.points.back() }) {
|
||||
EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON);
|
||||
EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, coord_t(SCALED_EPSILON));
|
||||
if (cp.valid()) {
|
||||
// The infill end point shall lie on the contour.
|
||||
assert(cp.distance <= 3.);
|
||||
|
|
@ -1163,21 +1163,29 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
contour_intersection_points.reserve(n_intersection_points);
|
||||
}
|
||||
for (size_t idx_point = 0; idx_point < contour_src.points.size(); ++ idx_point) {
|
||||
contour_dst.emplace_back(contour_src.points[idx_point]);
|
||||
const Point &ipt = contour_src.points[idx_point];
|
||||
if (contour_dst.empty() || contour_dst.back() != ipt)
|
||||
contour_dst.emplace_back(ipt);
|
||||
for (; it != it_end && it->first.contour_idx == idx_contour && it->first.start_point_idx == idx_point; ++ it) {
|
||||
// Add these points to the destination contour.
|
||||
const Polyline &infill_line = infill_ordered[it->second / 2];
|
||||
const Point &pt = (it->second & 1) ? infill_line.points.back() : infill_line.points.front();
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
const Vec2d pt1 = contour_src[idx_point].cast<double>();
|
||||
const Vec2d pt1 = ipt.cast<double>();
|
||||
const Vec2d pt2 = (idx_point + 1 == contour_src.size() ? contour_src.points.front() : contour_src.points[idx_point + 1]).cast<double>();
|
||||
const Vec2d ptx = lerp(pt1, pt2, it->first.t);
|
||||
assert(std::abs(pt.x() - pt.x()) < SCALED_EPSILON);
|
||||
assert(std::abs(pt.y() - pt.y()) < SCALED_EPSILON);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
map_infill_end_point_to_boundary[it->second] = ContourIntersectionPoint{ idx_contour, contour_dst.size() };
|
||||
size_t idx_tjoint_pt = 0;
|
||||
if (idx_point + 1 < contour_src.size() || pt != contour_dst.front()) {
|
||||
if (pt != contour_dst.back())
|
||||
contour_dst.emplace_back(pt);
|
||||
idx_tjoint_pt = contour_dst.size() - 1;
|
||||
}
|
||||
map_infill_end_point_to_boundary[it->second] = ContourIntersectionPoint{ idx_contour, idx_tjoint_pt };
|
||||
ContourIntersectionPoint *pthis = &map_infill_end_point_to_boundary[it->second];
|
||||
if (pprev) {
|
||||
pprev->next_on_contour = pthis;
|
||||
|
|
@ -1186,8 +1194,6 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
pfirst = pthis;
|
||||
contour_intersection_points.emplace_back(pthis);
|
||||
pprev = pthis;
|
||||
//add new point here
|
||||
contour_dst.emplace_back(pt);
|
||||
}
|
||||
if (pfirst) {
|
||||
pprev->next_on_contour = pfirst;
|
||||
|
|
@ -1195,16 +1201,19 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
}
|
||||
}
|
||||
// Parametrize the new boundary with the intersection points inserted.
|
||||
std::vector<float> &contour_params = boundary_params[idx_contour];
|
||||
contour_params.assign(contour_dst.size() + 1, 0.f);
|
||||
for (size_t i = 1; i < contour_dst.size(); ++ i)
|
||||
contour_params[i] = contour_params[i - 1] + (contour_dst[i].cast<float>() - contour_dst[i - 1].cast<float>()).norm();
|
||||
contour_params.back() = contour_params[contour_params.size() - 2] + (contour_dst.back().cast<float>() - contour_dst.front().cast<float>()).norm();
|
||||
std::vector<double> &contour_params = boundary_params[idx_contour];
|
||||
contour_params.assign(contour_dst.size() + 1, 0.);
|
||||
for (size_t i = 1; i < contour_dst.size(); ++i) {
|
||||
contour_params[i] = contour_params[i - 1] + (contour_dst[i].cast<double>() - contour_dst[i - 1].cast<double>()).norm();
|
||||
assert(contour_params[i] > contour_params[i - 1]);
|
||||
}
|
||||
contour_params.back() = contour_params[contour_params.size() - 2] + (contour_dst.back().cast<double>() - contour_dst.front().cast<double>()).norm();
|
||||
assert(contour_params.back() > contour_params[contour_params.size() - 2]);
|
||||
// Map parameters from contour_params to boundary_intersection_points.
|
||||
for (ContourIntersectionPoint *ip : contour_intersection_points)
|
||||
ip->param = contour_params[ip->point_idx];
|
||||
// and measure distance to the previous and next intersection point.
|
||||
const float contour_length = contour_params.back();
|
||||
const double contour_length = contour_params.back();
|
||||
for (ContourIntersectionPoint *ip : contour_intersection_points)
|
||||
if (ip->next_on_contour == ip) {
|
||||
assert(ip->prev_on_contour == ip);
|
||||
|
|
@ -1238,9 +1247,9 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
}
|
||||
|
||||
// Connection from end of one infill line to the start of another infill line.
|
||||
//const float length_max = scale_(spacing);
|
||||
// const auto length_max = float(scale_((2. / params.density) * spacing));
|
||||
const auto length_max = float(scale_((1000. / params.density) * spacing));
|
||||
//const double length_max = scale_(spacing);
|
||||
// const auto length_max = double(scale_((2. / params.density) * spacing));
|
||||
const auto length_max = double(scale_((1000. / params.density) * spacing));
|
||||
std::vector<size_t> merged_with(infill_ordered.size());
|
||||
std::iota(merged_with.begin(), merged_with.end(), 0);
|
||||
struct ConnectionCost {
|
||||
|
|
@ -1258,7 +1267,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
const ContourIntersectionPoint *cp2 = &map_infill_end_point_to_boundary[idx_chain * 2];
|
||||
if (cp1->contour_idx != boundary_idx_unconnected && cp1->contour_idx == cp2->contour_idx) {
|
||||
// End points on the same contour. Try to connect them.
|
||||
std::pair<float, float> len = path_lengths_along_contour(cp1, cp2, boundary_params[cp1->contour_idx].back());
|
||||
std::pair<double, double> len = path_lengths_along_contour(cp1, cp2, boundary_params[cp1->contour_idx].back());
|
||||
if (len.first < length_max)
|
||||
connections_sorted.emplace_back(idx_chain - 1, len.first, false);
|
||||
if (len.second < length_max)
|
||||
|
|
@ -1281,7 +1290,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
return std::numeric_limits<size_t>::max();
|
||||
};
|
||||
|
||||
const float line_half_width = 0.5f * scale_(spacing);
|
||||
const double line_half_width = 0.5 * scale_(spacing);
|
||||
|
||||
#if 0
|
||||
for (ConnectionCost &connection_cost : connections_sorted) {
|
||||
|
|
@ -1291,7 +1300,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
assert(cp1->contour_idx == cp2->contour_idx && cp1->contour_idx != boundary_idx_unconnected);
|
||||
if (cp1->consumed || cp2->consumed)
|
||||
continue;
|
||||
const float length = connection_cost.cost;
|
||||
const double length = connection_cost.cost;
|
||||
bool could_connect;
|
||||
{
|
||||
// cp1, cp2 sorted CCW.
|
||||
|
|
@ -1334,7 +1343,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
|
||||
struct Arc {
|
||||
ContourIntersectionPoint *intersection;
|
||||
float arc_length;
|
||||
double arc_length;
|
||||
};
|
||||
std::vector<Arc> arches;
|
||||
arches.reserve(map_infill_end_point_to_boundary.size());
|
||||
|
|
@ -1352,7 +1361,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
size_t polyline_idx1 = get_and_update_merged_with(((cp1 - map_infill_end_point_to_boundary.data()) / 2));
|
||||
size_t polyline_idx2 = get_and_update_merged_with(((cp2 - map_infill_end_point_to_boundary.data()) / 2));
|
||||
const Points &contour = boundary[cp1->contour_idx];
|
||||
const std::vector<float> &contour_params = boundary_params[cp1->contour_idx];
|
||||
const std::vector<double> &contour_params = boundary_params[cp1->contour_idx];
|
||||
if (polyline_idx1 != polyline_idx2) {
|
||||
Polyline &polyline1 = infill_ordered[polyline_idx1];
|
||||
Polyline &polyline2 = infill_ordered[polyline_idx2];
|
||||
|
|
@ -1385,23 +1394,23 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
// Connect the remaining open infill lines to the perimeter lines if possible.
|
||||
for (ContourIntersectionPoint &contour_point : map_infill_end_point_to_boundary)
|
||||
if (! contour_point.consumed && contour_point.contour_idx != boundary_idx_unconnected) {
|
||||
const Points &contour = boundary[contour_point.contour_idx];
|
||||
const std::vector<float> &contour_params = boundary_params[contour_point.contour_idx];
|
||||
const size_t contour_pt_idx = contour_point.point_idx;
|
||||
const Points &contour = boundary[contour_point.contour_idx];
|
||||
const std::vector<double> &contour_params = boundary_params[contour_point.contour_idx];
|
||||
const size_t contour_pt_idx = contour_point.point_idx;
|
||||
|
||||
float lprev = contour_point.could_connect_prev() ?
|
||||
double lprev = contour_point.could_connect_prev() ?
|
||||
path_length_along_contour_ccw(contour_point.prev_on_contour, &contour_point, contour_params.back()) :
|
||||
std::numeric_limits<float>::max();
|
||||
float lnext = contour_point.could_connect_next() ?
|
||||
std::numeric_limits<double>::max();
|
||||
double lnext = contour_point.could_connect_next() ?
|
||||
path_length_along_contour_ccw(&contour_point, contour_point.next_on_contour, contour_params.back()) :
|
||||
std::numeric_limits<float>::max();
|
||||
std::numeric_limits<double>::max();
|
||||
size_t polyline_idx = get_and_update_merged_with(((&contour_point - map_infill_end_point_to_boundary.data()) / 2));
|
||||
Polyline &polyline = infill_ordered[polyline_idx];
|
||||
assert(! polyline.empty());
|
||||
assert(contour[contour_point.point_idx] == polyline.points.front() || contour[contour_point.point_idx] == polyline.points.back());
|
||||
bool connected = false;
|
||||
for (float l : { std::min(lprev, lnext), std::max(lprev, lnext) }) {
|
||||
if (l == std::numeric_limits<float>::max() || l > anchor_length_max)
|
||||
for (double l : { std::min(lprev, lnext), std::max(lprev, lnext) }) {
|
||||
if (l == std::numeric_limits<double>::max() || l > anchor_length_max)
|
||||
break;
|
||||
// Take the complete contour.
|
||||
bool reversed = l == lprev;
|
||||
|
|
@ -1439,7 +1448,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
|
|||
// 2) Hook length
|
||||
// ...
|
||||
// Let's take the longer now, as this improves the chance of another hook to be placed on the other side of this contour point.
|
||||
float l = std::max(contour_point.contour_not_taken_length_prev, contour_point.contour_not_taken_length_next);
|
||||
double l = std::max(contour_point.contour_not_taken_length_prev, contour_point.contour_not_taken_length_next);
|
||||
if (l > SCALED_EPSILON) {
|
||||
if (contour_point.contour_not_taken_length_prev > contour_point.contour_not_taken_length_next)
|
||||
take_limited(polyline, contour, contour_params, &contour_point, contour_point.prev_on_contour, true, anchor_length, line_half_width);
|
||||
|
|
|
|||
|
|
@ -24,22 +24,22 @@ void FillConcentric::_fill_surface_single(
|
|||
this->spacing = unscale<double>(distance);
|
||||
}
|
||||
|
||||
Polygons loops = (Polygons)expolygon;
|
||||
Polygons loops = to_polygons(std::move(expolygon));
|
||||
Polygons last = loops;
|
||||
while (! last.empty()) {
|
||||
last = offset2(last, -(distance + min_spacing/2), +min_spacing/2);
|
||||
loops.insert(loops.end(), last.begin(), last.end());
|
||||
append(loops, last);
|
||||
}
|
||||
|
||||
// generate paths from the outermost to the innermost, to avoid
|
||||
// adhesion problems of the first central tiny loops
|
||||
loops = union_pt_chained(loops, false);
|
||||
loops = union_pt_chained_outside_in(loops, false);
|
||||
|
||||
// split paths using a nearest neighbor search
|
||||
size_t iPathFirst = polylines_out.size();
|
||||
Point last_pos(0, 0);
|
||||
for (const Polygon &loop : loops) {
|
||||
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop.points)));
|
||||
polylines_out.emplace_back(loop.split_at_index(last_pos.nearest_point_index(loop.points)));
|
||||
last_pos = polylines_out.back().last_point();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Slic3r {
|
|||
class FillConcentric : public Fill
|
||||
{
|
||||
public:
|
||||
~FillConcentric() override {}
|
||||
~FillConcentric() override = default;
|
||||
|
||||
protected:
|
||||
Fill* clone() const override { return new FillConcentric(*this); };
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ protected:
|
|||
|
||||
bool _can_connect(coord_t dist_X, coord_t dist_Y)
|
||||
{
|
||||
coord_t TOLERANCE = 10 * SCALED_EPSILON;
|
||||
const auto TOLERANCE = coord_t(10 * SCALED_EPSILON);
|
||||
return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE)
|
||||
&& (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE)
|
||||
&& (dist_Y <= this->_diagonal_distance);
|
||||
|
|
|
|||
|
|
@ -56,58 +56,59 @@ const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_poin
|
|||
const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";
|
||||
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
|
||||
|
||||
const char* MODEL_TAG = "model";
|
||||
const char* RESOURCES_TAG = "resources";
|
||||
const char* OBJECT_TAG = "object";
|
||||
const char* MESH_TAG = "mesh";
|
||||
const char* VERTICES_TAG = "vertices";
|
||||
const char* VERTEX_TAG = "vertex";
|
||||
const char* TRIANGLES_TAG = "triangles";
|
||||
const char* TRIANGLE_TAG = "triangle";
|
||||
const char* COMPONENTS_TAG = "components";
|
||||
const char* COMPONENT_TAG = "component";
|
||||
const char* BUILD_TAG = "build";
|
||||
const char* ITEM_TAG = "item";
|
||||
const char* METADATA_TAG = "metadata";
|
||||
static constexpr const char* MODEL_TAG = "model";
|
||||
static constexpr const char* RESOURCES_TAG = "resources";
|
||||
static constexpr const char* OBJECT_TAG = "object";
|
||||
static constexpr const char* MESH_TAG = "mesh";
|
||||
static constexpr const char* VERTICES_TAG = "vertices";
|
||||
static constexpr const char* VERTEX_TAG = "vertex";
|
||||
static constexpr const char* TRIANGLES_TAG = "triangles";
|
||||
static constexpr const char* TRIANGLE_TAG = "triangle";
|
||||
static constexpr const char* COMPONENTS_TAG = "components";
|
||||
static constexpr const char* COMPONENT_TAG = "component";
|
||||
static constexpr const char* BUILD_TAG = "build";
|
||||
static constexpr const char* ITEM_TAG = "item";
|
||||
static constexpr const char* METADATA_TAG = "metadata";
|
||||
|
||||
const char* CONFIG_TAG = "config";
|
||||
const char* VOLUME_TAG = "volume";
|
||||
static constexpr const char* CONFIG_TAG = "config";
|
||||
static constexpr const char* VOLUME_TAG = "volume";
|
||||
|
||||
const char* UNIT_ATTR = "unit";
|
||||
const char* NAME_ATTR = "name";
|
||||
const char* TYPE_ATTR = "type";
|
||||
const char* ID_ATTR = "id";
|
||||
const char* X_ATTR = "x";
|
||||
const char* Y_ATTR = "y";
|
||||
const char* Z_ATTR = "z";
|
||||
const char* V1_ATTR = "v1";
|
||||
const char* V2_ATTR = "v2";
|
||||
const char* V3_ATTR = "v3";
|
||||
const char* OBJECTID_ATTR = "objectid";
|
||||
const char* TRANSFORM_ATTR = "transform";
|
||||
const char* PRINTABLE_ATTR = "printable";
|
||||
const char* INSTANCESCOUNT_ATTR = "instances_count";
|
||||
const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
|
||||
const char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam";
|
||||
static constexpr const char* UNIT_ATTR = "unit";
|
||||
static constexpr const char* NAME_ATTR = "name";
|
||||
static constexpr const char* TYPE_ATTR = "type";
|
||||
static constexpr const char* ID_ATTR = "id";
|
||||
static constexpr const char* X_ATTR = "x";
|
||||
static constexpr const char* Y_ATTR = "y";
|
||||
static constexpr const char* Z_ATTR = "z";
|
||||
static constexpr const char* V1_ATTR = "v1";
|
||||
static constexpr const char* V2_ATTR = "v2";
|
||||
static constexpr const char* V3_ATTR = "v3";
|
||||
static constexpr const char* OBJECTID_ATTR = "objectid";
|
||||
static constexpr const char* TRANSFORM_ATTR = "transform";
|
||||
static constexpr const char* PRINTABLE_ATTR = "printable";
|
||||
static constexpr const char* INSTANCESCOUNT_ATTR = "instances_count";
|
||||
static constexpr const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
|
||||
static constexpr const char* CUSTOM_SEAM_ATTR = "slic3rpe:custom_seam";
|
||||
|
||||
const char* KEY_ATTR = "key";
|
||||
const char* VALUE_ATTR = "value";
|
||||
const char* FIRST_TRIANGLE_ID_ATTR = "firstid";
|
||||
const char* LAST_TRIANGLE_ID_ATTR = "lastid";
|
||||
static constexpr const char* KEY_ATTR = "key";
|
||||
static constexpr const char* VALUE_ATTR = "value";
|
||||
static constexpr const char* FIRST_TRIANGLE_ID_ATTR = "firstid";
|
||||
static constexpr const char* LAST_TRIANGLE_ID_ATTR = "lastid";
|
||||
|
||||
const char* OBJECT_TYPE = "object";
|
||||
const char* VOLUME_TYPE = "volume";
|
||||
static constexpr const char* OBJECT_TYPE = "object";
|
||||
static constexpr const char* VOLUME_TYPE = "volume";
|
||||
|
||||
const char* NAME_KEY = "name";
|
||||
const char* MODIFIER_KEY = "modifier";
|
||||
const char* VOLUME_TYPE_KEY = "volume_type";
|
||||
const char* MATRIX_KEY = "matrix";
|
||||
const char* SOURCE_FILE_KEY = "source_file";
|
||||
const char* SOURCE_OBJECT_ID_KEY = "source_object_id";
|
||||
const char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
|
||||
const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
|
||||
const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
|
||||
const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
|
||||
static constexpr const char* NAME_KEY = "name";
|
||||
static constexpr const char* MODIFIER_KEY = "modifier";
|
||||
static constexpr const char* VOLUME_TYPE_KEY = "volume_type";
|
||||
static constexpr const char* MATRIX_KEY = "matrix";
|
||||
static constexpr const char* SOURCE_FILE_KEY = "source_file";
|
||||
static constexpr const char* SOURCE_OBJECT_ID_KEY = "source_object_id";
|
||||
static constexpr const char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
|
||||
static constexpr const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
|
||||
static constexpr const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
|
||||
static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
|
||||
static constexpr const char* SOURCE_IN_INCHES = "source_in_inches";
|
||||
|
||||
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
|
||||
const char* VALID_OBJECT_TYPES[] =
|
||||
|
|
@ -1926,6 +1927,8 @@ namespace Slic3r {
|
|||
volume->source.mesh_offset(1) = ::atof(metadata.value.c_str());
|
||||
else if (metadata.key == SOURCE_OFFSET_Z_KEY)
|
||||
volume->source.mesh_offset(2) = ::atof(metadata.value.c_str());
|
||||
else if (metadata.key == SOURCE_IN_INCHES)
|
||||
volume->source.is_converted_from_inches = metadata.value == "1";
|
||||
else
|
||||
volume->config.set_deserialize(metadata.key, metadata.value);
|
||||
}
|
||||
|
|
@ -2248,7 +2251,7 @@ namespace Slic3r {
|
|||
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
|
||||
std::string name = boost::filesystem::path(filename).stem().string();
|
||||
std::string name = xml_escape(boost::filesystem::path(filename).stem().string());
|
||||
stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"Designer\">" << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"Description\">" << name << "</" << METADATA_TAG << ">\n";
|
||||
|
|
@ -2749,15 +2752,20 @@ namespace Slic3r {
|
|||
stream << "\"/>\n";
|
||||
|
||||
// stores volume's source data
|
||||
if (!volume->source.input_file.empty())
|
||||
{
|
||||
std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
|
||||
std::string prefix = std::string(" <") + METADATA_TAG + " " + TYPE_ATTR + "=\"" + VOLUME_TYPE + "\" " + KEY_ATTR + "=\"";
|
||||
if (! volume->source.input_file.empty()) {
|
||||
stream << prefix << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
|
||||
stream << prefix << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
|
||||
stream << prefix << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
|
||||
stream << prefix << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
|
||||
stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
|
||||
stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
|
||||
stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
|
||||
}
|
||||
if (volume->source.is_converted_from_inches)
|
||||
stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n";
|
||||
}
|
||||
|
||||
// stores volume's config data
|
||||
|
|
|
|||
|
|
@ -778,6 +778,9 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
else if (strcmp(opt_key, "source_offset_z") == 0) {
|
||||
m_volume->source.mesh_offset(2) = ::atof(m_value[1].c_str());
|
||||
}
|
||||
else if (strcmp(opt_key, "source_in_inches") == 0) {
|
||||
m_volume->source.is_converted_from_inches = m_value[1] == "1";
|
||||
}
|
||||
}
|
||||
} else if (m_path.size() == 3) {
|
||||
if (m_path[1] == NODE_TYPE_MATERIAL) {
|
||||
|
|
@ -1017,6 +1020,12 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model
|
|||
#endif // forward compatibility
|
||||
|
||||
close_zip_reader(&archive);
|
||||
|
||||
for (ModelObject *o : model->objects)
|
||||
for (ModelVolume *v : o->volumes)
|
||||
if (v->source.input_file.empty() && (v->type() == ModelVolumeType::MODEL_PART))
|
||||
v->source.input_file = path;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1205,6 +1214,8 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
|
|||
stream << " <metadata type=\"slic3r.source_offset_y\">" << volume->source.mesh_offset(1) << "</metadata>\n";
|
||||
stream << " <metadata type=\"slic3r.source_offset_z\">" << volume->source.mesh_offset(2) << "</metadata>\n";
|
||||
}
|
||||
if (volume->source.is_converted_from_inches)
|
||||
stream << " <metadata type=\"slic3r.source_in_inches\">1</metadata>\n";
|
||||
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
|
||||
const indexed_triangle_set &its = volume->mesh().its;
|
||||
for (size_t i = 0; i < its.indices.size(); ++i) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIR_SEPARATOR '\\'
|
||||
#else
|
||||
|
|
@ -22,7 +24,7 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
|
|||
// Parse the OBJ file.
|
||||
ObjParser::ObjData data;
|
||||
if (! ObjParser::objparse(path, data)) {
|
||||
// die "Failed to parse $file\n" if !-e $path;
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +72,8 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
|
|||
++ num_normals;
|
||||
}
|
||||
}
|
||||
if (data.vertices[i].coordIdx != -1) {
|
||||
// Result of obj_parseline() call is not checked, thus not all vertices are necessarily finalized with coord_Idx == -1.
|
||||
if (i < data.vertices.size() && data.vertices[i].coordIdx != -1) {
|
||||
// This is a quad. Produce the other triangle.
|
||||
stl_facet &facet2 = stl.facet_start[i_face++];
|
||||
facet2.vertex[0] = facet.vertex[0];
|
||||
|
|
@ -102,7 +105,7 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
|
|||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
if (mesh.facets_count() == 0) {
|
||||
// die "This OBJ file couldn't be read because it's empty.\n"
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
#include "objparser.hpp"
|
||||
|
|
@ -312,7 +313,7 @@ static bool obj_parseline(const char *line, ObjData &data)
|
|||
break;
|
||||
}
|
||||
default:
|
||||
printf("ObjParser: Unknown command: %c\r\n", c1);
|
||||
BOOST_LOG_TRIVIAL(error) << "ObjParser: Unknown command: " << c1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -338,15 +339,22 @@ bool objparse(const char *path, ObjData &data)
|
|||
char *c = buf + lastLine;
|
||||
while (*c == ' ' || *c == '\t')
|
||||
++ c;
|
||||
//FIXME check the return value and exit on error?
|
||||
// Will it break parsing of some obj files?
|
||||
obj_parseline(c, data);
|
||||
lastLine = i + 1;
|
||||
}
|
||||
lenPrev = len - lastLine;
|
||||
if (lenPrev > 65536) {
|
||||
BOOST_LOG_TRIVIAL(error) << "ObjParser: Excessive line length";
|
||||
::fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
memmove(buf, buf + lastLine, lenPrev);
|
||||
}
|
||||
}
|
||||
catch (std::bad_alloc&) {
|
||||
printf("Out of memory\r\n");
|
||||
BOOST_LOG_TRIVIAL(error) << "ObjParser: Out of memory";
|
||||
}
|
||||
::fclose(pFile);
|
||||
|
||||
|
|
@ -378,7 +386,8 @@ bool objparse(std::istream &stream, ObjData &data)
|
|||
}
|
||||
}
|
||||
catch (std::bad_alloc&) {
|
||||
printf("Out of memory\r\n");
|
||||
BOOST_LOG_TRIVIAL(error) << "ObjParser: Out of memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -413,14 +413,11 @@ namespace Slic3r {
|
|||
|
||||
std::string WipeTowerIntegration::prime(GCode& gcodegen)
|
||||
{
|
||||
assert(m_layer_idx == 0);
|
||||
std::string gcode;
|
||||
|
||||
for (const WipeTower::ToolChangeResult& tcr : m_priming) {
|
||||
if (! tcr.extrusions.empty())
|
||||
gcode += append_tcr(gcodegen, tcr, tcr.new_tool);
|
||||
}
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
|
|
@ -717,8 +714,21 @@ namespace DoExport {
|
|||
if (region->config().get_abs_value("infill_speed") == 0 ||
|
||||
region->config().get_abs_value("solid_infill_speed") == 0 ||
|
||||
region->config().get_abs_value("top_solid_infill_speed") == 0 ||
|
||||
region->config().get_abs_value("bridge_speed") == 0)
|
||||
mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm());
|
||||
region->config().get_abs_value("bridge_speed") == 0)
|
||||
{
|
||||
// Minimal volumetric flow should not be calculated over ironing extrusions.
|
||||
// Use following lambda instead of the built-it method.
|
||||
// https://github.com/prusa3d/PrusaSlicer/issues/5082
|
||||
auto min_mm3_per_mm_no_ironing = [](const ExtrusionEntityCollection& eec) -> double {
|
||||
double min = std::numeric_limits<double>::max();
|
||||
for (const ExtrusionEntity* ee : eec.entities)
|
||||
if (ee->role() != erIroning)
|
||||
min = std::min(min, ee->min_mm3_per_mm());
|
||||
return min;
|
||||
};
|
||||
|
||||
mm3_per_mm.push_back(min_mm3_per_mm_no_ironing(layerm->fills));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (object->config().get_abs_value("support_material_speed") == 0 ||
|
||||
|
|
@ -1243,18 +1253,30 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
_write(file, "M1 S10\n");
|
||||
bool overlap = bbox_prime.overlap(bbox_print);
|
||||
|
||||
if (print.config().gcode_flavor == gcfMarlin) {
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
|
||||
if (overlap) {
|
||||
// Wait for the user to remove the priming extrusions.
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
} else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
_write(file, "M1 S10\n");
|
||||
}
|
||||
} else {
|
||||
// This is not Marlin, M1 command is probably not supported.
|
||||
// (See https://github.com/prusa3d/PrusaSlicer/issues/5441.)
|
||||
if (overlap) {
|
||||
print.active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
|
||||
_(L("Your print is very close to the priming regions. "
|
||||
"Make sure there is no collision.")));
|
||||
} else {
|
||||
// Just continue printing, no action necessary.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
|
|
@ -1700,7 +1722,9 @@ namespace Skirt {
|
|||
//FIXME infinite or high skirt does not make sense for sequential print!
|
||||
(skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) &&
|
||||
// This print_z has not been extruded yet (sequential print)
|
||||
skirt_done.back() < layer_tools.print_z - EPSILON &&
|
||||
// FIXME: The skirt_done should not be empty at this point. The check is a workaround
|
||||
// of https://github.com/prusa3d/PrusaSlicer/issues/5652, but it deserves a real fix.
|
||||
(! skirt_done.empty() && skirt_done.back() < layer_tools.print_z - EPSILON) &&
|
||||
// and this layer is an object layer, or it is a raft layer.
|
||||
(layer_tools.has_object || support_layer->id() < (size_t)support_layer->object()->config().raft_layers.value)) {
|
||||
#if 0
|
||||
|
|
@ -2087,6 +2111,8 @@ void GCode::process_layer(
|
|||
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role));
|
||||
m_layer = layers[instance_to_print.layer_id].layer();
|
||||
}
|
||||
//FIXME order islands?
|
||||
// Sequential tool path ordering of multiple parts within the same object, aka. perimeter tracking (#5511)
|
||||
for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
|
||||
const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, static_cast<unsigned int>(instance_to_print.instance_id), extruder_id, print_wipe_extrusions != 0) : island.by_region;
|
||||
//FIXME the following code prints regions in the order they are defined, the path is not optimized in any way.
|
||||
|
|
|
|||
|
|
@ -269,10 +269,10 @@ static std::vector<TravelPoint> simplify_travel(const AvoidCrossingPerimeters::B
|
|||
}
|
||||
|
||||
// Called by avoid_perimeters() and by simplify_travel_heuristics().
|
||||
static size_t avoid_perimeters_inner(const GCode &gcodegen, const AvoidCrossingPerimeters::Boundary &boundary,
|
||||
const Point &start,
|
||||
const Point &end,
|
||||
std::vector<TravelPoint> &result_out)
|
||||
static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &boundary,
|
||||
const Point &start,
|
||||
const Point &end,
|
||||
std::vector<TravelPoint> &result_out)
|
||||
{
|
||||
const Polygons &boundaries = boundary.boundaries;
|
||||
const EdgeGrid::Grid &edge_grid = boundary.grid;
|
||||
|
|
@ -372,14 +372,14 @@ static size_t avoid_perimeters_inner(const GCode &gcodegen, const AvoidCrossingP
|
|||
}
|
||||
|
||||
// Called by AvoidCrossingPerimeters::travel_to()
|
||||
static size_t avoid_perimeters(const GCode &gcodegen, const AvoidCrossingPerimeters::Boundary &boundary,
|
||||
const Point &start,
|
||||
const Point &end,
|
||||
Polyline &result_out)
|
||||
static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &boundary,
|
||||
const Point &start,
|
||||
const Point &end,
|
||||
Polyline &result_out)
|
||||
{
|
||||
// Travel line is completely or partially inside the bounding box.
|
||||
std::vector<TravelPoint> path;
|
||||
size_t num_intersections = avoid_perimeters_inner(gcodegen, boundary, start, end, path);
|
||||
size_t num_intersections = avoid_perimeters_inner(boundary, start, end, path);
|
||||
result_out = to_polyline(path);
|
||||
|
||||
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
|
||||
|
|
@ -726,6 +726,7 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid,
|
|||
}
|
||||
|
||||
// Polygon offset which ensures that if a polygon breaks up into several separate parts, the original polygon will be used in these places.
|
||||
// ExPolygons are handled one by one so returned ExPolygons could intersect.
|
||||
static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset, double min_contour_width = scale_(0.001))
|
||||
{
|
||||
double search_radius = 2. * (offset + min_contour_width);
|
||||
|
|
@ -781,12 +782,25 @@ static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset, dou
|
|||
return ex_poly_result;
|
||||
}
|
||||
|
||||
//#define INCLUDE_SUPPORTS_IN_BOUNDARY
|
||||
|
||||
// called by AvoidCrossingPerimeters::travel_to()
|
||||
static ExPolygons get_boundary(const Layer &layer)
|
||||
{
|
||||
const float perimeter_spacing = get_perimeter_spacing(layer);
|
||||
const float perimeter_offset = perimeter_spacing / 2.f;
|
||||
auto const *support_layer = dynamic_cast<const SupportLayer *>(&layer);
|
||||
ExPolygons boundary = union_ex(inner_offset(layer.lslices, perimeter_offset));
|
||||
if(support_layer) {
|
||||
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
|
||||
append(boundary, inner_offset(support_layer->support_islands.expolygons, perimeter_offset));
|
||||
#endif
|
||||
auto *layer_below = layer.object()->get_first_layer_bellow_printz(layer.print_z, EPSILON);
|
||||
if (layer_below)
|
||||
append(boundary, inner_offset(layer_below->lslices, perimeter_offset));
|
||||
// After calling inner_offset it is necessary to call union_ex because of the possibility of intersection ExPolygons
|
||||
boundary = union_ex(boundary);
|
||||
}
|
||||
// Collect all top layers that will not be crossed.
|
||||
size_t polygons_count = 0;
|
||||
for (const LayerRegion *layer_region : layer.regions())
|
||||
|
|
@ -810,20 +824,41 @@ static ExPolygons get_boundary(const Layer &layer)
|
|||
// called by AvoidCrossingPerimeters::travel_to()
|
||||
static Polygons get_boundary_external(const Layer &layer)
|
||||
{
|
||||
const float perimeter_spacing = get_perimeter_spacing(layer);
|
||||
const float perimeter_spacing = get_perimeter_spacing_external(layer);
|
||||
const float perimeter_offset = perimeter_spacing / 2.f;
|
||||
auto const *support_layer = dynamic_cast<const SupportLayer *>(&layer);
|
||||
Polygons boundary;
|
||||
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
|
||||
ExPolygons supports_boundary;
|
||||
#endif
|
||||
// Collect all holes for all printed objects and their instances, which will be printed at the same time as passed "layer".
|
||||
for (const PrintObject *object : layer.object()->print()->objects()) {
|
||||
Polygons polygons_per_obj;
|
||||
Polygons polygons_per_obj;
|
||||
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
|
||||
ExPolygons supports_per_obj;
|
||||
#endif
|
||||
if (const Layer *l = object->get_layer_at_printz(layer.print_z, EPSILON); l)
|
||||
for (const ExPolygon &island : l->lslices) append(polygons_per_obj, island.holes);
|
||||
if (support_layer) {
|
||||
auto *layer_below = object->get_first_layer_bellow_printz(layer.print_z, EPSILON);
|
||||
if (layer_below)
|
||||
for (const ExPolygon &island : layer_below->lslices) append(polygons_per_obj, island.holes);
|
||||
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
|
||||
append(supports_per_obj, support_layer->support_islands.expolygons);
|
||||
#endif
|
||||
}
|
||||
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
size_t boundary_idx = boundary.size();
|
||||
append(boundary, polygons_per_obj);
|
||||
for (; boundary_idx < boundary.size(); ++boundary_idx)
|
||||
boundary[boundary_idx].translate(instance.shift);
|
||||
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
|
||||
size_t support_idx = supports_boundary.size();
|
||||
append(supports_boundary, supports_per_obj);
|
||||
for (; support_idx < supports_boundary.size(); ++support_idx)
|
||||
supports_boundary[support_idx].translate(instance.shift);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -832,7 +867,9 @@ static Polygons get_boundary_external(const Layer &layer)
|
|||
// Reverse all polygons for making normals point from the polygon out.
|
||||
for (Polygon &poly : boundary)
|
||||
poly.reverse();
|
||||
|
||||
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
|
||||
append(boundary, to_polygons(inner_offset(supports_boundary, perimeter_offset)));
|
||||
#endif
|
||||
return boundary;
|
||||
}
|
||||
|
||||
|
|
@ -873,14 +910,17 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
|||
Vec2d startf = start.cast<double>();
|
||||
Vec2d endf = end .cast<double>();
|
||||
|
||||
if (!use_external && !gcodegen.layer()->lslices.empty() && !any_expolygon_contains(gcodegen.layer()->lslices, gcodegen.layer()->lslices_bboxes, m_grid_lslice, travel)) {
|
||||
const ExPolygons &lslices = gcodegen.layer()->lslices;
|
||||
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
|
||||
bool is_support_layer = dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr;
|
||||
if (!use_external && (is_support_layer || (!lslices.empty() && !any_expolygon_contains(lslices, lslices_bboxes, m_grid_lslice, travel)))) {
|
||||
// Initialize m_internal only when it is necessary.
|
||||
if (m_internal.boundaries.empty())
|
||||
init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer())));
|
||||
|
||||
// Trim the travel line by the bounding box.
|
||||
if (Geometry::liang_barsky_line_clipping(startf, endf, m_internal.bbox)) {
|
||||
travel_intersection_count = avoid_perimeters(gcodegen, m_internal, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl);
|
||||
if (!m_internal.boundaries.empty() && Geometry::liang_barsky_line_clipping(startf, endf, m_internal.bbox)) {
|
||||
travel_intersection_count = avoid_perimeters(m_internal, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl);
|
||||
result_pl.points.front() = start;
|
||||
result_pl.points.back() = end;
|
||||
}
|
||||
|
|
@ -891,7 +931,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
|||
|
||||
// Trim the travel line by the bounding box.
|
||||
if (!m_external.boundaries.empty() && Geometry::liang_barsky_line_clipping(startf, endf, m_external.bbox)) {
|
||||
travel_intersection_count = avoid_perimeters(gcodegen, m_external, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl);
|
||||
travel_intersection_count = avoid_perimeters(m_external, startf.cast<coord_t>(), endf.cast<coord_t>(), result_pl);
|
||||
result_pl.points.front() = start;
|
||||
result_pl.points.back() = end;
|
||||
}
|
||||
|
|
@ -903,13 +943,25 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
|||
travel_intersection_count = 0;
|
||||
}
|
||||
|
||||
double max_detour_length scale_(gcodegen.config().avoid_crossing_perimeters_max_detour);
|
||||
if (max_detour_length > 0 && (result_pl.length() - travel.length()) > max_detour_length)
|
||||
result_pl = {start, end};
|
||||
const ConfigOptionFloatOrPercent &opt_max_detour = gcodegen.config().avoid_crossing_perimeters_max_detour;
|
||||
bool max_detour_length_exceeded = false;
|
||||
if (opt_max_detour.value > 0) {
|
||||
double direct_length = travel.length();
|
||||
double detour = result_pl.length() - direct_length;
|
||||
double max_detour_length = opt_max_detour.percent ?
|
||||
direct_length * 0.01 * opt_max_detour.value :
|
||||
scale_(opt_max_detour.value);
|
||||
if (detour > max_detour_length) {
|
||||
result_pl = {start, end};
|
||||
max_detour_length_exceeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_external) {
|
||||
result_pl.translate(-scaled_origin);
|
||||
*could_be_wipe_disabled = false;
|
||||
} else if (max_detour_length_exceeded) {
|
||||
*could_be_wipe_disabled = false;
|
||||
} else
|
||||
*could_be_wipe_disabled = !need_wipe(gcodegen, m_grid_lslice, travel, result_pl, travel_intersection_count);
|
||||
|
||||
|
|
|
|||
|
|
@ -540,7 +540,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
m_filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
|
||||
}
|
||||
|
||||
if (config.machine_limits_usage.value != MachineLimitsUsage::Ignore)
|
||||
if (m_flavor == gcfMarlin && config.machine_limits_usage.value != MachineLimitsUsage::Ignore)
|
||||
m_time_processor.machine_limits = reinterpret_cast<const MachineEnvelopeConfig&>(config);
|
||||
|
||||
// Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
|
||||
|
|
@ -562,6 +562,10 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
}
|
||||
|
||||
m_time_processor.export_remaining_time_enabled = config.remaining_times.value;
|
||||
|
||||
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
m_use_volumetric_e = config.use_volumetric_e;
|
||||
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
}
|
||||
|
||||
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
|
|
@ -620,7 +624,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// replace missing values with default
|
||||
std::string default_color = "#FF8000";
|
||||
for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) {
|
||||
|
|
@ -649,75 +652,86 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
const ConfigOptionFloats* machine_max_acceleration_x = config.option<ConfigOptionFloats>("machine_max_acceleration_x");
|
||||
if (machine_max_acceleration_x != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_x.values = machine_max_acceleration_x->values;
|
||||
if (m_flavor == gcfMarlin) {
|
||||
const ConfigOptionFloats* machine_max_acceleration_x = config.option<ConfigOptionFloats>("machine_max_acceleration_x");
|
||||
if (machine_max_acceleration_x != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_x.values = machine_max_acceleration_x->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_acceleration_y = config.option<ConfigOptionFloats>("machine_max_acceleration_y");
|
||||
if (machine_max_acceleration_y != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_y.values = machine_max_acceleration_y->values;
|
||||
const ConfigOptionFloats* machine_max_acceleration_y = config.option<ConfigOptionFloats>("machine_max_acceleration_y");
|
||||
if (machine_max_acceleration_y != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_y.values = machine_max_acceleration_y->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_acceleration_z = config.option<ConfigOptionFloats>("machine_max_acceleration_z");
|
||||
if (machine_max_acceleration_z != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_z.values = machine_max_acceleration_z->values;
|
||||
const ConfigOptionFloats* machine_max_acceleration_z = config.option<ConfigOptionFloats>("machine_max_acceleration_z");
|
||||
if (machine_max_acceleration_z != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_z.values = machine_max_acceleration_z->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_acceleration_e = config.option<ConfigOptionFloats>("machine_max_acceleration_e");
|
||||
if (machine_max_acceleration_e != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_e.values = machine_max_acceleration_e->values;
|
||||
const ConfigOptionFloats* machine_max_acceleration_e = config.option<ConfigOptionFloats>("machine_max_acceleration_e");
|
||||
if (machine_max_acceleration_e != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_e.values = machine_max_acceleration_e->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_feedrate_x = config.option<ConfigOptionFloats>("machine_max_feedrate_x");
|
||||
if (machine_max_feedrate_x != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_feedrate_x.values = machine_max_feedrate_x->values;
|
||||
const ConfigOptionFloats* machine_max_feedrate_x = config.option<ConfigOptionFloats>("machine_max_feedrate_x");
|
||||
if (machine_max_feedrate_x != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_feedrate_x.values = machine_max_feedrate_x->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_feedrate_y = config.option<ConfigOptionFloats>("machine_max_feedrate_y");
|
||||
if (machine_max_feedrate_y != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_feedrate_y.values = machine_max_feedrate_y->values;
|
||||
const ConfigOptionFloats* machine_max_feedrate_y = config.option<ConfigOptionFloats>("machine_max_feedrate_y");
|
||||
if (machine_max_feedrate_y != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_feedrate_y.values = machine_max_feedrate_y->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_feedrate_z = config.option<ConfigOptionFloats>("machine_max_feedrate_z");
|
||||
if (machine_max_feedrate_z != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_feedrate_z.values = machine_max_feedrate_z->values;
|
||||
const ConfigOptionFloats* machine_max_feedrate_z = config.option<ConfigOptionFloats>("machine_max_feedrate_z");
|
||||
if (machine_max_feedrate_z != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_feedrate_z.values = machine_max_feedrate_z->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_feedrate_e = config.option<ConfigOptionFloats>("machine_max_feedrate_e");
|
||||
if (machine_max_feedrate_e != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_feedrate_e.values = machine_max_feedrate_e->values;
|
||||
const ConfigOptionFloats* machine_max_feedrate_e = config.option<ConfigOptionFloats>("machine_max_feedrate_e");
|
||||
if (machine_max_feedrate_e != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_feedrate_e.values = machine_max_feedrate_e->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_jerk_x = config.option<ConfigOptionFloats>("machine_max_jerk_x");
|
||||
if (machine_max_jerk_x != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_jerk_x.values = machine_max_jerk_x->values;
|
||||
const ConfigOptionFloats* machine_max_jerk_x = config.option<ConfigOptionFloats>("machine_max_jerk_x");
|
||||
if (machine_max_jerk_x != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_jerk_x.values = machine_max_jerk_x->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_jerk_y = config.option<ConfigOptionFloats>("machine_max_jerk_y");
|
||||
if (machine_max_jerk_y != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_jerk_y.values = machine_max_jerk_y->values;
|
||||
const ConfigOptionFloats* machine_max_jerk_y = config.option<ConfigOptionFloats>("machine_max_jerk_y");
|
||||
if (machine_max_jerk_y != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_jerk_y.values = machine_max_jerk_y->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_jerk_z = config.option<ConfigOptionFloats>("machine_max_jerkz");
|
||||
if (machine_max_jerk_z != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_jerk_z.values = machine_max_jerk_z->values;
|
||||
const ConfigOptionFloats* machine_max_jerk_z = config.option<ConfigOptionFloats>("machine_max_jerkz");
|
||||
if (machine_max_jerk_z != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_jerk_z.values = machine_max_jerk_z->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_jerk_e = config.option<ConfigOptionFloats>("machine_max_jerk_e");
|
||||
if (machine_max_jerk_e != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_jerk_e.values = machine_max_jerk_e->values;
|
||||
const ConfigOptionFloats* machine_max_jerk_e = config.option<ConfigOptionFloats>("machine_max_jerk_e");
|
||||
if (machine_max_jerk_e != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_jerk_e.values = machine_max_jerk_e->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_acceleration_extruding = config.option<ConfigOptionFloats>("machine_max_acceleration_extruding");
|
||||
if (machine_max_acceleration_extruding != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_extruding.values = machine_max_acceleration_extruding->values;
|
||||
const ConfigOptionFloats* machine_max_acceleration_extruding = config.option<ConfigOptionFloats>("machine_max_acceleration_extruding");
|
||||
if (machine_max_acceleration_extruding != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_extruding.values = machine_max_acceleration_extruding->values;
|
||||
|
||||
const ConfigOptionFloats* machine_max_acceleration_retracting = config.option<ConfigOptionFloats>("machine_max_acceleration_retracting");
|
||||
if (machine_max_acceleration_retracting != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values;
|
||||
const ConfigOptionFloats* machine_max_acceleration_retracting = config.option<ConfigOptionFloats>("machine_max_acceleration_retracting");
|
||||
if (machine_max_acceleration_retracting != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values;
|
||||
|
||||
const ConfigOptionFloats* machine_min_extruding_rate = config.option<ConfigOptionFloats>("machine_min_extruding_rate");
|
||||
if (machine_min_extruding_rate != nullptr)
|
||||
m_time_processor.machine_limits.machine_min_extruding_rate.values = machine_min_extruding_rate->values;
|
||||
const ConfigOptionFloats* machine_min_extruding_rate = config.option<ConfigOptionFloats>("machine_min_extruding_rate");
|
||||
if (machine_min_extruding_rate != nullptr)
|
||||
m_time_processor.machine_limits.machine_min_extruding_rate.values = machine_min_extruding_rate->values;
|
||||
|
||||
const ConfigOptionFloats* machine_min_travel_rate = config.option<ConfigOptionFloats>("machine_min_travel_rate");
|
||||
if (machine_min_travel_rate != nullptr)
|
||||
m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values;
|
||||
const ConfigOptionFloats* machine_min_travel_rate = config.option<ConfigOptionFloats>("machine_min_travel_rate");
|
||||
if (machine_min_travel_rate != nullptr)
|
||||
m_time_processor.machine_limits.machine_min_travel_rate.values = machine_min_travel_rate->values;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
||||
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
|
||||
m_time_processor.machines[i].max_acceleration = max_acceleration;
|
||||
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
|
||||
}
|
||||
|
||||
if (m_time_processor.machine_limits.machine_max_acceleration_x.values.size() > 1)
|
||||
enable_stealth_time_estimator(true);
|
||||
|
||||
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
const ConfigOptionBool* use_volumetric_e = config.option<ConfigOptionBool>("use_volumetric_e");
|
||||
if (use_volumetric_e != nullptr)
|
||||
m_use_volumetric_e = use_volumetric_e->value;
|
||||
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
}
|
||||
|
||||
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
|
||||
|
|
@ -772,6 +786,10 @@ void GCodeProcessor::reset()
|
|||
m_result.reset();
|
||||
m_result.id = ++s_result_id;
|
||||
|
||||
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
m_use_volumetric_e = false;
|
||||
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_mm3_per_mm_compare.reset();
|
||||
m_height_compare.reset();
|
||||
|
|
@ -1652,8 +1670,14 @@ void GCodeProcessor::process_G0(const GCodeReader::GCodeLine& line)
|
|||
|
||||
void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
auto absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1)
|
||||
{
|
||||
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_filament_diameters.size()) ? m_filament_diameters[m_extruder_id] : m_filament_diameters.back();
|
||||
float filament_radius = 0.5f * filament_diameter;
|
||||
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
|
||||
auto absolute_position = [this, area_filament_cross_section](Axis axis, const GCodeReader::GCodeLine& lineG1) {
|
||||
#else
|
||||
auto absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1) {
|
||||
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
|
||||
if (axis == E)
|
||||
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
|
||||
|
|
@ -1661,6 +1685,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
if (lineG1.has(Slic3r::Axis(axis))) {
|
||||
float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
|
||||
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
|
||||
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
if (axis == E && m_use_volumetric_e)
|
||||
ret /= area_filament_cross_section;
|
||||
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
|
||||
}
|
||||
else
|
||||
|
|
@ -1718,9 +1746,11 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
|
||||
if (type == EMoveType::Extrude) {
|
||||
float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
|
||||
#if !ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_filament_diameters.size()) ? m_filament_diameters[m_extruder_id] : m_filament_diameters.back();
|
||||
float filament_radius = 0.5f * filament_diameter;
|
||||
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
|
||||
#endif // !ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
|
||||
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
|
||||
|
||||
|
|
@ -1772,7 +1802,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
|
||||
|
||||
// clamp width to avoid artifacts which may arise from wrong values of m_height
|
||||
m_width = std::min(m_width, 1.0f);
|
||||
m_width = std::min(m_width, std::max(1.0f, 4.0f * m_height));
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_width_compare.update(m_width, m_extrusion_role);
|
||||
|
|
@ -2113,32 +2143,29 @@ void GCodeProcessor::process_M135(const GCodeReader::GCodeLine& line)
|
|||
|
||||
void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (!m_time_processor.machine_envelope_processing_enabled)
|
||||
return;
|
||||
|
||||
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
|
||||
float factor = ((m_flavor != gcfRepRapSprinter && m_flavor != gcfRepRapFirmware) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
||||
if (line.has_x())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor);
|
||||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
||||
m_time_processor.machine_envelope_processing_enabled) {
|
||||
if (line.has_x())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, i, line.x() * factor);
|
||||
|
||||
if (line.has_y())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, i, line.y() * factor);
|
||||
if (line.has_y())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, i, line.y() * factor);
|
||||
|
||||
if (line.has_z())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, i, line.z() * factor);
|
||||
if (line.has_z())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, i, line.z() * factor);
|
||||
|
||||
if (line.has_e())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, i, line.e() * factor);
|
||||
if (line.has_e())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, i, line.e() * factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (!m_time_processor.machine_envelope_processing_enabled)
|
||||
return;
|
||||
|
||||
// see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate
|
||||
if (m_flavor == gcfRepetier)
|
||||
return;
|
||||
|
|
@ -2148,45 +2175,48 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
|
|||
float factor = (m_flavor == gcfMarlin || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC;
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
||||
if (line.has_x())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor);
|
||||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
||||
m_time_processor.machine_envelope_processing_enabled) {
|
||||
if (line.has_x())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_x, i, line.x() * factor);
|
||||
|
||||
if (line.has_y())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_y, i, line.y() * factor);
|
||||
if (line.has_y())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_y, i, line.y() * factor);
|
||||
|
||||
if (line.has_z())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_z, i, line.z() * factor);
|
||||
if (line.has_z())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_z, i, line.z() * factor);
|
||||
|
||||
if (line.has_e())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_e, i, line.e() * factor);
|
||||
if (line.has_e())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_feedrate_e, i, line.e() * factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (!m_time_processor.machine_envelope_processing_enabled)
|
||||
return;
|
||||
|
||||
float value;
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
||||
if (line.has_value('S', value)) {
|
||||
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
|
||||
// and it is also generated by Slic3r to control acceleration per extrusion type
|
||||
// (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
|
||||
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
||||
if (line.has_value('T', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||
}
|
||||
else {
|
||||
// New acceleration format, compatible with the upstream Marlin.
|
||||
if (line.has_value('P', value))
|
||||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
||||
m_time_processor.machine_envelope_processing_enabled) {
|
||||
if (line.has_value('S', value)) {
|
||||
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
|
||||
// and it is also generated by Slic3r to control acceleration per extrusion type
|
||||
// (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
|
||||
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
||||
if (line.has_value('R', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||
if (line.has_value('T', value)) {
|
||||
// Interpret the T value as the travel acceleration in the new Marlin format.
|
||||
//FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
|
||||
// set_travel_acceleration(value);
|
||||
if (line.has_value('T', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||
}
|
||||
else {
|
||||
// New acceleration format, compatible with the upstream Marlin.
|
||||
if (line.has_value('P', value))
|
||||
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
||||
if (line.has_value('R', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||
if (line.has_value('T', value)) {
|
||||
// Interpret the T value as the travel acceleration in the new Marlin format.
|
||||
//FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
|
||||
// set_travel_acceleration(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2194,31 +2224,31 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
|
|||
|
||||
void GCodeProcessor::process_M205(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (!m_time_processor.machine_envelope_processing_enabled)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
||||
if (line.has_x()) {
|
||||
float max_jerk = line.x();
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, max_jerk);
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, max_jerk);
|
||||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
||||
m_time_processor.machine_envelope_processing_enabled) {
|
||||
if (line.has_x()) {
|
||||
float max_jerk = line.x();
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_x, i, max_jerk);
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, max_jerk);
|
||||
}
|
||||
|
||||
if (line.has_y())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, line.y());
|
||||
|
||||
if (line.has_z())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_z, i, line.z());
|
||||
|
||||
if (line.has_e())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_e, i, line.e());
|
||||
|
||||
float value;
|
||||
if (line.has_value('S', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, i, value);
|
||||
|
||||
if (line.has_value('T', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_min_travel_rate, i, value);
|
||||
}
|
||||
|
||||
if (line.has_y())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_y, i, line.y());
|
||||
|
||||
if (line.has_z())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_z, i, line.z());
|
||||
|
||||
if (line.has_e())
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_jerk_e, i, line.e());
|
||||
|
||||
float value;
|
||||
if (line.has_value('S', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_min_extruding_rate, i, value);
|
||||
|
||||
if (line.has_value('T', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_min_travel_rate, i, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2315,7 +2345,9 @@ void GCodeProcessor::process_T(const std::string_view command)
|
|||
if (command.length() > 1) {
|
||||
int eid;
|
||||
if (! parse_number(command.substr(1), eid) || eid < 0 || eid > 255) {
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange (" << command << ").";
|
||||
// T-1 is a valid gcode line for RepRap Firmwares (used to deselects all tools) see https://github.com/prusa3d/PrusaSlicer/issues/5677
|
||||
if ((m_flavor != gcfRepRapFirmware && m_flavor != gcfRepRapSprinter) || eid != -1)
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange (" << command << ").";
|
||||
} else {
|
||||
unsigned char id = static_cast<unsigned char>(eid);
|
||||
if (m_extruder_id != id) {
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ namespace Slic3r {
|
|||
bool extruder_unloaded;
|
||||
// whether or not to export post-process the gcode to export lines M73 in it
|
||||
bool export_remaining_time_enabled;
|
||||
// allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope()
|
||||
// allow to skip the lines M201/M203/M204/M205 generated by GCode::print_machine_envelope() for non-Normal time estimate mode
|
||||
bool machine_envelope_processing_enabled;
|
||||
MachineEnvelopeConfig machine_limits;
|
||||
// Additional load / unload times for a filament exchange sequence.
|
||||
|
|
@ -421,6 +421,9 @@ namespace Slic3r {
|
|||
unsigned int m_g1_line_id;
|
||||
unsigned int m_layer_id;
|
||||
CpColor m_cp_color;
|
||||
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
bool m_use_volumetric_e;
|
||||
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
|
||||
|
||||
enum class EProducer
|
||||
{
|
||||
|
|
|
|||
|
|
@ -504,7 +504,7 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon, size
|
|||
bool* saw_custom) const
|
||||
{
|
||||
// Parametrize the polygon by its length.
|
||||
std::vector<float> lengths = polygon.parameter_by_length();
|
||||
const std::vector<float> lengths = polygon.parameter_by_length();
|
||||
|
||||
// Which of the points are inside enforcers/blockers?
|
||||
std::vector<size_t> enforcers_idxs;
|
||||
|
|
@ -516,23 +516,73 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon, size
|
|||
if (saw_custom)
|
||||
*saw_custom = has_enforcers || has_blockers;
|
||||
|
||||
// FIXME FIXME FIXME: This is just to test the outcome and whether it is
|
||||
// reasonable. The algorithm should really sum the length of all available
|
||||
// pieces, get a random length and find the respective point.
|
||||
float rand_len = 0.f;
|
||||
size_t pt_idx = 0;
|
||||
do {
|
||||
rand_len = lengths.back() * (rand()/float(RAND_MAX));
|
||||
auto it = std::lower_bound(lengths.begin(), lengths.end(), rand_len);
|
||||
pt_idx = it == lengths.end() ? 0 : (it-lengths.begin()-1);
|
||||
assert(std::is_sorted(enforcers_idxs.begin(), enforcers_idxs.end()));
|
||||
assert(std::is_sorted(blockers_idxs.begin(), blockers_idxs.end()));
|
||||
std::vector<float> edges;
|
||||
|
||||
// If there are blockers and the point is inside, repeat.
|
||||
// If there are enforcers and the point is NOT inside, repeat.
|
||||
} while ((has_blockers && std::binary_search(blockers_idxs.begin(), blockers_idxs.end(), pt_idx))
|
||||
|| (has_enforcers && ! std::binary_search(enforcers_idxs.begin(), enforcers_idxs.end(), pt_idx)));
|
||||
// Lambda to calculate lengths of all edges of interest. Last parameter
|
||||
// decides whether to measure edges inside or outside idxs.
|
||||
// Negative number = not an edge of interest.
|
||||
auto get_valid_length = [&lengths](const std::vector<size_t>& idxs,
|
||||
std::vector<float>& edges,
|
||||
bool measure_inside_edges) -> float
|
||||
{
|
||||
// First mark edges we are interested in by assigning a positive number.
|
||||
edges.assign(lengths.size()-1, measure_inside_edges ? -1.f : 1.f);
|
||||
for (size_t i=0; i<idxs.size(); ++i) {
|
||||
size_t this_pt_idx = idxs[i];
|
||||
// Two concurrent indices in the list -> the edge between them is the enforcer/blocker.
|
||||
bool inside_edge = ((i != idxs.size()-1 && idxs[i+1] == this_pt_idx + 1)
|
||||
|| (i == idxs.size()-1 && idxs.back() == lengths.size()-2 && idxs[0] == 0));
|
||||
if (inside_edge)
|
||||
edges[this_pt_idx] = measure_inside_edges ? 1.f : -1.f;
|
||||
}
|
||||
// Now measure them.
|
||||
float running_total = 0.f;
|
||||
for (size_t i=0; i<edges.size(); ++i) {
|
||||
if (edges[i] > 0.f) {
|
||||
edges[i] = lengths[i+1] - lengths[i];
|
||||
running_total += edges[i];
|
||||
}
|
||||
}
|
||||
return running_total;
|
||||
};
|
||||
|
||||
// Find all seam candidate edges and their lengths.
|
||||
float valid_length = 0.f;
|
||||
if (has_enforcers)
|
||||
valid_length = get_valid_length(enforcers_idxs, edges, true);
|
||||
|
||||
if (! has_enforcers || valid_length == 0.f) {
|
||||
// Second condition covers case with isolated enf points. Given how the painted
|
||||
// triangles are projected, this should not happen. Stay on the safe side though.
|
||||
if (has_blockers)
|
||||
valid_length = get_valid_length(blockers_idxs, edges, false);
|
||||
if (valid_length == 0.f) // No blockers or everything blocked - use the whole polygon.
|
||||
valid_length = lengths.back();
|
||||
}
|
||||
assert(valid_length != 0.f);
|
||||
// Now generate a random length and find the respective edge.
|
||||
float rand_len = valid_length * (rand()/float(RAND_MAX));
|
||||
size_t pt_idx = 0; // Index of the edge where to put the seam.
|
||||
if (valid_length == lengths.back()) {
|
||||
// Whole polygon is used for placing the seam.
|
||||
auto it = std::lower_bound(lengths.begin(), lengths.end(), rand_len);
|
||||
pt_idx = it == lengths.begin() ? 0 : (it-lengths.begin()-1); // this takes care of a corner case where rand() returns 0
|
||||
} else {
|
||||
float running = 0.f;
|
||||
for (size_t i=0; i<edges.size(); ++i) {
|
||||
running += edges[i] > 0.f ? edges[i] : 0.f;
|
||||
if (running >= rand_len) {
|
||||
pt_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! has_enforcers && ! has_blockers) {
|
||||
// The polygon may be too coarse, calculate the point exactly.
|
||||
assert(valid_length == lengths.back());
|
||||
bool last_seg = pt_idx == polygon.points.size()-1;
|
||||
size_t next_idx = last_seg ? 0 : pt_idx+1;
|
||||
const Point& prev = polygon.points[pt_idx];
|
||||
|
|
|
|||
|
|
@ -461,11 +461,11 @@ bool Model::looks_like_imperial_units() const
|
|||
return false;
|
||||
}
|
||||
|
||||
void Model::convert_from_imperial_units()
|
||||
void Model::convert_from_imperial_units(bool only_small_volumes)
|
||||
{
|
||||
double in_to_mm = 25.4;
|
||||
for (ModelObject* obj : this->objects)
|
||||
if (obj->get_object_stl_stats().volume < 9.0) { // 9 = 3*3*3;
|
||||
if (! only_small_volumes || obj->get_object_stl_stats().volume < 9.0) { // 9 = 3*3*3;
|
||||
obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
|
||||
for (ModelVolume* v : obj->volumes)
|
||||
v->source.is_converted_from_inches = true;
|
||||
|
|
@ -1042,8 +1042,6 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
|
|||
int vol_idx = 0;
|
||||
for (ModelVolume* volume : volumes)
|
||||
{
|
||||
volume->supported_facets.clear();
|
||||
volume->seam_facets.clear();
|
||||
if (!volume->mesh().empty()) {
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
mesh.require_shared_vertices();
|
||||
|
|
@ -1060,9 +1058,14 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
|
|||
vol->source.object_idx = (int)new_objects.size();
|
||||
vol->source.volume_idx = vol_idx;
|
||||
|
||||
// Perform conversion
|
||||
if (volume_idxs.empty() ||
|
||||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end()) {
|
||||
vol->supported_facets.assign(volume->supported_facets);
|
||||
vol->seam_facets.assign(volume->seam_facets);
|
||||
|
||||
// Perform conversion only if the target "imperial" state is different from the current one.
|
||||
// This check supports conversion of "mixed" set of volumes, each with different "imperial" state.
|
||||
if (//vol->source.is_converted_from_inches != from_imperial &&
|
||||
(volume_idxs.empty() ||
|
||||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
|
||||
vol->scale_geometry_after_creation(versor);
|
||||
vol->set_offset(versor.cwiseProduct(volume->get_offset()));
|
||||
vol->source.is_converted_from_inches = from_imperial;
|
||||
|
|
|
|||
|
|
@ -1018,7 +1018,7 @@ public:
|
|||
bool looks_like_multipart_object() const;
|
||||
void convert_multipart_object(unsigned int max_extruders);
|
||||
bool looks_like_imperial_units() const;
|
||||
void convert_from_imperial_units();
|
||||
void convert_from_imperial_units(bool only_small_volumes);
|
||||
|
||||
// Ensures that the min z of the model is not negative
|
||||
void adjust_min_z();
|
||||
|
|
|
|||
|
|
@ -1152,18 +1152,22 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
|
|||
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
|
||||
{
|
||||
if (opt_key == "bed_shape" || opt_key == "thumbnails" || opt_key == "compatible_prints" || opt_key == "compatible_printers") {
|
||||
// Scalar variable, or a vector variable, which is independent from number of extruders,
|
||||
// thus the vector is presented to the user as a single input.
|
||||
diff.emplace_back(opt_key);
|
||||
continue;
|
||||
}
|
||||
switch (other_opt->type())
|
||||
{
|
||||
case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, config_other, config_this); break;
|
||||
case coBools: add_correct_opts_to_diff<ConfigOptionBools >(opt_key, diff, config_other, config_this); break;
|
||||
case coFloats: add_correct_opts_to_diff<ConfigOptionFloats >(opt_key, diff, config_other, config_this); break;
|
||||
case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, config_other, config_this); break;
|
||||
case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, config_other, config_this); break;
|
||||
case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, config_other, config_this); break;
|
||||
default: diff.emplace_back(opt_key); break;
|
||||
} else if (opt_key == "default_filament_profile") {
|
||||
// Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help.
|
||||
// Also the length of this field may differ, which may lead to a crash if the block below is used.
|
||||
} else {
|
||||
switch (other_opt->type()) {
|
||||
case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, config_other, config_this); break;
|
||||
case coBools: add_correct_opts_to_diff<ConfigOptionBools >(opt_key, diff, config_other, config_this); break;
|
||||
case coFloats: add_correct_opts_to_diff<ConfigOptionFloats >(opt_key, diff, config_other, config_this); break;
|
||||
case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, config_other, config_this); break;
|
||||
case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, config_other, config_this); break;
|
||||
case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, config_other, config_this); break;
|
||||
default: diff.emplace_back(opt_key); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1849,6 +1853,13 @@ void PhysicalPrinterCollection::select_printer(const std::string& full_name)
|
|||
m_selected_preset = it->get_preset_name(full_name);
|
||||
}
|
||||
|
||||
void PhysicalPrinterCollection::select_printer(const std::string& printer_name, const std::string& preset_name)
|
||||
{
|
||||
if (preset_name.empty())
|
||||
return select_printer(printer_name);
|
||||
return select_printer(printer_name + PhysicalPrinter::separator() + preset_name);
|
||||
}
|
||||
|
||||
void PhysicalPrinterCollection::select_printer(const PhysicalPrinter& printer)
|
||||
{
|
||||
return select_printer(printer.name);
|
||||
|
|
|
|||
|
|
@ -701,6 +701,7 @@ public:
|
|||
// If full_name doesn't contain name of selected preset, then select first preset in the list for this printer
|
||||
void select_printer(const std::string& full_name);
|
||||
void select_printer(const PhysicalPrinter& printer);
|
||||
void select_printer(const std::string& printer_name, const std::string& preset_name);
|
||||
bool has_selection() const;
|
||||
void unselect_printer() ;
|
||||
bool is_selected(ConstIterator it, const std::string &preset_name) const;
|
||||
|
|
|
|||
|
|
@ -86,9 +86,8 @@ PresetBundle::PresetBundle() :
|
|||
preset.config.optptr(key, true);
|
||||
if (i == 0) {
|
||||
preset.config.optptr("default_print_profile", true);
|
||||
preset.config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" };
|
||||
}
|
||||
else {
|
||||
preset.config.option<ConfigOptionStrings>("default_filament_profile", true);
|
||||
} else {
|
||||
preset.config.optptr("default_sla_print_profile", true);
|
||||
preset.config.optptr("default_sla_material_profile", true);
|
||||
}
|
||||
|
|
@ -668,7 +667,7 @@ DynamicPrintConfig PresetBundle::full_sla_config() const
|
|||
// If the file is loaded successfully, its print / filament / printer profiles will be activated.
|
||||
void PresetBundle::load_config_file(const std::string &path)
|
||||
{
|
||||
if (boost::iends_with(path, ".gcode") || boost::iends_with(path, ".g")) {
|
||||
if (is_gcode_file(path)) {
|
||||
DynamicPrintConfig config;
|
||||
config.apply(FullPrintConfig::defaults());
|
||||
config.load_from_gcode_file(path);
|
||||
|
|
@ -752,7 +751,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
|
|||
switch (printer_technology) {
|
||||
case ptFFF:
|
||||
config.option<ConfigOptionString>("default_print_profile", true);
|
||||
config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
|
||||
config.option<ConfigOptionStrings>("default_filament_profile", true);
|
||||
break;
|
||||
case ptSLA:
|
||||
config.option<ConfigOptionString>("default_sla_print_profile", true);
|
||||
|
|
@ -876,7 +875,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
|
|||
// Activate the physical printer profile if possible.
|
||||
PhysicalPrinter *pp = this->physical_printers.find_printer(physical_printer, true);
|
||||
if (pp != nullptr && std::find(pp->preset_names.begin(), pp->preset_names.end(), this->printers.get_edited_preset().name) != pp->preset_names.end())
|
||||
this->physical_printers.select_printer(*pp);
|
||||
this->physical_printers.select_printer(pp->name, this->printers.get_edited_preset().name);
|
||||
else
|
||||
this->physical_printers.unselect_printer();
|
||||
}
|
||||
|
|
@ -1396,7 +1395,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||
if (! active_printer.empty())
|
||||
printers.select_preset_by_name(active_printer, true);
|
||||
if (! active_physical_printer.empty())
|
||||
physical_printers.select_printer(active_physical_printer +" * " + active_printer);
|
||||
physical_printers.select_printer(active_physical_printer, active_printer);
|
||||
// Activate the first filament preset.
|
||||
if (! active_filaments.empty() && ! active_filaments.front().empty())
|
||||
filaments.select_preset_by_name(active_filaments.front(), true);
|
||||
|
|
|
|||
|
|
@ -1851,10 +1851,7 @@ void Print::_make_brim()
|
|||
}
|
||||
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
|
||||
}
|
||||
loops = union_pt_chained(loops, false);
|
||||
// The function above produces ordering well suited for concentric infill (from outside to inside).
|
||||
// For Brim, the ordering should be reversed (from inside to outside).
|
||||
std::reverse(loops.begin(), loops.end());
|
||||
loops = union_pt_chained_outside_in(loops, false);
|
||||
|
||||
// If there is a possibility that brim intersects skirt, go through loops and split those extrusions
|
||||
// The result is either the original Polygon or a list of Polylines
|
||||
|
|
|
|||
|
|
@ -161,6 +161,8 @@ public:
|
|||
// Get a layer approximately at print_z.
|
||||
const Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) const;
|
||||
Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon);
|
||||
// Get the first layer approximately bellow print_z.
|
||||
const Layer* get_first_layer_bellow_printz(coordf_t print_z, coordf_t epsilon) const;
|
||||
|
||||
// print_z: top of the layer; slice_z: center of the layer.
|
||||
Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ void PrintConfigDef::init_common_params()
|
|||
|
||||
def = this->add("thumbnails", coPoints);
|
||||
def->label = L("G-code thumbnails");
|
||||
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 files");
|
||||
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 files, in the following format: \"XxY, XxY, ...\"");
|
||||
def->mode = comExpert;
|
||||
def->gui_type = "one_string";
|
||||
def->set_default_value(new ConfigOptionPoints());
|
||||
|
|
@ -184,15 +184,16 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("avoid_crossing_perimeters_max_detour", coFloat);
|
||||
def->label = L("Avoid crossing perimeters - The max detour lenght");
|
||||
def = this->add("avoid_crossing_perimeters_max_detour", coFloatOrPercent);
|
||||
def->label = L("Avoid crossing perimeters - Max detour length");
|
||||
def->category = L("Layers and Perimeters");
|
||||
def->tooltip = L("The maximum detour length for avoid crossing perimeters. "
|
||||
"If the detour is longer than this value, avoid crossing perimeters is not applied for this path.");
|
||||
def->sidetext = L("mm (zero to disable)");
|
||||
"If the detour is longer than this value, avoid crossing perimeters is not applied for this travel path. "
|
||||
"Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path.");
|
||||
def->sidetext = L("mm or % (zero to disable)");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0.));
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
|
||||
|
||||
def = this->add("bed_temperature", coInts);
|
||||
def->label = L("Other layers");
|
||||
|
|
@ -606,6 +607,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"this setting to get nice surface finish and correct single wall widths. "
|
||||
"Usual values are between 0.9 and 1.1. If you think you need to change this more, "
|
||||
"check filament diameter and your firmware E steps.");
|
||||
def->max = 2;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloats { 1. });
|
||||
|
||||
|
|
@ -618,6 +620,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"If expressed as percentage (for example: 230%), it will be computed over layer height.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->min = 0;
|
||||
def->max = 1000;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||
|
||||
|
|
@ -3375,6 +3378,8 @@ void DynamicPrintConfig::set_num_extruders(unsigned int num_extruders)
|
|||
const auto &defaults = FullPrintConfig::defaults();
|
||||
for (const std::string &key : print_config_def.extruder_option_keys()) {
|
||||
if (key == "default_filament_profile")
|
||||
// Don't resize this field, as it is presented to the user at the "Dependencies" page of the Printer profile and we don't want to present
|
||||
// empty fields there, if not defined by the system profile.
|
||||
continue;
|
||||
auto *opt = this->option(key, false);
|
||||
assert(opt != nullptr);
|
||||
|
|
|
|||
|
|
@ -828,7 +828,7 @@ class PrintConfig : public MachineEnvelopeConfig, public GCodeConfig
|
|||
public:
|
||||
|
||||
ConfigOptionBool avoid_crossing_perimeters;
|
||||
ConfigOptionFloat avoid_crossing_perimeters_max_detour;
|
||||
ConfigOptionFloatOrPercent avoid_crossing_perimeters_max_detour;
|
||||
ConfigOptionPoints bed_shape;
|
||||
ConfigOptionInts bed_temperature;
|
||||
ConfigOptionFloat bridge_acceleration;
|
||||
|
|
|
|||
|
|
@ -567,12 +567,18 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
|| opt_key == "dont_support_bridges"
|
||||
|| opt_key == "first_layer_extrusion_width") {
|
||||
steps.emplace_back(posSupportMaterial);
|
||||
} else if (opt_key == "bottom_solid_layers") {
|
||||
steps.emplace_back(posPrepareInfill);
|
||||
if(m_print->config().spiral_vase) {
|
||||
// Changing the number of bottom layers when a spiral vase is enabled requires re-slicing the object again.
|
||||
// Otherwise, holes in the bottom layers could be filled, as is reported in GH #5528.
|
||||
steps.emplace_back(posSlice);
|
||||
}
|
||||
} else if (
|
||||
opt_key == "interface_shells"
|
||||
|| opt_key == "infill_only_where_needed"
|
||||
|| opt_key == "infill_every_layers"
|
||||
|| opt_key == "solid_infill_every_layers"
|
||||
|| opt_key == "bottom_solid_layers"
|
||||
|| opt_key == "bottom_solid_min_thickness"
|
||||
|| opt_key == "top_solid_layers"
|
||||
|| opt_key == "top_solid_min_thickness"
|
||||
|
|
@ -655,6 +661,7 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
|
|||
} else if (step == posPrepareInfill) {
|
||||
invalidated |= this->invalidate_steps({ posInfill, posIroning });
|
||||
} else if (step == posInfill) {
|
||||
invalidated |= this->invalidate_steps({ posIroning });
|
||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||
} else if (step == posSlice) {
|
||||
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial });
|
||||
|
|
@ -2943,4 +2950,11 @@ const Layer* PrintObject::get_layer_at_printz(coordf_t print_z, coordf_t epsilon
|
|||
|
||||
Layer* PrintObject::get_layer_at_printz(coordf_t print_z, coordf_t epsilon) { return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z, epsilon)); }
|
||||
|
||||
const Layer *PrintObject::get_first_layer_bellow_printz(coordf_t print_z, coordf_t epsilon) const
|
||||
{
|
||||
coordf_t limit = print_z + epsilon;
|
||||
auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; });
|
||||
return (it == m_layers.begin()) ? nullptr : *(--it);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -906,13 +906,15 @@ namespace SupportMaterialInternal {
|
|||
polyline.extend_start(fw);
|
||||
polyline.extend_end(fw);
|
||||
// Is the straight perimeter segment supported at both sides?
|
||||
for (size_t i = 0; i < lower_layer.lslices.size(); ++ i)
|
||||
if (lower_layer.lslices_bboxes[i].contains(polyline.first_point()) && lower_layer.lslices_bboxes[i].contains(polyline.last_point()) &&
|
||||
lower_layer.lslices[i].contains(polyline.first_point()) && lower_layer.lslices[i].contains(polyline.last_point())) {
|
||||
// Offset a polyline into a thick line.
|
||||
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
|
||||
break;
|
||||
}
|
||||
Point pts[2] = { polyline.first_point(), polyline.last_point() };
|
||||
bool supported[2] = { false, false };
|
||||
for (size_t i = 0; i < lower_layer.lslices.size() && ! (supported[0] && supported[1]); ++ i)
|
||||
for (int j = 0; j < 2; ++ j)
|
||||
if (! supported[j] && lower_layer.lslices_bboxes[i].contains(pts[j]) && lower_layer.lslices[i].contains(pts[j]))
|
||||
supported[j] = true;
|
||||
if (supported[0] && supported[1])
|
||||
// Offset a polyline into a thick line.
|
||||
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
|
||||
}
|
||||
bridges = union_(bridges);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||
#define ENABLE_RENDER_SELECTION_CENTER 0
|
||||
// Shows an imgui dialog with render related data
|
||||
#define ENABLE_RENDER_STATISTICS 1
|
||||
#define ENABLE_RENDER_STATISTICS 0
|
||||
// Shows an imgui dialog with camera related data
|
||||
#define ENABLE_CAMERA_STATISTICS 0
|
||||
// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)
|
||||
|
|
@ -99,4 +99,12 @@
|
|||
#define ENABLE_TOOLPATHS_WIDTH_HEIGHT_FROM_GCODE (1 && ENABLE_2_3_0_BETA3)
|
||||
#define ENABLE_RENDER_PATH_REFRESH_AFTER_OPTIONS_CHANGE (1 && ENABLE_2_3_0_BETA3)
|
||||
|
||||
|
||||
//=================
|
||||
// 2.3.0.rc1 techs
|
||||
//=================
|
||||
#define ENABLE_2_3_0_RC1 1
|
||||
|
||||
#define ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING (1 && ENABLE_2_3_0_RC1)
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@
|
|||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/tbb_thread.h>
|
||||
#include <tbb/task_arena.h>
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
|
||||
#include "Thread.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -194,7 +195,10 @@ void name_tbb_thread_pool_threads()
|
|||
return;
|
||||
initialized = true;
|
||||
|
||||
const size_t nthreads_hw = std::thread::hardware_concurrency();
|
||||
// see GH issue #5661 PrusaSlicer hangs on Linux when run with non standard task affinity
|
||||
// TBB will respect the task affinity mask on Linux and spawn less threads than std::thread::hardware_concurrency().
|
||||
// const size_t nthreads_hw = std::thread::hardware_concurrency();
|
||||
const size_t nthreads_hw = tbb::this_task_arena::max_concurrency();
|
||||
size_t nthreads = nthreads_hw;
|
||||
|
||||
#ifdef SLIC3R_PROFILE
|
||||
|
|
|
|||
|
|
@ -45,9 +45,11 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
|||
m_cursor = Cursor(hit, source, radius, cursor_type, trafo);
|
||||
|
||||
// In case user changed cursor size since last time, update triangle edge limit.
|
||||
if (m_old_cursor_radius != radius) {
|
||||
set_edge_limit(radius / 5.f);
|
||||
m_old_cursor_radius = radius;
|
||||
// It is necessary to compare the internal radius in m_cursor! radius is in
|
||||
// world coords and does not change after scaling.
|
||||
if (m_old_cursor_radius_sqr != m_cursor.radius_sqr) {
|
||||
set_edge_limit(std::sqrt(m_cursor.radius_sqr) / 5.f);
|
||||
m_old_cursor_radius_sqr = m_cursor.radius_sqr;
|
||||
}
|
||||
|
||||
// Now start with the facet the pointer points to and check all adjacent facets.
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ protected:
|
|||
};
|
||||
|
||||
Cursor m_cursor;
|
||||
float m_old_cursor_radius;
|
||||
float m_old_cursor_radius_sqr;
|
||||
|
||||
// Private functions:
|
||||
bool select_triangle(int facet_idx, EnforcerBlockerType type,
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ extern CopyFileResult check_copy(const std::string& origin, const std::string& c
|
|||
extern bool is_plain_file(const boost::filesystem::directory_entry &path);
|
||||
extern bool is_ini_file(const boost::filesystem::directory_entry &path);
|
||||
extern bool is_idx_file(const boost::filesystem::directory_entry &path);
|
||||
extern bool is_gcode_file(const std::string &path);
|
||||
|
||||
// File path / name / extension splitting utilities, working with UTF-8,
|
||||
// to be published to Perl.
|
||||
|
|
@ -353,8 +354,12 @@ inline std::string get_time_dhm(float time_in_secs)
|
|||
|
||||
#if WIN32
|
||||
#define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE)
|
||||
//FIXME this is an inprecise hack. Add the hash table size and possibly some estimate of the linked list at each of the used bin.
|
||||
#define SLIC3R_STDUNORDEREDSET_MEMSIZE(NAME, TYPE) NAME.size() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE)
|
||||
#else
|
||||
#define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE)
|
||||
//FIXME this is an inprecise hack. Add the hash table size and possibly some estimate of the linked list at each of the used bin.
|
||||
#define SLIC3R_STDUNORDEREDSET_MEMSIZE(NAME, TYPE) NAME.size() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE)
|
||||
#endif
|
||||
|
||||
#endif // slic3r_Utils_hpp_
|
||||
|
|
|
|||
|
|
@ -522,6 +522,12 @@ bool is_idx_file(const boost::filesystem::directory_entry &dir_entry)
|
|||
return is_plain_file(dir_entry) && strcasecmp(dir_entry.path().extension().string().c_str(), ".idx") == 0;
|
||||
}
|
||||
|
||||
bool is_gcode_file(const std::string &path)
|
||||
{
|
||||
return boost::iends_with(path, ".gcode") || boost::iends_with(path, ".gco") ||
|
||||
boost::iends_with(path, ".g") || boost::iends_with(path, ".ngc");
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#ifdef WIN32
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue