Merge branch 'main' into feat/plugin-storage-api

This commit is contained in:
Ian Chua
2026-08-14 15:19:26 +08:00
committed by GitHub
456 changed files with 14134 additions and 2496 deletions
+1 -1
View File
@@ -7054,7 +7054,7 @@ int CLI::run(int argc, char **argv)
gcode_viewer.render_calibration_thumbnail(*calibration_data, cali_thumbnail_width, cali_thumbnail_height,
calibration_params, partplate_list, opengl_mgr);
//generate_calibration_thumbnail(*calibration_data, thumbnail_width, thumbnail_height, calibration_params);
//*plate_bboxes[index] = p->generate_first_layer_bbox();
// *plate_bboxes[index] = p->generate_first_layer_bbox();
calibration_thumbnails.push_back(calibration_data);*/
PlateBBoxData* plate_bbox = new PlateBBoxData();
+7 -8
View File
@@ -54,12 +54,12 @@ public:
int & i,
Eigen::Matrix<double, 1, 3> &closest)
{
size_t idx_unsigned = 0;
Vec3d closest_vec3d(closest);
double dist =
size_t idx_unsigned { 0 };
Vec3d closest_vec3d { Vec3d::Zero() };
const double dist {
AABBTreeIndirect::squared_distance_to_indexed_triangle_set(
its.vertices, its.indices, m_tree, point, idx_unsigned,
closest_vec3d);
closest_vec3d) };
i = int(idx_unsigned);
closest = closest_vec3d;
return dist;
@@ -311,10 +311,9 @@ AABBMesh::hit_result IndexedMesh::filter_hits(
double AABBMesh::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
double sqdst = 0;
Eigen::Matrix<double, 1, 3> pp = p;
Eigen::Matrix<double, 1, 3> cc;
sqdst = m_aabb->squared_distance(*m_tm, pp, i, cc);
const Eigen::Matrix<double, 1, 3> pp { p };
Eigen::Matrix<double, 1, 3> cc { Vec3d::Zero() };
const double sqdst { m_aabb->squared_distance(*m_tm, pp, i, cc) };
c = cc;
return sqdst;
}
+1 -1
View File
@@ -883,7 +883,7 @@ std::string AppConfig::load()
}
}
}
} catch(std::exception err) {
} catch(const std::exception &err) {
BOOST_LOG_TRIVIAL(info) << format("parse app config \"%1%\", error: %2%", AppConfig::loading_path(), err.what());
return err.what();
+8 -8
View File
@@ -23,14 +23,14 @@ inline coord_t meshfix_maximum_extrusion_area_deviation() { return scaled<coo
class WallToolPathsParams
{
public:
float min_bead_width;
float min_feature_size;
float min_length_factor;
float wall_transition_length;
float wall_transition_angle;
float wall_transition_filter_deviation;
int wall_distribution_count;
bool is_top_or_bottom_layer;
float min_bead_width = 0.f;
float min_feature_size = 0.f;
float min_length_factor = 0.5f;
float wall_transition_length = 0.f;
float wall_transition_angle = 10.f;
float wall_transition_filter_deviation = 0.f;
int wall_distribution_count = 1;
bool is_top_or_bottom_layer = false;
coord_t wall_maximum_resolution = meshfix_maximum_resolution();
coord_t wall_maximum_deviation = meshfix_maximum_deviation();
+2
View File
@@ -2982,6 +2982,8 @@ public:
const double & opt_float(const t_config_option_key &opt_key, unsigned int idx) const;
double & opt_float_nullable(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionFloatsNullable>(opt_key)->get_at(idx); }
const double & opt_float_nullable(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionFloatsNullable *>(this->option(opt_key))->get_at(idx); }
FloatOrPercent & opt_float_or_percent_nullable(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionFloatsOrPercentsNullable>(opt_key)->get_at(idx); }
const FloatOrPercent & opt_float_or_percent_nullable(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionFloatsOrPercentsNullable *>(this->option(opt_key))->get_at(idx); }
int& opt_int(const t_config_option_key &opt_key) { return this->option<ConfigOptionInt>(opt_key)->value; }
int opt_int(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionInt*>(this->option(opt_key))->value; }
+1 -1
View File
@@ -3576,7 +3576,7 @@ Polylines FillLateralHoneycomb::fill_surface(const Surface *surface, const FillP
// |
// |
// 0 --+--
// / \
// ⟋ ⟍
// why inverted?
// it makes determining some of the properties easier
// and the two angled legs provide additional horizontal stiffness
+1 -1
View File
@@ -712,7 +712,7 @@ unsigned int Step::get_triangle_num(double linear_deflection, double angle_defle
return 0;
}
}
} catch(Exception e) {
} catch(const Exception &e) {
return 0;
}
+87 -50
View File
@@ -768,30 +768,31 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
return changes;
}
// Clearance the tower-approach router keeps around the tower: the avoid box is
// inflated by this much before routing, and the inflated corners must stay on the
// bed for a route to be generated at all.
static constexpr float wipe_tower_routing_clearance = 2.f;
// BBS
// start_pos refers to the last position before the wipe_tower.
// end_pos refers to the wipe tower's start_pos.
// using the print coordinate system
Polyline WipeTowerIntegration::generate_path_to_wipe_tower(const Point& start_pos,const Point &end_pos , const BoundingBox& avoid_polygon , const BoundingBox& printer_bbx) const
Polyline WipeTowerIntegration::generate_path_to_wipe_tower(const Point& start_pos,const Point &end_pos , const BoundingBox& avoid_polygon , const Polygons& bed_polygons) const
{
Polyline res;
coord_t alpha = scaled(2.f); // offset distance
coord_t alpha = scaled(wipe_tower_routing_clearance); // offset distance
BoundingBox avoid_polygon_inner = avoid_polygon;
avoid_polygon_inner.offset(alpha);
coord_t width = avoid_polygon_inner.max[0] - avoid_polygon_inner.min[0];
Polygon bed_polygon = printer_bbx.polygon();
Vec2f v(1, 0); // the first print direction of end_pos.
if (abs(end_pos[0] - avoid_polygon_inner.min[0]) < width / 2) v = -v; // judge whether the wipe tower's infill goes to the left or right.
// Judge whether the avoid_polygon_inner is outside the printer_bbx.
// Judge whether the avoid_polygon_inner is outside the bed. The real printable
// outline is tested (not its bounding box), so on circular/custom beds corners
// hanging off the bed are rejected.
// If so, do nothing and just go directly to the end_pos.
bool is_bbx_in_bed = true;
Points avoid_points = avoid_polygon_inner.polygon().points;
for (auto &wipe_tower_bbx_p : avoid_points) {
if (ClipperLib::PointInPolygon(wipe_tower_bbx_p, bed_polygon.points) != 1) {
is_bbx_in_bed = false;
break;
}
}
const bool is_bbx_in_bed = std::all_of(avoid_points.begin(), avoid_points.end(),
[&bed_polygons](const Point &pt) { return contains(bed_polygons, pt, /*border_result=*/false); });
if (!is_bbx_in_bed) {
res.points.push_back(end_pos);
return res;
@@ -898,27 +899,17 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
return Eigen::Rotation2Df(alpha) * (pt + m_rib_offset) + m_wipe_tower_pos;
}
// Printable-area bounds for tower-approach routing, in object coordinates (shared by
// the BBL avoid-perimeter path in append_tcr and the Type2 skip-points router).
// Multi-nozzle: clamp the travel bounds to the region every extruder can reach
// (get_extruder_shared_printable_polygon) instead of the full bed. Gated on the
// multi-nozzle predicate so every existing single/dual printer keeps the historic
// full-printable_area routing byte-identical.
BoundingBox WipeTowerIntegration::printer_travel_bounds(GCode &gcodegen) const
// Bed outline the tower-approach router plans against, in object coordinates. The real
// outline is returned, not its bounding box, so the router's containment tests fail off
// the bed on circular/custom shapes; the multi-nozzle narrowing lives in the accessor.
Polygons WipeTowerIntegration::shared_printable_area(GCode &gcodegen) const
{
const Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
BoundingBox printer_bbx;
if (is_multi_nozzle_printer(gcodegen.m_config)) {
printer_bbx = get_extents(gcodegen.m_print->get_extruder_shared_printable_polygon());
printer_bbx.min = wipe_tower_point_to_object_point(gcodegen, unscaled<float>(printer_bbx.min) + plate_origin_2d);
printer_bbx.max = wipe_tower_point_to_object_point(gcodegen, unscaled<float>(printer_bbx.max) + plate_origin_2d);
} else {
Points bed_points;
for (const auto& p : gcodegen.m_config.printable_area.values)
bed_points.push_back(wipe_tower_point_to_object_point(gcodegen, p.cast<float>() + plate_origin_2d));
printer_bbx = BoundingBox(bed_points);
}
return printer_bbx;
// The frame change is a pure translation, so transform the origin once.
const Point offset = wipe_tower_point_to_object_point(gcodegen, Vec2f(m_plate_origin(0), m_plate_origin(1)));
Polygons bed_polygons = gcodegen.m_print->get_extruder_shared_printable_polygon();
for (Polygon &poly : bed_polygons)
poly.translate(offset);
return bed_polygons;
}
// With skip points enabled the Type2 tower wall has an opening at each toolchange's
@@ -933,15 +924,37 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
if (!WipeTower2::use_gap_wall(gcodegen.m_config))
return {};
const Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
// Transform the tower-local bbx corners exactly like the tcr points; a rotated
// tower gets a conservative axis-aligned envelope.
Polygon avoid_points = scaled(m_wipe_tower_bbx).polygon();
for (auto& p : avoid_points.points)
p = wipe_tower_point_to_object_point(gcodegen, transform_wt2_pt(unscale(p).cast<float>()) + plate_origin_2d);
BoundingBox avoid_bbx(avoid_points.points);
if (avoid_bbx.contains(route_start))
// Transform tower-local corners exactly like the tcr points; a rotated tower gets a
// conservative axis-aligned envelope from the result.
auto tower_polygon = [&](const BoundingBoxf &bbx) {
Polygon poly = scaled(bbx).polygon();
for (Point &p : poly.points)
p = wipe_tower_point_to_object_point(gcodegen, transform_wt2_pt(unscale(p).cast<float>()) + plate_origin_2d);
return poly;
};
// The avoid envelope covers the first-layer brim (and rib flare), which a travel may
// cross freely: early-out only when the approach already starts over the tower body
// itself, so a start between the wall and the brim edge still gets routed in through
// the wall opening. Test the rotated polygon, not its bounding box — at angles off the
// axes the box's corner triangles cover most of the brim ring.
const float body_width = gcodegen.m_config.wipe_tower_wall_type.value == WipeTowerWallType::wtwRib ? m_wipe_tower_depth : m_right;
if (tower_polygon(BoundingBoxf(Vec2d(0., 0.), Vec2d(body_width, m_wipe_tower_depth))).contains(route_start))
return {};
Polyline travel_polyline = generate_path_to_wipe_tower(route_start, start_wipe_pos, avoid_bbx, printer_travel_bounds(gcodegen));
const Polygons bed = shared_printable_area(gcodegen);
BoundingBox avoid_bbx = get_extents(tower_polygon(m_wipe_tower_bbx));
// The inflated corners must stay on the bed for the router to generate a route at all:
// clamp the box against the bed shrunk by the clearance the router adds, so a tower
// parked near the bed edge is still routed along the clamped side instead of always
// travelling straight across the tower.
BoundingBox clamp_bbx = get_extents(bed);
clamp_bbx.offset(-(scaled(wipe_tower_routing_clearance) + SCALED_EPSILON));
avoid_bbx.min = avoid_bbx.min.cwiseMax(clamp_bbx.min);
avoid_bbx.max = avoid_bbx.max.cwiseMin(clamp_bbx.max);
if (avoid_bbx.min.x() >= avoid_bbx.max.x() || avoid_bbx.min.y() >= avoid_bbx.max.y())
return {};
Polyline travel_polyline = generate_path_to_wipe_tower(route_start, start_wipe_pos, avoid_bbx, bed);
std::string gcode;
// The polyline's last point is start_wipe_pos itself — emitted by the caller.
for (size_t i = 0; i + 1 < travel_polyline.points.size(); ++i)
@@ -1322,7 +1335,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
Vec2f gcode_last_pos2d{gcode_last_pos[0], gcode_last_pos[1]};
Point gcode_last_pos2d_object = gcodegen.gcode_to_point(gcode_last_pos2d.cast<double>() + plate_origin_2d.cast<double>());
Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, tool_change_start_pos + plate_origin_2d);
BoundingBox avoid_bbx, printer_bbx = printer_travel_bounds(gcodegen);
BoundingBox avoid_bbx;
{
// set avoid_bbx
avoid_bbx = scaled(m_wipe_tower_bbx);
@@ -1334,7 +1347,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
avoid_bbx = BoundingBox(avoid_points.points);
}
std::string travel_to_wipe_tower_gcode;
Polyline travel_polyline = generate_path_to_wipe_tower(gcode_last_pos2d_object, start_wipe_pos, avoid_bbx, printer_bbx);
Polyline travel_polyline = generate_path_to_wipe_tower(gcode_last_pos2d_object, start_wipe_pos, avoid_bbx, shared_printable_area(gcodegen));
for (size_t i = 0; i < travel_polyline.points.size(); ++i) {
const auto &p = travel_polyline.points[i];
@@ -1554,7 +1567,8 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
interface_temp = gcodegen.config().nozzle_temperature_range_high.get_at(new_extruder_id);
toolchange_temp_override = interface_temp;
}
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z, false, toolchange_temp_override); // TODO: toolchange_z vs print_z
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z, false, toolchange_temp_override,
WipeTower2::wait_for_temp_enabled(gcodegen.m_config)); // TODO: toolchange_z vs print_z
if (!travel_to_tower_now && !tcr.priming && WipeTower2::use_gap_wall(gcodegen.m_config)) {
// The tool changed in place (multi-tool printer without ramming), so the
// tower entry is the tcr's own positioning move — a straight line across
@@ -1705,7 +1719,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
std::string trimmed = line;
trimmed.erase(0, trimmed.find_first_not_of(" \t"));
bool skip_line = false;
if (boost::starts_with(trimmed, "M109")) {
if (boost::starts_with(trimmed, "M109") && trimmed.find(WipeTower2::wait_for_temp_tag()) == std::string::npos) {
bool matches_extruder = true;
if (trimmed.find('T') != std::string::npos)
matches_extruder = trimmed.find(t_token) != std::string::npos;
@@ -5511,7 +5525,7 @@ LayerResult GCode::process_layer(
// add tag for processor
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change) + "\n";
// export layer z
char buf[64];
char buf[80];
sprintf(buf, print.is_BBL_printer() ? "; Z_HEIGHT: %g\n" : ";Z:%g\n", print_z);
gcode += buf;
// export layer height
@@ -6712,6 +6726,7 @@ void GCode::append_full_config(const Print &print, std::string &str)
"farthest_point_timelapse"sv,
"compatible_printers"sv,
"compatible_prints"sv,
"filament_colour_type"sv,
"print_host"sv,
"print_host_webui"sv,
"printhost_apikey"sv,
@@ -7636,8 +7651,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (sloped) {
speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(speed));
}
}
else if(path.role() == erInternalBridgeInfill) {
} else if(path.role() == erInternalBridgeInfill) {
speed = m_config.get_abs_value_at("internal_bridge_speed", get_nozzle_config_index(m_writer.filament()->id()));
} else if (path.role() == erOverhangPerimeter || path.role() == erSupportTransition || path.role() == erBridgeInfill) {
speed = NOZZLE_CONFIG(bridge_speed);
@@ -7648,7 +7662,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
} else if (path.role() == erTopSolidInfill) {
speed = NOZZLE_CONFIG(top_surface_speed);
} else if (path.role() == erIroning) {
speed = m_config.get_abs_value("ironing_speed");
const size_t filament_idx = get_filament_config_index(m_writer.filament()->id());
speed = m_config.filament_ironing_speed.is_nil(filament_idx)
? m_config.get_abs_value("ironing_speed")
: m_config.filament_ironing_speed.get_at(filament_idx);
} else if (path.role() == erBottomSurface) {
speed = NOZZLE_CONFIG(initial_layer_infill_speed);
} else if (path.role() == erGapFill) {
@@ -8939,7 +8956,7 @@ void GCode::update_placeholder_parser_with_variant_params()
}
}
std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bool by_object, int toolchange_temp_override)
std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bool by_object, int toolchange_temp_override, bool defer_temp_wait)
{
int new_extruder_id = get_extruder_id(new_filament_id);
if (!m_writer.need_toolchange(new_filament_id))
@@ -9046,6 +9063,24 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
if (toolchange_temp_override > 0)
new_filament_temp = toolchange_temp_override;
// With wait_for_temp_on_wipe_tower the blocking M109 is deferred to the wipe tower, so raise
// the incoming filament's target here — ahead of the tool change rather than after it — and
// let the heat-up overlap the change itself as well as the travel to the tower. The command
// always carries an explicit tool index (the option is off for single extruder MM, so the
// writer emits one), leaving the outgoing filament that pre_toolchange just dropped to its
// standby temperature alone. nozzle_temperature == 0 means "use the first layer temperature".
if (defer_temp_wait) {
// Target what the tower will wait on. It waits on the first layer temperature not only on
// the first layer but also while priming, which runs before any layer is set: there
// on_first_layer() is false and print_z is the initial layer height, so neither test above
// catches it. nozzle_temperature == 0 means "use the first layer temperature" as well.
int preheat_temp = new_filament_temp;
if (toolchange_temp_override <= 0 && (m_layer == nullptr || preheat_temp <= 0))
preheat_temp = m_config.nozzle_temperature_initial_layer.get_at(new_fi);
if (preheat_temp > 0)
gcode += m_writer.set_temperature(preheat_temp, false, new_filament_id);
}
Vec3d nozzle_pos = m_writer.get_position();
float old_retract_length, old_retract_length_toolchange, wipe_volume;
int old_filament_temp, old_filament_e_feedrate;
@@ -9349,8 +9384,10 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
}
check_add_eol(gcode);
}
// Set the new extruder to the operating temperature.
if (m_ooze_prevention.enable)
// Set the new extruder to the operating temperature. With defer_temp_wait the target was
// already raised before the tool change and the blocking wait belongs to the wipe tower
// generator, so there is nothing left to restore here.
if (m_ooze_prevention.enable && !defer_temp_wait)
gcode += m_ooze_prevention.post_toolchange(*this);
if (m_config.enable_pressure_advance.get_at(new_filament_id)) {
+3 -3
View File
@@ -130,11 +130,11 @@ public:
private:
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
Polyline generate_path_to_wipe_tower(const Point &start_pos, const Point &end_pos, const BoundingBox &avoid_polygon, const BoundingBox &printer_bbx) const;
Polyline generate_path_to_wipe_tower(const Point &start_pos, const Point &end_pos, const BoundingBox &avoid_polygon, const Polygons &bed_polygons) const;
std::string append_tcr2(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
std::string travel_to_tower_gap(GCode &gcodegen, const Point &route_start, const Point &start_wipe_pos) const;
Vec2f transform_wt2_pt(const Vec2f &pt) const;
BoundingBox printer_travel_bounds(GCode &gcodegen) const;
Polygons shared_printable_area(GCode &gcodegen) const;
// Postprocesses gcode: rotates and moves G1 extrusions and returns result
std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const;
@@ -262,7 +262,7 @@ public:
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift, bool apply_instantly = false, ExtrusionRole role = erNone);
// extra_retract forwards a PETG pre-extrusion over-extrusion; default 0 -> identical to the plain deretract.
std::string unretract(float extra_retract = 0.f) { return m_writer.unlift() + m_writer.unretract(extra_retract); }
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override = -1);
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override = -1, bool defer_temp_wait = false);
bool is_BBL_Printer();
WipeTowerType wipe_tower_type();
+45 -14
View File
@@ -1630,25 +1630,56 @@ float WipeTower::get_auto_brim_by_height(float max_height) {
return 8.f;
}
Vec2f WipeTower::move_box_inside_box(const BoundingBox &box1, const BoundingBox &box2,int scaled_offset)
Vec2f WipeTower::move_box_inside_polygon(const BoundingBox &box, const Polygons &polygons, coord_t offset)
{
Vec2f res{0, 0};
if (box1.size()[0] >= box2.size()[0]- 2*scaled_offset || box1.size()[1] >= box2.size()[1]-2*scaled_offset) return res;
if (polygons.empty()) return Vec2f{0.f, 0.f};
if (box1.max[0] > box2.max[0] - scaled_offset) {
res[0] = unscaled<float>((box2.max[0] - scaled_offset) - box1.max[0]);
}
else if (box1.min[0] < box2.min[0] + scaled_offset) {
res[0] = unscaled<float>((box2.min[0] + scaled_offset) - box1.min[0]);
const BoundingBox bed = get_extents(polygons);
// No position fits the footprint.
if (box.size().x() >= bed.size().x() - 2 * offset || box.size().y() >= bed.size().y() - 2 * offset)
return Vec2f{0.f, 0.f};
// Clamp against the bounding box first, moving only along the axis that is violated so a dragged
// prime tower slides along the bed edge instead of jumping inwards.
Point shift(0, 0);
for (int axis = 0; axis < 2; ++axis) {
if (box.max[axis] > bed.max[axis] - offset)
shift[axis] = (bed.max[axis] - offset) - box.max[axis];
else if (box.min[axis] < bed.min[axis] + offset)
shift[axis] = (bed.min[axis] + offset) - box.min[axis];
}
if (box1.max[1] > box2.max[1] - scaled_offset) {
res[1] = unscaled<float>((box2.max[1] - scaled_offset) - box1.max[1]);
// A bed that fills its own bounding box is fully clamped by that, so every rectangular bed — all
// but the delta-style profiles — stops here and keeps its historic placement, including when a
// negative margin lets the footprint hang over the edge. The tolerance is relative because an
// exact rectangle loses a few ulps once the areas are squared world coordinates.
double area = 0.;
for (const Polygon &poly : polygons) area += std::abs(poly.area());
const double bed_area = double(bed.size().x()) * double(bed.size().y());
if (area >= bed_area * (1. - EPSILON)) return unscaled<float>(shift);
// Clamp a negative margin (an auto brim width that has not been resolved yet) to zero: padding by
// it would shrink the footprint and hand back a position the validation still rejects. The
// epsilon lets the move's round trip through millimeters land on the outline without counting as
// a violation.
BoundingBox padded = box.inflated(std::max<coord_t>(offset, 0) - SCALED_EPSILON);
padded.translate(shift);
auto fits = [&padded, &polygons](const Point &move) {
BoundingBox moved = padded;
moved.translate(move);
return diff(Polygons{moved.polygon()}, polygons).empty();
};
if (fits(Point(0, 0))) return unscaled<float>(shift);
// Walk towards the middle of the bed. On every non-rectangular bed we ship, the fitting positions
// form a convex region around it, so bisecting stops just inside the outline.
Point lo(0, 0), hi = bed.center() - padded.center();
if (!fits(hi)) return unscaled<float>(shift);
for (int i = 0; i < 12; ++i) {
const Point mid = (lo + hi) / 2;
if (fits(mid)) hi = mid; else lo = mid;
}
else if (box1.min[1] < box2.min[1] + scaled_offset) {
res[1] = unscaled<float>((box2.min[1] + scaled_offset) - box1.min[1]);
}
return res;
return unscaled<float>(Point(shift + hi));
}
Polygon WipeTower::rib_section(float width, float depth, float rib_length, float rib_width,bool fillet_wall)
+5 -1
View File
@@ -45,7 +45,11 @@ public:
static TriangleMesh its_make_rib_tower(float width, float depth, float height, float rib_length, float rib_width, bool fillet_wall);
static TriangleMesh its_make_rib_brim(const Polygon& brim, float layer_height);
static Polygon rib_section(float width, float depth, float rib_length, float rib_width, bool fillet_wall);
static Vec2f move_box_inside_box(const BoundingBox &box1, const BoundingBox &box2, int offset = 0);
// Translation that brings a footprint inside the printable outline, padded by offset. The prime
// tower is validated against the real outline (see layered_print_cleareance_valid), so clamping
// against the bounding box alone would leave it off a delta or hexagonal bed. box and polygons
// must share one scaled coordinate frame; the translation comes back in millimeters.
static Vec2f move_box_inside_polygon(const BoundingBox &box, const Polygons &polygons, coord_t offset = 0);
static Polygon rounding_polygon(Polygon &polygon, double rounding = 2., double angle_tol = 30. / 180. * PI);
struct Extrusion
{
+165 -19
View File
@@ -413,6 +413,7 @@ public:
const Vec2f& pos() const { return m_current_pos; }
const Vec2f start_pos_rotated() const { return m_start_pos; }
const Vec2f pos_rotated() const { return this->rotate(m_current_pos); }
const Vec2f rotated(const Vec2f &pt) const { return this->rotate(pt); }
float elapsed_time() const { return m_elapsed_time; }
float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; }
@@ -624,10 +625,13 @@ public:
}
// Set extruder temperature, don't wait by default.
WipeTowerWriter2& set_extruder_temp(int temperature, bool wait = false)
WipeTowerWriter2& set_extruder_temp(int temperature, bool wait = false, const std::string& comment = std::string())
{
flush_planner_queue();
m_gcode += "M" + std::to_string(wait ? 109 : 104) + " S" + std::to_string(temperature) + "\n";
m_gcode += "M" + std::to_string(wait ? 109 : 104) + " S" + std::to_string(temperature);
if (!comment.empty())
m_gcode += " " + comment;
m_gcode += "\n";
return *this;
}
@@ -1006,6 +1010,13 @@ bool WipeTower2::use_gap_wall(const PrintConfig& config)
return config.prime_tower_skip_points.value && config.wipe_tower_wall_type.value != wtwCone;
}
bool WipeTower2::wait_for_temp_enabled(const PrintConfig& config)
{
// SEMM runs its own unload/load temperature sequence; the GUI hides the option
// there but a profile may still carry it set.
return config.wait_for_temp_on_wipe_tower.value && !config.single_extruder_multi_material.value;
}
WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& default_region_config,int plate_idx, Vec3d plate_origin, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) :
m_semm(config.single_extruder_multi_material.value),
m_enable_filament_ramming(config.enable_filament_ramming.value),
@@ -1035,7 +1046,8 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau
m_wall_type((int)config.wipe_tower_wall_type),
m_use_gap_wall(use_gap_wall(config)),
m_enable_tower_interface_features(config.enable_tower_interface_features.value),
m_enable_tower_interface_cooldown_during_tower(config.enable_tower_interface_cooldown_during_tower.value)
m_enable_tower_interface_cooldown_during_tower(config.enable_tower_interface_cooldown_during_tower.value),
m_wait_for_temp_on_wipe_tower(wait_for_temp_enabled(config))
{
// Read absolute value of first layer speed, if given as percentage,
// it is taken over following default. Speeds from config are not
@@ -1085,6 +1097,7 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau
m_bed_bottom_left = m_bed_shape == RectangularBed
? Vec2f(bed_points.front().x(), bed_points.front().y())
: Vec2f::Zero();
m_bed_polygon = Polygon::new_scale(bed_points);
}
@@ -1236,7 +1249,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower2::prime(
unsigned int tool = tools[idx_tool];
m_left_to_right = true;
toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials.
toolchange_Change(writer, tool, m_filpar[tool].material, m_filpar[tool].first_layer_temperature, false); // Select the tool, set a speed override for soluble and flex materials.
toolchange_Load(writer, cleaning_box); // Prime the tool.
if (idx_tool + 1 == tools.size()) {
// Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
@@ -1355,13 +1368,19 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
(is_first_layer() ? m_filpar[m_current_tool].first_layer_temperature : m_filpar[m_current_tool].temperature),
new_tool_temp);
toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
// Wait-at-tower target: the interface temp when an interface boost applies on this layer,
// otherwise the print temp (nozzle_temperature == 0 means "use the first layer temp").
int wait_for_temp = interface_layer && m_filpar[tool].interface_print_temperature > 0 ?
m_filpar[tool].interface_print_temperature :
(is_first_layer() || m_filpar[tool].temperature == 0 ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
toolchange_Change(writer, tool, m_filpar[tool].material, wait_for_temp, true); // Change the tool, set a speed override for soluble and flex materials.
toolchange_Load(writer, cleaning_box);
writer.travel(writer.x(), writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
int base_temp = is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature;
if (interface_layer) {
int interface_temp = m_filpar[tool].interface_print_temperature;
if (interface_temp > 0 && interface_temp != base_temp)
// With wait-for-temp-on-wipe-tower the toolchange already blocked for the interface temp.
if (interface_temp > 0 && interface_temp != base_temp && !m_wait_for_temp_on_wipe_tower)
writer.set_extruder_temp(interface_temp, true);
if (m_enable_tower_interface_cooldown_during_tower && interface_temp > 0 && interface_temp != base_temp)
writer.set_extruder_temp(base_temp, false);
@@ -1671,7 +1690,9 @@ void WipeTower2::toolchange_Unload(
void WipeTower2::toolchange_Change(
WipeTowerWriter2 &writer,
const size_t new_tool,
const std::string& new_material)
const std::string& new_material,
const int wait_for_temp,
const bool wait_beside_tower)
{
// Ask the writer about how much of the old filament we consumed:
if (m_current_tool < m_used_filament_length.size())
@@ -1685,6 +1706,90 @@ void WipeTower2::toolchange_Change(
if (m_is_mk4mmu3)
writer.switch_filament_monitoring(true);
const bool wait_for_temp_here = m_wait_for_temp_on_wipe_tower && wait_for_temp > 0;
// The Tn above was issued without a blocking temperature wait (GCode::set_extruder only raises
// the target, ahead of the Tn); block here, before the deretraction below, which must not
// extrude on a cold nozzle. Like the Bambu H2C, park beside the tower for the heat-up so drool lands
// next to it instead of on its top surface — nearest x side first, then that side clamped
// toward the bed edge, then the far side, in place if all would leave the bed. Raw
// pre-rotated moves (see the repositioning move below) keep the writer's tracked position
// at the tower entry. The tag keeps the
// interface-temp deduplication pass in append_tcr2 from stripping the M109.
if (wait_for_temp_here && wait_beside_tower) {
// The rib wall and the stabilization cone bulge past the nominal width rectangle
// (widest near the bottom), and the first-layer brim is printed around the wall later
// in the layer — clear the widest of them, not just the rectangle, so the park point
// and its drool stay off the tower.
float min_x = 0.f, max_x = m_wipe_tower_width;
if (m_wall_type == (int)wtwRib) {
WipeTower::box_coordinates wt_box(Vec2f(0.f, 0.f), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
const BoundingBox rib_bbox = get_extents(generate_rib_polygon(wt_box)); // the fillet stays within this bbox
min_x = std::min(min_x, unscaled<float>(rib_bbox.min.x()));
max_x = std::max(max_x, unscaled<float>(rib_bbox.max.x()));
} else if (m_wall_type == (int)wtwCone) {
const double support_scale = get_wipe_tower_cone_base(m_wipe_tower_width, m_wipe_tower_height, m_wipe_tower_depth,
m_wipe_tower_cone_angle).second;
const double z = m_no_sparse_layers ? (m_current_height + m_layer_info->height) : m_layer_info->z;
const double r = std::tan(Geometry::deg2rad(m_wipe_tower_cone_angle / 2.f)) * (m_wipe_tower_height - z);
const double w = m_layer_info->depth + m_perimeter_width;
if (r > 0.5 * w + 0.01) { // same guard as generate_support_cone_wall
const float bulge = float(std::sqrt(r * r - 0.25 * w * w) / support_scale);
min_x = std::min(min_x, m_wipe_tower_width / 2.f - bulge);
max_x = std::max(max_x, m_wipe_tower_width / 2.f + bulge);
}
}
if (is_first_layer()) {
const float brim = m_wipe_tower_brim_width < 0.f ? WipeTower::get_auto_brim_by_height(m_wipe_tower_height) :
m_wipe_tower_brim_width;
min_x -= brim;
max_x += brim;
}
constexpr float gap = 2.f;
constexpr float min_gap = 0.5f;
const bool on_left = writer.x() < m_wipe_tower_width / 2.f;
const float near_x = on_left ? min_x - gap : max_x + gap;
const float far_x = on_left ? max_x + gap : min_x - gap;
const Eigen::Rotation2Df to_bed(float(Geometry::deg2rad(m_wipe_tower_rotation_angle)));
auto park_pt_on_bed = [this, &writer, to_bed](float side_x) {
const Vec2f bed_pt = to_bed * (writer.rotated(Vec2f(side_x, writer.y())) + m_rib_offset) + m_wipe_tower_pos;
return m_bed_polygon.contains(Point::new_scale(bed_pt.x(), bed_pt.y()));
};
float park_x = near_x;
bool have_park = park_pt_on_bed(near_x);
if (!have_park) {
// The ideal near point hangs off the bed: pull it back to the bed edge as long
// as that still clears the tower envelope by min_gap (the BBL tower clamps its
// stop_pos against the bed the same way in append_tcr). Bisection, because with
// tower rotation and non-rectangular beds the bed edge is not axis-aligned.
const float limit_x = on_left ? min_x - min_gap : max_x + min_gap;
if (park_pt_on_bed(limit_x)) {
float on = limit_x, off = near_x;
for (int i = 0; i < 8; ++i) {
const float mid = 0.5f * (on + off);
if (park_pt_on_bed(mid))
on = mid;
else
off = mid;
}
park_x = on;
have_park = true;
}
}
if (!have_park && park_pt_on_bed(far_x)) {
park_x = far_x;
have_park = true;
}
if (have_park) {
const Vec2f stop = writer.rotated(Vec2f(park_x, writer.y()));
writer.feedrate(m_travel_speed * 60.f)
.append(std::string("G1 X") + Slic3r::float_to_string_decimal_point(stop.x())
+ " Y" + Slic3r::float_to_string_decimal_point(stop.y())
+ never_skip_tag() + "\n");
}
writer.set_extruder_temp(wait_for_temp, true, wait_for_temp_tag());
}
// Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc)
// gcode could have left the extruder somewhere, we cannot just start extruding. We should also inform the
// postprocessor that we absolutely want to have this in the gcode, even if it thought it is the same as before.
@@ -1695,6 +1800,10 @@ void WipeTower2::toolchange_Change(
+ never_skip_tag() + "\n"
);
// Priming has no tower to park beside — wait right at the priming line instead.
if (wait_for_temp_here && !wait_beside_tower)
writer.set_extruder_temp(wait_for_temp, true, wait_for_temp_tag());
writer.append("[deretraction_from_wipe_tower_generator]");
// The toolchange Tn command will be inserted later, only in case that the user does
@@ -2021,31 +2130,68 @@ std::pair<double, double> WipeTower2::get_wipe_tower_cone_base(double width, dou
}
// Static method to extract wipe_volumes[from][to] from the configuration.
std::vector<std::vector<float>> WipeTower2::extract_wipe_volumes(const PrintConfig& config)
// Takes a ConfigBase so the GUI's wipe tower size estimate can pass the plate's
// DynamicPrintConfig directly instead of materializing a full PrintConfig per call.
std::vector<std::vector<float>> WipeTower2::extract_wipe_volumes(const ConfigBase& config)
{
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
std::vector<float> wiping_matrix(cast<float>(config.flush_volumes_matrix.values));
auto scale = config.flush_multiplier.get_at(0);
// flush_volumes_matrix holds one filaments x filaments block per nozzle (written by
// PresetBundle::update_multi_material_filament_presets), so the filament count is
// sqrt(size / nozzles). One tower serves every nozzle and the filament to nozzle assignment is
// only decided later by ToolOrdering, so fold the blocks with std::max: the depth reserved here
// has to cover the worst nozzle. With a single nozzle the fold has one term.
const std::vector<double> &raw_matrix = config.option<ConfigOptionFloats>("flush_volumes_matrix")->values;
const auto *nozzle_diameter = config.option<ConfigOptionFloats>("nozzle_diameter");
size_t nozzle_nums = (nozzle_diameter == nullptr || nozzle_diameter->values.empty()) ? 1 : nozzle_diameter->values.size();
unsigned int number_of_extruders = (unsigned int)(sqrt(raw_matrix.size() / nozzle_nums) + EPSILON);
if (size_t(number_of_extruders) * number_of_extruders * nozzle_nums != raw_matrix.size()) {
// Saved for a different nozzle count (older project, or the printer was just switched):
// fall back to reading the whole option as one block, as this did before.
nozzle_nums = 1;
number_of_extruders = (unsigned int)(sqrt(raw_matrix.size()) + EPSILON);
}
// The values shall only be used when SEMM is enabled. The purging for other printers
// is determined by filament_minimal_purge_on_wipe_tower.
if (! config.purge_in_prime_tower.value || ! config.single_extruder_multi_material.value)
std::fill(wiping_matrix.begin(), wiping_matrix.end(), 0.f);
const bool purge = config.option<ConfigOptionBool>("purge_in_prime_tower")->value
&& config.option<ConfigOptionBool>("single_extruder_multi_material")->value;
// Extract purging volumes for each extruder pair:
std::vector<std::vector<float>> wipe_volumes;
const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
for (size_t i = 0; i<number_of_extruders; ++i)
wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
// Extract purging volumes for each extruder pair, each nozzle's block scaled by its own multiplier:
std::vector<std::vector<float>> wipe_volumes(number_of_extruders, std::vector<float>(number_of_extruders, 0.f));
if (purge) {
const auto *multiplier = config.option<ConfigOptionFloats>("flush_multiplier");
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
const std::vector<double> block = get_flush_volumes_matrix(raw_matrix, nozzle_id, nozzle_nums);
const double scale = multiplier->get_at(nozzle_id);
for (unsigned int i = 0; i<number_of_extruders; ++i)
for (unsigned int j = 0; j<number_of_extruders; ++j)
wipe_volumes[i][j] = std::max<float>(wipe_volumes[i][j], float(block[size_t(i) * number_of_extruders + j]) * scale);
}
}
// Also include filament_minimal_purge_on_wipe_tower. This is needed for the preview.
const auto *minimal_purge = config.option<ConfigOptionFloats>("filament_minimal_purge_on_wipe_tower");
for (unsigned int i = 0; i<number_of_extruders; ++i)
for (unsigned int j = 0; j<number_of_extruders; ++j)
wipe_volumes[i][j] = std::max<float>(wipe_volumes[i][j] * scale, config.filament_minimal_purge_on_wipe_tower.get_at(j));
wipe_volumes[i][j] = std::max<float>(wipe_volumes[i][j], minimal_purge->get_at(j));
return wipe_volumes;
}
float WipeTower2::estimate_semm_flush_volume(const ConfigBase& config, size_t filaments_cnt)
{
const std::vector<std::vector<float>> wipe_volumes = extract_wipe_volumes(config);
if (wipe_volumes.empty()) // an empty flush matrix would make the average below 0/0
return 0.f;
float maximum = 0.f;
for (const std::vector<float> &v : wipe_volumes)
maximum += *std::max_element(v.begin(), v.end());
maximum = maximum * filaments_cnt / wipe_volumes.size();
// Orca: it's overshooting a bit, so let's reduce it a bit
maximum *= 0.6;
return maximum;
}
static float get_wipe_depth(float volume, float layer_height, float perimeter_width, float extra_flow, float extra_spacing, float width)
{
float length_to_extrude = (volume_to_length(volume, perimeter_width, layer_height)) / extra_flow;
+18 -2
View File
@@ -17,13 +17,20 @@ namespace Slic3r
class WipeTowerWriter2;
class PrintRegionConfig;
class ConfigBase;
class WipeTower2
{
public:
static const std::string never_skip_tag() { return "_GCODE_WIPE_TOWER_NEVER_SKIP_TAG"; }
// Marks the wait-for-temp-on-wipe-tower M109 so the interface-temp deduplication pass
// in WipeTowerIntegration::append_tcr2 does not strip it.
static const std::string wait_for_temp_tag() { return ";_WAIT_FOR_TEMP_ON_WIPE_TOWER"; }
static std::pair<double, double> get_wipe_tower_cone_base(double width, double height, double depth, double angle_deg);
static std::vector<std::vector<float>> extract_wipe_volumes(const PrintConfig& config);
static std::vector<std::vector<float>> extract_wipe_volumes(const ConfigBase& config);
// Estimated total flush volume of a SEMM print with the given number of filaments,
// used to reserve wipe tower space before the tower is generated.
static float estimate_semm_flush_volume(const ConfigBase& config, size_t filaments_cnt);
// Construct ToolChangeResult from current state of WipeTower2 and WipeTowerWriter2.
@@ -38,6 +45,11 @@ public:
// Shared with the entry routing in GCode.cpp so the router and the tower agree.
static bool use_gap_wall(const PrintConfig& config);
// Whether the blocking toolchange temperature wait moves onto the wipe tower.
// Shared with the defer flag in GCode.cpp append_tcr2 so the deferral and the
// tower's tagged M109 can never disagree.
static bool wait_for_temp_enabled(const PrintConfig& config);
// x -- x coordinates of wipe tower in mm ( left bottom corner )
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
@@ -227,6 +239,7 @@ private:
size_t m_first_layer_idx = size_t(-1);
bool m_enable_tower_interface_features = false;
bool m_enable_tower_interface_cooldown_during_tower = false;
bool m_wait_for_temp_on_wipe_tower = false;
bool m_prev_layer_had_interface = false;
bool m_current_layer_has_interface = false;
@@ -263,6 +276,7 @@ private:
} m_bed_shape;
float m_bed_width; // width of the bed bounding box
Vec2f m_bed_bottom_left; // bottom-left corner coordinates (for rectangular beds)
Polygon m_bed_polygon; // printable_area contour (scaled)
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
@@ -385,7 +399,9 @@ private:
void toolchange_Change(
WipeTowerWriter2 &writer,
const size_t new_tool,
const std::string& new_material);
const std::string& new_material,
const int wait_for_temp,
const bool wait_beside_tower);
void toolchange_Load(
WipeTowerWriter2 &writer,
+1 -1
View File
@@ -94,7 +94,7 @@ public:
void* volume{nullptr};
std::vector<int>* plane_indices{nullptr};
Transform3d world_tran;
Transform3d world_tran = Transform3d::Identity();
std::shared_ptr<std::vector<SurfaceFeature>> world_plane_features{nullptr};
std::shared_ptr<SurfaceFeature> origin_surface_feature{nullptr};
+2 -2
View File
@@ -3243,9 +3243,9 @@ double Model::findMaxSpeed(const ModelObject* object) {
if (objectKey == "outer_wall_speed")
externalPerimeterSpeedObj = object->config.get().opt_float_nullable(objectKey, 0);
if (objectKey == "small_perimeter_speed")
smallPerimeterSpeedObj = object->config.get().opt_float_nullable(objectKey, 0);
smallPerimeterSpeedObj = object->config.get().opt_float_or_percent_nullable(objectKey, 0).get_abs_value(externalPerimeterSpeedObj);
if (objectKey == "small_support_perimeter_speed")
smallSupportPerimeterSpeedObj = object->config.get().opt_float_nullable(objectKey, 0);
smallSupportPerimeterSpeedObj = object->config.get().opt_float_or_percent_nullable(objectKey, 0).get_abs_value(supportSpeedObj);
}
objMaxSpeed = std::max(perimeterSpeedObj, std::max(externalPerimeterSpeedObj, std::max(infillSpeedObj, std::max(solidInfillSpeedObj, std::max(topSolidInfillSpeedObj, std::max(supportSpeedObj, std::max(smallPerimeterSpeedObj, std::max(smallSupportPerimeterSpeedObj, objMaxSpeed))))))));
if (objMaxSpeed <= 0) objMaxSpeed = 250.;
+35 -85
View File
@@ -48,100 +48,50 @@ struct OrientMesh {
};
// params for minimizing support area
struct OrientParamsArea {
float TAR_A = 0.015f;
float TAR_B = 0.177f;
float RELATIVE_F = 20;
float CONTOUR_F = 0.5f;
float BOTTOM_F = 2.5f;
float BOTTOM_HULL_F = 0.1f;
float TAR_C = 0.1f;
float TAR_D = 1;
float TAR_E = 0.0115f;
float FIRST_LAY_H = 0.2f;//0.0475;
float VECTOR_TOL = -0.00083f;
float NEGL_FACE_SIZE = 0.01f;
float ASCENT = -0.5f;
float PLAFOND_ADV = 0.0599f;
float CONTOUR_AMOUNT = 0.0182427f;
float OV_H = 2.574f;
float height_offset = 2.3728f;
float height_log = 0.041375f;
float height_log_k = 1.9325457f;
float LAF_MAX = 0.999f; // cos(1.4\degree) for low angle face 0.9997f
float LAF_MIN = 0.97f; // cos(14\degree) 0.9703f
float TAR_LAF = 0.001f; //0.01f
float TAR_PROJ_AREA = 0.1f;
float BOTTOM_MIN = 0.1f; // min bottom area. If lower than it the object may be unstable
float BOTTOM_MAX = 2000; // max bottom area. If get to it the object is stable enough (further increase bottom area won't do more help)
float height_to_bottom_hull_ratio_MIN = 1;
float BOTTOM_HULL_MAX = 2000;// max bottom hull area
float APPERANCE_FACE_SUPP=3; // penalty of generating supports on appearance face
float overhang_angle = 60.f;
bool use_low_angle_face = true;
bool min_volume = false;
Eigen::Vector3f fun_dir;
/// Allow parallel execution.
bool parallel = true;
/// Progress indicator callback called when an object gets packed.
/// The unsigned argument is the number of items remaining to pack.
std::function<void(unsigned, std::string)> progressind = {};
/// A predicate returning true if abort is needed.
std::function<bool(void)> stopcondition = {};
OrientParamsArea() = default;
};
struct OrientParams {
float TAR_A = 0.01f;//0.128f;
float TAR_B = 0.177f;
float RELATIVE_F= 6.610621027964314f;
float CONTOUR_F = 0.23228623269775997f;
float BOTTOM_F = 1.167152017941474f;
float BOTTOM_HULL_F = 0.1f;
float TAR_C = 0.24308070476924726f;
float TAR_D = 0.6284515508160871f;
float TAR_E = 0;//0.032157292647062234;
float FIRST_LAY_H = 0.2f;//0.029;
float VECTOR_TOL = -0.0011163303070972383f;
float NEGL_FACE_SIZE = 0.1f;
float ASCENT= -0.5f;
float PLAFOND_ADV = 0.04079208948120519f;
float CONTOUR_AMOUNT = 0.0101472219892684f;
float OV_H = 1.0370178217794535f;
float height_offset = 2.7417608343142073f;
float height_log = 0.06442030687034085f;
float height_log_k = 0.3933594673063997f;
float LAF_MAX = 0.999f; // cos(1.4\degree) for low angle face //0.9997f;
float LAF_MIN= 0.9703f; // cos(14\degree) 0.9703f;
float TAR_LAF = 0.01f; //0.1f
float TAR_PROJ_AREA = 0.1f;
float BOTTOM_MIN = 0.1f; // min bottom area. If lower than it the objects may be unstable
float BOTTOM_MAX = 2000; //400
float height_to_bottom_hull_ratio_MIN = 1;
float BOTTOM_HULL_MAX = 2000;// max bottom hull area to clip //600
float APPERANCE_FACE_SUPP=3; // penalty of generating supports on appearance face
float overhang_angle = 60.f;
bool use_low_angle_face = true;
bool min_volume = false;
Eigen::Vector3f fun_dir;
float TAR_A { 0.01f }; // 0.128f;
float TAR_B { 0.177f };
float RELATIVE_F { 6.610621027964314f };
float CONTOUR_F { 0.23228623269775997f };
float BOTTOM_F { 1.167152017941474f };
float BOTTOM_HULL_F { 0.1f };
float TAR_C { 0.24308070476924726f };
float TAR_D { 0.6284515508160871f };
float TAR_E { 0}; // 0.032157292647062234;
float FIRST_LAY_H { 0.2f}; // 0.029;
float VECTOR_TOL { -0.0011163303070972383f };
float NEGL_FACE_SIZE { 0.1f };
float ASCENT { -0.5f };
float PLAFOND_ADV { 0.04079208948120519f };
float CONTOUR_AMOUNT { 0.0101472219892684f };
float OV_H { 1.0370178217794535f };
float height_offset { 2.7417608343142073f };
float height_log { 0.06442030687034085f };
float height_log_k { 0.3933594673063997f };
float LAF_MAX { 0.999f }; // cos(1.4\degree) for low angle face //0.9997f;
float LAF_MIN { 0.9703f }; // cos(14\degree) 0.9703f;
float TAR_LAF { 0.01f }; // 0.1f
float TAR_PROJ_AREA { 0.1f };
float BOTTOM_MIN { 0.1f }; // min bottom area. If lower than it the objects may be unstable
float BOTTOM_MAX { 2000 }; // 400
float height_to_bottom_hull_ratio_MIN { 1 };
float BOTTOM_HULL_MAX { 2000 }; // max bottom hull area to clip //600
float APPERANCE_FACE_SUPP { 3 }; // penalty of generating supports on appearance face
float overhang_angle { 60.f };
bool use_low_angle_face { true };
bool min_volume { false };
Eigen::Vector3f fun_dir {};
/// Allow parallel execution.
bool parallel = false;
bool parallel { false };
/// Progress indicator callback called when an object gets packed.
/// The unsigned argument is the number of items remaining to pack.
std::function<void(unsigned, std::string)> progressind = {};
std::function<void(unsigned, std::string)> progressind {};
/// A predicate returning true if abort is needed.
std::function<bool(void)> stopcondition = {};
std::function<bool(void)> stopcondition {};
OrientParams() = default;
};
+1 -1
View File
@@ -1423,7 +1423,7 @@ static std::vector<std::string> s_Preset_printer_options {
"use_relative_e_distances", "extruder_type", "use_firmware_retraction", "printer_notes",
"grab_length", "support_object_skip_flush", "physical_extruder_map",
"cooling_tube_retraction",
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "wipe_tower_type", "purge_in_prime_tower", "enable_filament_ramming", "tool_change_on_wipe_tower",
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "wipe_tower_type", "purge_in_prime_tower", "enable_filament_ramming", "tool_change_on_wipe_tower", "wait_for_temp_on_wipe_tower",
"z_offset",
"disable_m73", "preferred_orientation", "emit_machine_limits_to_gcode", "pellet_modded_printer", "support_multi_bed_types", "use_3mf", "default_bed_type", "bed_mesh_min","bed_mesh_max","bed_mesh_probe_distance", "adaptive_bed_mesh_margin", "enable_long_retraction_when_cut","long_retractions_when_cut","retraction_distances_when_cut",
"bed_temperature_formula", "nozzle_flush_dataset",
+1 -1
View File
@@ -5378,7 +5378,7 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
f_multiplier.resize(nozzle_nums, 1.f);
}
if ( (num_filaments * num_filaments) != size_t(old_matrix.size() / old_nozzle_nums) ) {
if (old_matrix.size() != num_filaments * num_filaments * nozzle_nums) {
// First verify if purging volumes presets for each extruder matches number of extruders
std::vector<double>& filaments = this->project_config.option<ConfigOptionFloats>("flush_volumes_vector")->values;
while (filaments.size() < 2* num_filaments) {
+39 -21
View File
@@ -282,6 +282,14 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "wipe_tower_x"
|| opt_key == "wipe_tower_y"
|| opt_key == "wipe_tower_rotation_angle") {
// The tower gcode itself is position-independent (position and rotation are applied
// at export), except that the wait_for_temp_on_wipe_tower park bakes a bed-relative
// side choice into it (WipeTower2::toolchange_Change) — regenerate it when the tower
// moves. Gating on the old config is safe: both inputs of wait_for_temp_enabled
// invalidate psWipeTower themselves when they are part of the same diff.
if ((opt_key == "wipe_tower_x" || opt_key == "wipe_tower_y" || opt_key == "wipe_tower_rotation_angle")
&& WipeTower2::wait_for_temp_enabled(m_config))
steps.emplace_back(psWipeTower);
steps.emplace_back(psSkirtBrim);
} else if (
opt_key == "slicing_pipeline_plugin"
@@ -382,6 +390,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "wiping_volumes_extruders"
|| opt_key == "enable_filament_ramming"
|| opt_key == "tool_change_on_wipe_tower"
|| opt_key == "wait_for_temp_on_wipe_tower"
|| opt_key == "purge_in_prime_tower"
|| opt_key == "z_offset"
|| opt_key == "support_multi_bed_types"
@@ -1039,13 +1048,14 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y));
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y + depth));
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y + depth));
wipe_tower_convex_hull.rotate(a);
wipe_tower_convex_hull.rotate(Geometry::deg2rad(a), Point(scale_(x), scale_(y)));
convex_hulls_temp.push_back(wipe_tower_convex_hull);
} else {
//here, wipe_tower_polygon is not always convex.
Polygon wipe_tower_polygon;
if (print.wipe_tower_data().wipe_tower_mesh_data)
wipe_tower_polygon = print.wipe_tower_data().wipe_tower_mesh_data->bottom;
wipe_tower_polygon.rotate(Geometry::deg2rad(a));
wipe_tower_polygon.translate(Point(scale_(x), scale_(y)));
convex_hulls_temp.push_back(wipe_tower_polygon);
}
@@ -1064,6 +1074,22 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
if (print_config.enable_wrapping_detection.value && !intersection({wrapping_poly}, convex_hulls_temp).empty()) {
return {L("Prime Tower") + L(" is too close to clumping detection area, and collisions will be caused.\n")};
}
// Skip the containment check for towers that will never be printed (single-filament
// prints without smooth timelapse keep the config's tower position but emit nothing).
// Pre-generation only the body square is tested — the auto-brim estimate can overshoot
// the generated brim by several mm and must not hard-fail a print that physically fits.
// Post-generation the mesh bottom already includes the real brim, so the exact
// footprint is tested.
if (filaments_count > 1 || print.enable_timelapse_print()) {
// The shared printable polygon is plate-local, while the tower polygons above are
// already shifted by the plate origin.
Polygons printable_polys = print.get_extruder_shared_printable_polygon();
const Point plate_shift(scale_(plate_origin.x()), scale_(plate_origin.y()));
for (Polygon &p : printable_polys)
p.translate(plate_shift);
if (!diff(convex_hulls_temp, printable_polys).empty())
return {L("Prime Tower") + L(" is partially outside the printable area, and it cannot be printed.\n")};
}
return {};
}
@@ -3918,6 +3944,12 @@ const WipeTowerData &Print::wipe_tower_data(size_t filaments_cnt) const
double volume = wipe_volume * filament_depth_count;
if (m_config.nozzle_diameter.values.size() == 2) volume += filament_change_volume * (int) (filaments_cnt / 2);
// Sizing 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.
const bool semm_flush = m_config.purge_in_prime_tower && m_config.single_extruder_multi_material;
if (semm_flush) volume = WipeTower2::estimate_semm_flush_volume(m_config, filaments_cnt);
if (m_config.wipe_tower_wall_type.value == WipeTowerWallType::wtwRib) {
double depth = std::sqrt(volume / layer_height * extra_spacing);
if (need_wipe_tower || filaments_cnt > 1) {
@@ -3929,30 +3961,16 @@ const WipeTowerData &Print::wipe_tower_data(size_t filaments_cnt) const
}
}
else {
double width = m_config.prime_tower_width;
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.
std::vector<std::vector<float>> wipe_volumes = WipeTower2::extract_wipe_volumes(m_config);
std::vector<float> max_wipe_volumes;
for (const std::vector<float> &v : wipe_volumes)
max_wipe_volumes.emplace_back(*std::max_element(v.begin(), v.end()));
float maximum = std::accumulate(max_wipe_volumes.begin(), max_wipe_volumes.end(), 0.f);
maximum = maximum * filaments_cnt / max_wipe_volumes.size();
// Orca: it's overshooting a bit, so let's reduce it a bit
maximum *= 0.6;
const_cast<Print *>(this)->m_wipe_tower_data.depth = maximum / (layer_height * width);
} else {
double depth = volume / (layer_height * width) * extra_spacing;
if (need_wipe_tower || m_wipe_tower_data.depth > EPSILON) {
double width = m_config.prime_tower_width;
double depth = volume / (layer_height * width);
// The flush volumes already hold the spacing between wipes.
if (!semm_flush) depth *= extra_spacing;
if (need_wipe_tower || depth > EPSILON) {
float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height);
depth = std::max((double) min_wipe_tower_depth, depth);
}
const_cast<Print *>(this)->m_wipe_tower_data.depth = depth;
}
const_cast<Print *>(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width;
const_cast<Print *>(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width;
}
if (m_config.prime_tower_brim_width < 0) const_cast<Print *>(this)->m_wipe_tower_data.brim_width = WipeTower::get_auto_brim_by_height(max_height);
}
+5 -3
View File
@@ -559,9 +559,11 @@ static inline bool model_volume_solid_or_modifier(const ModelVolume &mv)
static inline Transform3f trafo_for_bbox(const Transform3d &object_trafo, const Transform3d &volume_trafo)
{
Transform3d m = object_trafo * volume_trafo;
m.translation().x() = 0.;
m.translation().y() = 0.;
// Orca: Keep the volume's local XY offset for multipart overlap checks, but remove the object's bed placement.
Transform3d object_trafo_local = object_trafo;
object_trafo_local.translation().x() = 0.;
object_trafo_local.translation().y() = 0.;
Transform3d m = object_trafo_local * volume_trafo;
return m.cast<float>();
}
+22 -1
View File
@@ -6570,6 +6570,17 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("wait_for_temp_on_wipe_tower", coBool);
def->label = L("Wait for temperature on wipe tower");
def->tooltip = L("Pick up the new tool without waiting for it to reach printing temperature, travel to the wipe "
"tower, and wait for the temperature there, right before purging. Ooze from the heat-up lands on "
"the tower instead of the model, and the travel overlaps with the heating. "
"Only relevant for multi-extruder (multi-toolhead) printers using a Type 2 wipe tower. "
"The firmware or tool change macro must not wait for the temperature itself. "
"When disabled, the temperature wait is issued right after the tool change command.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("wipe_tower_no_sparse_layers", coBool);
def->label = L("No sparse layers (beta)");
@@ -10415,6 +10426,16 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector<st
}
void set_variant_override(ConfigOptionVectorBase &target, const ConfigOptionVectorBase &source,
const std::vector<int> &variant_index, int stride)
{
// A single-value object or region override applies to every nozzle variant.
std::vector<int> indices = variant_index;
if (source.size() == 1 && !source.is_nil(0))
std::fill(indices.begin(), indices.end(), 0);
target.set_to_index(&source, indices, stride);
}
//used for object/region config
//use the smallest of multiple to single
@@ -11492,7 +11513,7 @@ void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPr
else {
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(opt_dest);
opt_vec_src->set_to_index(opt_vec_dest, variant_index, stride);
set_variant_override(*opt_vec_src, *opt_vec_dest, variant_index, stride);
}
}
}
+53
View File
@@ -842,6 +842,9 @@ extern std::set<std::string> printer_options_with_variant_1;
extern std::set<std::string> printer_options_with_variant_2;
extern std::set<std::string> empty_options;
void set_variant_override(ConfigOptionVectorBase &target, const ConfigOptionVectorBase &source,
const std::vector<int> &variant_index, int stride = 1);
extern std::set<std::string> filament_dev_options;
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride = 1);
@@ -1660,6 +1663,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, purge_in_prime_tower))
((ConfigOptionBool, enable_filament_ramming))
((ConfigOptionBool, tool_change_on_wipe_tower))
((ConfigOptionBool, wait_for_temp_on_wipe_tower))
((ConfigOptionBool, support_multi_bed_types))
((ConfigOptionBool, use_3mf))
@@ -2393,6 +2397,55 @@ static void set_flush_volumes_matrix(std::vector<T> &out_matrix, const std::vect
}
}
template<class T>
static bool has_zero_flush_volume_for_used_filaments(const std::vector<T> &fv_matrix,
const std::vector<T> &flush_multipliers,
const std::vector<int> &used_filaments)
{
if (used_filaments.size() < 2 || flush_multipliers.empty())
return false;
if (fv_matrix.size() % flush_multipliers.size() != 0)
return false;
const size_t matrix_len = fv_matrix.size() / flush_multipliers.size();
const size_t row_len = size_t(std::sqrt(double(matrix_len)));
if (row_len < 2 || row_len * row_len != matrix_len)
return false;
std::vector<int> filtered_filaments;
filtered_filaments.reserve(used_filaments.size());
for (int filament_id : used_filaments) {
if (filament_id <= 0 || filament_id > int(row_len))
continue;
if (std::find(filtered_filaments.begin(), filtered_filaments.end(), filament_id) == filtered_filaments.end())
filtered_filaments.push_back(filament_id);
}
if (filtered_filaments.size() < 2)
return false;
for (T multiplier : flush_multipliers) {
if (multiplier == 0)
return true;
}
for (size_t nozzle_idx = 0; nozzle_idx < flush_multipliers.size(); nozzle_idx++) {
const size_t block_offset = nozzle_idx * matrix_len;
for (int from_id : filtered_filaments) {
for (int to_id : filtered_filaments) {
if (from_id == to_id)
continue;
const size_t matrix_idx = block_offset + size_t(from_id - 1) * row_len + size_t(to_id - 1);
if (matrix_idx < fv_matrix.size() && fv_matrix[matrix_idx] == 0)
return true;
}
}
}
return false;
}
size_t get_extruder_index(const GCodeConfig& config, unsigned int filament_id);
} // namespace Slic3r
+1 -1
View File
@@ -3812,7 +3812,7 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr
else {
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(my_opt);
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(it->second.get());
opt_vec_src->set_to_index(opt_vec_dest, variant_index, 1);
set_variant_override(*opt_vec_src, *opt_vec_dest, variant_index);
}
}
}
+4 -4
View File
@@ -56,12 +56,12 @@ public:
int & i,
Eigen::Matrix<double, 1, 3> &closest)
{
size_t idx_unsigned = 0;
Vec3d closest_vec3d(closest);
double dist =
size_t idx_unsigned { 0 };
Vec3d closest_vec3d { Vec3d::Zero() };
const double dist {
AABBTreeIndirect::squared_distance_to_indexed_triangle_set(
its.vertices, its.indices, m_tree, point, idx_unsigned,
closest_vec3d);
closest_vec3d) };
i = int(idx_unsigned);
closest = closest_vec3d;
return dist;
+89 -37
View File
@@ -4,6 +4,7 @@
#include "GUI_App.hpp"
#include "libslic3r/Preset.hpp"
#include "I18N.hpp"
#include <algorithm>
#include <boost/log/trivial.hpp>
#include <wx/colordlg.h>
#include <wx/dcgraph.h>
@@ -1075,54 +1076,105 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
// Sort the filaments
{
static std::unordered_map<wxString, int> sorted_names
{ {"Bambu PLA Basic", 0},
{"Bambu PLA Matte", 1},
{"Bambu PETG HF", 2},
{"Bambu ABS", 3},
{"Bambu PLA Silk", 4},
{"Bambu PLA-CF" , 5},
{"Bambu PLA Galaxy", 6},
{"Bambu PLA Metal", 7},
{"Bambu PLA Marble", 8},
{"Bambu PETG-CF", 9},
{"Bambu PETG Translucent", 10},
{"Bambu ABS-GF", 11}
std::unordered_map<wxString, int> selected_filament_ranks;
// Helper lambda to find a filament Preset by name. We can call this multiple times to walk the inheritance chain and find the base filament.
auto find_filament_by_name = [](const std::string& wanted, const PresetCollection& filaments) -> const Preset* {
for (auto it = filaments.begin(); it != filaments.end(); ++it) {
if (it->name == wanted) {
return &(*it);
}
}
return nullptr;
};
static std::vector<wxString> sorted_vendors { "Bambu Lab", "Generic" };
static std::vector<wxString> sorted_types { "PLA", "PETG", "ABS", "TPU" };
auto _filament_sorter = [&query_filament_vendors, &query_filament_types](const wxString& left, const wxString& right) -> bool
{
{ // Compare name order
const auto& iter1 = sorted_names.find(left);
int name_order1 = (iter1 != sorted_names.end()) ? iter1->second : INT_MAX;
// For each active filament preset, find its base filament alias and promote it in extruder order.
auto bundle = wxGetApp().preset_bundle;
const auto& preset_names = bundle->filament_presets;
for (size_t i = preset_names.size(); i-- > 0; ) {
std::string wanted = preset_names[i];
const int sort_rank = -static_cast<int>(preset_names.size() - i);
const Preset* match = nullptr;
const auto& iter2 = sorted_names.find(right);
int name_order2 = (iter2 != sorted_names.end()) ? iter2->second : INT_MAX;
if (name_order1 != name_order2)
do {
auto find_result = find_filament_by_name(wanted, bundle->filaments);
if (!find_result) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " No available filament name matches " << wanted;
break;
}
match = find_result;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Found available filament matching current preset name " << wanted
<< " - Name: " << match->name << " - Alias: " << match->alias
<< " - Inherits: " << match->inherits();
if (match->inherits().length() == 0) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " No more inherits so we reached the base filament";
break;
}
wanted = match->inherits();
} while (1); // Or loop while (match->alias.length() == 0) because existence of alias and inherits on a Preset seem to be exclusive
if (!match) {
continue;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Update filament rank to " + std::to_string(sort_rank) + " for preset Name: "
<< match->name << " - Alias: " << match->alias;
selected_filament_ranks.insert_or_assign(match->alias, sort_rank);
}
static const std::vector<wxString> sorted_vendors { "Generic" };
static const std::vector<wxString> sorted_types { "PLA", "PETG", "ABS", "TPU" };
auto priority_rank = [](const std::vector<wxString>& priorities, const wxString& value) {
const auto iter = std::find_if(priorities.begin(), priorities.end(), [&value](const wxString& priority) {
return priority.CmpNoCase(value) == 0;
});
return iter - priorities.begin();
};
auto _filament_sorter = [&query_filament_vendors, &query_filament_types, &selected_filament_ranks, &priority_rank](const wxString& left, const wxString& right) -> bool
{
{ // Compare selected filament order
const auto& iter1 = selected_filament_ranks.find(left);
int selected_order1 = (iter1 != selected_filament_ranks.end()) ? iter1->second : INT_MAX;
const auto& iter2 = selected_filament_ranks.find(right);
int selected_order2 = (iter2 != selected_filament_ranks.end()) ? iter2->second : INT_MAX;
if (selected_order1 != selected_order2)
{
return name_order1 < name_order2;
return selected_order1 < selected_order2;
}
}
{ // Compare vendor
auto iter1 = std::find(sorted_vendors.begin(), sorted_vendors.end(), query_filament_vendors[left]);
auto iter2 = std::find(sorted_vendors.begin(), sorted_vendors.end(), query_filament_vendors[right]);
if (iter1 != iter2)
{
return iter1 < iter2;
};
const wxString& vendor1 = query_filament_vendors.at(left);
const wxString& vendor2 = query_filament_vendors.at(right);
const auto rank1 = priority_rank(sorted_vendors, vendor1);
const auto rank2 = priority_rank(sorted_vendors, vendor2);
if (rank1 != rank2)
return rank1 < rank2;
const int vendor_compare = vendor1.CmpNoCase(vendor2);
if (vendor_compare != 0)
return vendor_compare < 0;
}
{ // Compare type
auto iter1 = std::find(sorted_types.begin(), sorted_types.end(), query_filament_types[left]);
auto iter2 = std::find(sorted_types.begin(), sorted_types.end(), query_filament_types[right]);
if (iter1 != iter2)
{
return iter1 < iter2;
}
const wxString& type1 = query_filament_types.at(left);
const wxString& type2 = query_filament_types.at(right);
const auto rank1 = priority_rank(sorted_types, type1);
const auto rank2 = priority_rank(sorted_types, type2);
if (rank1 != rank2)
return rank1 < rank2;
const int type_compare = type1.CmpNoCase(type2);
if (type_compare != 0)
return type_compare < 0;
}
return left < right;
const int name_compare = left.CmpNoCase(right);
return name_compare != 0 ? name_compare < 0 : left < right;
};
std::sort(filament_items.begin(), filament_items.end(), _filament_sorter);
+54 -15
View File
@@ -12,6 +12,7 @@
#include "libslic3r/GCode/AdaptivePAProcessor.hpp"
#include "Plater.hpp"
#include <algorithm>
#include <sstream>
#include <wx/msgdlg.h>
@@ -250,6 +251,59 @@ void ConfigManipulation::check_chamber_minimal_temperature(DynamicPrintConfig* c
}
}
void ConfigManipulation::layer_height_limits(double& min_layer_height, double& max_layer_height) const
{
const DynamicPrintConfig& printer_config = GUI::wxGetApp().preset_bundle->printers.get_edited_preset().config;
const std::vector<double>& min_limits = printer_config.option<ConfigOptionFloats>("min_layer_height")->values;
const std::vector<double>& max_limits = printer_config.option<ConfigOptionFloats>("max_layer_height")->values;
min_layer_height = *std::min_element(min_limits.begin(), min_limits.end());
max_layer_height = *std::max_element(max_limits.begin(), max_limits.end());
}
bool ConfigManipulation::check_layer_height(DynamicPrintConfig* config)
{
double min_layer_height = 0., max_layer_height = 0.;
layer_height_limits(min_layer_height, max_layer_height);
const double layer_height = config->opt_float("layer_height");
if (min_layer_height > EPSILON && layer_height < EPSILON) {
const wxString msg_text = wxString::Format(_L("Layer height is too small. It will be set to the minimum (%g mm)."), min_layer_height);
MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxOK);
dialog.SetButtonLabel(wxID_OK, _L("OK"));
is_msg_dlg_already_exist = true;
dialog.ShowModal();
is_msg_dlg_already_exist = false;
DynamicPrintConfig new_conf = *config;
new_conf.set_key_value("layer_height", new ConfigOptionFloat(min_layer_height));
apply(config, &new_conf);
return true;
}
if (max_layer_height > EPSILON && layer_height > max_layer_height + EPSILON)
return layer_height_out_of_range_dialog(config, max_layer_height);
if (min_layer_height > EPSILON && layer_height < min_layer_height - EPSILON)
return layer_height_out_of_range_dialog(config, min_layer_height);
return false;
}
bool ConfigManipulation::layer_height_out_of_range_dialog(DynamicPrintConfig* config, double clamp_to)
{
wxString msg_text = _(L("Layer height is outside the limits set in Printer Settings -> Extruder -> Layer height limits, "
"this may cause printing quality issues."));
msg_text += "\n\n" + wxString::Format(_L("Adjust it to the limit (%g mm) automatically?"), clamp_to);
MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO);
dialog.SetButtonLabel(wxID_YES, _L("Adjust"));
dialog.SetButtonLabel(wxID_NO, _L("Ignore"));
is_msg_dlg_already_exist = true;
const bool adjust = dialog.ShowModal() == wxID_YES;
if (adjust) {
DynamicPrintConfig new_conf = *config;
new_conf.set_key_value("layer_height", new ConfigOptionFloat(clamp_to));
apply(config, &new_conf);
}
is_msg_dlg_already_exist = false;
return adjust;
}
void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config, const bool is_plate_config)
{
// #ys_FIXME_to_delete
@@ -264,7 +318,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
// layer_height shouldn't be equal to zero
auto layer_height = config->opt_float("layer_height");
auto gpreset = GUI::wxGetApp().preset_bundle->printers.get_edited_preset();
if (layer_height < EPSILON)
{
const wxString msg_text = _(L("Layer height too small\nIt has been reset to 0.2"));
@@ -277,20 +330,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
is_msg_dlg_already_exist = false;
}
//BBS: limite the max layer_herght
auto max_lh = gpreset.config.opt_float("max_layer_height",0);
if (max_lh > 0.2 && layer_height > max_lh+ EPSILON)
{
const wxString msg_text = wxString::Format(L"Too large layer height.\nReset to %0.3f.", max_lh);
MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *config;
is_msg_dlg_already_exist = true;
dialog.ShowModal();
new_conf.set_key_value("layer_height", new ConfigOptionFloat(max_lh));
apply(config, &new_conf);
is_msg_dlg_already_exist = false;
}
//BBS: ironing_spacing shouldn't be too small or equal to zero
if (config->opt_float("ironing_spacing") < 0.05)
{
+3
View File
@@ -86,6 +86,9 @@ public:
void check_filament_max_volumetric_speed(DynamicPrintConfig *config);
void check_chamber_temperature(DynamicPrintConfig* config);
void check_chamber_minimal_temperature(DynamicPrintConfig* config);
bool check_layer_height(DynamicPrintConfig* config);
bool layer_height_out_of_range_dialog(DynamicPrintConfig* config, double clamp_to);
void layer_height_limits(double& min_layer_height, double& max_layer_height) const;
void set_is_BBL_Printer(bool is_bbl_printer) { is_BBL_Printer = is_bbl_printer; };
bool get_is_BBL_Printer() { return is_BBL_Printer; };
// SLA print
+3 -1
View File
@@ -156,6 +156,8 @@ void ConnectPrinterDialog::on_input_enter(wxCommandEvent& evt)
void ConnectPrinterDialog::on_button_confirm(wxCommandEvent &event)
{
wxString code = m_textCtrl_code->GetTextCtrl()->GetValue();
if (code.empty())
code = "88888888";
for (char c : code) {
if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))) {
show_error(this, _L("Invalid input"));
@@ -163,7 +165,7 @@ void ConnectPrinterDialog::on_button_confirm(wxCommandEvent &event)
}
}
if (m_obj) {
m_obj->set_user_access_code(code.ToStdString());
m_obj->set_access_code(code.ToStdString());
}
EndModal(wxID_OK);
}
+14 -5
View File
@@ -15,6 +15,18 @@
using namespace nlohmann;
namespace {
// Orca: access_code and user_access_code used to be separate AppConfig keys before the two
// fields were merged; fall back to the legacy key so existing users' saved codes aren't lost.
std::string get_access_code_with_legacy_fallback(Slic3r::AppConfig* config, const std::string& dev_id)
{
std::string code = config->get("access_code", dev_id);
if (code.empty())
code = config->get("user_access_code", dev_id);
return code;
}
}
namespace Slic3r
{
DeviceManager::DeviceManager(NetworkAgent* agent)
@@ -48,8 +60,7 @@ namespace Slic3r
obj->bind_sec_link = "secure";
obj->m_is_online = true;
obj->last_alive = Slic3r::Utils::get_current_time_utc();
obj->set_access_code(config->get("access_code", m.dev_id), false);
obj->set_user_access_code(config->get("user_access_code", m.dev_id), false);
obj->set_access_code(get_access_code_with_legacy_fallback(config, m.dev_id), false);
if (obj->has_access_right()) {
localMachineList.insert(std::make_pair(m.dev_id, obj));
} else {
@@ -339,8 +350,7 @@ namespace Slic3r
//load access code
AppConfig* config = Slic3r::GUI::wxGetApp().app_config;
if (config) {
obj->set_access_code(Slic3r::GUI::wxGetApp().app_config->get("access_code", dev_id), false);
obj->set_user_access_code(Slic3r::GUI::wxGetApp().app_config->get("user_access_code", dev_id), false);
obj->set_access_code(get_access_code_with_legacy_fallback(config, dev_id), false);
}
localMachineList.insert(std::make_pair(dev_id, obj));
@@ -382,7 +392,6 @@ namespace Slic3r
obj->m_is_online = true;
obj->last_alive = Slic3r::Utils::get_current_time_utc();
obj->set_access_code(access_code, false);
obj->set_user_access_code(access_code, false);
update_local_machine(*obj);
+1 -35
View File
@@ -449,9 +449,7 @@ bool MachineObject::HasRecentLanMessage()
std::string MachineObject::get_access_code() const
{
if (get_user_access_code().empty())
return access_code;
return get_user_access_code();
return access_code;
}
void MachineObject::set_access_code(std::string code, bool only_refresh)
@@ -470,37 +468,6 @@ void MachineObject::set_access_code(std::string code, bool only_refresh)
}
}
void MachineObject::erase_user_access_code()
{
this->user_access_code = "";
AppConfig* config = GUI::wxGetApp().app_config;
if (config) {
GUI::wxGetApp().app_config->erase("user_access_code", get_dev_id());
//GUI::wxGetApp().app_config->save();
}
}
void MachineObject::set_user_access_code(std::string code, bool only_refresh)
{
this->user_access_code = code;
if (only_refresh && !code.empty()) {
AppConfig* config = GUI::wxGetApp().app_config;
if (config && !code.empty()) {
GUI::wxGetApp().app_config->set_str("user_access_code", get_dev_id(), code);
DeviceManager::update_local_machine(*this);
}
}
}
std::string MachineObject::get_user_access_code() const
{
AppConfig* config = GUI::wxGetApp().app_config;
if (config) {
return GUI::wxGetApp().app_config->get("user_access_code", get_dev_id());
}
return "";
}
std::string MachineObject::get_show_printer_type() const
{
std::string printer_type = this->printer_type;
@@ -2907,7 +2874,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
std::string access_code = j_pre["system"]["access_code"].get<std::string>();
if (!access_code.empty()) {
set_access_code(access_code);
set_user_access_code(access_code);
}
}
}
-6
View File
@@ -113,7 +113,6 @@ private:
std::string dev_name;
std::string dev_ip;
std::string access_code;
std::string user_access_code;
// type, time stamp, delay
std::vector<std::tuple<std::string, uint64_t, uint64_t>> message_delay;
@@ -228,11 +227,6 @@ public:
std::string get_access_code() const;
void set_access_code(std::string code, bool only_refresh = true);
/*user access code*/
void set_user_access_code(std::string code, bool only_refresh = true);
void erase_user_access_code();
std::string get_user_access_code() const;
//PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN;
std::string printer_type; /* model_id */
std::string get_show_printer_type() const;
@@ -1,9 +1,9 @@
//**********************************************************/
/* File: uiAmsHumidityPopup.cpp
/**********************************************************
* File: uiAmsHumidityPopup.cpp
* Description: The popup with DevAms Humidity
*
* \n class uiAmsHumidityPopup
//**********************************************************/
**********************************************************/
#include "uiAmsHumidityPopup.h"
@@ -191,4 +191,4 @@ void uiAmsPercentHumidityDryPopup::msw_rescale()
} // namespace GUI
} // namespace Slic3r
} // namespace Slic3r
@@ -1,9 +1,9 @@
//**********************************************************/
/* File: uiAmsHumidityPopup.h
/**********************************************************
* File: uiAmsHumidityPopup.h
* Description: The popup with DevAms Humidity
*
* \n class uiAmsHumidityPopup
//**********************************************************/
**********************************************************/
#pragma once
#include "slic3r/GUI/Widgets/AMSItem.hpp"
@@ -68,7 +68,7 @@ private:
wxStaticBitmap* m_dry_state_img;
Label* m_dry_state;
Label* m_humidity_header;
Label* m_humidity_label;
@@ -81,4 +81,4 @@ private:
wxSizer* m_sizer;
};
}} // namespace Slic3r::GUI
}} // namespace Slic3r::GUI
@@ -1,9 +1,9 @@
//**********************************************************/
/* File: uiDeviceUpdateVersion.cpp
/**********************************************************
* File: uiDeviceUpdateVersion.cpp
* Description: The panel with firmware info
*
* \n class uiDeviceUpdateVersion
//**********************************************************/
**********************************************************/
#include "uiDeviceUpdateVersion.h"
@@ -114,4 +114,4 @@ void uiDeviceUpdateVersion::CreateWidgets()
Layout();
wxGetApp().UpdateDarkUIWin(this);
}
}
@@ -1,9 +1,9 @@
//**********************************************************/
/* File: uiDeviceUpdateVersion.h
/**********************************************************
* File: uiDeviceUpdateVersion.h
* Description: The panel with firmware info
*
* \n class uiDeviceUpdateVersion
//**********************************************************/
**********************************************************/
#pragma once
#include <wx/panel.h>
@@ -44,4 +44,4 @@ private:
wxStaticText* m_dev_version;
wxStaticBitmap* m_dev_upgrade_indicator;
};
};// end of namespace Slic3r::GUI
};// end of namespace Slic3r::GUI
+29 -62
View File
@@ -2871,6 +2871,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
}
if (wt && (need_wipe_tower || filaments_count > 1) && !wxGetApp().plater()->only_gcode_mode() && !wxGetApp().plater()->is_gcode_3mf()) {
// The tower size estimate reads printer- and filament-scope keys, which the print preset
// does not carry; built once here rather than per plate.
const DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config();
for (int plate_id = 0; plate_id < n_plates; plate_id++) {
// If print ByObject and there is only one object in the plate, the wipe tower is allowed to be generated.
PartPlate* part_plate = ppl.get_plate(plate_id);
@@ -2895,51 +2898,26 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
if (part_plate->get_objects_on_this_plate().empty()) continue;
float brim_width = print->wipe_tower_data(filaments_count).brim_width;
const DynamicPrintConfig &print_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
int nozzle_nums = wxGetApp().preset_bundle->get_printer_extruder_count();
Vec3d wipe_tower_size = ppl.get_plate(plate_id)->estimate_wipe_tower_size(print_cfg, w, v, nozzle_nums, 0, false, dynamic_cast<const ConfigOptionBool*>(dconfig.option("enable_wrapping_detection"))->value);
Vec3d wipe_tower_size = ppl.get_plate(plate_id)->estimate_wipe_tower_size(full_config, w, v, nozzle_nums, 0, false, dynamic_cast<const ConfigOptionBool*>(dconfig.option("enable_wrapping_detection"))->value);
{
const float margin = WIPE_TOWER_MARGIN + brim_width;
BoundingBoxf3 plate_bbox = part_plate->get_bounding_box();
BoundingBoxf plate_bbox_2d(Vec2d(plate_bbox.min(0), plate_bbox.min(1)), Vec2d(plate_bbox.max(0), plate_bbox.max(1)));
const std::vector<Pointfs> &extruder_areas = part_plate->get_extruder_areas();
for (Pointfs points : extruder_areas) {
BoundingBoxf bboxf(points);
plate_bbox_2d.min = plate_bbox_2d.min(0) >= bboxf.min(0) ? plate_bbox_2d.min : bboxf.min;
plate_bbox_2d.max = plate_bbox_2d.max(0) <= bboxf.max(0) ? plate_bbox_2d.max : bboxf.max;
}
coordf_t plate_bbox_x_min_local_coord = plate_bbox_2d.min(0) - plate_origin(0);
coordf_t plate_bbox_x_max_local_coord = plate_bbox_2d.max(0) - plate_origin(0);
coordf_t plate_bbox_y_max_local_coord = plate_bbox_2d.max(1) - plate_origin(1);
if (!current_print->is_step_done(psWipeTower) || !current_print->wipe_tower_data().wipe_tower_mesh_data) {
// update for wipe tower position
{
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(1000 + plate_id, x + plate_origin(0), y + plate_origin(1),
(float) wipe_tower_size(0), (float) wipe_tower_size(1), (float) wipe_tower_size(2),
a,
/*!print->is_step_done(psWipeTower)*/ true, brim_width);
int volume_idx_wipe_tower_old = volume_idxs_wipe_tower_old[plate_id];
if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
}
} else {
const float margin = 2.f;
auto tower_bottom = current_print->wipe_tower_data().wipe_tower_mesh_data->bottom;
tower_bottom.translate(scaled(Vec2d{x, y}));
tower_bottom.translate(scaled(Vec2d{plate_origin[0], plate_origin[1]}));
auto tower_bottom_bbox = get_extents(tower_bottom);
BoundingBoxf3 plate_bbox = wxGetApp().plater()->get_partplate_list().get_plate(plate_id)->get_build_volume(true);
BoundingBox plate_bbox2d = BoundingBox(scaled(Vec2f(plate_bbox.min[0], plate_bbox.min[1])), scaled(Vec2f(plate_bbox.max[0], plate_bbox.max[1])));
Vec2f offset = WipeTower::move_box_inside_box(tower_bottom_bbox, plate_bbox2d, scaled(margin));
int volume_idx_wipe_tower_new = m_volumes.load_real_wipe_tower_preview(1000 + plate_id, x + plate_origin(0), y + plate_origin(1),
current_print->wipe_tower_data().wipe_tower_mesh_data->real_wipe_tower_mesh,
current_print->wipe_tower_data().wipe_tower_mesh_data->real_brim_mesh,
true,a,/*!print->is_step_done(psWipeTower)*/ true, m_initialized);
int volume_idx_wipe_tower_old = volume_idxs_wipe_tower_old[plate_id];
if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
}
// The stored position is already clamped onto the bed, by
// set_default_wipe_tower_pos_for_plate and again on every drag.
if (!current_print->is_step_done(psWipeTower) || !current_print->wipe_tower_data().wipe_tower_mesh_data) {
// update for wipe tower position
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(1000 + plate_id, x + plate_origin(0), y + plate_origin(1),
(float) wipe_tower_size(0), (float) wipe_tower_size(1), (float) wipe_tower_size(2),
a,
/*!print->is_step_done(psWipeTower)*/ true, brim_width);
int volume_idx_wipe_tower_old = volume_idxs_wipe_tower_old[plate_id];
if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
} else {
int volume_idx_wipe_tower_new = m_volumes.load_real_wipe_tower_preview(1000 + plate_id, x + plate_origin(0), y + plate_origin(1),
current_print->wipe_tower_data().wipe_tower_mesh_data->real_wipe_tower_mesh,
current_print->wipe_tower_data().wipe_tower_mesh_data->real_brim_mesh,
true,a,/*!print->is_step_done(psWipeTower)*/ true, m_initialized);
int volume_idx_wipe_tower_old = volume_idxs_wipe_tower_old[plate_id];
if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
}
}
}
@@ -6999,7 +6977,7 @@ void GLCanvas3D::_update_select_plate_toolbar_stats_item(bool force_selected) {
else
m_sel_plate_toolbar.show_stats_item = false;
if (force_selected && m_sel_plate_toolbar.show_stats_item)
if (force_selected && m_sel_plate_toolbar.show_stats_item && m_sel_plate_toolbar.m_all_plates_stats_item)
m_sel_plate_toolbar.m_all_plates_stats_item->selected = true;
}
@@ -10624,9 +10602,8 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
wxString region = L"en";
if (language.find("zh") == 0)
region = L"zh";
// Use the generic dual-nozzle PLA+PETG guide rather than the H2D-specific page
// so the link is relevant for all dual-extrusion printers, not just Bambu H2D. (#12073)
wxGetApp().open_browser_with_warning_dialog(wxString::Format(L"https://wiki.bambulab.com/%s/filament-acc/filament/pla-and-petg-dual-extrusion", region));
// Although this link looks like it's only for the H2D, its guidance is generic.
wxGetApp().open_browser_with_warning_dialog(wxString::Format(L"https://wiki.bambulab.com/%s/filament-acc/filament/h2d-pla-and-petg-mutual-support", region));
return false;
});
}
@@ -10760,24 +10737,14 @@ bool GLCanvas3D::is_flushing_matrix_error() {
if (!Sidebar::should_show_SEMM_buttons())
return false;
std::vector<int> plate_extruders = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_extruders(true);
if (plate_extruders.size() < 2)
return false;
const auto &project_config = wxGetApp().preset_bundle->project_config;
const std::vector<double> &config_matrix = (project_config.option<ConfigOptionFloats>("flush_volumes_matrix"))->values;
const std::vector<double> &config_multiplier = (project_config.option<ConfigOptionFloats>("flush_multiplier"))->values;
for (auto multiplier : config_multiplier) {
if (multiplier == 0) return true;
}
int matrix_len = config_matrix.size() / config_multiplier.size();
int row_len = std::sqrt(matrix_len);
for (int i = 0; i < config_matrix.size(); i++)
{
int relative_id = i % matrix_len;
int row_id = relative_id / row_len;
int col_id = relative_id % row_len;
if (row_id != col_id && config_matrix[i] == 0) return true;
}
return false;
return has_zero_flush_volume_for_used_filaments(config_matrix, config_multiplier, plate_extruders);
}
bool GLCanvas3D::_is_any_volume_outside() const
+1 -3
View File
@@ -2166,7 +2166,6 @@ void GUI_App::init_networking_callbacks()
obj->is_tunnel_mqtt = tunnel;
obj->command_request_push_all(true);
obj->command_get_version();
obj->erase_user_access_code();
obj->command_get_access_code();
if (m_agent)
m_agent->install_device_cert(obj->get_dev_id(), obj->is_lan_mode_printer());
@@ -2216,7 +2215,6 @@ void GUI_App::init_networking_callbacks()
wxString text;
if (msg == "5") {
obj->set_access_code("");
obj->erase_user_access_code();
text = wxString::Format(_L("Incorrect password"));
wxGetApp().show_dialog(text);
} else {
@@ -8286,7 +8284,7 @@ bool GUI_App::show_modal_ip_address_enter_dialog(bool input_sn, wxString title)
wxGetApp().app_config->save();
obj->set_dev_ip(ip_address.ToStdString());
obj->set_user_access_code(access_code.ToStdString());
obj->set_access_code(access_code.ToStdString());
}
}
});
+1 -1
View File
@@ -3213,7 +3213,7 @@ void ObjectList::merge(bool to_multipart_object)
//changed_object(obj_idx);
//remove();
}
/* wxGetApp().plater()->load_model_objects(objects);
// wxGetApp().plater()->load_model_objects(objects);
Selection& selection = p->view3D->get_canvas3d()->get_selection();
size_t last_obj_idx = p->model.objects.size() - 1;
+6 -2
View File
@@ -139,7 +139,7 @@ bool ObjectSettings::update_settings_list()
optgroup->sidetext_width = 5;
optgroup->m_on_change = [this, config](const t_config_option_key& opt_id, const boost::any& value) {
this->update_config_values(config);
this->update_config_values(config, opt_id);
wxGetApp().obj_list()->changed_object(); };
// call back for rescaling of the extracolumn control
@@ -325,7 +325,7 @@ bool ObjectSettings::add_missed_options(ModelConfig* config_to, const DynamicPri
return is_added;
}
void ObjectSettings::update_config_values(ModelConfig* config)
void ObjectSettings::update_config_values(ModelConfig* config, const std::string& changed_opt_key)
{
const auto objects_model = wxGetApp().obj_list()->GetModel();
const auto item = wxGetApp().obj_list()->GetSelection();
@@ -403,6 +403,10 @@ void ObjectSettings::update_config_values(ModelConfig* config)
}
main_config.apply(config->get(), true);
if (printer_technology == ptFFF && changed_opt_key == "layer_height")
config_manipulation.check_layer_height(&main_config);
printer_technology == ptFFF ? config_manipulation.update_print_fff_config(&main_config) :
config_manipulation.update_print_sla_config(&main_config) ;
+1 -1
View File
@@ -66,7 +66,7 @@ public:
* we should add sparse_infill_pattern to avoid endless loop in update
*/
bool add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from);
void update_config_values(ModelConfig *config);
void update_config_values(ModelConfig *config, const std::string& changed_opt_key = "");
void UpdateAndShow(const bool show);
void msw_rescale();
void sys_color_changed();
+5 -2
View File
@@ -223,7 +223,7 @@ bool ObjectTableSettings::update_settings_list(bool is_object, bool is_multiple_
std::weak_ptr<ConfigOptionsGroup> weak_optgroup(optgroup);
optgroup->m_on_change = [this, is_object, object, config, group_category](const t_config_option_key &opt_id, const boost::any &value) {
this->m_parent->Freeze();
this->update_config_values(is_object, object, config, group_category);
this->update_config_values(is_object, object, config, group_category, opt_id);
wxGetApp().obj_list()->changed_object();
this->m_parent->Thaw();
//update_extra_column_visible_status(optgroup.get(), cat.second, config);
@@ -369,7 +369,7 @@ int ObjectTableSettings::update_extra_column_visible_status(ConfigOptionsGroup*
return count;
}
void ObjectTableSettings::update_config_values(bool is_object, ModelObject* object, ModelConfig* config, const std::string& category)
void ObjectTableSettings::update_config_values(bool is_object, ModelObject* object, ModelConfig* config, const std::string& category, const std::string& changed_opt_key)
{
int different_count = 0;
const auto printer_technology = wxGetApp().plater()->printer_technology();
@@ -403,6 +403,9 @@ void ObjectTableSettings::update_config_values(bool is_object, ModelObject* obje
config_manipulation.set_is_BBL_Printer(wxGetApp().preset_bundle->is_bbl_vendor());
if (printer_technology == ptFFF && changed_opt_key == "layer_height")
config_manipulation.check_layer_height(&main_config);
printer_technology == ptFFF ? config_manipulation.update_print_fff_config(&main_config) :
config_manipulation.update_print_sla_config(&main_config) ;
+1 -1
View File
@@ -70,7 +70,7 @@ public:
bool add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from);
//return visible count
int update_extra_column_visible_status(ConfigOptionsGroup* option_group, const std::vector<SimpleSettingData>& option_keys, ModelConfig* config);
void update_config_values(bool is_object, ModelObject* object, ModelConfig* config, const std::string& category);
void update_config_values(bool is_object, ModelObject* object, ModelConfig* config, const std::string& category, const std::string& changed_opt_key = "");
void UpdateAndShow(int row, const bool show, bool is_object, bool is_multiple_selection, ModelObject* object, ModelConfig* config, const std::string& category);
void ValueChanged(int row, bool is_object, ModelObject* object, ModelConfig* config, const std::string& category, const std::string& key);
void resetAllValues(int row, bool is_object, ModelObject* object, ModelConfig* config, const std::string& category);
+1 -1
View File
@@ -1733,7 +1733,7 @@ std::string IMSlider::get_label(int tick, LabelType label_type)
::sprintf(layer_height, "%.2f", m_values.empty() ? m_label_koef * value : m_values[value]);
if (label_type == ltHeight) return std::string(layer_height);
if (label_type == ltHeightWithLayer) {
char buffer[64];
char buffer[90];
size_t layer_number;
layer_number = m_draw_mode == dmSequentialFffPrint ? (m_values.empty() ? value : value + 1) : m_is_wipe_tower ? get_layer_number(value, label_type) + 1 : (m_values.empty() ? value : value + 1);
::sprintf(buffer, "%5s\n%5s", std::to_string(layer_number).c_str(), layer_height);
+14
View File
@@ -2809,12 +2809,26 @@ void ImGuiWrapper::init_font(bool compress)
}
}
if (m_glyph_ranges == ImGui::GetIO().Fonts->GetGlyphRangesThai()) {
ImFontConfig fallback_cfg = cfg;
fallback_cfg.MergeMode = true;
static constexpr ImWchar celsius_range[] = { 0x2103, 0x2103, 0 };
io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/HarmonyOS_Sans_SC_Regular.ttf").c_str(), m_font_size, &fallback_cfg, celsius_range);
}
bold_font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + font_name_bold).c_str(), m_font_size, &cfg, ranges.Data);
if (bold_font == nullptr) {
bold_font = io.Fonts->AddFontDefault();
if (bold_font == nullptr) { throw Slic3r::RuntimeError("ImGui: Could not load deafult font"); }
}
if (m_glyph_ranges == ImGui::GetIO().Fonts->GetGlyphRangesThai()) {
ImFontConfig fallback_cfg = cfg;
fallback_cfg.MergeMode = true;
static constexpr ImWchar celsius_range[] = { 0x2103, 0x2103, 0 };
io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/HarmonyOS_Sans_SC_Bold.ttf").c_str(), m_font_size, &fallback_cfg, celsius_range);
}
if (m_glyph_ranges == ImGui::GetIO().Fonts->GetGlyphRangesThai()) {
default_font->Scale *= 1.25f;
bold_font->Scale *= 1.25f;
@@ -45,24 +45,26 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
for (const ExPolygon &shape : shapes)
bounding_box.merge(BoundingBox(shape.contour.points));
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
// calculate conversion from FontPoint to screen pixels by size of font
double scale = get_text_shape_scale(item.prop, *item.font.font_file) * m_input.ppm;
scales[index] = scale;
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
BoundingBoxf bb2(bounding_box.min.cast<double>(),
bounding_box.max.cast<double>());
if (bounding_box.size().x() < 1 || bounding_box.size().y() < 1)
continue; // or however the font job's degenerate-box case is handled
// Normalize to fit max_size, exactly like CreateFontImageJob does against m_input.size.
// Fit by height (matches row height), then clamp width if needed.
constexpr float preview_padding_px = 2.f; // margin for AA sampling, tune to your AA kernel radius
double scale = m_input.max_size.y() / (double) bounding_box.size().y();
BoundingBoxf bb2(bounding_box.min.cast<double>(), bounding_box.max.cast<double>());
bb2.scale(scale);
image.tex_size.x = std::ceil(bb2.max.x() - bb2.min.x());
image.tex_size.y = std::ceil(bb2.max.y() - bb2.min.y());
// crop image width
if (image.tex_size.x > m_input.max_size.x())
// crop width only if the (now height-normalized) text is too wide
image.tex_size.x = std::ceil(bb2.max.x() - bb2.min.x()) + 2 * preview_padding_px;
image.tex_size.y = std::ceil(bb2.max.y() - bb2.min.y()) + 2 * preview_padding_px;
if (image.tex_size.x > m_input.max_size.x())
image.tex_size.x = m_input.max_size.x();
// crop image height
if (image.tex_size.y > m_input.max_size.y())
image.tex_size.y = m_input.max_size.y();
scales[index] = scale;
}
// arrange bounding boxes
+41 -2
View File
@@ -149,6 +149,46 @@ void OrientJob::prepare()
}
}
/// parameters to minimize support area
static void setMinimalSupportAreaPrams(Slic3r::orientation::OrientParams &out)
{
out.TAR_A = 0.015f;
out.TAR_B = 0.177f;
out.RELATIVE_F = 20;
out.CONTOUR_F = 0.5f;
out.BOTTOM_F = 2.5f;
out.BOTTOM_HULL_F = 0.1f;
out.TAR_C = 0.1f;
out.TAR_D = 1;
out.TAR_E = 0.0115f;
out.FIRST_LAY_H = 0.2f; // 0.0475;
out.VECTOR_TOL = -0.00083f;
out.NEGL_FACE_SIZE = 0.01f;
out.ASCENT = -0.5f;
out.PLAFOND_ADV = 0.0599f;
out.CONTOUR_AMOUNT = 0.0182427f;
out.OV_H = 2.574f;
out.height_offset = 2.3728f;
out.height_log = 0.041375f;
out.height_log_k = 1.9325457f;
out.LAF_MAX = 0.999f; // cos(1.4\degree) for low angle face 0.9997f
out.LAF_MIN = 0.97f; // cos(14\degree) 0.9703f
out.TAR_LAF = 0.001f; // 0.01f
out.TAR_PROJ_AREA = 0.1f;
out.BOTTOM_MIN = 0.1f; // min bottom area. If lower than it the object may be unstable
out.BOTTOM_MAX = 2000; // max bottom area. If get to it the object is stable enough (further increase bottom area won't do more help)
out.height_to_bottom_hull_ratio_MIN = 1,
out.BOTTOM_HULL_MAX = 2000; // max bottom hull area
out.APPERANCE_FACE_SUPP = 3; // penalty of generating supports on appearance face
out.overhang_angle = 60.f;
out.use_low_angle_face = true;
out.min_volume = false;
out.fun_dir = {};
out.parallel = true;
out.progressind = {};
out.stopcondition = {};
}
void OrientJob::process(Ctl &ctl)
{
static const auto arrangestr = _u8L("Orienting...");
@@ -161,9 +201,8 @@ void OrientJob::process(Ctl &ctl)
const GLCanvas3D::OrientSettings& settings = m_plater->canvas3D()->get_orient_settings();
orientation::OrientParams params;
orientation::OrientParamsArea params_area;
if (settings.min_area) {
memcpy(&params, &params_area, sizeof(params));
setMinimalSupportAreaPrams(params);
params.min_volume = false;
}
else {
+18 -6
View File
@@ -1373,8 +1373,8 @@ void MainFrame::show_device(bool should_use_native) {
const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents");
// The legacy page is appended when printer agents are enabled. Remove that
// extra page before switching back to the normal native/legacy layout.
// The web page is appended when printer agents are enabled. Remove that
// extra page before switching back to the normal native/Web layout.
if (!use_printer_agents) {
if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND && idx != tpMonitor) {
m_printer_view->Show(false);
@@ -1434,10 +1434,10 @@ void MainFrame::show_device(bool should_use_native) {
if ((idx = m_tabpanel->FindPage(m_printer_view)) == wxNOT_FOUND) {
m_printer_view->Show(false);
m_tabpanel->AddPage(m_printer_view, _L("Device (legacy)"), std::string("tab_monitor_active"),
m_tabpanel->AddPage(m_printer_view, _L("Device (Web)"), std::string("tab_monitor_active"),
std::string("tab_monitor_active"), false);
} else {
m_tabpanel->SetPageText(idx, _L("Device (legacy)"));
m_tabpanel->SetPageText(idx, _L("Device (Web)"));
}
#ifdef _MSW_DARK_MODE
@@ -4333,14 +4333,26 @@ void MainFrame::load_printer_url(wxString url, wxString apikey)
void MainFrame::load_printer_url()
{
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
if (preset_bundle.use_bbl_device_tab() || wxGetApp().app_config->get_bool("use_printer_agents"))
if (preset_bundle.use_bbl_device_tab() && !wxGetApp().app_config->get_bool("use_printer_agents"))
return;
auto cfg = preset_bundle.printers.get_edited_preset().config;
if (cfg.opt_string("print_host").empty()) {
if (auto *device_manager = wxGetApp().getDeviceManager()) {
auto *machine = device_manager->get_selected_machine();
if (!machine) {
auto machines = device_manager->get_my_machine_list();
if (machines.size() == 1)
machine = machines.begin()->second;
}
if (machine && !machine->get_dev_ip().empty())
cfg.opt_string("print_host") = machine->get_dev_ip();
}
}
wxString url = from_u8(PrintHost::get_print_host_webui(&cfg));
wxString apikey;
const auto host_type = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
if (cfg.has("printhost_apikey") && (host_type == htPrusaLink || host_type == htPrusaConnect))
if (cfg.has("printhost_apikey") && host_type != htSimplyPrint)
apikey = cfg.opt_string("printhost_apikey");
if (!url.empty()) {
load_printer_url(url, apikey);
+30 -6
View File
@@ -2262,6 +2262,12 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con
}
double volume = wipe_volume * (extruder_count == 2 ? plate_extruder_size : (plate_extruder_size - 1));
if (extruder_count == 2) volume += filament_change_volume * (int) (plate_extruder_size / 2);
// Read from the passed plate config — m_print may not have been applied yet
// (fresh plates, CLI), in which case its PrintConfig still holds defaults.
const auto *purge_opt = config.option<ConfigOptionBool>("purge_in_prime_tower");
const auto *semm_opt = config.option<ConfigOptionBool>("single_extruder_multi_material");
const bool semm_flush = purge_opt && purge_opt->value && semm_opt && semm_opt->value;
if (semm_flush) volume = WipeTower2::estimate_semm_flush_volume(config, plate_extruder_size);
if (use_rib_wall) {
depth = std::sqrt(volume / layer_height * extra_spacing);
if (need_wipe_tower || plate_extruder_size > 1) {
@@ -2274,7 +2280,9 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con
}
}
else {
depth = volume/ (layer_height * w) *extra_spacing;
depth = volume / (layer_height * w);
// The flush volumes already hold the spacing between wipes.
if (!semm_flush) depth *= extra_spacing;
if (need_wipe_tower || depth > EPSILON) {
float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height);
depth = std::max((double)min_wipe_tower_depth, depth);
@@ -3337,6 +3345,11 @@ BoundingBoxf3 PartPlate::get_build_volume(bool use_share)
return plate_box;
}
Polygon PartPlate::get_shared_printable_polygon() const
{
return m_extruder_areas.empty() ? Polygon::new_scale(m_shape) : get_shared_poly(m_extruder_areas);
}
bool PartPlate::contains(const Vec3d& point) const
{
return m_bounding_box.contains(point);
@@ -4375,22 +4388,21 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini
f_volume_maps = wxGetApp().preset_bundle->get_default_nozzle_volume_types_for_filaments(filament_maps);
}
DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config(false, filament_maps, f_volume_maps);
const DynamicPrintConfig &print_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
float w = dynamic_cast<const ConfigOptionFloat *>(print_cfg.option("prime_tower_width"))->value;
float w = dynamic_cast<const ConfigOptionFloat *>(full_config.option("prime_tower_width"))->value;
float v = dynamic_cast<const ConfigOptionFloat *>(full_config.option("prime_volume"))->value;
bool enable_wrapping = false;
const ConfigOptionBool *wrapping_opt = dynamic_cast<const ConfigOptionBool *>(full_config.option("enable_wrapping_detection"));
if (wrapping_opt) enable_wrapping = wrapping_opt->value;
int nozzle_nums = wxGetApp().preset_bundle->get_printer_extruder_count();
Vec3d wipe_tower_size = part_plate->estimate_wipe_tower_size(print_cfg, w, v, nozzle_nums, init_pos ? 2 : 0, false, enable_wrapping);
Vec3d wipe_tower_size = part_plate->estimate_wipe_tower_size(full_config, w, v, nozzle_nums, init_pos ? 2 : 0, false, enable_wrapping);
if (!init_pos && (is_approx(wipe_tower_size(0), 0.0) || is_approx(wipe_tower_size(1), 0.0))) {
wipe_tower_size = part_plate->estimate_wipe_tower_size(print_cfg, w, v, nozzle_nums, 2, false, enable_wrapping);
wipe_tower_size = part_plate->estimate_wipe_tower_size(full_config, w, v, nozzle_nums, 2, false, enable_wrapping);
}
// Compute brim-aware margin: brim extends outward from tower position
float brim_width = 0.f;
const ConfigOptionFloat *brim_opt = print_cfg.option<ConfigOptionFloat>("prime_tower_brim_width");
const ConfigOptionFloat *brim_opt = full_config.option<ConfigOptionFloat>("prime_tower_brim_width");
if (brim_opt) {
brim_width = brim_opt->value;
if (brim_width < 0) brim_width = WipeTower::get_auto_brim_by_height((float) wipe_tower_size.z());
@@ -4412,6 +4424,18 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini
}
}
// The bounding box above still allows a corner a delta or hexagonal bed does not have, and the
// prime tower is validated against the real outline — pull it onto the bed before storing.
{
Polygons bed{part_plate->get_shared_printable_polygon()};
bed.front().translate(Point(-scaled(plate_origin.x()), -scaled(plate_origin.y()))); // into the frame x/y live in
const BoundingBox tower(Point::new_scale(x, y),
Point::new_scale(x + wipe_tower_size(0), y + wipe_tower_size(1)));
const Vec2f move = WipeTower::move_box_inside_polygon(tower, bed, scaled<coord_t>(margin));
x += move.x();
y += move.y();
}
ConfigOptionFloat wt_x_opt(x);
ConfigOptionFloat wt_y_opt(y);
dynamic_cast<ConfigOptionFloats *>(proj_cfg.option("wipe_tower_x"))->set_at(&wt_x_opt, plate_idx, 0);
+3
View File
@@ -425,6 +425,9 @@ public:
const BoundingBox get_bounding_box_crd();
BoundingBoxf3 get_plate_box() {return get_build_volume();}
BoundingBoxf3 get_build_volume(bool use_share = false);
// Polygon counterpart of get_build_volume(true), in scaled world coordinates. The bounding box
// that one returns hides the corners a non-rectangular bed does not have.
Polygon get_shared_printable_polygon() const;
const std::vector<BoundingBoxf3>& get_exclude_areas() { return m_exclude_bounding_box; }
+15 -8
View File
@@ -3287,7 +3287,9 @@ void Sidebar::update_all_preset_comboboxes()
: MainFrame::PrintSelectType::eSendGcode;
}
if (!use_native_device_tab || use_printer_agents)
if (use_printer_agents)
p_mainframe->load_printer_url();
else if (!use_native_device_tab)
p_mainframe->load_printer_url(url, apikey);
@@ -9501,7 +9503,7 @@ void Plater::priv::replace_all_with_stl()
return;
}
std::string status = _L("Replaced with 3D files from directory:\n").ToStdString() + out_path.string() + "\n\n";
wxString status = _L("Replaced with 3D files from directory:\n") + from_u8(out_path.string()) + "\n\n";
for (unsigned int idx : volume_idxs) {
const GLVolume* v = selection.get_volume(idx);
@@ -9521,13 +9523,13 @@ void Plater::priv::replace_all_with_stl()
std::string volume_name = volume->name;
if (new_path == input_path) {
status += boost::str(boost::format(_L("✖ Skipped %1%: same file.\n").ToStdString()) % volume_name);
status += wxString::Format(_L("✖ Skipped %s: same file.\n"), from_u8(volume_name));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " skipping replace volume : same filename " << new_path;
continue;
}
if (!fs::exists(new_path)) {
status += boost::str(boost::format(_L("✖ Skipped %1%: file does not exist.\n").ToStdString()) % volume_name);
status += wxString::Format(_L("✖ Skipped %s: file does not exist.\n"), from_u8(volume_name));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cannot replace volume : filen does not exist " << new_path;
continue;
}
@@ -9535,12 +9537,12 @@ void Plater::priv::replace_all_with_stl()
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " replacing volume : " << input_path << " with " << new_path;
if (!replace_volume_with_stl(object_idx, volume_idx, new_path, _u8L("Replace with 3D file"))) {
status += boost::str(boost::format(_L("✖ Skipped %1%: failed to replace.\n").ToStdString()) % volume_name);
status += wxString::Format(_L("✖ Skipped %s: failed to replace.\n"), from_u8(volume_name));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cannot replace volume : failed to replace with " << new_path;
continue;
}
status += boost::str(boost::format(_L("✔ Replaced %1%.\n").ToStdString()) % volume_name);
status += wxString::Format(_L("✔ Replaced %s.\n"), from_u8(volume_name));
}
// update 3D scene
@@ -11236,9 +11238,14 @@ void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e)
}
}
} else {
if (new_sel == MainFrame::tpMonitor && wxGetApp().preset_bundle != nullptr) {
const bool selecting_web_device_tab = main_frame->m_printer_view &&
main_frame->m_tabpanel->GetPage(new_sel) == main_frame->m_printer_view;
if (selecting_web_device_tab) {
// Use the selected discovered machine when the preset has no host.
main_frame->load_printer_url();
} else if (new_sel == MainFrame::tpMonitor && wxGetApp().preset_bundle != nullptr) {
auto cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
wxString url = cfg.opt_string("print_host_webui").empty() ? cfg.opt_string("print_host") : cfg.opt_string("print_host_webui");
wxString url = from_u8(PrintHost::get_print_host_webui(&cfg));
if (main_frame->m_printer_view && url.empty()) {
// It's missing_connection page, reload so that we can replay the gif image
main_frame->m_printer_view->reload();
+7 -2
View File
@@ -1991,7 +1991,7 @@ void InputIpAddressDialog::workerThreadFunc(std::string str_ip, std::string str_
if (w.expired()) return;
if (m_obj) {
m_obj->set_user_access_code(str_access_code);
m_obj->set_access_code(str_access_code);
wxGetApp().getDeviceManager()->set_selected_machine(m_obj->get_dev_id());
}
@@ -2055,6 +2055,11 @@ void InputIpAddressDialog::on_text(wxCommandEvent &evt)
{
auto str_ip = m_input_ip->GetTextCtrl()->GetValue();
auto str_access_code = m_input_access_code->GetTextCtrl()->GetValue();
if (str_access_code.empty()) {
str_access_code = "88888888";
}
auto str_name = m_input_printer_name->GetTextCtrl()->GetValue().Strip(wxString::both);
auto str_sn = m_input_sn->GetTextCtrl()->GetValue().Strip(wxString::both);
bool invalid_access_code = true;
@@ -2062,7 +2067,7 @@ void InputIpAddressDialog::on_text(wxCommandEvent &evt)
for (char c : str_access_code) {
if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))) {
invalid_access_code = false;
return;
break;
}
}
+50 -6
View File
@@ -112,12 +112,56 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string &suffix, wxBox
sizer->Add(m_radio_group, 0, wxEXPAND | wxTOP | wxLEFT, BORDER_W);
if (parent->m_mode == comDevelop) {
m_detach_checkbox = new wxCheckBox(parent, wxID_ANY, _L("Detach from parent"));
sizer->Add(m_detach_checkbox, 0, wxALIGN_LEFT | wxALL, BORDER_W);
// Set initial state (unchecked by default)
m_detach_checkbox->SetValue(m_detach);
// Bind the checkbox event to update the detach state for this item
m_detach_checkbox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { m_detach = m_detach_checkbox->GetValue(); });
// A new user copy of a system preset inherits from the selected system preset.
const std::string parent_name = sel_preset.is_system ? sel_preset.name : sel_preset.inherits();
const bool can_detach = !parent_name.empty();
wxBoxSizer *detach_sizer = new wxBoxSizer(wxHORIZONTAL);
auto detach_tooltip = _L("Copies all inherited values from the parent into this preset and removes the parent relationship. Presets compatible only with the parent may become unsupported.");
auto detach_checkbox = new ::CheckBox(parent);
detach_checkbox->SetToolTip(detach_tooltip);
auto detach_label = new wxStaticText(parent, wxID_ANY, _L("Detach from parent"));
detach_label->SetFont(::Label::Body_14);
detach_label->SetToolTip(detach_tooltip);
detach_sizer->Add(detach_checkbox, 0, wxALIGN_LEFT | wxLEFT, BORDER_W);
detach_sizer->Add(detach_label , 0, wxALIGN_CENTRE_VERTICAL | wxLEFT, FromDIP(5));
sizer->Add(detach_sizer, 0, wxEXPAND | wxTOP, BORDER_W);
sizer->AddSpacer(FromDIP(5));
const wxString parent_text = can_detach ? from_u8(parent_name) : _L("Unique preset");
auto parent_label = new wxStaticText(parent, wxID_ANY, parent_text);
parent_label->SetFont(::Label::Body_12);
parent_label->SetForegroundColour(wxColour("#6B6B6B"));
parent_label->SetToolTip(can_detach ? _L("Parent preset") : _L("This preset does not inherit from another preset."));
sizer->Add(parent_label, 0, wxEXPAND | wxLEFT, BORDER_W + FromDIP(24));
sizer->AddSpacer(FromDIP(5));
if (!can_detach) {
detach_checkbox->Disable();
detach_label->SetForegroundColour(wxColour("#6B6B6B"));
}
else {
// Set initial state (unchecked by default)
detach_checkbox->SetValue(m_detach);
// Bind the checkbox event to update the detach state for this item
detach_checkbox->Bind(wxEVT_TOGGLEBUTTON, [this, detach_checkbox](wxCommandEvent&) { m_detach = detach_checkbox->GetValue(); });
detach_label->SetForegroundColour(wxColour("#363636"));
auto on_toggle = [this, detach_checkbox]() {
detach_checkbox->SetValue(!detach_checkbox->GetValue());
wxCommandEvent ev(wxEVT_TOGGLEBUTTON, detach_checkbox->GetId());
ev.SetEventObject(detach_checkbox);
detach_checkbox->GetEventHandler()->ProcessEvent(ev);
};
detach_label->Bind(wxEVT_LEFT_DOWN, [on_toggle](wxMouseEvent& e) {if(!e.LeftDClick()) on_toggle();});
detach_label->Bind(wxEVT_LEFT_DCLICK, [on_toggle](wxMouseEvent& e) {on_toggle();});
}
}
m_radio_group->Bind(wxEVT_COMMAND_RADIOBOX_SELECTED, [this](wxCommandEvent &e) {
-1
View File
@@ -75,7 +75,6 @@ class SavePresetDialog : public DPIDialog
bool m_save_to_project {false};
RadioGroup* m_radio_group; // ORCA
bool m_detach{false};
wxCheckBox* m_detach_checkbox{nullptr};
void update();
};
+6 -7
View File
@@ -3629,7 +3629,7 @@ void SelectMachineDialog::on_send_print()
BOOST_LOG_TRIVIAL(error) << "build_nozzle_info errors";
}
m_print_job->sdcard_state = obj_->GetStorage()->get_sdcard_state();
m_print_job->sdcard_state = obj_->GetStorage()->get_sdcard_state();
m_print_job->has_sdcard = wxGetApp().app_config->get("allow_abnormal_storage") == "true"
? (m_print_job->sdcard_state == DevStorage::SdcardState::HAS_SDCARD_NORMAL
|| m_print_job->sdcard_state == DevStorage::SdcardState::HAS_SDCARD_ABNORMAL)
@@ -3868,12 +3868,11 @@ _compare_obj_names(MachineObject* obj1, MachineObject* obj2)
}
/*******************************************************************
*@note _collect_machine_list
*@param dev_manager -- the device manager
*@param sorted_machine_objs -- return the sorted machine objects
*@param best_one -- return the best one
*/
/*******************************************************************/
* @note _collect_machine_list
* @param dev_manager -- the device manager
* @param sorted_machine_objs -- return the sorted machine objects
* @param best_one -- return the best one
*******************************************************************/
static void
_collect_sorted_machines(Slic3r::DeviceManager* dev_manager,
std::vector<MachineObject*>& sorted_machine_objs)
-1
View File
@@ -704,7 +704,6 @@ void SelectMachinePopup::update_user_devices()
}
mobj->set_access_code("");
mobj->erase_user_access_code();
}
if (GUI::wxGetApp().plater())
+2 -15
View File
@@ -1270,9 +1270,7 @@ void Selection::translate(const Vec3d &displacement, TransformationType transfor
} else {
if (v.is_wipe_tower) {//in world cs
int plate_idx = v.object_idx() - 1000;
BoundingBoxf3 plate_bbox = wxGetApp().plater()->get_partplate_list().get_plate(plate_idx)->get_build_volume(true);
BoundingBox plate_bbox2d = BoundingBox(scaled(Vec2f(plate_bbox.min[0], plate_bbox.min[1])), scaled(Vec2f(plate_bbox.max[0], plate_bbox.max[1])));
Vec3d tower_size = v.bounding_box().size();
const Polygons bed_polys{wxGetApp().plater()->get_partplate_list().get_plate(plate_idx)->get_shared_printable_polygon()};
Vec3d tower_origin = m_cache.volumes_data[i].get_volume_position();
Vec3d actual_displacement = displacement;
bool show_read_wipe_tower = wxGetApp().plater()->get_partplate_list().get_plate(plate_idx)->fff_print()->is_step_done(psWipeTower);
@@ -1287,18 +1285,7 @@ void Selection::translate(const Vec3d &displacement, TransformationType transfor
BoundingBoxf3 tower_bbox = v.bounding_box();
tower_bbox.translate(actual_displacement + tower_origin);
BoundingBox tower_bbox2d = BoundingBox(scaled(Vec2f(tower_bbox.min[0], tower_bbox.min[1])), scaled(Vec2f(tower_bbox.max[0], tower_bbox.max[1])));
Vec2f offset = WipeTower::move_box_inside_box(tower_bbox2d, plate_bbox2d,scaled(margin));
//if (tower_origin(0) + actual_displacement(0) - margin < plate_bbox.min(0)) {
// actual_displacement(0) = plate_bbox.min(0) - tower_origin(0) + margin;
//} else if (tower_origin(0) + actual_displacement(0) + tower_size(0) + margin > plate_bbox.max(0)) {
// actual_displacement(0) = plate_bbox.max(0) - tower_origin(0) - tower_size(0) - margin;
//}
//if (tower_origin(1) + actual_displacement(1) - margin < plate_bbox.min(1)) {
// actual_displacement(1) = plate_bbox.min(1) - tower_origin(1) + margin;
//} else if (tower_origin(1) + actual_displacement(1) + tower_size(1) + margin > plate_bbox.max(1)) {
// actual_displacement(1) = plate_bbox.max(1) - tower_origin(1) - tower_size(1) - margin;
//}
const Vec2f offset = WipeTower::move_box_inside_polygon(tower_bbox2d, bed_polys, scaled(margin));
actual_displacement += Vec3d(offset[0], offset[1],0);
v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + actual_displacement);
}
+4 -36
View File
@@ -2136,43 +2136,9 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
m_last_sparse_infill_rotate_template_value = m_config->opt_string("sparse_infill_rotate_template");
}
if(opt_key=="layer_height"){
auto min_layer_height_from_nozzle=m_preset_bundle->full_config().option<ConfigOptionFloats>("min_layer_height")->values;
auto max_layer_height_from_nozzle=m_preset_bundle->full_config().option<ConfigOptionFloats>("max_layer_height")->values;
auto layer_height_floor = *std::min_element(min_layer_height_from_nozzle.begin(), min_layer_height_from_nozzle.end());
auto layer_height_ceil = *std::max_element(max_layer_height_from_nozzle.begin(), max_layer_height_from_nozzle.end());
const auto lh = m_config->opt_float("layer_height");
bool exceed_minimum_flag = lh < layer_height_floor;
bool exceed_maximum_flag = lh > layer_height_ceil;
if (exceed_maximum_flag || exceed_minimum_flag) {
if (lh < EPSILON) {
auto msg_text = _(L("Layer height is too small.\nIt will set to min_layer_height\n"));
MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxOK);
dialog.SetButtonLabel(wxID_OK, _L("OK"));
dialog.ShowModal();
auto new_conf = *m_config;
new_conf.set_key_value("layer_height", new ConfigOptionFloat(layer_height_floor));
m_config_manipulation.apply(m_config, &new_conf);
} else {
wxString msg_text = _(L("Layer height exceeds the limit in Printer Settings -> Extruder -> Layer height limits, "
"this may cause printing quality issues."));
msg_text += "\n\n" + _(L("Adjust to the set range automatically?\n"));
MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO);
dialog.SetButtonLabel(wxID_YES, _L("Adjust"));
dialog.SetButtonLabel(wxID_NO, _L("Ignore"));
auto answer = dialog.ShowModal();
auto new_conf = *m_config;
if (answer == wxID_YES) {
if (exceed_maximum_flag)
new_conf.set_key_value("layer_height", new ConfigOptionFloat(layer_height_ceil));
if (exceed_minimum_flag)
new_conf.set_key_value("layer_height", new ConfigOptionFloat(layer_height_floor));
m_config_manipulation.apply(m_config, &new_conf);
}
}
if (opt_key == "layer_height") {
if (m_config_manipulation.check_layer_height(m_config))
wxGetApp().plater()->update();
}
}
string opt_key_without_idx = opt_key.substr(0, opt_key.find('#'));
@@ -5627,6 +5593,7 @@ if (is_marlin_flavor)
optgroup->append_single_option_line("purge_in_prime_tower", "printer_multimaterial_wipe_tower#purge-in-prime-tower");
optgroup->append_single_option_line("enable_filament_ramming", "printer_multimaterial_wipe_tower#enable-filament-ramming");
optgroup->append_single_option_line("tool_change_on_wipe_tower", "printer_multimaterial_wipe_tower#tool-change-on-wipe-tower");
optgroup->append_single_option_line("wait_for_temp_on_wipe_tower", "printer_multimaterial_wipe_tower#wait-for-temperature-on-wipe-tower");
optgroup = page->new_optgroup(L("Single extruder multi-material parameters"), "param_settings");
@@ -6160,6 +6127,7 @@ void TabPrinter::toggle_options()
// so the option is irrelevant there.
const size_t extruders_count = m_config->option<ConfigOptionFloats>("nozzle_diameter")->size();
toggle_option("tool_change_on_wipe_tower", !bSEMM && supports_wipe_tower_2 && extruders_count > 1);
toggle_option("wait_for_temp_on_wipe_tower", !bSEMM && supports_wipe_tower_2 && extruders_count > 1);
}
wxString extruder_number;
long val = 1;
@@ -1359,7 +1359,6 @@ void MoonrakerPrinterAgent::announce_printhost_device()
if (auto* app_config = GUI::wxGetApp().app_config) {
const std::string access_code = device_info.api_key.empty() ? "88888888" : device_info.api_key;
app_config->set_str("access_code", device_info.dev_id, access_code);
app_config->set_str("user_access_code", device_info.dev_id, access_code);
}
nlohmann::json payload;
+1 -1
View File
@@ -107,7 +107,7 @@ std::optional<RaycastManager::Hit> RaycastManager::first_hit(const Vec3d& point,
const AABBMesh *hit_mesh = nullptr;
double hit_squared_distance = 0.;
int hit_face = -1;
Vec3d hit_world;
Vec3d hit_world { Vec3d::Zero() };
const Transform3d *hit_tramsformation = nullptr;
const TrKey *hit_key = nullptr;