mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-19 04:37:52 -06:00
Merge branch 'main' into dev/support-paint-vertical
This commit is contained in:
commit
a9a6f45f08
344 changed files with 29108 additions and 10950 deletions
|
@ -114,11 +114,6 @@ void AppConfig::set_defaults()
|
|||
set_bool("background_processing", false);
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_SHOW_DROP_PROJECT
|
||||
if (get("show_drop_project_dialog").empty())
|
||||
set_bool("show_drop_project_dialog", true);
|
||||
#endif
|
||||
|
||||
if (get("drop_project_action").empty())
|
||||
set_bool("drop_project_action", true);
|
||||
|
||||
|
@ -347,7 +342,11 @@ void AppConfig::set_defaults()
|
|||
if (get("mouse_wheel").empty()) {
|
||||
set("mouse_wheel", "0");
|
||||
}
|
||||
|
||||
|
||||
if (get(SETTING_PROJECT_LOAD_BEHAVIOUR).empty()) {
|
||||
set(SETTING_PROJECT_LOAD_BEHAVIOUR, OPTION_PROJECT_LOAD_BEHAVIOUR_ASK_WHEN_RELEVANT);
|
||||
}
|
||||
|
||||
if (get("max_recent_count").empty()) {
|
||||
set("max_recent_count", "18");
|
||||
}
|
||||
|
|
|
@ -18,6 +18,12 @@ using namespace nlohmann;
|
|||
#define ENV_PRE_HOST "2"
|
||||
#define ENV_PRODUCT_HOST "3"
|
||||
|
||||
#define SETTING_PROJECT_LOAD_BEHAVIOUR "project_load_behaviour"
|
||||
#define OPTION_PROJECT_LOAD_BEHAVIOUR_LOAD_ALL "load_all"
|
||||
#define OPTION_PROJECT_LOAD_BEHAVIOUR_ASK_WHEN_RELEVANT "ask_when_relevant"
|
||||
#define OPTION_PROJECT_LOAD_BEHAVIOUR_ALWAYS_ASK "always_ask"
|
||||
#define OPTION_PROJECT_LOAD_BEHAVIOUR_LOAD_GEOMETRY "load_geometry_only"
|
||||
|
||||
#define SUPPORT_DARK_MODE
|
||||
//#define _MSW_DARK_MODE
|
||||
|
||||
|
|
|
@ -806,8 +806,8 @@ double configBrimWidthByVolumeGroups(double adhesion, double maxSpeed, const std
|
|||
|
||||
// Generate ears
|
||||
// Ported from SuperSlicer: https://github.com/supermerill/SuperSlicer/blob/45d0532845b63cd5cefe7de7dc4ef0e0ed7e030a/src/libslic3r/Brim.cpp#L1116
|
||||
static ExPolygons make_brim_ears(ExPolygons& obj_expoly, coord_t size_ear, coord_t ear_detection_length,
|
||||
coordf_t brim_ears_max_angle, bool is_outer_brim) {
|
||||
static ExPolygons make_brim_ears_auto(const ExPolygons& obj_expoly, coord_t size_ear, coord_t ear_detection_length,
|
||||
coordf_t brim_ears_max_angle, bool is_outer_brim) {
|
||||
ExPolygons mouse_ears_ex;
|
||||
if (size_ear <= 0) {
|
||||
return mouse_ears_ex;
|
||||
|
@ -815,7 +815,7 @@ static ExPolygons make_brim_ears(ExPolygons& obj_expoly, coord_t size_ear, coord
|
|||
// Detect places to put ears
|
||||
const coordf_t angle_threshold = (180 - brim_ears_max_angle) * PI / 180.0;
|
||||
Points pt_ears;
|
||||
for (ExPolygon &poly : obj_expoly) {
|
||||
for (const ExPolygon &poly : obj_expoly) {
|
||||
Polygon decimated_polygon = poly.contour;
|
||||
if (ear_detection_length > 0) {
|
||||
// decimate polygon
|
||||
|
@ -835,8 +835,8 @@ static ExPolygons make_brim_ears(ExPolygons& obj_expoly, coord_t size_ear, coord
|
|||
// Then add ears
|
||||
// create ear pattern
|
||||
Polygon point_round;
|
||||
for (size_t i = 0; i < POLY_SIDES; i++) {
|
||||
double angle = (2.0 * PI * i) / POLY_SIDES;
|
||||
for (size_t i = 0; i < POLY_SIDE_COUNT; i++) {
|
||||
double angle = (2.0 * PI * i) / POLY_SIDE_COUNT;
|
||||
point_round.points.emplace_back(size_ear * cos(angle), size_ear * sin(angle));
|
||||
}
|
||||
|
||||
|
@ -850,6 +850,41 @@ static ExPolygons make_brim_ears(ExPolygons& obj_expoly, coord_t size_ear, coord
|
|||
return mouse_ears_ex;
|
||||
}
|
||||
|
||||
static ExPolygons make_brim_ears(const PrintObject* object, const double& flowWidth, float brim_offset, Flow &flow, bool is_outer_brim)
|
||||
{
|
||||
ExPolygons mouse_ears_ex;
|
||||
BrimPoints brim_ear_points = object->model_object()->brim_points;
|
||||
if (brim_ear_points.size() <= 0) {
|
||||
return mouse_ears_ex;
|
||||
}
|
||||
const Geometry::Transformation& trsf = object->model_object()->instances[0]->get_transformation();
|
||||
Transform3d model_trsf = trsf.get_matrix_no_offset();
|
||||
const Point ¢er_offset = object->center_offset();
|
||||
model_trsf = model_trsf.pretranslate(Vec3d(- unscale<double>(center_offset.x()), - unscale<double>(center_offset.y()), 0));
|
||||
for (auto &pt : brim_ear_points) {
|
||||
Vec3f world_pos = pt.transform(trsf.get_matrix());
|
||||
if ( world_pos.z() > 0) continue;
|
||||
Polygon point_round;
|
||||
float brim_width = floor(scale_(pt.head_front_radius) / flowWidth / 2) * flowWidth * 2;
|
||||
if (is_outer_brim) {
|
||||
double flowWidthScale = flowWidth / SCALING_FACTOR;
|
||||
brim_width = floor(brim_width / flowWidthScale / 2) * flowWidthScale * 2;
|
||||
}
|
||||
coord_t size_ear = (brim_width - brim_offset - flow.scaled_spacing());
|
||||
for (size_t i = 0; i < POLY_SIDE_COUNT; i++) {
|
||||
double angle = (2.0 * PI * i) / POLY_SIDE_COUNT;
|
||||
point_round.points.emplace_back(size_ear * cos(angle), size_ear * sin(angle));
|
||||
}
|
||||
mouse_ears_ex.emplace_back();
|
||||
mouse_ears_ex.back().contour = point_round;
|
||||
Vec3f pos = pt.transform(model_trsf);
|
||||
int32_t pt_x = scale_(pos.x());
|
||||
int32_t pt_y = scale_(pos.y());
|
||||
mouse_ears_ex.back().contour.translate(Point(pt_x, pt_y));
|
||||
}
|
||||
return mouse_ears_ex;
|
||||
}
|
||||
|
||||
//BBS: create all brims
|
||||
static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap,
|
||||
|
@ -888,9 +923,10 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
|||
const float scaled_additional_brim_width = scale_(floor(5 / flowWidth / 2) * flowWidth * 2);
|
||||
const float scaled_half_min_adh_length = scale_(1.1);
|
||||
bool has_brim_auto = object->config().brim_type == btAutoBrim;
|
||||
const bool use_brim_ears = object->config().brim_type == btEar;
|
||||
const bool has_inner_brim = brim_type == btInnerOnly || brim_type == btOuterAndInner || use_brim_ears;
|
||||
const bool has_outer_brim = brim_type == btOuterOnly || brim_type == btOuterAndInner || brim_type == btAutoBrim || use_brim_ears;
|
||||
const bool use_auto_brim_ears = object->config().brim_type == btEar;
|
||||
const bool use_brim_ears = object->config().brim_type == btPainted;
|
||||
const bool has_inner_brim = brim_type == btInnerOnly || brim_type == btOuterAndInner || use_auto_brim_ears || use_brim_ears;
|
||||
const bool has_outer_brim = brim_type == btOuterOnly || brim_type == btOuterAndInner || brim_type == btAutoBrim || use_auto_brim_ears || use_brim_ears;
|
||||
coord_t ear_detection_length = scale_(object->config().brim_ears_detection_length.value);
|
||||
coordf_t brim_ears_max_angle = object->config().brim_ears_max_angle.value;
|
||||
|
||||
|
@ -951,27 +987,30 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
|||
if (has_outer_brim) {
|
||||
// BBS: inner and outer boundary are offset from the same polygon incase of round off error.
|
||||
auto innerExpoly = offset_ex(ex_poly.contour, brim_offset, jtRound, SCALED_RESOLUTION);
|
||||
auto &clipExpoly = innerExpoly;
|
||||
|
||||
ExPolygons outerExpoly;
|
||||
if (use_brim_ears) {
|
||||
outerExpoly = make_brim_ears(object, flowWidth, brim_offset, flow, true);
|
||||
//outerExpoly = offset_ex(outerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION);
|
||||
} else if (use_auto_brim_ears) {
|
||||
coord_t size_ear = (brim_width_mod - brim_offset - flow.scaled_spacing());
|
||||
append(brim_area_object, diff_ex(make_brim_ears(innerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, true), clipExpoly));
|
||||
} else {
|
||||
// Normal brims
|
||||
append(brim_area_object, diff_ex(offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION), clipExpoly));
|
||||
outerExpoly = make_brim_ears_auto(innerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, true);
|
||||
}else {
|
||||
outerExpoly = offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION);
|
||||
}
|
||||
append(brim_area_object, diff_ex(outerExpoly, innerExpoly));
|
||||
}
|
||||
if (has_inner_brim) {
|
||||
auto outerExpoly = offset_ex(ex_poly_holes_reversed, -brim_offset);
|
||||
auto clipExpoly = offset_ex(ex_poly_holes_reversed, -brim_width - brim_offset);
|
||||
|
||||
ExPolygons outerExpoly;
|
||||
auto innerExpoly = offset_ex(ex_poly_holes_reversed, -brim_width - brim_offset);
|
||||
if (use_brim_ears) {
|
||||
outerExpoly = make_brim_ears(object, flowWidth, brim_offset, flow, false);
|
||||
} else if (use_auto_brim_ears) {
|
||||
coord_t size_ear = (brim_width - brim_offset - flow.scaled_spacing());
|
||||
append(brim_area_object, diff_ex(make_brim_ears(outerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, false), clipExpoly));
|
||||
} else {
|
||||
// Normal brims
|
||||
append(brim_area_object, diff_ex(outerExpoly, clipExpoly));
|
||||
outerExpoly = make_brim_ears_auto(offset_ex(ex_poly_holes_reversed, -brim_offset), size_ear, ear_detection_length, brim_ears_max_angle, false);
|
||||
}else {
|
||||
outerExpoly = offset_ex(ex_poly_holes_reversed, -brim_offset);
|
||||
}
|
||||
append(brim_area_object, diff_ex(outerExpoly, innerExpoly));
|
||||
}
|
||||
if (!has_inner_brim) {
|
||||
// BBS: brim should be apart from holes
|
||||
|
|
63
src/libslic3r/BrimEarsPoint.hpp
Normal file
63
src/libslic3r/BrimEarsPoint.hpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
#ifndef BRIMEARSPOINT_HPP
|
||||
#define BRIMEARSPOINT_HPP
|
||||
|
||||
#include <libslic3r/Point.hpp>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// An enum to keep track of where the current points on the ModelObject came from.
|
||||
enum class PointsStatus {
|
||||
NoPoints, // No points were generated so far.
|
||||
Generating, // The autogeneration algorithm triggered, but not yet finished.
|
||||
AutoGenerated, // Points were autogenerated (i.e. copied from the backend).
|
||||
UserModified // User has done some edits.
|
||||
};
|
||||
|
||||
struct BrimPoint
|
||||
{
|
||||
Vec3f pos;
|
||||
float head_front_radius;
|
||||
|
||||
BrimPoint()
|
||||
: pos(Vec3f::Zero()), head_front_radius(0.f)
|
||||
{}
|
||||
|
||||
BrimPoint(float pos_x,
|
||||
float pos_y,
|
||||
float pos_z,
|
||||
float head_radius)
|
||||
: pos(pos_x, pos_y, pos_z)
|
||||
, head_front_radius(head_radius)
|
||||
{}
|
||||
|
||||
BrimPoint(Vec3f position, float head_radius)
|
||||
: pos(position)
|
||||
, head_front_radius(head_radius)
|
||||
{}
|
||||
|
||||
Vec3f transform(const Transform3d &trsf)
|
||||
{
|
||||
Vec3d result = trsf * pos.cast<double>();
|
||||
return result.cast<float>();
|
||||
}
|
||||
|
||||
bool operator==(const BrimPoint &sp) const
|
||||
{
|
||||
float rdiff = std::abs(head_front_radius - sp.head_front_radius);
|
||||
return (pos == sp.pos) && rdiff < float(EPSILON);
|
||||
}
|
||||
|
||||
bool operator!=(const BrimPoint &sp) const { return !(sp == (*this)); }
|
||||
template<class Archive> void serialize(Archive &ar)
|
||||
{
|
||||
ar(pos, head_front_radius);
|
||||
}
|
||||
};
|
||||
|
||||
using BrimPoints = std::vector<BrimPoint>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // BRIMEARSPOINT_HPP
|
|
@ -43,6 +43,7 @@ set(lisbslic3r_sources
|
|||
FaceDetector.hpp
|
||||
Brim.cpp
|
||||
Brim.hpp
|
||||
BrimEarsPoint.hpp
|
||||
BuildVolume.cpp
|
||||
BuildVolume.hpp
|
||||
Circle.cpp
|
||||
|
|
|
@ -185,6 +185,15 @@ bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool overlaps(const ExPolygons& expolys, const ExPolygon& expoly)
|
||||
{
|
||||
for (const ExPolygon& el : expolys) {
|
||||
if (el.overlaps(expoly))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Point projection_onto(const ExPolygons& polygons, const Point& from)
|
||||
{
|
||||
Point projected_pt;
|
||||
|
|
|
@ -457,6 +457,7 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc
|
|||
bool expolygons_match(const ExPolygon &l, const ExPolygon &r);
|
||||
|
||||
bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2);
|
||||
bool overlaps(const ExPolygons& expolys, const ExPolygon& expoly);
|
||||
|
||||
Point projection_onto(const ExPolygons& expolys, const Point& pt);
|
||||
|
||||
|
|
|
@ -64,6 +64,10 @@ struct SurfaceFillParams
|
|||
float top_surface_speed = 0;
|
||||
float solid_infill_speed = 0;
|
||||
|
||||
// Params for lattice infill angles
|
||||
float lattice_angle_1 = 0.f;
|
||||
float lattice_angle_2 = 0.f;
|
||||
|
||||
bool operator<(const SurfaceFillParams &rhs) const {
|
||||
#define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false;
|
||||
#define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false;
|
||||
|
@ -90,6 +94,8 @@ struct SurfaceFillParams
|
|||
RETURN_COMPARE_NON_EQUAL(sparse_infill_speed);
|
||||
RETURN_COMPARE_NON_EQUAL(top_surface_speed);
|
||||
RETURN_COMPARE_NON_EQUAL(solid_infill_speed);
|
||||
RETURN_COMPARE_NON_EQUAL(lattice_angle_1);
|
||||
RETURN_COMPARE_NON_EQUAL(lattice_angle_2);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -111,7 +117,9 @@ struct SurfaceFillParams
|
|||
this->extrusion_role == rhs.extrusion_role &&
|
||||
this->sparse_infill_speed == rhs.sparse_infill_speed &&
|
||||
this->top_surface_speed == rhs.top_surface_speed &&
|
||||
this->solid_infill_speed == rhs.solid_infill_speed;
|
||||
this->solid_infill_speed == rhs.solid_infill_speed &&
|
||||
this->lattice_angle_1 == rhs.lattice_angle_1 &&
|
||||
this->lattice_angle_2 == rhs.lattice_angle_2;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -611,6 +619,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
params.extruder = layerm.region().extruder(extrusion_role);
|
||||
params.pattern = region_config.sparse_infill_pattern.value;
|
||||
params.density = float(region_config.sparse_infill_density);
|
||||
params.lattice_angle_1 = region_config.lattice_angle_1;
|
||||
params.lattice_angle_2 = region_config.lattice_angle_2;
|
||||
|
||||
if (surface.is_solid()) {
|
||||
params.density = 100.f;
|
||||
|
@ -953,6 +963,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
|||
params.resolution = resolution;
|
||||
params.use_arachne = surface_fill.params.pattern == ipConcentric || surface_fill.params.pattern == ipConcentricInternal;
|
||||
params.layer_height = layerm->layer()->height;
|
||||
params.lattice_angle_1 = surface_fill.params.lattice_angle_1;
|
||||
params.lattice_angle_2 = surface_fill.params.lattice_angle_2;
|
||||
|
||||
// BBS
|
||||
params.flow = surface_fill.params.flow;
|
||||
|
@ -972,6 +984,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
|||
params.density = layerm->region().config().bridge_density.get_abs_value(1.0);
|
||||
params.dont_adjust = true;
|
||||
}
|
||||
if(surface_fill.surface.is_internal_bridge()){
|
||||
params.density = f->print_object_config->internal_bridge_density.get_abs_value(1.0);
|
||||
params.dont_adjust = true;
|
||||
}
|
||||
// BBS: make fill
|
||||
f->fill_surface_extrusion(&surface_fill.surface,
|
||||
params,
|
||||
|
@ -1022,6 +1038,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
|||
case ipMonotonicLine:
|
||||
case ipAlignedRectilinear:
|
||||
case ipGrid:
|
||||
case ip2DLattice:
|
||||
case ipTriangles:
|
||||
case ipStars:
|
||||
case ipCubic:
|
||||
|
@ -1076,6 +1093,8 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
|||
params.resolution = resolution;
|
||||
params.use_arachne = false;
|
||||
params.layer_height = layerm.layer()->height;
|
||||
params.lattice_angle_1 = surface_fill.params.lattice_angle_1;
|
||||
params.lattice_angle_2 = surface_fill.params.lattice_angle_2;
|
||||
|
||||
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||
|
|
|
@ -47,6 +47,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
|||
case ipMonotonic: return new FillMonotonic();
|
||||
case ipLine: return new FillLine();
|
||||
case ipGrid: return new FillGrid();
|
||||
case ip2DLattice: return new Fill2DLattice();
|
||||
case ipTriangles: return new FillTriangles();
|
||||
case ipStars: return new FillStars();
|
||||
case ipCubic: return new FillCubic();
|
||||
|
|
|
@ -69,6 +69,10 @@ struct FillParams
|
|||
// Layer height for Concentric infill with Arachne.
|
||||
coordf_t layer_height { 0.f };
|
||||
|
||||
// For 2D lattice
|
||||
coordf_t lattice_angle_1 { 0.f };
|
||||
coordf_t lattice_angle_2 { 0.f };
|
||||
|
||||
// BBS
|
||||
Flow flow;
|
||||
ExtrusionRole extrusion_role{ ExtrusionRole(0) };
|
||||
|
|
|
@ -3002,6 +3002,23 @@ Polylines FillGrid::fill_surface(const Surface *surface, const FillParams ¶m
|
|||
return polylines_out;
|
||||
}
|
||||
|
||||
Polylines Fill2DLattice::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
Polylines polylines_out;
|
||||
coordf_t dx1 = tan(Geometry::deg2rad(params.lattice_angle_1)) * z;
|
||||
coordf_t dx2 = tan(Geometry::deg2rad(params.lattice_angle_2)) * z;
|
||||
if (! this->fill_surface_by_multilines(
|
||||
surface, params,
|
||||
{ { float(M_PI / 2.), float(dx1) }, { float(M_PI / 2.), float(dx2) } },
|
||||
polylines_out))
|
||||
BOOST_LOG_TRIVIAL(error) << "Fill2DLattice::fill_surface() failed to fill a region.";
|
||||
|
||||
if (this->layer_id % 2 == 1)
|
||||
for (int i = 0; i < polylines_out.size(); i++)
|
||||
std::reverse(polylines_out[i].begin(), polylines_out[i].end());
|
||||
return polylines_out;
|
||||
}
|
||||
|
||||
Polylines FillTriangles::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
Polylines polylines_out;
|
||||
|
|
|
@ -71,6 +71,18 @@ protected:
|
|||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
class Fill2DLattice : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new Fill2DLattice(*this); }
|
||||
~Fill2DLattice() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
class FillTriangles : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -170,6 +170,7 @@ const std::string BBS_MODEL_CONFIG_RELS_FILE = "Metadata/_rels/model_settings.co
|
|||
const std::string SLICE_INFO_CONFIG_FILE = "Metadata/slice_info.config";
|
||||
const std::string BBS_LAYER_HEIGHTS_PROFILE_FILE = "Metadata/layer_heights_profile.txt";
|
||||
const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/layer_config_ranges.xml";
|
||||
const std::string BRIM_EAR_POINTS_FILE = "Metadata/brim_ear_points.txt";
|
||||
/*const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
|
||||
const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";*/
|
||||
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/custom_gcode_per_layer.xml";
|
||||
|
@ -807,10 +808,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
//typedef std::map<Id, ComponentsList> IdToAliasesMap;
|
||||
typedef std::vector<Instance> InstancesList;
|
||||
typedef std::map<int, ObjectMetadata> IdToMetadataMap;
|
||||
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
|
||||
//typedef std::map<Id, Geometry> IdToGeometryMap;
|
||||
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
|
||||
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
|
||||
typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
|
||||
typedef std::map<int, BrimPoints> IdToBrimPointsMap;
|
||||
/*typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
|
||||
typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;*/
|
||||
using PathToEmbossShapeFileMap = std::map<std::string, std::shared_ptr<std::string>>;
|
||||
|
@ -1000,9 +1002,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
//IdToGeometryMap m_orig_geometries; // backup & restore
|
||||
CurrentConfig m_curr_config;
|
||||
IdToMetadataMap m_objects_metadata;
|
||||
IdToCutObjectInfoMap m_cut_object_infos;
|
||||
IdToCutObjectInfoMap m_cut_object_infos;
|
||||
IdToLayerHeightsProfileMap m_layer_heights_profiles;
|
||||
IdToLayerConfigRangesMap m_layer_config_ranges;
|
||||
IdToBrimPointsMap m_brim_ear_points;
|
||||
/*IdToSlaSupportPointsMap m_sla_support_points;
|
||||
IdToSlaDrainHolesMap m_sla_drain_holes;*/
|
||||
PathToEmbossShapeFileMap m_path_to_emboss_shape_files;
|
||||
|
@ -1064,11 +1067,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
|
||||
bool _extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
|
||||
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
|
||||
void _extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions);
|
||||
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
|
||||
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_brim_ear_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
|
||||
void _extract_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
|
||||
|
@ -1270,6 +1274,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
m_objects_metadata.clear();
|
||||
m_layer_heights_profiles.clear();
|
||||
m_layer_config_ranges.clear();
|
||||
m_brim_ear_points.clear();
|
||||
//m_sla_support_points.clear();
|
||||
m_curr_metadata_name.clear();
|
||||
m_curr_characters.clear();
|
||||
|
@ -1759,6 +1764,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
// extract slic3r layer config ranges file
|
||||
_extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
|
||||
}
|
||||
else if (boost::algorithm::iequals(name, BRIM_EAR_POINTS_FILE)) {
|
||||
// extract slic3r config file
|
||||
_extract_brim_ear_points_from_archive(archive, stat);
|
||||
}
|
||||
//BBS: disable SLA related files currently
|
||||
/*else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE)) {
|
||||
// extract sla support points file
|
||||
|
@ -1942,6 +1951,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
if (obj_layer_config_ranges != m_layer_config_ranges.end())
|
||||
model_object->layer_config_ranges = std::move(obj_layer_config_ranges->second);
|
||||
|
||||
IdToBrimPointsMap::iterator obj_brim_points = m_brim_ear_points.find(object.second + 1);
|
||||
if (obj_brim_points != m_brim_ear_points.end())
|
||||
model_object->brim_points = std::move(obj_brim_points->second);
|
||||
|
||||
// m_sla_support_points are indexed by a 1 based model object index.
|
||||
/*IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.second + 1);
|
||||
if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) {
|
||||
|
@ -2762,6 +2775,77 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _BBS_3MF_Importer::_extract_brim_ear_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
|
||||
{
|
||||
if (stat.m_uncomp_size > 0) {
|
||||
std::string buffer((size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0) {
|
||||
add_error("Error while reading brim ear points data to buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer.back() == '\n')
|
||||
buffer.pop_back();
|
||||
|
||||
std::vector<std::string> objects;
|
||||
boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
|
||||
|
||||
// Info on format versioning - see bbs_3mf.hpp
|
||||
int version = 0;
|
||||
std::string key("brim_points_format_version=");
|
||||
if (!objects.empty() && objects[0].find(key) != std::string::npos) {
|
||||
objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string
|
||||
version = std::stoi(objects[0]);
|
||||
objects.erase(objects.begin()); // pop the header
|
||||
}
|
||||
|
||||
for (const std::string& object : objects) {
|
||||
std::vector<std::string> object_data;
|
||||
boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
|
||||
|
||||
if (object_data.size() != 2) {
|
||||
add_error("Error while reading object data");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> object_data_id;
|
||||
boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
|
||||
if (object_data_id.size() != 2) {
|
||||
add_error("Error while reading object id");
|
||||
continue;
|
||||
}
|
||||
|
||||
int object_id = std::atoi(object_data_id[1].c_str());
|
||||
if (object_id == 0) {
|
||||
add_error("Found invalid object id");
|
||||
continue;
|
||||
}
|
||||
|
||||
IdToBrimPointsMap::iterator object_item = m_brim_ear_points.find(object_id);
|
||||
if (object_item != m_brim_ear_points.end()) {
|
||||
add_error("Found duplicated brim ear points");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> object_data_points;
|
||||
boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off);
|
||||
|
||||
std::vector<BrimPoint> brim_ear_points;
|
||||
if (version == 0) {
|
||||
for (unsigned int i=0; i<object_data_points.size(); i+=4)
|
||||
brim_ear_points.emplace_back(float(std::atof(object_data_points[i+0].c_str())),
|
||||
float(std::atof(object_data_points[i+1].c_str())),
|
||||
float(std::atof(object_data_points[i+2].c_str())),
|
||||
float(std::atof(object_data_points[i+3].c_str())));
|
||||
}
|
||||
|
||||
if (!brim_ear_points.empty())
|
||||
m_brim_ear_points.insert({ object_id, brim_ear_points });
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
void _BBS_3MF_Importer::_extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
|
||||
{
|
||||
|
@ -5504,9 +5588,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
//BBS: change volume to seperate objects
|
||||
bool _add_mesh_to_object_stream(std::function<bool(std::string &, bool)> const &flush, ObjectData const &object_data) const;
|
||||
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items) const;
|
||||
bool _add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_brim_ear_points_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
|
||||
|
@ -5515,6 +5599,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
//BBS: add project embedded preset files
|
||||
bool _add_project_embedded_presets_to_archive(mz_zip_archive& archive, Model& model, std::vector<Preset*> project_presets);
|
||||
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, int export_plate_idx = -1, bool save_gcode = true, bool use_loaded_id = false);
|
||||
bool _add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model);
|
||||
bool _add_slice_info_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config);
|
||||
bool _add_gcode_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, Export3mfProgressFn proFn = nullptr);
|
||||
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config);
|
||||
|
@ -5914,6 +5999,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!_add_brim_ear_points_file_to_archive(archive, model)) {
|
||||
close_zip_writer(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
// BBS progress point
|
||||
/*BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format("export 3mf EXPORT_STAGE_ADD_SUPPORT\n");
|
||||
if (proFn) {
|
||||
|
@ -7159,6 +7249,40 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool _BBS_3MF_Exporter::_add_brim_ear_points_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
std::string out = "";
|
||||
char buffer[1024];
|
||||
|
||||
unsigned int count = 0;
|
||||
for (const ModelObject* object : model.objects) {
|
||||
++count;
|
||||
const BrimPoints& brim_points = object->brim_points;
|
||||
if (!brim_points.empty()) {
|
||||
sprintf(buffer, "object_id=%d|", count);
|
||||
out += buffer;
|
||||
|
||||
// Store the layer height profile as a single space separated list.
|
||||
for (size_t i = 0; i < brim_points.size(); ++i) {
|
||||
sprintf(buffer, (i==0 ? "%f %f %f %f" : " %f %f %f %f"), brim_points[i].pos(0), brim_points[i].pos(1), brim_points[i].pos(2), brim_points[i].head_front_radius);
|
||||
out += buffer;
|
||||
}
|
||||
out += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!out.empty()) {
|
||||
// Adds version header at the beginning:
|
||||
out = std::string("brim_points_format_version=") + std::to_string(brim_points_format_version) + std::string("\n") + out;
|
||||
|
||||
if (!mz_zip_writer_add_mem(&archive, BRIM_EAR_POINTS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
|
||||
add_error("Unable to add brim ear points file to archive");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
bool _BBS_3MF_Exporter::_add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
|
|
|
@ -141,6 +141,10 @@ inline bool operator & (SaveStrategy & lhs, SaveStrategy rhs)
|
|||
return ((static_cast<T>(lhs) & static_cast<T>(rhs))) == static_cast<T>(rhs);
|
||||
}
|
||||
|
||||
enum {
|
||||
brim_points_format_version = 0
|
||||
};
|
||||
|
||||
enum class LoadStrategy
|
||||
{
|
||||
Default = 0,
|
||||
|
|
|
@ -1838,6 +1838,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
m_max_layer_z = 0.f;
|
||||
m_last_width = 0.f;
|
||||
m_is_overhang_fan_on = false;
|
||||
m_is_internal_bridge_fan_on = false;
|
||||
m_is_supp_interface_fan_on = false;
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_last_mm3_per_mm = 0.;
|
||||
|
@ -5320,11 +5321,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
ref_speed = std::min(ref_speed, m_config.scarf_joint_speed.get_abs_value(ref_speed));
|
||||
}
|
||||
|
||||
ConfigOptionPercents overhang_overlap_levels({75, 50, 25, 13, 12.99, 0});
|
||||
ConfigOptionPercents overhang_overlap_levels({90, 75, 50, 25, 13, 0});
|
||||
|
||||
if (m_config.slowdown_for_curled_perimeters){
|
||||
ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
|
||||
{(m_config.get_abs_value("overhang_1_4_speed", ref_speed) < 0.5) ?
|
||||
{FloatOrPercent{100, true},
|
||||
(m_config.get_abs_value("overhang_1_4_speed", ref_speed) < 0.5) ?
|
||||
FloatOrPercent{100, true} :
|
||||
FloatOrPercent{m_config.get_abs_value("overhang_1_4_speed", ref_speed) * 100 / ref_speed, true},
|
||||
(m_config.get_abs_value("overhang_2_4_speed", ref_speed) < 0.5) ?
|
||||
|
@ -5336,9 +5338,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
(m_config.get_abs_value("overhang_4_4_speed", ref_speed) < 0.5) ?
|
||||
FloatOrPercent{100, true} :
|
||||
FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed", ref_speed) * 100 / ref_speed, true},
|
||||
(m_config.get_abs_value("overhang_4_4_speed", ref_speed) < 0.5) ?
|
||||
FloatOrPercent{100, true} :
|
||||
FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed", ref_speed) * 100 / ref_speed, true},
|
||||
(m_config.get_abs_value("overhang_4_4_speed", ref_speed) < 0.5) ?
|
||||
FloatOrPercent{100, true} :
|
||||
FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed", ref_speed) * 100 / ref_speed, true}});
|
||||
|
@ -5347,7 +5346,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
ref_speed, speed, m_config.slowdown_for_curled_perimeters);
|
||||
}else{
|
||||
ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
|
||||
{(m_config.get_abs_value("overhang_1_4_speed", ref_speed) < 0.5) ?
|
||||
{FloatOrPercent{100, true},
|
||||
(m_config.get_abs_value("overhang_1_4_speed", ref_speed) < 0.5) ?
|
||||
FloatOrPercent{100, true} :
|
||||
FloatOrPercent{m_config.get_abs_value("overhang_1_4_speed", ref_speed) * 100 / ref_speed, true},
|
||||
(m_config.get_abs_value("overhang_2_4_speed", ref_speed) < 0.5) ?
|
||||
|
@ -5356,10 +5356,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
(m_config.get_abs_value("overhang_3_4_speed", ref_speed) < 0.5) ?
|
||||
FloatOrPercent{100, true} :
|
||||
FloatOrPercent{m_config.get_abs_value("overhang_3_4_speed", ref_speed) * 100 / ref_speed, true},
|
||||
(m_config.get_abs_value("overhang_4_4_speed", ref_speed) < 0.5) ?
|
||||
FloatOrPercent{100, true} :
|
||||
FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed", ref_speed) * 100 / ref_speed, true},
|
||||
FloatOrPercent{m_config.get_abs_value("bridge_speed") * 100 / ref_speed, true},
|
||||
(m_config.get_abs_value("overhang_4_4_speed", ref_speed) < 0.5) ?
|
||||
FloatOrPercent{100, true} :
|
||||
FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed", ref_speed) * 100 / ref_speed, true},
|
||||
FloatOrPercent{m_config.get_abs_value("bridge_speed") * 100 / ref_speed, true}});
|
||||
|
||||
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds,
|
||||
|
@ -5500,7 +5499,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
// { "75%", Overhang_threshold_4_4 },
|
||||
// { "95%", Overhang_threshold_bridge }
|
||||
auto check_overhang_fan = [&overhang_fan_threshold](float overlap, ExtrusionRole role) {
|
||||
if (is_bridge(role)) {
|
||||
if (role == erBridgeInfill || role == erOverhangPerimeter) { // ORCA: Split out bridge infill to internal and external to apply separate fan settings
|
||||
return true;
|
||||
}
|
||||
switch (overhang_fan_threshold) {
|
||||
|
@ -5593,7 +5592,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
int overhang_threshold = overhang_fan_threshold == Overhang_threshold_none ? Overhang_threshold_none
|
||||
: overhang_fan_threshold - 1;
|
||||
if ((overhang_fan_threshold == Overhang_threshold_none && is_external_perimeter(path.role())) ||
|
||||
(path.get_overhang_degree() > overhang_threshold || is_bridge(path.role()))) {
|
||||
(path.get_overhang_degree() > overhang_threshold ||
|
||||
(path.role() == erBridgeInfill || path.role() == erOverhangPerimeter))) { // ORCA: Add support for separate internal bridge fan speed control
|
||||
if (!m_is_overhang_fan_on) {
|
||||
gcode += ";_OVERHANG_FAN_START\n";
|
||||
m_is_overhang_fan_on = true;
|
||||
|
@ -5604,6 +5604,17 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
gcode += ";_OVERHANG_FAN_END\n";
|
||||
}
|
||||
}
|
||||
if (path.role() == erInternalBridgeInfill) { // ORCA: Add support for separate internal bridge fan speed control
|
||||
if (!m_is_internal_bridge_fan_on) {
|
||||
gcode += ";_INTERNAL_BRIDGE_FAN_START\n";
|
||||
m_is_internal_bridge_fan_on = true;
|
||||
}
|
||||
} else {
|
||||
if (m_is_internal_bridge_fan_on) {
|
||||
m_is_internal_bridge_fan_on = false;
|
||||
gcode += ";_INTERNAL_BRIDGE_FAN_END\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (supp_interface_fan_speed >= 0 && path.role() == erSupportMaterialInterface) {
|
||||
if (!m_is_supp_interface_fan_on) {
|
||||
|
@ -5738,6 +5749,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
bool cur_fan_enabled = false;
|
||||
if( m_enable_cooling_markers && enable_overhang_bridge_fan)
|
||||
pre_fan_enabled = check_overhang_fan(new_points[0].overlap, path.role());
|
||||
|
||||
if(path.role() == erInternalBridgeInfill) // ORCA: Add support for separate internal bridge fan speed control
|
||||
pre_fan_enabled = true;
|
||||
|
||||
double path_length = 0.;
|
||||
for (size_t i = 1; i < new_points.size(); i++) {
|
||||
|
@ -5761,6 +5775,19 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
}
|
||||
pre_fan_enabled = cur_fan_enabled;
|
||||
}
|
||||
// ORCA: Add support for separate internal bridge fan speed control
|
||||
if (path.role() == erInternalBridgeInfill) {
|
||||
if (!m_is_internal_bridge_fan_on) {
|
||||
gcode += ";_INTERNAL_BRIDGE_FAN_START\n";
|
||||
m_is_internal_bridge_fan_on = true;
|
||||
}
|
||||
} else {
|
||||
if (m_is_internal_bridge_fan_on) {
|
||||
gcode += ";_INTERNAL_BRIDGE_FAN_END\n";
|
||||
m_is_internal_bridge_fan_on = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (supp_interface_fan_speed >= 0 && path.role() == erSupportMaterialInterface) {
|
||||
if (!m_is_supp_interface_fan_on) {
|
||||
gcode += ";_SUPP_INTERFACE_FAN_START\n";
|
||||
|
|
|
@ -515,6 +515,7 @@ private:
|
|||
std::string _encode_label_ids_to_base64(std::vector<size_t> ids);
|
||||
// Orca
|
||||
bool m_is_overhang_fan_on;
|
||||
bool m_is_internal_bridge_fan_on; // ORCA: Add support for separate internal bridge fan speed control
|
||||
bool m_is_supp_interface_fan_on;
|
||||
// Markers for the Pressure Equalizer to recognize the extrusion type.
|
||||
// The Pressure Equalizer removes the markers from the final G-code.
|
||||
|
|
|
@ -65,6 +65,9 @@ struct CoolingLine
|
|||
TYPE_FORCE_RESUME_FAN = 1 << 14,
|
||||
TYPE_SUPPORT_INTERFACE_FAN_START = 1 << 15,
|
||||
TYPE_SUPPORT_INTERFACE_FAN_END = 1 << 16,
|
||||
// ORCA: Add support for separate internal bridge fan speed control
|
||||
TYPE_INTERNAL_BRIDGE_FAN_START = 1 << 17,
|
||||
TYPE_INTERNAL_BRIDGE_FAN_END = 1 << 18,
|
||||
};
|
||||
|
||||
CoolingLine(unsigned int type, size_t line_start, size_t line_end) :
|
||||
|
@ -511,6 +514,10 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||
line.type = CoolingLine::TYPE_OVERHANG_FAN_START;
|
||||
} else if (boost::starts_with(sline, ";_OVERHANG_FAN_END")) {
|
||||
line.type = CoolingLine::TYPE_OVERHANG_FAN_END;
|
||||
} else if (boost::starts_with(sline, ";_INTERNAL_BRIDGE_FAN_START")) { // ORCA: Add support for separate internal bridge fan speed control
|
||||
line.type = CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START;
|
||||
} else if (boost::starts_with(sline, ";_INTERNAL_BRIDGE_FAN_END")) { // ORCA: Add support for separate internal bridge fan speed control
|
||||
line.type = CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_END;
|
||||
} else if (boost::starts_with(sline, ";_SUPP_INTERFACE_FAN_START")) {
|
||||
line.type = CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START;
|
||||
} else if (boost::starts_with(sline, ";_SUPP_INTERFACE_FAN_END")) {
|
||||
|
@ -705,9 +712,11 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||
new_gcode.reserve(gcode.size() * 2);
|
||||
bool overhang_fan_control= false;
|
||||
int overhang_fan_speed = 0;
|
||||
bool internal_bridge_fan_control= false; // ORCA: Add support for separate internal bridge fan speed control
|
||||
int internal_bridge_fan_speed = 0; // ORCA: Add support for separate internal bridge fan speed control
|
||||
bool supp_interface_fan_control= false;
|
||||
int supp_interface_fan_speed = 0;
|
||||
auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, &overhang_fan_control, &overhang_fan_speed, &supp_interface_fan_control, &supp_interface_fan_speed](bool immediately_apply) {
|
||||
auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, &overhang_fan_control, &overhang_fan_speed, &internal_bridge_fan_control, &internal_bridge_fan_speed, &supp_interface_fan_control, &supp_interface_fan_speed](bool immediately_apply) {
|
||||
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder)
|
||||
float fan_min_speed = EXTRUDER_CONFIG(fan_min_speed);
|
||||
float fan_speed_new = EXTRUDER_CONFIG(reduce_fan_stop_start_freq) ? fan_min_speed : 0;
|
||||
|
@ -749,15 +758,27 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||
supp_interface_fan_speed = EXTRUDER_CONFIG(support_material_interface_fan_speed);
|
||||
supp_interface_fan_control = supp_interface_fan_speed >= 0;
|
||||
|
||||
overhang_fan_control = overhang_fan_speed > fan_speed_new;
|
||||
|
||||
// ORCA: Add support for separate internal bridge fan speed control
|
||||
internal_bridge_fan_speed = EXTRUDER_CONFIG(internal_bridge_fan_speed);
|
||||
internal_bridge_fan_control = internal_bridge_fan_speed >=0;
|
||||
|
||||
if( internal_bridge_fan_speed < 0 ) { // ORCA: Backwards compatibility setting for Orca internal bridge fan speed setting - if set at -1 (which is the default) use the overhang fan speed settings.
|
||||
internal_bridge_fan_speed = overhang_fan_speed;
|
||||
internal_bridge_fan_control = overhang_fan_control;
|
||||
}
|
||||
#undef EXTRUDER_CONFIG
|
||||
overhang_fan_control= overhang_fan_speed > fan_speed_new;
|
||||
|
||||
} else {
|
||||
overhang_fan_control= false;
|
||||
overhang_fan_control = false;
|
||||
overhang_fan_speed = 0;
|
||||
fan_speed_new = 0;
|
||||
additional_fan_speed_new = 0;
|
||||
supp_interface_fan_control= false;
|
||||
supp_interface_fan_control = false;
|
||||
supp_interface_fan_speed = 0;
|
||||
internal_bridge_fan_control = false; // ORCA: Add support for separate internal bridge fan speed control
|
||||
internal_bridge_fan_speed = 0; // ORCA: Add support for separate internal bridge fan speed control
|
||||
}
|
||||
if (fan_speed_new != m_fan_speed) {
|
||||
m_fan_speed = fan_speed_new;
|
||||
|
@ -780,6 +801,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||
// Orca: Reduce set fan commands by deferring the GCodeWriter::set_fan calls. Inspired by SuperSlicer
|
||||
// define fan_speed_change_requests and initialize it with all possible types fan speed change requests
|
||||
std::unordered_map<int, bool> fan_speed_change_requests = {{CoolingLine::TYPE_OVERHANG_FAN_START, false},
|
||||
{CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START, false}, // ORCA: Add support for separate internal bridge fan speed control
|
||||
{CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START, false},
|
||||
{CoolingLine::TYPE_FORCE_RESUME_FAN, false}};
|
||||
bool need_set_fan = false;
|
||||
|
@ -809,6 +831,16 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||
fan_speed_change_requests[CoolingLine::TYPE_OVERHANG_FAN_START] = false;
|
||||
}
|
||||
need_set_fan = true;
|
||||
} else if (line->type & CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START) { // ORCA: Add support for separate internal bridge fan speed control
|
||||
if (internal_bridge_fan_control && !fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START]) {
|
||||
need_set_fan = true;
|
||||
fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START] = true;
|
||||
}
|
||||
} else if (line->type & CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_END) { // ORCA: Add support for separate internal bridge fan speed control
|
||||
if (internal_bridge_fan_control && fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START]) {
|
||||
fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START] = false;
|
||||
}
|
||||
need_set_fan = true;
|
||||
} else if (line->type & CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START) {
|
||||
if (supp_interface_fan_control && !fan_speed_change_requests[CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START]) {
|
||||
fan_speed_change_requests[CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START] = true;
|
||||
|
@ -917,6 +949,9 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
|||
if (fan_speed_change_requests[CoolingLine::TYPE_OVERHANG_FAN_START]){
|
||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, overhang_fan_speed);
|
||||
m_current_fan_speed = overhang_fan_speed;
|
||||
} else if (fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START]){ // ORCA: Add support for separate internal bridge fan speed control
|
||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, internal_bridge_fan_speed);
|
||||
m_current_fan_speed = internal_bridge_fan_speed;
|
||||
}
|
||||
else if (fan_speed_change_requests[CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START]){
|
||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, supp_interface_fan_speed);
|
||||
|
|
|
@ -65,7 +65,9 @@ public:
|
|||
: regex_fan_speed("S[0-9]+"),
|
||||
nb_seconds_delay(nb_seconds_delay>0 ? std::max(0.01f,nb_seconds_delay) : 0),
|
||||
with_D_option(with_D_option)
|
||||
, relative_e(relative_e), only_overhangs(only_overhangs), kickstart(kickstart), m_writer(writer){}
|
||||
, relative_e(relative_e), only_overhangs(only_overhangs), kickstart(kickstart), m_writer(writer){
|
||||
m_parser.apply_config(writer.config);
|
||||
}
|
||||
|
||||
// Adds the gcode contained in the given string to the analysis and returns it after removing the workcodes
|
||||
const std::string& process_gcode(const std::string& gcode, bool flush);
|
||||
|
|
|
@ -404,359 +404,6 @@ void GCodeProcessor::TimeProcessor::reset()
|
|||
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
|
||||
}
|
||||
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, size_t total_layer_num)
|
||||
{
|
||||
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
|
||||
if (in.f == nullptr)
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
|
||||
|
||||
const bool disable_m73 = this->disable_m73;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": before process %1%")%filename.c_str();
|
||||
// temporary file to contain modified gcode
|
||||
std::string out_path = filename + ".postprocess";
|
||||
FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") };
|
||||
if (out.f == nullptr) {
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
|
||||
}
|
||||
|
||||
auto time_in_minutes = [](float time_in_seconds) {
|
||||
assert(time_in_seconds >= 0.f);
|
||||
return int((time_in_seconds + 0.5f) / 60.0f);
|
||||
};
|
||||
|
||||
auto time_in_last_minute = [](float time_in_seconds) {
|
||||
assert(time_in_seconds <= 60.0f);
|
||||
return time_in_seconds / 60.0f;
|
||||
};
|
||||
|
||||
auto format_line_M73_main = [](const std::string& mask, int percent, int time) {
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(),
|
||||
std::to_string(percent).c_str(),
|
||||
std::to_string(time).c_str());
|
||||
return std::string(line_M73);
|
||||
};
|
||||
|
||||
auto format_line_M73_stop_int = [](const std::string& mask, int time) {
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(), std::to_string(time).c_str());
|
||||
return std::string(line_M73);
|
||||
};
|
||||
|
||||
auto format_line_exhaust_fan_control = [](const std::string& mask,int fan_index,int percent) {
|
||||
char line_fan[64] = { 0 };
|
||||
sprintf(line_fan,mask.c_str(),
|
||||
std::to_string(fan_index).c_str(),
|
||||
std::to_string(int((percent/100.0)*255)).c_str());
|
||||
return std::string(line_fan);
|
||||
};
|
||||
|
||||
auto format_time_float = [](float time) {
|
||||
return Slic3r::float_to_string_decimal_point(time, 2);
|
||||
};
|
||||
|
||||
auto format_line_M73_stop_float = [format_time_float](const std::string& mask, float time) {
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(), format_time_float(time).c_str());
|
||||
return std::string(line_M73);
|
||||
};
|
||||
|
||||
std::string gcode_line;
|
||||
size_t g1_lines_counter = 0;
|
||||
// keeps track of last exported pair <percent, remaining time>
|
||||
std::array<std::pair<int, int>, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_main;
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
last_exported_main[i] = { 0, time_in_minutes(machines[i].time) };
|
||||
}
|
||||
|
||||
// keeps track of last exported remaining time to next printer stop
|
||||
std::array<int, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> last_exported_stop;
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
last_exported_stop[i] = time_in_minutes(machines[i].time);
|
||||
}
|
||||
|
||||
// buffer line to export only when greater than 64K to reduce writing calls
|
||||
std::string export_line;
|
||||
|
||||
// replace placeholder lines with the proper final value
|
||||
// gcode_line is in/out parameter, to reduce expensive memory allocation
|
||||
auto process_placeholders = [&](std::string& gcode_line) {
|
||||
int extra_lines_count = 0;
|
||||
|
||||
// remove trailing '\n'
|
||||
auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
|
||||
|
||||
std::string ret;
|
||||
if (line.length() > 1) {
|
||||
line = line.substr(1);
|
||||
if (line == reserved_tag(ETags::First_Line_M73_Placeholder) || line == reserved_tag(ETags::Last_Line_M73_Placeholder)) {
|
||||
if (disable_m73) {
|
||||
// Remove current line
|
||||
gcode_line = "";
|
||||
return std::tuple(true, -1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = machines[i];
|
||||
if (machine.enabled) {
|
||||
// export pair <percent, remaining time>
|
||||
ret += format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0);
|
||||
++extra_lines_count;
|
||||
|
||||
// export remaining time to next printer stop
|
||||
if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) {
|
||||
int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time);
|
||||
ret += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
++extra_lines_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = machines[i];
|
||||
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
|
||||
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
|
||||
char buf[128];
|
||||
if(!s_IsBBLPrinter)
|
||||
// SoftFever: compatibility with klipper_estimator
|
||||
sprintf(buf, "; estimated printing time (normal mode) = %s\n", get_time_dhms(machine.time).c_str());
|
||||
else {
|
||||
//sprintf(buf, "; estimated printing time (%s mode) = %s\n",
|
||||
// (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
// get_time_dhms(machine.time).c_str());
|
||||
sprintf(buf, "; model printing time: %s; total estimated time: %s\n",
|
||||
get_time_dhms(machine.time - machine.prepare_time).c_str(),
|
||||
get_time_dhms(machine.time).c_str());
|
||||
}
|
||||
ret += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
//BBS: write total layer number
|
||||
else if (line == reserved_tag(ETags::Total_Layer_Number_Placeholder)) {
|
||||
char buf[128];
|
||||
sprintf(buf, "; total layer number: %zd\n", total_layer_num);
|
||||
ret += buf;
|
||||
}
|
||||
}
|
||||
|
||||
if (! ret.empty())
|
||||
// Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
|
||||
gcode_line = ret;
|
||||
return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
|
||||
};
|
||||
|
||||
// check for temporary lines
|
||||
auto is_temporary_decoration = [](const std::string_view gcode_line) {
|
||||
// remove trailing '\n'
|
||||
assert(! gcode_line.empty());
|
||||
assert(gcode_line.back() == '\n');
|
||||
|
||||
// return true for decorations which are used in processing the gcode but that should not be exported into the final gcode
|
||||
// i.e.:
|
||||
// bool ret = gcode_line.substr(0, gcode_line.length() - 1) == ";" + Layer_Change_Tag;
|
||||
// ...
|
||||
// return ret;
|
||||
return false;
|
||||
};
|
||||
|
||||
// Iterators for the normal and silent cached time estimate entry recently processed, used by process_line_G1.
|
||||
auto g1_times_cache_it = Slic3r::reserve_vector<std::vector<TimeMachine::G1LinesCacheItem>::const_iterator>(machines.size());
|
||||
for (const auto& machine : machines)
|
||||
g1_times_cache_it.emplace_back(machine.g1_times_cache.begin());
|
||||
|
||||
// add lines M73 to exported gcode
|
||||
auto process_line_move = [
|
||||
// Lambdas, mostly for string formatting, all with an empty capture block.
|
||||
time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,format_line_exhaust_fan_control,
|
||||
&self = std::as_const(*this),
|
||||
// Caches, to be modified
|
||||
&g1_times_cache_it, &last_exported_main, &last_exported_stop,
|
||||
// String output
|
||||
&export_line]
|
||||
(const size_t g1_lines_counter) {
|
||||
unsigned int exported_lines_count = 0;
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = self.machines[i];
|
||||
if (machine.enabled) {
|
||||
// export pair <percent, remaining time>
|
||||
// Skip all machine.g1_times_cache below g1_lines_counter.
|
||||
auto& it = g1_times_cache_it[i];
|
||||
while (it != machine.g1_times_cache.end() && it->id < g1_lines_counter)
|
||||
++it;
|
||||
if (it != machine.g1_times_cache.end() && it->id == g1_lines_counter) {
|
||||
std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time),
|
||||
time_in_minutes(machine.time - it->elapsed_time) };
|
||||
|
||||
if (last_exported_main[i] != to_export_main) {
|
||||
export_line += format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
to_export_main.first, to_export_main.second);
|
||||
last_exported_main[i] = to_export_main;
|
||||
++exported_lines_count;
|
||||
}
|
||||
// export remaining time to next printer stop
|
||||
auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time,
|
||||
[](float value, const TimeMachine::StopTime& t) { return value < t.elapsed_time; });
|
||||
if (it_stop != machine.stop_times.end()) {
|
||||
int to_export_stop = time_in_minutes(it_stop->elapsed_time - it->elapsed_time);
|
||||
if (last_exported_stop[i] != to_export_stop) {
|
||||
if (to_export_stop > 0) {
|
||||
if (last_exported_stop[i] != to_export_stop) {
|
||||
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
++exported_lines_count;
|
||||
}
|
||||
}
|
||||
else {
|
||||
bool is_last = false;
|
||||
auto next_it = it + 1;
|
||||
is_last |= (next_it == machine.g1_times_cache.end());
|
||||
|
||||
if (next_it != machine.g1_times_cache.end()) {
|
||||
auto next_it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), next_it->elapsed_time,
|
||||
[](float value, const TimeMachine::StopTime& t) { return value < t.elapsed_time; });
|
||||
is_last |= (next_it_stop != it_stop);
|
||||
|
||||
std::string time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
|
||||
std::string next_time_float_str = format_time_float(time_in_last_minute(it_stop->elapsed_time - next_it->elapsed_time));
|
||||
is_last |= (string_to_double_decimal_point(time_float_str) > 0. && string_to_double_decimal_point(next_time_float_str) == 0.);
|
||||
}
|
||||
|
||||
if (is_last) {
|
||||
if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
|
||||
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
else
|
||||
export_line += format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
|
||||
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
++exported_lines_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return exported_lines_count;
|
||||
};
|
||||
|
||||
// helper function to write to disk
|
||||
size_t out_file_pos = 0;
|
||||
lines_ends.clear();
|
||||
auto write_string = [&export_line, &out, &out_path, &out_file_pos, &lines_ends](const std::string& str) {
|
||||
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
|
||||
if (ferror(out.f)) {
|
||||
out.close();
|
||||
boost::nowide::remove(out_path.c_str());
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
|
||||
}
|
||||
for (size_t i = 0; i < export_line.size(); ++ i)
|
||||
if (export_line[i] == '\n')
|
||||
lines_ends.emplace_back(out_file_pos + i + 1);
|
||||
out_file_pos += export_line.size();
|
||||
export_line.clear();
|
||||
};
|
||||
|
||||
unsigned int line_id = 0;
|
||||
std::vector<std::pair<unsigned int, unsigned int>> offsets;
|
||||
|
||||
{
|
||||
// Read the input stream 64kB at a time, extract lines and process them.
|
||||
std::vector<char> buffer(65536 * 10, 0);
|
||||
// Line buffer.
|
||||
assert(gcode_line.empty());
|
||||
for (;;) {
|
||||
size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
|
||||
if (::ferror(in.f))
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
|
||||
bool eof = cnt_read == 0;
|
||||
auto it = buffer.begin();
|
||||
auto it_bufend = buffer.begin() + cnt_read;
|
||||
while (it != it_bufend || (eof && ! gcode_line.empty())) {
|
||||
// Find end of line.
|
||||
bool eol = false;
|
||||
auto it_end = it;
|
||||
for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
|
||||
// End of line is indicated also if end of file was reached.
|
||||
eol |= eof && it_end == it_bufend;
|
||||
gcode_line.insert(gcode_line.end(), it, it_end);
|
||||
if (eol) {
|
||||
++line_id;
|
||||
|
||||
// determine the end of line character and pass to output
|
||||
gcode_line += *it_end;
|
||||
if(*it_end == '\r' && *(++ it_end) == '\n')
|
||||
gcode_line += '\n';
|
||||
// replace placeholder lines
|
||||
auto [processed, lines_added_count] = process_placeholders(gcode_line);
|
||||
if (processed && lines_added_count != 0)
|
||||
offsets.push_back({ line_id, lines_added_count });
|
||||
|
||||
if (!disable_m73 && !processed &&!is_temporary_decoration(gcode_line) &&
|
||||
(GCodeReader::GCodeLine::cmd_is(gcode_line, "G1") ||
|
||||
GCodeReader::GCodeLine::cmd_is(gcode_line, "G2") ||
|
||||
GCodeReader::GCodeLine::cmd_is(gcode_line, "G3") ||
|
||||
GCodeReader::GCodeLine::cmd_is(gcode_line, "G10")||
|
||||
GCodeReader::GCodeLine::cmd_is(gcode_line, "G11"))) {
|
||||
// remove temporary lines, add lines M73 where needed
|
||||
unsigned int extra_lines_count = process_line_move(g1_lines_counter ++);
|
||||
if (extra_lines_count > 0)
|
||||
offsets.push_back({ line_id, extra_lines_count });
|
||||
}
|
||||
|
||||
if (disable_m73 && !processed && GCodeReader::GCodeLine::cmd_is(gcode_line, "M73")) {
|
||||
// Remove any existing M73 command
|
||||
gcode_line = "";
|
||||
offsets.push_back({line_id, -1});
|
||||
}
|
||||
|
||||
export_line += gcode_line;
|
||||
if (export_line.length() > 65535)
|
||||
write_string(export_line);
|
||||
gcode_line.clear();
|
||||
}
|
||||
// Skip EOL.
|
||||
it = it_end;
|
||||
if (it != it_bufend && *it == '\r')
|
||||
++ it;
|
||||
if (it != it_bufend && *it == '\n')
|
||||
++ it;
|
||||
}
|
||||
if (eof)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!export_line.empty())
|
||||
write_string(export_line);
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": after process %1%")%filename.c_str();
|
||||
|
||||
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
|
||||
unsigned int curr_offset_id = 0;
|
||||
unsigned int total_offset = 0;
|
||||
for (GCodeProcessorResult::MoveVertex& move : moves) {
|
||||
while (curr_offset_id < static_cast<unsigned int>(offsets.size()) && offsets[curr_offset_id].first <= move.gcode_id) {
|
||||
total_offset += offsets[curr_offset_id].second;
|
||||
++curr_offset_id;
|
||||
}
|
||||
move.gcode_id += total_offset;
|
||||
}
|
||||
|
||||
if (rename_file(out_path, filename)) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Failed to rename the output G-code file from %1% to %2%")%out_path.c_str() % filename.c_str();
|
||||
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' +
|
||||
"Is " + out_path + " locked?" + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeProcessor::UsedFilaments::reset()
|
||||
{
|
||||
color_change_cache = 0.0f;
|
||||
|
@ -1136,7 +783,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
DEFAULT_TRAVEL_ACCELERATION;
|
||||
}
|
||||
|
||||
m_time_processor.disable_m73 = config.disable_m73;
|
||||
m_disable_m73 = config.disable_m73;
|
||||
|
||||
const ConfigOptionFloat* initial_layer_print_height = config.option<ConfigOptionFloat>("initial_layer_print_height");
|
||||
if (initial_layer_print_height != nullptr)
|
||||
|
@ -1694,9 +1341,6 @@ void GCodeProcessor::finalize(bool post_process)
|
|||
m_height_compare.output();
|
||||
m_width_compare.output();
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
if (post_process){
|
||||
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends, m_layer_id);
|
||||
}
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
@ -4411,7 +4055,10 @@ void GCodeProcessor::run_post_process()
|
|||
return time_in_seconds / 60.0f;
|
||||
};
|
||||
|
||||
auto format_line_M73_main = [](const std::string& mask, int percent, int time) {
|
||||
auto format_line_M73_main = [this](const std::string& mask, int percent, int time) {
|
||||
if(this->m_disable_m73)
|
||||
return std::string("");
|
||||
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(),
|
||||
std::to_string(percent).c_str(),
|
||||
|
@ -4419,7 +4066,9 @@ void GCodeProcessor::run_post_process()
|
|||
return std::string(line_M73);
|
||||
};
|
||||
|
||||
auto format_line_M73_stop_int = [](const std::string& mask, int time) {
|
||||
auto format_line_M73_stop_int = [this](const std::string& mask, int time) {
|
||||
if (this->m_disable_m73)
|
||||
return std::string("");
|
||||
char line_M73[64];
|
||||
sprintf(line_M73, mask.c_str(), std::to_string(time).c_str());
|
||||
return std::string(line_M73);
|
||||
|
@ -4766,33 +4415,43 @@ void GCodeProcessor::run_post_process()
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
|
||||
} else if (line == reserved_tag(ETags::Estimated_Printing_Time_Placeholder)) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = m_time_processor.machines[i];
|
||||
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
|
||||
const TimeMachine& machine = m_time_processor.machines[i];
|
||||
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
|
||||
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
|
||||
char buf[128];
|
||||
sprintf(buf, "; estimated printing time (%s mode) = %s\n",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
get_time_dhms(machine.time).c_str());
|
||||
if (!s_IsBBLPrinter)
|
||||
// Orca: compatibility with klipper_estimator
|
||||
sprintf(buf, "; estimated printing time (%s mode) = %s\n",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
get_time_dhms(machine.time).c_str());
|
||||
else {
|
||||
sprintf(buf, "; model printing time: %s; total estimated time: %s\n",
|
||||
get_time_dhms(machine.time - machine.prepare_time).c_str(), get_time_dhms(machine.time).c_str());
|
||||
}
|
||||
export_lines.append_line(buf);
|
||||
processed = true;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = m_time_processor.machines[i];
|
||||
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
|
||||
const TimeMachine& machine = m_time_processor.machines[i];
|
||||
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
|
||||
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
|
||||
char buf[128];
|
||||
sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
get_time_dhms(machine.prepare_time).c_str());
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
get_time_dhms(machine.prepare_time).c_str());
|
||||
export_lines.append_line(buf);
|
||||
processed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Orca: write total layer number, this is used by Bambu printers only as of now
|
||||
else if (line == reserved_tag(ETags::Total_Layer_Number_Placeholder)) {
|
||||
char buf[128];
|
||||
sprintf(buf, "; total layer number: %u\n", m_layer_id);
|
||||
export_lines.append_line(buf);
|
||||
}
|
||||
}
|
||||
|
||||
return processed;
|
||||
|
|
|
@ -380,7 +380,7 @@ class Print;
|
|||
EMoveType move_type{ EMoveType::Noop };
|
||||
ExtrusionRole role{ erNone };
|
||||
unsigned int g1_line_id{ 0 };
|
||||
unsigned int remaining_internal_g1_lines;
|
||||
unsigned int remaining_internal_g1_lines{ 0 };
|
||||
unsigned int layer_id{ 0 };
|
||||
float distance{ 0.0f }; // mm
|
||||
float acceleration{ 0.0f }; // mm/s^2
|
||||
|
@ -429,7 +429,7 @@ class Print;
|
|||
struct G1LinesCacheItem
|
||||
{
|
||||
unsigned int id;
|
||||
unsigned int remaining_internal_g1_lines;
|
||||
unsigned int remaining_internal_g1_lines{ 0 };
|
||||
float elapsed_time;
|
||||
};
|
||||
|
||||
|
@ -495,15 +495,10 @@ class Print;
|
|||
float filament_unload_times;
|
||||
//Orca: time for tool change
|
||||
float machine_tool_change_time;
|
||||
bool disable_m73;
|
||||
|
||||
std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> machines;
|
||||
|
||||
void reset();
|
||||
|
||||
// post process the file with the given filename to add remaining time lines M73
|
||||
// and updates moves' gcode ids accordingly
|
||||
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, size_t total_layer_num);
|
||||
};
|
||||
|
||||
struct UsedFilaments // filaments per ColorChange
|
||||
|
@ -735,6 +730,7 @@ class Print;
|
|||
bool m_single_extruder_multi_material;
|
||||
float m_preheat_time;
|
||||
int m_preheat_steps;
|
||||
bool m_disable_m73;
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
|
|
@ -121,9 +121,13 @@ std::string SpiralVase::process_layer(const std::string &gcode, bool last_layer)
|
|||
// layer.
|
||||
bool transition_in = m_transition_layer && m_config.use_relative_e_distances.value;
|
||||
bool transition_out = last_layer && m_config.use_relative_e_distances.value;
|
||||
|
||||
float starting_flowrate = float(m_config.spiral_starting_flow_ratio.value);
|
||||
float finishing_flowrate = float(m_config.spiral_finishing_flow_ratio.value);
|
||||
|
||||
float len = 0.f;
|
||||
SpiralVase::SpiralPoint last_point = previous_layer != NULL && previous_layer->size() >0? previous_layer->at(previous_layer->size()-1): SpiralVase::SpiralPoint(0,0);
|
||||
m_reader.parse_buffer(gcode, [&new_gcode, &z, total_layer_length, layer_height, transition_in, &len, ¤t_layer, &previous_layer, &transition_gcode, transition_out, smooth_spiral, &max_xy_dist_for_smoothing, &last_point]
|
||||
m_reader.parse_buffer(gcode, [&new_gcode, &z, total_layer_length, layer_height, transition_in, &len, ¤t_layer, &previous_layer, &transition_gcode, transition_out, smooth_spiral, &max_xy_dist_for_smoothing, &last_point, starting_flowrate, finishing_flowrate]
|
||||
(GCodeReader &reader, GCodeReader::GCodeLine line) {
|
||||
if (line.cmd_is("G1")) {
|
||||
// Orca: Filter out retractions at layer change
|
||||
|
@ -140,15 +144,18 @@ std::string SpiralVase::process_layer(const std::string &gcode, bool last_layer)
|
|||
if (dist_XY > 0 && line.extruding(reader)) { // Exclude wipe and retract
|
||||
len += dist_XY;
|
||||
float factor = len / total_layer_length;
|
||||
if (transition_in)
|
||||
// Transition layer, interpolate the amount of extrusion from zero to the final value.
|
||||
line.set(E, line.e() * factor, 5 /*decimal_digits*/);
|
||||
else if (transition_out) {
|
||||
if (transition_in){
|
||||
// Transition layer, interpolate the amount of extrusion starting from spiral_vase_starting_flow_rate to 100%.
|
||||
float starting_e_factor = starting_flowrate + (factor * (1.f - starting_flowrate));
|
||||
line.set(E, line.e() * starting_e_factor, 5 /*decimal_digits*/);
|
||||
} else if (transition_out) {
|
||||
// We want the last layer to ramp down extrusion, but without changing z height!
|
||||
// So clone the line before we mess with its Z and duplicate it into a new layer that ramps down E
|
||||
// We add this new layer at the very end
|
||||
// As with transition_in, the amount is ramped down from 100% to spiral_vase_finishing_flow_rate
|
||||
GCodeReader::GCodeLine transitionLine(line);
|
||||
transitionLine.set(E, line.e() * (1 - factor), 5 /*decimal_digits*/);
|
||||
float finishing_e_factor = finishing_flowrate + ((1.f -factor) * (1.f - finishing_flowrate));
|
||||
transitionLine.set(E, line.e() * finishing_e_factor, 5 /*decimal_digits*/);
|
||||
transition_gcode += transitionLine.raw() + '\n';
|
||||
}
|
||||
// This line is the core of Spiral Vase mode, ramp up the Z smoothly
|
||||
|
|
|
@ -842,7 +842,7 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume()
|
|||
const unsigned int number_of_extruders = (unsigned int) (sqrt(flush_matrix.size()) + EPSILON);
|
||||
// Extract purging volumes for each extruder pair:
|
||||
std::vector<std::vector<float>> wipe_volumes;
|
||||
if (print_config->purge_in_prime_tower || m_is_BBL_printer) {
|
||||
if ((print_config->purge_in_prime_tower && print_config->single_extruder_multi_material) || m_is_BBL_printer) {
|
||||
for (unsigned int i = 0; i < number_of_extruders; ++i)
|
||||
wipe_volumes.push_back( std::vector<float>(flush_matrix.begin() + i * number_of_extruders,
|
||||
flush_matrix.begin() + (i + 1) * number_of_extruders));
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
namespace Slic3r
|
||||
{
|
||||
static const double wipe_tower_wall_infill_overlap = 0.0;
|
||||
|
||||
inline float align_round(float value, float base)
|
||||
{
|
||||
|
@ -118,7 +119,7 @@ public:
|
|||
|
||||
WipeTowerWriter& set_initial_tool(size_t tool) { m_current_tool = tool; return *this; }
|
||||
|
||||
WipeTowerWriter& set_z(float z)
|
||||
WipeTowerWriter& set_z(float z)
|
||||
{ m_current_z = z; return *this; }
|
||||
|
||||
WipeTowerWriter& set_extrusion_flow(float flow)
|
||||
|
@ -237,7 +238,7 @@ public:
|
|||
WipeTowerWriter& travel(float x, float y, float f = 0.f)
|
||||
{ return extrude_explicit(x, y, 0.f, f); }
|
||||
|
||||
WipeTowerWriter& travel(const Vec2f &dest, float f = 0.f)
|
||||
WipeTowerWriter& travel(const Vec2f &dest, float f = 0.f)
|
||||
{ return extrude_explicit(dest.x(), dest.y(), 0.f, f); }
|
||||
|
||||
// Extrude a line from current position to x, y with the extrusion amount given by m_extrusion_flow.
|
||||
|
@ -248,7 +249,7 @@ public:
|
|||
return extrude_explicit(x, y, std::sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
|
||||
}
|
||||
|
||||
WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f)
|
||||
WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f)
|
||||
{ return extrude(dest.x(), dest.y(), f); }
|
||||
|
||||
WipeTowerWriter& rectangle(const Vec2f& ld,float width,float height,const float f = 0.f)
|
||||
|
@ -299,7 +300,7 @@ public:
|
|||
do {
|
||||
++i;
|
||||
if (i == 4) i = 0;
|
||||
if (need_change_flow) {
|
||||
if (need_change_flow) {
|
||||
if (i == 1) {
|
||||
// using bridge flow in bridge area, and add notes for gcode-check when flow changed
|
||||
set_extrusion_flow(wipe_tower->extrusion_flow(0.2));
|
||||
|
@ -358,7 +359,7 @@ public:
|
|||
|
||||
// Elevate the extruder head above the current print_z position.
|
||||
WipeTowerWriter& z_hop(float hop, float f = 0.f)
|
||||
{
|
||||
{
|
||||
m_gcode += std::string("G1") + set_format_Z(m_current_z + hop);
|
||||
if (f != 0 && f != m_current_feedrate)
|
||||
m_gcode += set_format_F(f);
|
||||
|
@ -367,7 +368,7 @@ public:
|
|||
}
|
||||
|
||||
// Lower the extruder head back to the current print_z position.
|
||||
WipeTowerWriter& z_hop_reset(float f = 0.f)
|
||||
WipeTowerWriter& z_hop_reset(float f = 0.f)
|
||||
{ return z_hop(0, f); }
|
||||
|
||||
// Move to x1, +y_increment,
|
||||
|
@ -455,14 +456,14 @@ public:
|
|||
}
|
||||
|
||||
WipeTowerWriter& flush_planner_queue()
|
||||
{
|
||||
m_gcode += "G4 S0\n";
|
||||
{
|
||||
m_gcode += "G4 S0\n";
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Reset internal extruder counter.
|
||||
WipeTowerWriter& reset_extruder()
|
||||
{
|
||||
{
|
||||
m_gcode += "G92 E0\n";
|
||||
return *this;
|
||||
}
|
||||
|
@ -725,7 +726,7 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
|
|||
// Returns gcode to prime the nozzles at the front edge of the print bed.
|
||||
std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
|
||||
// print_z of the first layer.
|
||||
float initial_layer_print_height,
|
||||
float initial_layer_print_height,
|
||||
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
|
||||
const std::vector<unsigned int> &tools,
|
||||
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
|
||||
|
@ -742,7 +743,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
|
|||
float wipe_depth = 0.f;
|
||||
float wipe_length = 0.f;
|
||||
float purge_volume = 0.f;
|
||||
|
||||
|
||||
// Finds this toolchange info
|
||||
if (tool != (unsigned int)(-1))
|
||||
{
|
||||
|
@ -802,12 +803,30 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
|
|||
box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED) ? m_layer_info->toolchanges_depth() - m_layer_info->depth : 0.f),
|
||||
m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
|
||||
// align the perimeter
|
||||
|
||||
Vec2f pos = initial_position;
|
||||
switch (m_cur_layer_id % 4){
|
||||
case 0:
|
||||
pos = wt_box.ld;
|
||||
break;
|
||||
case 1:
|
||||
pos = wt_box.rd;
|
||||
break;
|
||||
case 2:
|
||||
pos = wt_box.ru;
|
||||
break;
|
||||
case 3:
|
||||
pos = wt_box.lu;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
writer.set_initial_position(pos, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
wt_box = align_perimeter(wt_box);
|
||||
writer.rectangle(wt_box);
|
||||
writer.travel(initial_position);
|
||||
}
|
||||
|
||||
if (first_toolchange_to_nonsoluble) {
|
||||
{
|
||||
writer.travel(Vec2f(0, 0));
|
||||
writer.travel(initial_position);
|
||||
}
|
||||
|
@ -849,7 +868,7 @@ void WipeTower::toolchange_Unload(
|
|||
#if 0
|
||||
float xl = cleaning_box.ld.x() + 1.f * m_perimeter_width;
|
||||
float xr = cleaning_box.rd.x() - 1.f * m_perimeter_width;
|
||||
|
||||
|
||||
const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness
|
||||
const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
|
||||
|
||||
|
@ -1108,9 +1127,9 @@ void WipeTower::toolchange_Wipe(
|
|||
}
|
||||
|
||||
if (m_left_to_right)
|
||||
writer.extrude(xr + 0.25f * m_perimeter_width, writer.y(), wipe_speed);
|
||||
writer.extrude(xr + wipe_tower_wall_infill_overlap * m_perimeter_width, writer.y(), wipe_speed);
|
||||
else
|
||||
writer.extrude(xl - 0.25f * m_perimeter_width, writer.y(), wipe_speed);
|
||||
writer.extrude(xl - wipe_tower_wall_infill_overlap * m_perimeter_width, writer.y(), wipe_speed);
|
||||
|
||||
// BBS: recover the flow in non-bridging area
|
||||
if (need_change_flow) {
|
||||
|
@ -1139,7 +1158,7 @@ void WipeTower::toolchange_Wipe(
|
|||
//writer.add_wipe_point(writer.x(), writer.y())
|
||||
// .add_wipe_point(writer.x(), writer.y() - dy)
|
||||
// .add_wipe_point(! m_left_to_right ? m_wipe_tower_width : 0.f, writer.y() - dy);
|
||||
// BBS: modify the wipe_path after toolchange
|
||||
// BBS: modify the wipe_path after toolchange
|
||||
writer.add_wipe_point(writer.x(), writer.y())
|
||||
.add_wipe_point(! m_left_to_right ? m_wipe_tower_width : 0.f, writer.y());
|
||||
|
||||
|
@ -1473,7 +1492,7 @@ void WipeTower::plan_tower()
|
|||
m_wipe_tower_depth = 0.f;
|
||||
for (auto& layer : m_plan)
|
||||
layer.depth = 0.f;
|
||||
|
||||
|
||||
float max_depth_for_all = 0;
|
||||
for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index)
|
||||
{
|
||||
|
@ -1482,7 +1501,7 @@ void WipeTower::plan_tower()
|
|||
this_layer_depth = min_wipe_tower_depth;
|
||||
|
||||
m_plan[layer_index].depth = this_layer_depth;
|
||||
|
||||
|
||||
if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width)
|
||||
m_wipe_tower_depth = this_layer_depth + m_perimeter_width;
|
||||
|
||||
|
@ -1492,7 +1511,7 @@ void WipeTower::plan_tower()
|
|||
m_plan[i].depth = this_layer_depth;
|
||||
}
|
||||
|
||||
if (m_enable_timelapse_print && layer_index == 0)
|
||||
if (m_enable_timelapse_print && layer_index == 0)
|
||||
max_depth_for_all = m_plan[0].depth;
|
||||
}
|
||||
|
||||
|
@ -1600,10 +1619,11 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
|
|||
used = 0.f;
|
||||
|
||||
m_old_temperature = -1; // reset last temperature written in the gcode
|
||||
|
||||
int index = 0;
|
||||
std::vector<WipeTower::ToolChangeResult> layer_result;
|
||||
for (auto layer : m_plan)
|
||||
{
|
||||
m_cur_layer_id = index++;
|
||||
set_layer(layer.z, layer.height, 0, false/*layer.z == m_plan.front().z*/, layer.z == m_plan.back().z);
|
||||
// BBS
|
||||
//m_internal_rotation += 180.f;
|
||||
|
@ -1627,14 +1647,14 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
|
|||
// if there is no toolchange switching to non-soluble, finish layer
|
||||
// will be called at the very beginning. That's the last possibility
|
||||
// where a nonsoluble tool can be.
|
||||
if (m_enable_timelapse_print) {
|
||||
if (m_enable_timelapse_print) {
|
||||
timelapse_wall = only_generate_out_wall();
|
||||
}
|
||||
finish_layer_tcr = finish_layer(m_enable_timelapse_print ? false : true, layer.extruder_fill);
|
||||
}
|
||||
|
||||
for (int i=0; i<int(layer.tool_changes.size()); ++i) {
|
||||
if (i == 0 && m_enable_timelapse_print) {
|
||||
if (i == 0 && m_enable_timelapse_print) {
|
||||
timelapse_wall = only_generate_out_wall();
|
||||
}
|
||||
|
||||
|
|
|
@ -304,7 +304,7 @@ private:
|
|||
float m_travel_speed = 0.f;
|
||||
float m_first_layer_speed = 0.f;
|
||||
size_t m_first_layer_idx = size_t(-1);
|
||||
|
||||
size_t m_cur_layer_id;
|
||||
// G-code generator parameters.
|
||||
float m_cooling_tube_retraction = 0.f;
|
||||
float m_cooling_tube_length = 0.f;
|
||||
|
|
|
@ -1601,20 +1601,24 @@ void WipeTower2::save_on_last_wipe()
|
|||
auto& toolchange = m_layer_info->tool_changes[i];
|
||||
tool_change(toolchange.new_tool);
|
||||
|
||||
if (i == idx) {
|
||||
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
|
||||
// Orca: allow calculation of the required depth and wipe volume for soluable toolchanges as well
|
||||
// NOTE: it's not clear if this is the right way, technically we should disable wipe tower if soluble filament is used as it
|
||||
// will will make the wipe tower unstable. Need to revist this in the future.
|
||||
|
||||
float volume_to_save = length_to_volume(finish_layer().total_extrusion_length_in_plane(), m_perimeter_width, m_layer_info->height);
|
||||
float volume_left_to_wipe = std::max(m_filpar[toolchange.new_tool].filament_minimal_purge_on_wipe_tower, toolchange.wipe_volume_total - volume_to_save);
|
||||
float volume_we_need_depth_for = std::max(0.f, volume_left_to_wipe - length_to_volume(toolchange.first_wipe_line, m_perimeter_width*m_extra_flow, m_layer_info->height));
|
||||
float depth_to_wipe = get_wipe_depth(volume_we_need_depth_for, m_layer_info->height, m_perimeter_width, m_extra_flow, m_extra_spacing_wipe, width);
|
||||
// if (i == idx) {
|
||||
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
|
||||
|
||||
toolchange.required_depth = toolchange.ramming_depth + depth_to_wipe;
|
||||
toolchange.wipe_volume = volume_left_to_wipe;
|
||||
}
|
||||
}
|
||||
float volume_to_save = length_to_volume(finish_layer().total_extrusion_length_in_plane(), m_perimeter_width, m_layer_info->height);
|
||||
float volume_left_to_wipe = std::max(m_filpar[toolchange.new_tool].filament_minimal_purge_on_wipe_tower, toolchange.wipe_volume_total - volume_to_save);
|
||||
float volume_we_need_depth_for = std::max(0.f, volume_left_to_wipe - length_to_volume(toolchange.first_wipe_line, m_perimeter_width*m_extra_flow, m_layer_info->height));
|
||||
float depth_to_wipe = get_wipe_depth(volume_we_need_depth_for, m_layer_info->height, m_perimeter_width, m_extra_flow, m_extra_spacing_wipe, width);
|
||||
|
||||
toolchange.required_depth = toolchange.ramming_depth + depth_to_wipe;
|
||||
toolchange.wipe_volume = volume_left_to_wipe;
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return index of first toolchange that switches to non-soluble extruder
|
||||
|
|
|
@ -447,12 +447,6 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
|
|||
// this function, fix it first.
|
||||
//std::terminate();
|
||||
|
||||
// Orca: If moving down during below the current layer nominal Z, force XY->Z moves to avoid collisions with previous extrusions
|
||||
double nominal_z = m_pos(2) - m_lifted;
|
||||
if (point(2) < nominal_z - EPSILON) { // EPSILON to avoid false matches due to rounding errors
|
||||
this->set_current_position_clear(false); // This forces XYZ moves to be split into XY->Z
|
||||
}
|
||||
|
||||
/* If target Z is lower than current Z but higher than nominal Z we
|
||||
don't perform the Z move but we only move in the XY plane and
|
||||
adjust the nominal Z by reducing the lift amount that will be
|
||||
|
|
|
@ -391,6 +391,7 @@ coordf_t Layer::get_sparse_infill_max_void_area()
|
|||
max_void_area = std::max(max_void_area, spacing * spacing);
|
||||
break;
|
||||
case ipGrid:
|
||||
case ip2DLattice:
|
||||
case ipHoneycomb:
|
||||
case ipLightning:
|
||||
max_void_area = std::max(max_void_area, 4.0 * spacing * spacing);
|
||||
|
|
|
@ -1063,6 +1063,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
|
|||
this->sla_support_points = rhs.sla_support_points;
|
||||
this->sla_points_status = rhs.sla_points_status;
|
||||
this->sla_drain_holes = rhs.sla_drain_holes;
|
||||
this->brim_points = rhs.brim_points;
|
||||
this->layer_config_ranges = rhs.layer_config_ranges;
|
||||
this->layer_height_profile = rhs.layer_height_profile;
|
||||
this->printable = rhs.printable;
|
||||
|
@ -1102,6 +1103,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
|
|||
this->sla_support_points = std::move(rhs.sla_support_points);
|
||||
this->sla_points_status = std::move(rhs.sla_points_status);
|
||||
this->sla_drain_holes = std::move(rhs.sla_drain_holes);
|
||||
this->brim_points = std::move(brim_points);
|
||||
this->layer_config_ranges = std::move(rhs.layer_config_ranges);
|
||||
this->layer_height_profile = std::move(rhs.layer_height_profile);
|
||||
this->printable = std::move(rhs.printable);
|
||||
|
@ -1736,6 +1738,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
|
|||
new_object->sla_support_points.clear();
|
||||
new_object->sla_drain_holes.clear();
|
||||
new_object->sla_points_status = sla::PointsStatus::NoPoints;
|
||||
new_object->brim_points.clear();
|
||||
new_object->clear_volumes();
|
||||
new_object->input_file.clear();
|
||||
|
||||
|
@ -2040,6 +2043,7 @@ ModelObjectPtrs ModelObject::merge_volumes(std::vector<int>& vol_indeces)
|
|||
upper->sla_support_points.clear();
|
||||
upper->sla_drain_holes.clear();
|
||||
upper->sla_points_status = sla::PointsStatus::NoPoints;
|
||||
upper->brim_points.clear();
|
||||
upper->clear_volumes();
|
||||
upper->input_file.clear();
|
||||
|
||||
|
@ -3510,6 +3514,17 @@ bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObjec
|
|||
[](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.mmu_segmentation_facets.timestamp_matches(mv_new.mmu_segmentation_facets); });
|
||||
}
|
||||
|
||||
bool model_brim_points_data_changed(const ModelObject& mo, const ModelObject& mo_new)
|
||||
{
|
||||
if (mo.brim_points.size() != mo_new.brim_points.size())
|
||||
return true;
|
||||
for (size_t i = 0; i < mo.brim_points.size(); ++i) {
|
||||
if (mo.brim_points[i] != mo_new.brim_points[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool model_has_multi_part_objects(const Model &model)
|
||||
{
|
||||
for (const ModelObject *model_object : model.objects)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "Slicing.hpp"
|
||||
#include "SLA/SupportPoint.hpp"
|
||||
#include "SLA/Hollowing.hpp"
|
||||
#include "BrimEarsPoint.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
#include "calib.hpp"
|
||||
|
@ -383,9 +384,7 @@ public:
|
|||
// Holes to be drilled into the object so resin can flow out
|
||||
sla::DrainHoles sla_drain_holes;
|
||||
|
||||
// Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform
|
||||
CutConnectors cut_connectors;
|
||||
CutObjectBase cut_id;
|
||||
BrimPoints brim_points;
|
||||
|
||||
/* This vector accumulates the total translation applied to the object by the
|
||||
center_around_origin() method. Callers might want to apply the same translation
|
||||
|
@ -396,6 +395,10 @@ public:
|
|||
// BBS: save for compare with new load volumes
|
||||
std::vector<ObjectID> volume_ids;
|
||||
|
||||
// Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform
|
||||
CutConnectors cut_connectors;
|
||||
CutObjectBase cut_id;
|
||||
|
||||
Model* get_model() { return m_model; }
|
||||
const Model* get_model() const { return m_model; }
|
||||
// BBS: production extension
|
||||
|
@ -673,7 +676,7 @@ private:
|
|||
Internal::StaticSerializationWrapper<ModelConfigObject const> config_wrapper(config);
|
||||
Internal::StaticSerializationWrapper<LayerHeightProfile const> layer_heigth_profile_wrapper(layer_height_profile);
|
||||
ar(name, module_name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper,
|
||||
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation,
|
||||
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, brim_points,
|
||||
m_bounding_box_approx, m_bounding_box_approx_valid,
|
||||
m_bounding_box_exact, m_bounding_box_exact_valid, m_min_max_z_valid,
|
||||
m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid,
|
||||
|
@ -686,7 +689,7 @@ private:
|
|||
// BBS: add backup, check modify
|
||||
SaveObjectGaurd gaurd(*this);
|
||||
ar(name, module_name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper,
|
||||
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation,
|
||||
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, brim_points,
|
||||
m_bounding_box_approx, m_bounding_box_approx_valid,
|
||||
m_bounding_box_exact, m_bounding_box_exact_valid, m_min_max_z_valid,
|
||||
m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid,
|
||||
|
@ -1694,6 +1697,8 @@ bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo
|
|||
// The function assumes that volumes list is synchronized.
|
||||
extern bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new);
|
||||
|
||||
bool model_brim_points_data_changed(const ModelObject& mo, const ModelObject& mo_new);
|
||||
|
||||
// If the model has multi-part objects, then it is currently not supported by the SLA mode.
|
||||
// Either the model cannot be loaded, or a SLA printer has to be activated.
|
||||
bool model_has_multi_part_objects(const Model &model);
|
||||
|
|
|
@ -767,6 +767,8 @@ BedType Preset::get_default_bed_type(PresetBundle* preset_bundle)
|
|||
return BedType::btPC;
|
||||
} else if (model_id == "C11") {
|
||||
return BedType::btPEI;
|
||||
}else if (model_id == "Elegoo-CC" || model_id == "Elegoo-C") {//set default bed type to PTE for Elegoo-CC
|
||||
return BedType::btPTE;
|
||||
}
|
||||
return BedType::btPEI;
|
||||
}
|
||||
|
@ -781,10 +783,10 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle)
|
|||
}
|
||||
|
||||
static std::vector<std::string> s_Preset_print_options {
|
||||
"layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "slicing_mode",
|
||||
"layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "spiral_starting_flow_ratio", "spiral_finishing_flow_ratio", "slicing_mode",
|
||||
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
|
||||
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction",
|
||||
"seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
|
||||
"seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density", "sparse_infill_pattern", "lattice_angle_1", "lattice_angle_2", "top_surface_pattern", "bottom_surface_pattern",
|
||||
"infill_direction", "solid_infill_direction", "rotate_solid_infill_direction", "counterbore_hole_bridging",
|
||||
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target",
|
||||
"ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_inset",
|
||||
|
@ -801,7 +803,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
"independent_support_layer_height",
|
||||
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
|
||||
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
|
||||
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
|
||||
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges","enable_extra_bridge_layer", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
|
||||
"filename_format", "wall_filament", "support_bottom_z_distance",
|
||||
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
|
||||
"ooze_prevention", "standby_temperature_delta", "preheat_time","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width",
|
||||
|
@ -820,11 +822,11 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
"timelapse_type",
|
||||
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
||||
"wall_distribution_count", "min_feature_size", "min_bead_width", "post_process", "min_length_factor",
|
||||
"small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface",
|
||||
"small_perimeter_speed", "small_perimeter_threshold","bridge_angle","internal_bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface",
|
||||
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk",
|
||||
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap",
|
||||
"role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", "wipe_before_external_loop",
|
||||
"bridge_density", "precise_outer_wall", "overhang_speed_classic", "bridge_acceleration",
|
||||
"bridge_density","internal_bridge_density", "precise_outer_wall", "overhang_speed_classic", "bridge_acceleration",
|
||||
"sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_adaptive_layer_height", "tree_support_auto_brim",
|
||||
"tree_support_brim_width", "gcode_comments", "gcode_label_objects",
|
||||
"initial_layer_travel_speed", "exclude_object", "slow_down_layers", "infill_anchor", "infill_anchor_max","initial_layer_min_bead_width",
|
||||
|
@ -860,7 +862,7 @@ static std::vector<std::string> s_Preset_filament_options {
|
|||
"filament_wipe_distance", "additional_cooling_fan_speed",
|
||||
"nozzle_temperature_range_low", "nozzle_temperature_range_high",
|
||||
//SoftFever
|
||||
"enable_pressure_advance", "pressure_advance","adaptive_pressure_advance","adaptive_pressure_advance_model","adaptive_pressure_advance_overhangs", "adaptive_pressure_advance_bridges","chamber_temperature", "filament_shrink","filament_shrinkage_compensation_z", "support_material_interface_fan_speed", "filament_notes" /*,"filament_seam_gap"*/,
|
||||
"enable_pressure_advance", "pressure_advance","adaptive_pressure_advance","adaptive_pressure_advance_model","adaptive_pressure_advance_overhangs", "adaptive_pressure_advance_bridges","chamber_temperature", "filament_shrink","filament_shrinkage_compensation_z", "support_material_interface_fan_speed","internal_bridge_fan_speed", "filament_notes" /*,"filament_seam_gap"*/,
|
||||
"filament_loading_speed", "filament_loading_speed_start",
|
||||
"filament_unloading_speed", "filament_unloading_speed_start", "filament_toolchange_delay", "filament_cooling_moves", "filament_stamping_loading_speed", "filament_stamping_distance",
|
||||
"filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters",
|
||||
|
@ -2459,7 +2461,7 @@ const Preset *PresetCollection::get_preset_base(const Preset &child) const
|
|||
// Handle user preset
|
||||
if (child.inherits().empty())
|
||||
return &child; // this is user root
|
||||
auto inherits = find_preset(child.inherits());
|
||||
auto inherits = find_preset2(child.inherits(),true);
|
||||
return inherits ? get_preset_base(*inherits) : nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -610,7 +610,10 @@ public:
|
|||
// Orca: find preset, if not found, keep searching in the renamed history. This is function should only be used when find
|
||||
// system(parent) presets for custom preset.
|
||||
Preset* find_preset2(const std::string& name, bool auto_match = true);
|
||||
|
||||
const Preset* find_preset2(const std::string& name, bool auto_match = true) const
|
||||
{
|
||||
return const_cast<PresetCollection*>(this)->find_preset2(name, auto_match);
|
||||
}
|
||||
size_t first_visible_idx() const;
|
||||
// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
|
||||
// If one of the prefered_alternates is compatible, select it.
|
||||
|
|
|
@ -193,6 +193,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
"gcode_label_objects",
|
||||
"exclude_object",
|
||||
"support_material_interface_fan_speed",
|
||||
"internal_bridge_fan_speed", // ORCA: Add support for separate internal bridge fan speed control
|
||||
"single_extruder_multi_material_priming",
|
||||
"activate_air_filtration",
|
||||
"during_print_exhaust_fan_speed",
|
||||
|
@ -2595,7 +2596,7 @@ const WipeTowerData &Print::wipe_tower_data(size_t filaments_cnt) const
|
|||
if (!is_step_done(psWipeTower) && filaments_cnt != 0) {
|
||||
double width = m_config.prime_tower_width;
|
||||
double layer_height = 0.2; // hard code layer height
|
||||
if (m_config.purge_in_prime_tower) {
|
||||
if (m_config.purge_in_prime_tower && m_config.single_extruder_multi_material) {
|
||||
// Calculating depth should take into account currently set wiping volumes.
|
||||
// For a long time, the initial preview would just use 900/width per toolchange (15mm on a 60mm wide tower)
|
||||
// and it worked well enough. Let's try to do slightly better by accounting for the purging volumes.
|
||||
|
@ -2644,7 +2645,7 @@ void Print::_make_wipe_tower()
|
|||
|
||||
const auto bUseWipeTower2 = is_BBL_printer() ? false : true;
|
||||
// Orca: itertate over wipe_volumes and change the non-zero values to the prime_volume
|
||||
if (!m_config.purge_in_prime_tower && !is_BBL_printer()) {
|
||||
if ((!m_config.purge_in_prime_tower || !m_config.single_extruder_multi_material) && !is_BBL_printer()) {
|
||||
for (unsigned int i = 0; i < number_of_extruders; ++i) {
|
||||
for (unsigned int j = 0; j < number_of_extruders; ++j) {
|
||||
if (wipe_volumes[i][j] > 0) {
|
||||
|
@ -2823,7 +2824,7 @@ void Print::_make_wipe_tower()
|
|||
if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id !=
|
||||
current_extruder_id) {
|
||||
float volume_to_wipe = m_config.prime_volume;
|
||||
if (m_config.purge_in_prime_tower) {
|
||||
if (m_config.purge_in_prime_tower && m_config.single_extruder_multi_material) {
|
||||
volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
||||
volume_to_wipe *= m_config.flush_multiplier;
|
||||
// Not all of that can be used for infill purging:
|
||||
|
|
|
@ -1256,6 +1256,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
|
||||
bool layer_height_ranges_differ = ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty());
|
||||
bool model_origin_translation_differ = model_object.origin_translation != model_object_new.origin_translation;
|
||||
bool brim_points_differ = model_brim_points_data_changed(model_object, model_object_new);
|
||||
auto print_objects_range = print_object_status_db.get_range(model_object);
|
||||
// The list actually can be empty if all instances are out of the print bed.
|
||||
//assert(print_objects_range.begin() != print_objects_range.end());
|
||||
|
@ -1302,6 +1303,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
} else if (model_custom_seam_data_changed(model_object, model_object_new)) {
|
||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||
}
|
||||
if (brim_points_differ) {
|
||||
model_object.brim_points = model_object_new.brim_points;
|
||||
update_apply_status(this->invalidate_all_steps());
|
||||
}
|
||||
}
|
||||
if (! solid_or_modifier_differ) {
|
||||
// Synchronize Object's config.
|
||||
|
|
|
@ -88,6 +88,7 @@ static t_config_enum_values s_keys_map_PrintHostType {
|
|||
{ "obico", htObico },
|
||||
{ "flashforge", htFlashforge },
|
||||
{ "simplyprint", htSimplyPrint },
|
||||
{ "elegoolink", htElegooLink }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintHostType)
|
||||
|
||||
|
@ -136,6 +137,7 @@ static t_config_enum_values s_keys_map_InfillPattern {
|
|||
{ "concentric", ipConcentric },
|
||||
{ "zig-zag", ipRectilinear },
|
||||
{ "grid", ipGrid },
|
||||
{ "2dlattice", ip2DLattice },
|
||||
{ "line", ipLine },
|
||||
{ "cubic", ipCubic },
|
||||
{ "triangles", ipTriangles },
|
||||
|
@ -284,6 +286,14 @@ static t_config_enum_values s_keys_map_InternalBridgeFilter {
|
|||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InternalBridgeFilter)
|
||||
|
||||
static t_config_enum_values s_keys_map_EnableExtraBridgeLayer {
|
||||
{ "disabled", eblDisabled },
|
||||
{ "external_bridge_only", eblExternalBridgeOnly },
|
||||
{ "internal_bridge_only", eblInternalBridgeOnly },
|
||||
{ "apply_to_all", eblApplyToAll },
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(EnableExtraBridgeLayer)
|
||||
|
||||
// Orca
|
||||
static t_config_enum_values s_keys_map_GapFillTarget {
|
||||
{ "everywhere", gftEverywhere },
|
||||
|
@ -318,6 +328,7 @@ static const t_config_enum_values s_keys_map_BrimType = {
|
|||
{"outer_and_inner", btOuterAndInner},
|
||||
{"auto_brim", btAutoBrim}, // BBS
|
||||
{"brim_ears", btEar}, // Orca
|
||||
{"painted", btPainted}, // BBS
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BrimType)
|
||||
|
||||
|
@ -793,18 +804,18 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comSimple;
|
||||
def->enum_keys_map = &s_keys_map_BedType;
|
||||
// Orca: make sure the order of the values is the same as the BedType enum
|
||||
def->enum_values.emplace_back("Supertack Plate");
|
||||
def->enum_values.emplace_back("Cool Plate");
|
||||
def->enum_values.emplace_back("Engineering Plate");
|
||||
def->enum_values.emplace_back("High Temp Plate");
|
||||
def->enum_values.emplace_back("Textured PEI Plate");
|
||||
def->enum_values.emplace_back("Textured Cool Plate");
|
||||
def->enum_labels.emplace_back(L("Cool Plate (SuperTack)"));
|
||||
def->enum_values.emplace_back("Supertack Plate");
|
||||
def->enum_labels.emplace_back(L("Smooth Cool Plate"));
|
||||
def->enum_labels.emplace_back(L("Engineering Plate"));
|
||||
def->enum_labels.emplace_back(L("Smooth High Temp Plate"));
|
||||
def->enum_labels.emplace_back(L("Textured PEI Plate"));
|
||||
def->enum_labels.emplace_back(L("Textured Cool Plate"));
|
||||
def->enum_labels.emplace_back(L("Cool Plate (SuperTack)"));
|
||||
def->set_default_value(new ConfigOptionEnum<BedType>(btPC));
|
||||
|
||||
// BBS
|
||||
|
@ -908,15 +919,19 @@ void PrintConfigDef::init_fff_params()
|
|||
|
||||
|
||||
def = this->add("enable_overhang_bridge_fan", coBools);
|
||||
def->label = L("Force cooling for overhang and bridge");
|
||||
def->tooltip = L("Enable this option to optimize part cooling fan speed for overhang and bridge to get better cooling");
|
||||
def->label = L("Force cooling for overhangs and bridges");
|
||||
def->tooltip = L("Enable this option to allow adjustment of the part cooling fan speed for specifically for overhangs, internal and external "
|
||||
"bridges. Setting the fan speed specifically for these features can improve overall print quality and reduce warping.");
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionBools{ true });
|
||||
|
||||
def = this->add("overhang_fan_speed", coInts);
|
||||
def->label = L("Fan speed for overhang");
|
||||
def->tooltip = L("Force part cooling fan to be this speed when printing bridge or overhang wall which has large overhang degree. "
|
||||
"Forcing cooling for overhang and bridge can get better quality for these part");
|
||||
def->label = L("Overhangs and external bridges fan speed");
|
||||
def->tooltip = L("Use this part cooling fan speed when printing bridges or overhang walls with an overhang threshold that exceeds "
|
||||
"the value set in the 'Overhangs cooling threshold' parameter above. Increasing the cooling specifically for overhangs "
|
||||
"and bridges can improve the overall print quality of these features.\n\n"
|
||||
"Please note, this fan speed is clamped on the lower end by the minimum fan speed threshold set above. It is also adjusted "
|
||||
"upwards up to the maximum fan speed threshold when the minimum layer time threshold is not met.");
|
||||
def->sidetext = L("%");
|
||||
def->min = 0;
|
||||
def->max = 100;
|
||||
|
@ -924,10 +939,11 @@ void PrintConfigDef::init_fff_params()
|
|||
def->set_default_value(new ConfigOptionInts { 100 });
|
||||
|
||||
def = this->add("overhang_fan_threshold", coEnums);
|
||||
def->label = L("Cooling overhang threshold");
|
||||
def->tooltip = L("Force cooling fan to be specific speed when overhang degree of printed part exceeds this value. "
|
||||
"Expressed as percentage which indicates how much width of the line without support from lower layer. "
|
||||
"0% means forcing cooling for all outer wall no matter how much overhang degree");
|
||||
def->label = L("Overhang cooling activation threshold");
|
||||
// xgettext:no-c-format, no-boost-format
|
||||
def->tooltip = L("When the overhang exceeds this specified threshold, force the cooling fan to run at the 'Overhang Fan Speed' set below. "
|
||||
"This threshold is expressed as a percentage, indicating the portion of each line's width that is unsupported by the layer "
|
||||
"beneath it. Setting this value to 0% forces the cooling fan to run for all outer walls, regardless of the overhang degree.");
|
||||
def->sidetext = "";
|
||||
def->enum_keys_map = &ConfigOptionEnum<OverhangFanThreshold>::get_enum_values();
|
||||
def->mode = comAdvanced;
|
||||
|
@ -946,8 +962,9 @@ void PrintConfigDef::init_fff_params()
|
|||
def->set_default_value(new ConfigOptionEnumsGeneric{ (int)Overhang_threshold_bridge });
|
||||
|
||||
def = this->add("bridge_angle", coFloat);
|
||||
def->label = L("Bridge infill direction");
|
||||
def->label = L("External bridge infill direction");
|
||||
def->category = L("Strength");
|
||||
// xgettext:no-c-format, no-boost-format
|
||||
def->tooltip = L("Bridging angle override. If left to zero, the bridging angle will be calculated "
|
||||
"automatically. Otherwise the provided angle will be used for external bridges. "
|
||||
"Use 180°for zero angle.");
|
||||
|
@ -955,11 +972,39 @@ void PrintConfigDef::init_fff_params()
|
|||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.));
|
||||
|
||||
// ORCA: Internal bridge angle override
|
||||
def = this->add("internal_bridge_angle", coFloat);
|
||||
def->label = L("Internal bridge infill direction");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Internal bridging angle override. If left to zero, the bridging angle will be calculated "
|
||||
"automatically. Otherwise the provided angle will be used for internal bridges. "
|
||||
"Use 180°for zero angle.\n\nIt is recommended to leave it at 0 unless there is a specific model need not to.");
|
||||
def->sidetext = L("°");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.));
|
||||
|
||||
def = this->add("bridge_density", coPercent);
|
||||
def->label = L("Bridge density");
|
||||
def->label = L("External bridge density");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Density of external bridges. 100% means solid bridge. Default is 100%.");
|
||||
def->tooltip = L("Controls the density (spacing) of external bridge lines. 100% means solid bridge. Default is 100%.\n\n"
|
||||
"Lower density external bridges can help improve reliability as there is more space for air to circulate "
|
||||
"around the extruded bridge, improving its cooling speed.");
|
||||
def->sidetext = L("%");
|
||||
def->min = 10;
|
||||
def->max = 100;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionPercent(100));
|
||||
|
||||
def = this->add("internal_bridge_density", coPercent);
|
||||
def->label = L("Internal bridge density");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Controls the density (spacing) of internal bridge lines. 100% means solid bridge. Default is 100%.\n\n "
|
||||
"Lower density internal bridges can help reduce top surface pillowing and improve internal bridge reliability as there is more space for "
|
||||
"air to circulate around the extruded bridge, improving its cooling speed. \n\n"
|
||||
"This option works particularly well when combined with the second internal bridge over infill option, "
|
||||
"further improving internal bridging structure before solid infill is extruded.");
|
||||
def->sidetext = L("%");
|
||||
def->min = 10;
|
||||
def->max = 100;
|
||||
|
@ -1216,12 +1261,14 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_keys_map = &ConfigOptionEnum<BrimType>::get_enum_values();
|
||||
def->enum_values.emplace_back("auto_brim");
|
||||
def->enum_values.emplace_back("brim_ears");
|
||||
def->enum_values.emplace_back("painted");
|
||||
def->enum_values.emplace_back("outer_only");
|
||||
def->enum_values.emplace_back("inner_only");
|
||||
def->enum_values.emplace_back("outer_and_inner");
|
||||
def->enum_values.emplace_back("no_brim");
|
||||
def->enum_labels.emplace_back(L("Auto"));
|
||||
def->enum_labels.emplace_back(L("Mouse ear"));
|
||||
def->enum_labels.emplace_back(L("Painted"));
|
||||
def->enum_labels.emplace_back(L("Outer brim only"));
|
||||
def->enum_labels.emplace_back(L("Inner brim only"));
|
||||
def->enum_labels.emplace_back(L("Outer and inner brim"));
|
||||
|
@ -1415,7 +1462,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("thick_bridges", coBool);
|
||||
def->label = L("Thick bridges");
|
||||
def->label = L("Thick external bridges");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("If enabled, bridges are more reliable, can bridge longer distances, but may look worse. "
|
||||
"If disabled, bridges look better but are reliable just for shorter bridged distances.");
|
||||
|
@ -1429,23 +1476,53 @@ void PrintConfigDef::init_fff_params()
|
|||
"consider turning it off if you are using large nozzles.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("enable_extra_bridge_layer", coEnum);
|
||||
def->label = L("Extra bridge layers (beta)");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("This option enables the generation of an extra bridge layer over internal and/or external bridges.\n\n"
|
||||
"Extra bridge layers help improve bridge appearance and reliability, as the solid infill is better supported. "
|
||||
"This is especially useful in fast printers, where the bridge and solid infill speeds vary greatly. "
|
||||
"The extra bridge layer results in reduced pillowing on top surfaces, as well as reduced separation of the external bridge layer from its surrounding perimeters.\n\n"
|
||||
"It is generally recommended to set this to at least 'External bridge only', unless specific issues with the sliced model are found.\n\n"
|
||||
"Options:\n"
|
||||
"1. Disabled - does not generate second bridge layers. This is the default and is set for compatibility purposes.\n"
|
||||
"2. External bridge only - generates second bridge layers for external-facing bridges only. Please note that small bridges that are shorter "
|
||||
"or narrower than the set number of perimeters will be skipped as they would not benefit from a second bridge layer. If generated, the second bridge layer will be extruded "
|
||||
"parallel to the first bridge layer to reinforce the bridge strength.\n"
|
||||
"3. Internal bridge only - generates second bridge layers for internal bridges over sparse infill only. Please note that the internal "
|
||||
"bridges count towards the top shell layer count of your model. The second internal bridge layer will be extruded as close to perpendicular to the first as possible. If multiple regions "
|
||||
"in the same island, with varying bridge angles are present, the last region of that island will be selected as the angle reference.\n"
|
||||
"4. Apply to all - generates second bridge layers for both internal and external-facing bridges\n");
|
||||
|
||||
def->enum_keys_map = &ConfigOptionEnum<EnableExtraBridgeLayer>::get_enum_values();
|
||||
def->enum_values.push_back("disabled");
|
||||
def->enum_values.push_back("external_bridge_only");
|
||||
def->enum_values.push_back("internal_bridge_only");
|
||||
def->enum_values.push_back("apply_to_all");
|
||||
def->enum_labels.push_back(L("Disabled"));
|
||||
def->enum_labels.push_back(L("External bridge only"));
|
||||
def->enum_labels.push_back(L("Internal bridge only"));
|
||||
def->enum_labels.push_back(L("Apply to all"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<EnableExtraBridgeLayer>(eblDisabled));
|
||||
|
||||
def = this->add("dont_filter_internal_bridges", coEnum);
|
||||
def->label = L("Filter out small internal bridges (beta)");
|
||||
def->label = L("Filter out small internal bridges");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("This option can help reducing pillowing on top surfaces in heavily slanted or curved models.\n\n"
|
||||
"By default, small internal bridges are filtered out and the internal solid infill is printed directly"
|
||||
" over the sparse infill. This works well in most cases, speeding up printing without too much compromise"
|
||||
" on top surface quality. \n\nHowever, in heavily slanted or curved models especially where too low sparse"
|
||||
" infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.\n\n"
|
||||
"Disabling this option will print internal bridge layer over slightly unsupported internal"
|
||||
" solid infill. The options below control the amount of filtering, i.e. the amount of internal bridges "
|
||||
def->tooltip = L("This option can help reduce pillowing on top surfaces in heavily slanted or curved models.\n\n"
|
||||
"By default, small internal bridges are filtered out and the internal solid infill is printed directly "
|
||||
"over the sparse infill. This works well in most cases, speeding up printing without too much compromise "
|
||||
"on top surface quality. \n\nHowever, in heavily slanted or curved models, especially where too low a sparse "
|
||||
"infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.\n\n"
|
||||
"Enabling limited filtering or no filtering will print internal bridge layer over slightly unsupported internal "
|
||||
"solid infill. The options below control the sensitivity of the filtering, i.e. they control where internal bridges are "
|
||||
"created.\n\n"
|
||||
"Filter - enable this option. This is the default behavior and works well in most cases.\n\n"
|
||||
"Limited filtering - creates internal bridges on heavily slanted surfaces, while avoiding creating "
|
||||
"unnecessary internal bridges. This works well for most difficult models.\n\n"
|
||||
"No filtering - creates internal bridges on every potential internal overhang. This option is useful "
|
||||
"for heavily slanted top surface models. However, in most cases it creates too many unnecessary bridges.");
|
||||
"1. Filter - enables this option. This is the default behavior and works well in most cases.\n\n"
|
||||
"2. Limited filtering - creates internal bridges on heavily slanted surfaces while avoiding unnecessary bridges. "
|
||||
"This works well for most difficult models.\n\n"
|
||||
"3. No filtering - creates internal bridges on every potential internal overhang. This option is useful for "
|
||||
"heavily slanted top surface models; however, in most cases, it creates too many unnecessary bridges.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<InternalBridgeFilter>::get_enum_values();
|
||||
def->enum_values.push_back("disabled");
|
||||
def->enum_values.push_back("limited");
|
||||
|
@ -2266,6 +2343,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("concentric");
|
||||
def->enum_values.push_back("zig-zag");
|
||||
def->enum_values.push_back("grid");
|
||||
def->enum_values.push_back("2dlattice");
|
||||
def->enum_values.push_back("line");
|
||||
def->enum_values.push_back("cubic");
|
||||
def->enum_values.push_back("triangles");
|
||||
|
@ -2285,6 +2363,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Concentric"));
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Grid"));
|
||||
def->enum_labels.push_back(L("2D Lattice"));
|
||||
def->enum_labels.push_back(L("Line"));
|
||||
def->enum_labels.push_back(L("Cubic"));
|
||||
def->enum_labels.push_back(L("Triangles"));
|
||||
|
@ -2303,6 +2382,26 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Quarter Cubic"));
|
||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipCrossHatch));
|
||||
|
||||
def = this->add("lattice_angle_1", coFloat);
|
||||
def->label = L("Lattice angle 1");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("The angle of the first set of 2D lattice elements in the Z direction. Zero is vertical.");
|
||||
def->sidetext = L("°");
|
||||
def->min = -75;
|
||||
def->max = 75;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(-45));
|
||||
|
||||
def = this->add("lattice_angle_2", coFloat);
|
||||
def->label = L("Lattice angle 2");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("The angle of the second set of 2D lattice elements in the Z direction. Zero is vertical.");
|
||||
def->sidetext = L("°");
|
||||
def->min = -75;
|
||||
def->max = 75;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(45));
|
||||
|
||||
auto def_infill_anchor_min = def = this->add("infill_anchor", coFloatOrPercent);
|
||||
def->label = L("Sparse infill anchor length");
|
||||
def->category = L("Strength");
|
||||
|
@ -2470,7 +2569,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->label = L("Top surface");
|
||||
def->tooltip = L("Jerk for top surface");
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 1;
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(9));
|
||||
|
||||
|
@ -2478,7 +2577,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->label = L("Infill");
|
||||
def->tooltip = L("Jerk for infill");
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 1;
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(9));
|
||||
|
||||
|
@ -2486,7 +2585,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->label = L("Initial layer");
|
||||
def->tooltip = L("Jerk for initial layer");
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 1;
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(9));
|
||||
|
||||
|
@ -2494,7 +2593,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->label = L("Travel");
|
||||
def->tooltip = L("Jerk for travel");
|
||||
def->sidetext = L("mm/s");
|
||||
def->min = 1;
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(12));
|
||||
|
||||
|
@ -2586,15 +2685,27 @@ void PrintConfigDef::init_fff_params()
|
|||
|
||||
def = this->add("support_material_interface_fan_speed", coInts);
|
||||
def->label = L("Support interface fan speed");
|
||||
def->tooltip = L("This fan speed is enforced during all support interfaces, to be able to weaken their bonding with a high fan speed."
|
||||
"\nSet to -1 to disable this override."
|
||||
"\nCan only be overridden by disable_fan_first_layers.");
|
||||
def->tooltip = L("This part cooling fan speed is applied when printing support interfaces. Setting this parameter to a higher than regular speed "
|
||||
" reduces the layer binding strength between supports and the supported part, making them easier to separate."
|
||||
"\nSet to -1 to disable it."
|
||||
"\nThis setting is overridden by disable_fan_first_layers.");
|
||||
def->sidetext = L("%");
|
||||
def->min = -1;
|
||||
def->max = 100;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInts{ -1 });
|
||||
|
||||
// ORCA: Add support for separate internal bridge fan speed control
|
||||
def = this->add("internal_bridge_fan_speed", coInts);
|
||||
def->label = L("Internal bridges fan speed");
|
||||
def->tooltip = L("The part cooling fan speed used for all internal bridges. Set to -1 to use the overhang fan speed settings instead.\n\n"
|
||||
"Reducing the internal bridges fan speed, compared to your regular fan speed, can help reduce part warping due to excessive "
|
||||
"cooling applied over a large surface for a prolonged period of time.");
|
||||
def->sidetext = L("%");
|
||||
def->min = -1;
|
||||
def->max = 100;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInts{ -1 });
|
||||
|
||||
def = this->add("fuzzy_skin", coEnum);
|
||||
def->label = L("Fuzzy Skin");
|
||||
|
@ -2621,7 +2732,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->min = 0;
|
||||
def->max = 1;
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionFloat(0.3));
|
||||
def->set_default_value(new ConfigOptionFloat(0.2));
|
||||
|
||||
def = this->add("fuzzy_skin_point_distance", coFloat);
|
||||
def->label = L("Fuzzy skin point distance");
|
||||
|
@ -2631,7 +2742,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->min = 0;
|
||||
def->max = 5;
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionFloat(0.8));
|
||||
def->set_default_value(new ConfigOptionFloat(0.3));
|
||||
|
||||
def = this->add("fuzzy_skin_first_layer", coBool);
|
||||
def->label = L("Apply fuzzy skin to first layer");
|
||||
|
@ -3510,6 +3621,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("obico");
|
||||
def->enum_values.push_back("flashforge");
|
||||
def->enum_values.push_back("simplyprint");
|
||||
def->enum_values.push_back("elegoolink");
|
||||
def->enum_labels.push_back("PrusaLink");
|
||||
def->enum_labels.push_back("PrusaConnect");
|
||||
def->enum_labels.push_back("Octo/Klipper");
|
||||
|
@ -3523,6 +3635,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back("Obico");
|
||||
def->enum_labels.push_back("Flashforge");
|
||||
def->enum_labels.push_back("SimplyPrint");
|
||||
def->enum_labels.push_back("Elegoo Link");
|
||||
def->mode = comAdvanced;
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htOctoPrint));
|
||||
|
@ -3835,7 +3948,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->set_default_value(new ConfigOptionInt {0});
|
||||
|
||||
def = this->add("long_retractions_when_cut", coBools);
|
||||
def->label = L("Long retraction when cut(experimental)");
|
||||
def->label = L("Long retraction when cut(beta)");
|
||||
def->tooltip = L("Experimental feature.Retracting and cutting off the filament at a longer distance during changes to minimize purge."
|
||||
"While this reduces flush significantly, it may also raise the risk of nozzle clogs or other printing problems.");
|
||||
def->mode = comDevelop;
|
||||
|
@ -4316,6 +4429,26 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(200, true));
|
||||
|
||||
def = this->add("spiral_starting_flow_ratio", coFloat);
|
||||
def->label = "Spiral starting flow ratio";
|
||||
def->tooltip = L("Sets the starting flow ratio while transitioning from the last bottom layer to the spiral. "
|
||||
"Normally the spiral transition scales the flow ratio from 0% to 100% during the first loop "
|
||||
"which can in some cases lead to under extrusion at the start of the spiral.");
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
def->mode = comAdvanced;
|
||||
|
||||
def = this->add("spiral_finishing_flow_ratio", coFloat);
|
||||
def->label = "Spiral finishing flow ratio";
|
||||
def->tooltip = L("Sets the finishing flow ratio while ending the spiral. "
|
||||
"Normally the spiral transition scales the flow ratio from 100% to 0% during the last loop "
|
||||
"which can in some cases lead to under extrusion at the end of the spiral.");
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
def->mode = comAdvanced;
|
||||
|
||||
def = this->add("timelapse_type", coEnum);
|
||||
def->label = L("Timelapse");
|
||||
def->tooltip = L("If smooth or traditional mode is selected, a timelapse video will be generated for each print. "
|
||||
|
@ -6380,7 +6513,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
"retraction_distance_when_cut",
|
||||
"extruder_type",
|
||||
"internal_bridge_support_thickness","extruder_clearance_max_radius", "top_area_threshold", "reduce_wall_solid_infill","filament_load_time","filament_unload_time",
|
||||
"smooth_coefficient", "overhang_totally_speed"
|
||||
"smooth_coefficient", "overhang_totally_speed", "silent_mode"
|
||||
};
|
||||
|
||||
if (ignore.find(opt_key) != ignore.end()) {
|
||||
|
|
|
@ -50,7 +50,7 @@ enum class NoiseType {
|
|||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htCrealityPrint, htObico, htFlashforge, htSimplyPrint
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htCrealityPrint, htObico, htFlashforge, htSimplyPrint, htElegooLink
|
||||
};
|
||||
|
||||
enum AuthorizationType {
|
||||
|
@ -58,7 +58,7 @@ enum AuthorizationType {
|
|||
};
|
||||
|
||||
enum InfillPattern : int {
|
||||
ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
|
||||
ipConcentric, ipRectilinear, ipGrid, ip2DLattice, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
|
||||
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
|
||||
ipLightning, ipCrossHatch, ipQuarterCubic,
|
||||
ipCount,
|
||||
|
@ -187,6 +187,11 @@ enum InternalBridgeFilter {
|
|||
ibfDisabled, ibfLimited, ibfNofilter
|
||||
};
|
||||
|
||||
//Orca
|
||||
enum EnableExtraBridgeLayer {
|
||||
eblDisabled, eblExternalBridgeOnly, eblInternalBridgeOnly, eblApplyToAll
|
||||
};
|
||||
|
||||
//Orca
|
||||
enum GapFillTarget {
|
||||
gftEverywhere, gftTopBottom, gftNowhere
|
||||
|
@ -221,6 +226,7 @@ enum SLAPillarConnectionMode {
|
|||
enum BrimType {
|
||||
btAutoBrim, // BBS
|
||||
btEar, // Orca
|
||||
btPainted, // BBS
|
||||
btOuterOnly,
|
||||
btInnerOnly,
|
||||
btOuterAndInner,
|
||||
|
@ -262,12 +268,12 @@ enum OverhangFanThreshold {
|
|||
// BBS
|
||||
enum BedType {
|
||||
btDefault = 0,
|
||||
btSuperTack,
|
||||
btPC,
|
||||
btEP,
|
||||
btPEI,
|
||||
btPTE,
|
||||
btPCT,
|
||||
btSuperTack,
|
||||
btCount
|
||||
};
|
||||
|
||||
|
@ -827,6 +833,9 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionBool, thick_bridges))
|
||||
((ConfigOptionBool, thick_internal_bridges))
|
||||
((ConfigOptionEnum<InternalBridgeFilter>, dont_filter_internal_bridges))
|
||||
// Orca
|
||||
((ConfigOptionEnum<EnableExtraBridgeLayer>, enable_extra_bridge_layer))
|
||||
((ConfigOptionPercent, internal_bridge_density))
|
||||
// Overhang angle threshold.
|
||||
((ConfigOptionInt, support_threshold_angle))
|
||||
((ConfigOptionFloatOrPercent, support_threshold_overlap))
|
||||
|
@ -907,6 +916,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionInt, bottom_shell_layers))
|
||||
((ConfigOptionFloat, bottom_shell_thickness))
|
||||
((ConfigOptionFloat, bridge_angle))
|
||||
((ConfigOptionFloat, internal_bridge_angle)) // ORCA: Internal bridge angle override
|
||||
((ConfigOptionFloat, bridge_flow))
|
||||
((ConfigOptionFloat, internal_bridge_flow))
|
||||
((ConfigOptionFloat, bridge_speed))
|
||||
|
@ -922,6 +932,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionBool, rotate_solid_infill_direction))
|
||||
((ConfigOptionPercent, sparse_infill_density))
|
||||
((ConfigOptionEnum<InfillPattern>, sparse_infill_pattern))
|
||||
((ConfigOptionFloat, lattice_angle_1))
|
||||
((ConfigOptionFloat, lattice_angle_2))
|
||||
((ConfigOptionEnum<FuzzySkinType>, fuzzy_skin))
|
||||
((ConfigOptionFloat, fuzzy_skin_thickness))
|
||||
((ConfigOptionFloat, fuzzy_skin_point_distance))
|
||||
|
@ -1279,6 +1291,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||
((ConfigOptionBool, spiral_mode))
|
||||
((ConfigOptionBool, spiral_mode_smooth))
|
||||
((ConfigOptionFloatOrPercent, spiral_mode_max_xy_smoothing))
|
||||
((ConfigOptionFloat, spiral_finishing_flow_ratio))
|
||||
((ConfigOptionFloat, spiral_starting_flow_ratio))
|
||||
((ConfigOptionInt, standby_temperature_delta))
|
||||
((ConfigOptionFloat, preheat_time))
|
||||
((ConfigOptionInt, preheat_steps))
|
||||
|
@ -1331,6 +1345,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||
((ConfigOptionBool, gcode_comments))
|
||||
((ConfigOptionInt, slow_down_layers))
|
||||
((ConfigOptionInts, support_material_interface_fan_speed))
|
||||
((ConfigOptionInts, internal_bridge_fan_speed)) // ORCA: Add support for separate internal bridge fan speed control
|
||||
// Orca: notes for profiles from PrusaSlicer
|
||||
((ConfigOptionStrings, filament_notes))
|
||||
((ConfigOptionString, notes))
|
||||
|
|
|
@ -1077,8 +1077,10 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
|| opt_key == "rotate_solid_infill_direction"
|
||||
|| opt_key == "ensure_vertical_shell_thickness"
|
||||
|| opt_key == "bridge_angle"
|
||||
|| opt_key == "internal_bridge_angle" // ORCA: Internal bridge angle override
|
||||
//BBS
|
||||
|| opt_key == "bridge_density") {
|
||||
|| opt_key == "bridge_density"
|
||||
|| opt_key == "internal_bridge_density") {
|
||||
steps.emplace_back(posPrepareInfill);
|
||||
} else if (
|
||||
opt_key == "top_surface_pattern"
|
||||
|
@ -1089,7 +1091,9 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
|| opt_key == "infill_anchor_max"
|
||||
|| opt_key == "top_surface_line_width"
|
||||
|| opt_key == "initial_layer_line_width"
|
||||
|| opt_key == "small_area_infill_flow_compensation") {
|
||||
|| opt_key == "small_area_infill_flow_compensation"
|
||||
|| opt_key == "lattice_angle_1"
|
||||
|| opt_key == "lattice_angle_2") {
|
||||
steps.emplace_back(posInfill);
|
||||
} else if (opt_key == "sparse_infill_pattern") {
|
||||
steps.emplace_back(posPrepareInfill);
|
||||
|
@ -1442,7 +1446,111 @@ void PrintObject::detect_surfaces_type()
|
|||
for (size_t i = num_layers; i < m_layers.size(); ++ i)
|
||||
m_layers[i]->m_regions[region_id]->slices.set_type(stInternal);
|
||||
}
|
||||
|
||||
|
||||
// ==================================================================================================
|
||||
// === ORCA: Create a SECOND bridge layer above the first bridge layer. =============================
|
||||
// === ORCA: Surface is flagged as a new surface type called stInternalAfterExternalBridge ==================
|
||||
// === Algorithm only considers stInternal surfaces for re-classification, leaving stTop unaffected =
|
||||
// ==================================================================================================
|
||||
// Only iterate to the second-to-last layer, since we look at layer i+1.
|
||||
if( (this->config().enable_extra_bridge_layer.value == eblApplyToAll) || (this->config().enable_extra_bridge_layer.value == eblExternalBridgeOnly)){
|
||||
const size_t last = (m_layers.empty() ? 0 : m_layers.size() - 1);
|
||||
tbb::parallel_for( tbb::blocked_range<size_t>(0, last), [this, region_id](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t i = range.begin(); i < range.end(); ++i) {
|
||||
m_print->throw_if_canceled();
|
||||
|
||||
// Step 1: Find bridge polygons
|
||||
// Current layer (i): Search for stBottomBridge polygons.
|
||||
const Surfaces &bot_surfs = m_layers[i]->m_regions[region_id]->slices.surfaces;
|
||||
// Next layer (i+1): The layer where stInternal polygons may be re-classified.
|
||||
Surfaces &top_surfs = m_layers[i + 1]->m_regions[region_id]->slices.surfaces;
|
||||
|
||||
// Step 2: Collect the bridge polygons in the current layer region
|
||||
Polygons polygons_bridge;
|
||||
for (const Surface &sbot : bot_surfs) {
|
||||
if (sbot.surface_type == stBottomBridge) {
|
||||
polygons_append(polygons_bridge, to_polygons(sbot));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Early termination of loop if no meaningfull bridge found
|
||||
// No bridge polygons found, continue to the next layer
|
||||
if (polygons_bridge.empty())
|
||||
continue;
|
||||
|
||||
// Step 4: Bottom bridge polygons found - scan and create layer+1 bridge polygon
|
||||
Surfaces new_surfaces;
|
||||
new_surfaces.reserve(top_surfs.size());
|
||||
|
||||
//filtering parameters here. Filter bridges that are less than 2x external walls and 2xN internal perimeters wide.
|
||||
LayerRegion *layerm = m_layers[i]->m_regions[region_id];
|
||||
int number_of_internal_walls = std::max(0, layerm->m_region->config().wall_loops - 1); // number of internal walls, clamped to a minimum of 0 as a safety precaution
|
||||
float offset_distance = layerm->flow(frExternalPerimeter).scaled_width() // shrink down by external perimeter width (effectively filtering out 2x external perimeters wide bridges)
|
||||
+ ((layerm->flow(frPerimeter).scaled_width()) * number_of_internal_walls); // shrink down by number of external walls * width of them, effectively filtering out 2x internal perimeter wide bridges
|
||||
// The reason for doing the above filtering is that in pure bridges, the walls are always printed separately as overhang walls. Here we care about the bridge infill which is distinct and is the remainder
|
||||
// of the bridge area minus the perimeter width on both sides of the bridge itself.
|
||||
// This would also skip generation of very short dual bridge layers (that are shorter than N perimeters), but these are unecessary as the bridge distance is
|
||||
// We could reduce this slightly to account for innacurcies in the clipping operation.
|
||||
// TODO: Monitor GitHub issues to check whether second bridge layers are ommited where they should be generated. If yes, reduce the filtering distance
|
||||
|
||||
// For each surface in the layer above
|
||||
for (Surface &s_up : top_surfs) {
|
||||
// Only reclassify stInternal polygons (i.e. what will become later solid and sparse infill)
|
||||
// Leave the rest unaffected
|
||||
if (s_up.surface_type != stInternal) {
|
||||
new_surfaces.push_back(std::move(s_up)); // do not modify them
|
||||
continue; // continue to the next surface
|
||||
}
|
||||
// Identify stInternal polygons that overlap with the bridging polygons on the layer underneath.
|
||||
Polygons p_up = to_polygons(s_up);
|
||||
ExPolygons overlap = intersection_ex(p_up, polygons_bridge , ApplySafetyOffset::Yes);
|
||||
// Filter out the resulting candidate bridges based on size. First perform a shrink operation...
|
||||
// ...followed by an expand operation to bring them back to the original size (positive offset)
|
||||
overlap = offset_ex(shrink_ex(overlap, offset_distance), offset_distance);
|
||||
|
||||
// Now subtract the filtered new bridge layer from the remaining internal surfaces to create the new internal surface
|
||||
ExPolygons remainder = diff_ex(p_up, overlap, ApplySafetyOffset::Yes);
|
||||
|
||||
// Remainder stays as stInternal
|
||||
ExPolygons unified_remainder = union_safety_offset_ex(remainder);
|
||||
for (auto &ex_remainder : unified_remainder) {
|
||||
Surface s(stInternal, ex_remainder);
|
||||
new_surfaces.push_back(std::move(s));
|
||||
}
|
||||
// Overlap portion becomes the new polygon type - stInternalAfterExternalBridge
|
||||
ExPolygons unified_overlap = union_safety_offset_ex(overlap);
|
||||
for (auto &ex_overlap : unified_overlap) {
|
||||
Surface s(stInternalAfterExternalBridge, ex_overlap);
|
||||
new_surfaces.push_back(std::move(s));
|
||||
}
|
||||
}
|
||||
top_surfs = std::move(new_surfaces);
|
||||
}
|
||||
}
|
||||
);
|
||||
// ==============================================================================================================
|
||||
// === ORCA: Interim workaround - for now the new stInternalAfterExternalBridge surfaace is re-classified ==============
|
||||
// === back to a bottom bridge. As a starting point, this improves bridging reliability as it extrudes ==========
|
||||
// === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase ==========
|
||||
// ==============================================================================================================
|
||||
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
|
||||
tbb::parallel_for( tbb::blocked_range<size_t>(0, m_layers.size()), [this, region_id](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
|
||||
Surfaces &surfs = m_layers[idx_layer]->m_regions[region_id]->slices.surfaces;
|
||||
for (Surface &s : surfs) {
|
||||
if (s.surface_type == stInternalAfterExternalBridge) {
|
||||
s.surface_type = stBottomBridge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
// ==============================================================================================================
|
||||
// === ORCA: End of second external bridge layer changes =======================================================
|
||||
// ==============================================================================================================
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - start";
|
||||
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
|
||||
tbb::parallel_for(
|
||||
|
@ -2741,6 +2849,10 @@ void PrintObject::bridge_over_infill()
|
|||
// Also, use Infill pattern that is neutral for angle determination, since there are no infill lines.
|
||||
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine, 0);
|
||||
}
|
||||
|
||||
// ORCA: Internal bridge angle override
|
||||
if (candidate.region->region().config().internal_bridge_angle > 0)
|
||||
bridging_angle = candidate.region->region().config().internal_bridge_angle.value * PI / 180.0; // Convert degrees to radians
|
||||
|
||||
boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end());
|
||||
if (!lightning_area.empty() && !intersection(area_to_be_bridge, lightning_area).empty()) {
|
||||
|
@ -2849,7 +2961,7 @@ void PrintObject::bridge_over_infill()
|
|||
for (const ExPolygon &ep : new_internal_solids) {
|
||||
new_surfaces.emplace_back(stInternalSolid, ep);
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
debug_draw("Aensuring_" + std::to_string(reinterpret_cast<uint64_t>(®ion)), to_polylines(additional_ensuring),
|
||||
to_polylines(near_perimeters), to_polylines(to_polygons(internal_infills)),
|
||||
|
@ -2864,6 +2976,144 @@ void PrintObject::bridge_over_infill()
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ======================================================================================================================================
|
||||
// === ORCA: Create a second internal bridge layer above the first bridge layer. ========================================================
|
||||
// ======================================================================================================================================
|
||||
if ( this->m_config.enable_extra_bridge_layer == eblApplyToAll || this->m_config.enable_extra_bridge_layer == eblInternalBridgeOnly) {
|
||||
// Process layers in parallel up to second-to-last
|
||||
tbb::parallel_for( tbb::blocked_range<size_t>(0, this->layers().size() - 1), [this](const tbb::blocked_range<size_t>& r) {
|
||||
for (size_t lidx = r.begin(); lidx < r.end(); ++lidx)
|
||||
{
|
||||
Layer* layer = this->get_layer(lidx);
|
||||
|
||||
// (A) Gather internal bridging surfaces in the current layer
|
||||
ExPolygons bridging_current_layer;
|
||||
double bridging_angle_current = 0.0;
|
||||
|
||||
bool found_any_bridge = false;
|
||||
float offset_distance = 0.0f;
|
||||
|
||||
// Pick a region from which to retrieve the flow width
|
||||
if (!layer->regions().empty())
|
||||
offset_distance = layer->regions().front()->flow(frSolidInfill).scaled_width();
|
||||
|
||||
for (LayerRegion *region : layer->regions()) {
|
||||
for (const Surface &surf : region->fill_surfaces.surfaces) {
|
||||
if (surf.surface_type == stInternalBridge) {
|
||||
bridging_current_layer.push_back(surf.expolygon);
|
||||
bridging_angle_current = surf.bridge_angle; // Store the last bridging angle of the current print object
|
||||
found_any_bridge = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no bridging in this layer, continue with the next
|
||||
if (!found_any_bridge || bridging_current_layer.empty())
|
||||
continue;
|
||||
|
||||
// (B) Shrink-expand to remove trivial bridging areas
|
||||
bridging_current_layer = offset_ex( shrink_ex(bridging_current_layer, offset_distance), offset_distance );
|
||||
|
||||
if (bridging_current_layer.empty())
|
||||
continue; // all bridging was trivial, continue with the next layer
|
||||
|
||||
// (C) If there is a next layer, identify overlapping stInternal & stInternalSolid areas and convert the overlap to stSecondInternalBridge
|
||||
if (lidx + 1 < this->layers().size()) {
|
||||
Layer* next_layer = this->get_layer(lidx + 1);
|
||||
|
||||
// second bridging angle is 90 degrees offset
|
||||
double bridging_angle_second = bridging_angle_current + M_PI / 2.0;
|
||||
|
||||
// Union the bridging polygons
|
||||
ExPolygons bridging_union = union_safety_offset_ex(bridging_current_layer);
|
||||
|
||||
for (LayerRegion *next_region : next_layer->regions()) {
|
||||
Surfaces next_new_surfaces;
|
||||
Surfaces keep_surfaces;
|
||||
|
||||
// 1) Do not modify (keep) anything that isn't stInternal or stInternalSolid
|
||||
for (const Surface &s : next_region->fill_surfaces.surfaces) {
|
||||
if ( (s.surface_type != stInternal) && (s.surface_type != stInternalSolid)) {
|
||||
keep_surfaces.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
// 2) For stInternal & stInternalSolid surfaces, check if they overlap bridging_union
|
||||
// 2a) Gather the next internal stInternalSolid surfaces first
|
||||
SurfacesPtr next_internals = next_region->fill_surfaces.filter_by_types({ stInternal, stInternalSolid });
|
||||
|
||||
// 2b) For every collected next stInternalSolid surface
|
||||
for (const Surface *s : next_internals) {
|
||||
// Intersect it with the current layer bridging polygons
|
||||
ExPolygons overlap = intersection_ex( s->expolygon, bridging_union, ApplySafetyOffset::Yes );
|
||||
|
||||
// Shrink + expand to remove trivial polygons
|
||||
overlap = offset_ex(shrink_ex(overlap, offset_distance), offset_distance);
|
||||
|
||||
// Overlapping portion found -> this will become the second internal bridge
|
||||
if (!overlap.empty()) {
|
||||
// Create second bridge surface
|
||||
Surface tmp{*s, {}};
|
||||
tmp.surface_type = stSecondInternalBridge;
|
||||
tmp.bridge_angle = bridging_angle_second;
|
||||
|
||||
// Insert bridging polygons
|
||||
for (const ExPolygon &ep : overlap) {
|
||||
next_new_surfaces.emplace_back(tmp, ep);
|
||||
}
|
||||
|
||||
// Calculate leftover polygons = s->expolygon - bridging_union
|
||||
ExPolygons leftover = diff_ex(s->expolygon, bridging_union, ApplySafetyOffset::Yes);
|
||||
// Shrink + expand to remove trivial polygons
|
||||
leftover = offset_ex(shrink_ex(leftover, offset_distance), offset_distance);
|
||||
|
||||
// Leftover polygons exist. Add them to the new surface maintaining their original attributes
|
||||
if (!leftover.empty()) {
|
||||
ExPolygons unified_leftover = union_safety_offset_ex(leftover);
|
||||
for (const ExPolygon &ep : unified_leftover) {
|
||||
// keep same type / angle as original
|
||||
Surface leftover_surf{*s, {}};
|
||||
leftover_surf.surface_type = s->surface_type;
|
||||
leftover_surf.bridge_angle = s->bridge_angle;
|
||||
next_new_surfaces.emplace_back(leftover_surf, ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // No overlapping portion found
|
||||
// keep the surface intact
|
||||
keep_surfaces.push_back(*s);
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Rebuild next_region surfaces
|
||||
next_region->fill_surfaces.surfaces.clear();
|
||||
next_region->fill_surfaces.append(keep_surfaces);
|
||||
next_region->fill_surfaces.append(next_new_surfaces);
|
||||
} // end for next_layer->regions
|
||||
} // end if next layer
|
||||
}
|
||||
}); // end parallel_for
|
||||
|
||||
// =================================================================================================================
|
||||
// === ORCA: Interim workaround - for now the new stSecondInternalBridge surfaces are re-classified ===============
|
||||
// === back to an internal bridge. As a starting point, this improves bridging reliability as it extrudes ==========
|
||||
// === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase =============
|
||||
// =================================================================================================================
|
||||
for (size_t lidx = 0; lidx < this->layers().size(); ++lidx) {
|
||||
Layer* layer = this->get_layer(lidx);
|
||||
for (LayerRegion* region : layer->regions()) {
|
||||
for (Surface &surf : region->fill_surfaces.surfaces) {
|
||||
if (surf.surface_type == stSecondInternalBridge) {
|
||||
surf.surface_type = stInternalBridge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ===========================================================================================
|
||||
// === ORCA: End of second bridging pass =====================================================
|
||||
// ===========================================================================================
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill - End" << log_memory_info();
|
||||
|
||||
|
@ -3497,6 +3747,7 @@ void PrintObject::combine_infill()
|
|||
((infill_pattern == ipRectilinear ||
|
||||
infill_pattern == ipMonotonic ||
|
||||
infill_pattern == ipGrid ||
|
||||
infill_pattern == ip2DLattice ||
|
||||
infill_pattern == ipLine ||
|
||||
infill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
|
||||
layerms.back()->flow(frSolidInfill).scaled_width();
|
||||
|
|
|
@ -13,12 +13,16 @@ enum SurfaceType {
|
|||
stBottom,
|
||||
// Bottom horizontal surface, visible from the bottom, unsupported, printed with a bridging extrusion flow.
|
||||
stBottomBridge,
|
||||
// Second bridge surface above a bottom bridge.
|
||||
stInternalAfterExternalBridge,
|
||||
// Normal sparse infill.
|
||||
stInternal,
|
||||
// Full infill, supporting the top surfaces and/or defining the verticall wall thickness.
|
||||
stInternalSolid,
|
||||
// 1st layer of dense infill over sparse infill, printed with a bridging extrusion flow.
|
||||
stInternalBridge,
|
||||
// 2nd layer of dense infill over sparse infill, printed with a bridging extrusion flow.
|
||||
stSecondInternalBridge,
|
||||
// stInternal turns into void surfaces if the sparse infill is used for supports only,
|
||||
// or if sparse infill layers get combined into a single layer.
|
||||
stInternalVoid,
|
||||
|
|
|
@ -65,9 +65,8 @@ static constexpr double LARGE_BED_THRESHOLD = 2147;
|
|||
static constexpr size_t MAXIMUM_EXTRUDER_NUMBER = 64;
|
||||
|
||||
extern double SCALING_FACTOR;
|
||||
// for creating circles (for brim_ear)
|
||||
#define POLY_SIDES 24
|
||||
static constexpr double PI = 3.141592653589793238;
|
||||
#define POLY_SIDE_COUNT 24 // for brim ear circle
|
||||
// When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam.
|
||||
// SoftFever: replaced by seam_gap now
|
||||
// static constexpr double LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER = 0.15;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue