mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-26 10:21:00 +00:00
Merge branch 'main' into enh-update-wxwidgets
This commit is contained in:
@@ -22,9 +22,8 @@ public:
|
||||
} else {
|
||||
m_E = 0;
|
||||
m_retracted = 0;
|
||||
m_restart_extra = 0;
|
||||
}
|
||||
|
||||
m_restart_extra = 0;
|
||||
m_absolute_E = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -645,7 +645,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
}
|
||||
|
||||
// BBS: detect narrow internal solid infill area and use ipConcentricInternal pattern instead
|
||||
/*if (layer.object()->config().detect_narrow_internal_solid_infill)*/ {
|
||||
if (layer.object()->config().detect_narrow_internal_solid_infill) {
|
||||
size_t surface_fills_size = surface_fills.size();
|
||||
for (size_t i = 0; i < surface_fills_size; i++) {
|
||||
if (surface_fills[i].surface.surface_type != stInternalSolid)
|
||||
|
||||
+47
-5
@@ -710,7 +710,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
if (is_ramming)
|
||||
gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines.
|
||||
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z
|
||||
if (gcodegen.config().has_prime_tower)
|
||||
if (gcodegen.config().enable_prime_tower)
|
||||
deretraction_str = gcodegen.unretract();
|
||||
}
|
||||
|
||||
@@ -925,7 +925,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
assert(m_layer_idx >= 0);
|
||||
if (m_layer_idx >= (int) m_tool_changes.size())
|
||||
return gcode;
|
||||
if (gcodegen.config().purge_in_prime_tower) {
|
||||
if (!gcodegen.is_BBL_Printer()) {
|
||||
if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
|
||||
if (m_layer_idx < (int) m_tool_changes.size()) {
|
||||
if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()))
|
||||
@@ -1206,8 +1206,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
||||
// Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
|
||||
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) {
|
||||
double top_cd = object.config().support_top_z_distance;
|
||||
//double bottom_cd = object.config().support_bottom_z_distance == 0. ? top_cd : object.config().support_bottom_z_distance;
|
||||
double bottom_cd = top_cd;
|
||||
double bottom_cd = object.config().support_bottom_z_distance;
|
||||
|
||||
double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd);
|
||||
|
||||
@@ -1402,6 +1401,8 @@ namespace DoExport {
|
||||
//BBS
|
||||
//if (ret.size() < MAX_TAGS_COUNT) check(_(L("Printing by object G-code")), config.printing_by_object_gcode.value);
|
||||
//if (ret.size() < MAX_TAGS_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value);
|
||||
//Orca
|
||||
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Change extrusion role G-code")), config.change_extrusion_role_gcode.value);
|
||||
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Pause G-code")), config.machine_pause_gcode.value);
|
||||
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Template Custom G-code")), config.template_custom_gcode.value);
|
||||
if (ret.size() < MAX_TAGS_COUNT) {
|
||||
@@ -2187,7 +2188,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
bbox_wo_wt.merge(unscaled(objPtr->get_first_layer_bbox(data.area, data.layer_height, data.name)));
|
||||
}
|
||||
auto center = bbox_wo_wt.center();
|
||||
this->placeholder_parser().set("first_layer_center_no_wipe_tower", new ConfigOptionFloats(center.x(), center.y()));
|
||||
this->placeholder_parser().set("first_layer_center_no_wipe_tower", new ConfigOptionFloats({center.x(), center.y()}));
|
||||
}
|
||||
bool activate_chamber_temp_control = false;
|
||||
auto max_chamber_temp = 0;
|
||||
@@ -4755,6 +4756,18 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
|
||||
double F = speed * 60; // convert mm/sec to mm/min
|
||||
|
||||
//Orca: process custom gcode for extrusion role change
|
||||
if (path.role() != m_last_extrusion_role && !m_config.change_extrusion_role_gcode.value.empty()) {
|
||||
DynamicConfig config;
|
||||
config.set_key_value("extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(path.role())));
|
||||
config.set_key_value("last_extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(m_last_extrusion_role)));
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
|
||||
config.set_key_value("layer_z", new ConfigOptionFloat(m_layer == nullptr ? m_last_height : m_layer->print_z));
|
||||
gcode += this->placeholder_parser_process("change_extrusion_role_gcode",
|
||||
m_config.change_extrusion_role_gcode.value, m_writer.extruder()->id(), &config)
|
||||
+ "\n";
|
||||
}
|
||||
|
||||
// extrude arc or line
|
||||
if (m_enable_extrusion_role_markers) {
|
||||
if (path.role() != m_last_extrusion_role) {
|
||||
@@ -5002,6 +5015,35 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
return gcode;
|
||||
}
|
||||
|
||||
//Orca: get string name of extrusion role. used for change_extruder_role_gcode
|
||||
std::string GCode::extrusion_role_to_string_for_parser(const ExtrusionRole & role)
|
||||
{
|
||||
switch (role) {
|
||||
case erPerimeter: return "Perimeter";
|
||||
case erExternalPerimeter: return "ExternalPerimeter";
|
||||
case erOverhangPerimeter: return "OverhangPerimeter";
|
||||
case erInternalInfill: return "InternalInfill";
|
||||
case erSolidInfill: return "SolidInfill";
|
||||
case erTopSolidInfill: return "TopSolidInfill";
|
||||
case erBottomSurface: return "BottomSurface";
|
||||
case erBridgeInfill:
|
||||
case erInternalBridgeInfill: return "BridgeInfill";
|
||||
case erGapFill: return "GapFill";
|
||||
case erIroning: return "Ironing";
|
||||
case erSkirt: return "Skirt";
|
||||
case erBrim: return "Brim";
|
||||
case erSupportMaterial: return "SupportMaterial";
|
||||
case erSupportMaterialInterface: return "SupportMaterialInterface";
|
||||
case erSupportTransition: return "SupportTransition";
|
||||
case erWipeTower: return "WipeTower";
|
||||
case erCustom:
|
||||
case erMixed:
|
||||
case erCount:
|
||||
case erNone:
|
||||
default: return "Mixed";
|
||||
}
|
||||
}
|
||||
|
||||
std::string encodeBase64(uint64_t value)
|
||||
{
|
||||
//Always use big endian mode
|
||||
|
||||
@@ -205,6 +205,7 @@ public:
|
||||
// inside the generated string and after the G-code export finishes.
|
||||
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr);
|
||||
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
|
||||
std::string extrusion_role_to_string_for_parser(const ExtrusionRole &);
|
||||
|
||||
// For Perl bindings, to be used exclusively by unit tests.
|
||||
unsigned int layer_count() const { return m_layer_count; }
|
||||
@@ -213,7 +214,7 @@ public:
|
||||
|
||||
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment);
|
||||
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
|
||||
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::SpiralLift);
|
||||
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift);
|
||||
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
|
||||
std::string set_extruder(unsigned int extruder_id, double print_z);
|
||||
bool is_BBL_Printer();
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "../Layer.hpp"
|
||||
#include "../GCode.hpp"
|
||||
#include "../EdgeGrid.hpp"
|
||||
@@ -13,6 +17,8 @@
|
||||
#include <unordered_set>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
//#define AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct TravelPoint
|
||||
@@ -20,6 +26,8 @@ struct TravelPoint
|
||||
Point point;
|
||||
// Index of the polygon containing this point. A negative value indicates that the point is not on any border.
|
||||
int border_idx;
|
||||
// simplify_travel() doesn't remove this point.
|
||||
bool do_not_remove = false;
|
||||
};
|
||||
|
||||
struct Intersection
|
||||
@@ -32,6 +40,8 @@ struct Intersection
|
||||
Point point;
|
||||
// Distance from the first point in the corresponding boundary
|
||||
float distance;
|
||||
// simplify_travel() doesn't remove this point.
|
||||
bool do_not_remove = false;
|
||||
};
|
||||
|
||||
struct ClosestLine
|
||||
@@ -207,8 +217,8 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
||||
const ClosestLine &cl_start = start_lines[cl_indices.first];
|
||||
const ClosestLine &cl_end = end_lines[cl_indices.second];
|
||||
std::vector<Intersection> new_intersections;
|
||||
new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)});
|
||||
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)});
|
||||
new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true});
|
||||
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true});
|
||||
return new_intersections;
|
||||
}
|
||||
}
|
||||
@@ -259,7 +269,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
||||
if (cl_start_idx != std::numeric_limits<size_t>::max()) {
|
||||
// If there is any ClosestLine around the start point closer to the Intersection, then replace this Intersection with ClosestLine.
|
||||
const ClosestLine &cl_start = start_lines[cl_start_idx];
|
||||
new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)};
|
||||
new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true};
|
||||
} else {
|
||||
// Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the
|
||||
// vector of intersections. This allows in some cases when it is more than one around ClosestLine start point chose that one which
|
||||
@@ -267,7 +277,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
||||
// use the first one, which is the closest one to the start point.
|
||||
size_t start_closest_lines_idx = find_closest_line_with_same_boundary_idx(start_lines, new_intersections, true);
|
||||
const ClosestLine &cl_start = (start_closest_lines_idx != std::numeric_limits<size_t>::max()) ? start_lines[start_closest_lines_idx] : start_lines.front();
|
||||
new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)});
|
||||
new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +286,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
||||
if (cl_end_idx != std::numeric_limits<size_t>::max()) {
|
||||
// If there is any ClosestLine around the end point closer to the Intersection, then replace this Intersection with ClosestLine.
|
||||
const ClosestLine &cl_end = end_lines[cl_end_idx];
|
||||
new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)};
|
||||
new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true};
|
||||
} else {
|
||||
// Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the
|
||||
// vector of intersections. This allows in some cases when it is more than one around ClosestLine end point chose that one which
|
||||
@@ -284,7 +294,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
||||
// use the first one, which is the closest one to the end point.
|
||||
size_t end_closest_lines_idx = find_closest_line_with_same_boundary_idx(end_lines, new_intersections, false);
|
||||
const ClosestLine &cl_end = (end_closest_lines_idx != std::numeric_limits<size_t>::max()) ? end_lines[end_closest_lines_idx] : end_lines.front();
|
||||
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)});
|
||||
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true});
|
||||
}
|
||||
}
|
||||
return new_intersections;
|
||||
@@ -350,8 +360,6 @@ static Polyline to_polyline(const std::vector<TravelPoint> &travel)
|
||||
return result;
|
||||
}
|
||||
|
||||
// #define AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
|
||||
|
||||
#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
|
||||
static void export_travel_to_svg(const Polygons &boundary,
|
||||
const Line &original_travel,
|
||||
@@ -359,16 +367,17 @@ static void export_travel_to_svg(const Polygons &boundary,
|
||||
const std::vector<Intersection> &intersections,
|
||||
const std::string &path)
|
||||
{
|
||||
BoundingBox bbox = get_extents(boundary);
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
BoundingBox bbox = get_extents(boundary);
|
||||
::Slic3r::SVG svg(path, bbox);
|
||||
svg.draw_outline(boundary, "green");
|
||||
svg.draw(original_travel, "blue");
|
||||
svg.draw(result_travel, "red");
|
||||
svg.draw(original_travel.a, "black");
|
||||
svg.draw(original_travel.b, "grey");
|
||||
svg.draw_outline(boundary, "green", stroke_width);
|
||||
svg.draw(original_travel, "blue", stroke_width);
|
||||
svg.draw(result_travel, "red", stroke_width);
|
||||
svg.draw(original_travel.a, "black", stroke_width);
|
||||
svg.draw(original_travel.b, "grey", stroke_width);
|
||||
|
||||
for (const Intersection &intersection : intersections)
|
||||
svg.draw(intersection.point, "lightseagreen");
|
||||
svg.draw(intersection.point, "lightseagreen", stroke_width);
|
||||
}
|
||||
|
||||
static void export_travel_to_svg(const Polygons &boundary,
|
||||
@@ -433,21 +442,30 @@ static std::vector<TravelPoint> simplify_travel(const AvoidCrossingPerimeters::B
|
||||
|
||||
visitor.pt_current = ¤t_point;
|
||||
|
||||
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
||||
if (travel[point_idx_2].point == current_point) {
|
||||
next = travel[point_idx_2];
|
||||
point_idx = point_idx_2;
|
||||
continue;
|
||||
}
|
||||
if (!next.do_not_remove)
|
||||
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
||||
// Workaround for some issue in MSVC 19.29.30037 32-bit compiler.
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
if (bool volatile do_not_remove = travel[point_idx_2].do_not_remove; do_not_remove)
|
||||
break;
|
||||
#else
|
||||
if (travel[point_idx_2].do_not_remove)
|
||||
break;
|
||||
#endif
|
||||
if (travel[point_idx_2].point == current_point) {
|
||||
next = travel[point_idx_2];
|
||||
point_idx = point_idx_2;
|
||||
continue;
|
||||
}
|
||||
|
||||
visitor.pt_next = &travel[point_idx_2].point;
|
||||
boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
// Check if deleting point causes crossing a boundary
|
||||
if (!visitor.intersect) {
|
||||
next = travel[point_idx_2];
|
||||
point_idx = point_idx_2;
|
||||
visitor.pt_next = &travel[point_idx_2].point;
|
||||
boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
// Check if deleting point causes crossing a boundary
|
||||
if (!visitor.intersect) {
|
||||
next = travel[point_idx_2];
|
||||
point_idx = point_idx_2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
simplified_path.emplace_back(next);
|
||||
}
|
||||
@@ -473,7 +491,7 @@ static float get_perimeter_spacing(const Layer &layer)
|
||||
size_t regions_count = 0;
|
||||
float perimeter_spacing = 0.f;
|
||||
for (const LayerRegion *layer_region : layer.regions())
|
||||
if (layer_region != nullptr && !layer_region->slices.empty()) {
|
||||
if (layer_region != nullptr && ! layer_region->slices.empty()) {
|
||||
perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing();
|
||||
++regions_count;
|
||||
}
|
||||
@@ -494,7 +512,7 @@ static float get_perimeter_spacing_external(const Layer &layer)
|
||||
for (const PrintObject *object : layer.object()->print()->objects())
|
||||
if (const Layer *l = object->get_layer_at_printz(layer.print_z, EPSILON); l)
|
||||
for (const LayerRegion *layer_region : l->regions())
|
||||
if (layer_region != nullptr && !layer_region->slices.empty()) {
|
||||
if (layer_region != nullptr && ! layer_region->slices.empty()) {
|
||||
perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing();
|
||||
++ regions_count;
|
||||
}
|
||||
@@ -507,6 +525,25 @@ static float get_perimeter_spacing_external(const Layer &layer)
|
||||
return perimeter_spacing;
|
||||
}
|
||||
|
||||
// Returns average perimeter width calculated from all LayerRegion within the layer.
|
||||
static float get_external_perimeter_width(const Layer &layer)
|
||||
{
|
||||
size_t regions_count = 0;
|
||||
float perimeter_width = 0.f;
|
||||
for (const LayerRegion *layer_region : layer.regions())
|
||||
if (layer_region != nullptr && ! layer_region->slices.empty()) {
|
||||
perimeter_width += float(layer_region->flow(frExternalPerimeter).scaled_width());
|
||||
++regions_count;
|
||||
}
|
||||
|
||||
assert(perimeter_width >= 0.f);
|
||||
if (regions_count != 0)
|
||||
perimeter_width /= float(regions_count);
|
||||
else
|
||||
perimeter_width = get_default_perimeter_spacing(*layer.object());
|
||||
return perimeter_width;
|
||||
}
|
||||
|
||||
// Called by avoid_perimeters() and by simplify_travel_heuristics().
|
||||
static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &boundary,
|
||||
const Point &start,
|
||||
@@ -566,7 +603,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo
|
||||
// Offset of the polygon's point using get_middle_point_offset is used to simplify the calculation of intersection between the
|
||||
// boundary and the travel. The appended point is translated in the direction of inward normal. This translation ensures that the
|
||||
// appended point will be inside the polygon and not on the polygon border.
|
||||
result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx)});
|
||||
result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx), intersection_first.do_not_remove});
|
||||
|
||||
// Check if intersection line also exit the boundary polygon
|
||||
if (it_second_r != it_last_item) {
|
||||
@@ -590,7 +627,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo
|
||||
// Append the farthest intersection into the path
|
||||
left_idx = intersection_second.line_idx;
|
||||
right_idx = (intersection_second.line_idx >= (boundaries[intersection_second.border_idx].points.size() - 1)) ? 0 : (intersection_second.line_idx + 1);
|
||||
result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)), int(intersection_second.border_idx)});
|
||||
result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)), int(intersection_second.border_idx), intersection_second.do_not_remove});
|
||||
// Skip intersections in between
|
||||
it_first = it_second;
|
||||
}
|
||||
@@ -645,22 +682,22 @@ static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &boundary
|
||||
// Check if anyone of ExPolygons contains whole travel.
|
||||
// called by need_wipe() and AvoidCrossingPerimeters::travel_to()
|
||||
// FIXME Lukas H.: Maybe similar approach could also be used for ExPolygon::contains()
|
||||
static bool any_expolygon_contains(const ExPolygons &ex_polygons,
|
||||
const std::vector<BoundingBox> &ex_polygons_bboxes,
|
||||
const EdgeGrid::Grid &grid_lslice,
|
||||
static bool any_expolygon_contains(const ExPolygons &lslices_offset,
|
||||
const std::vector<BoundingBox> &lslices_offset_bboxes,
|
||||
const EdgeGrid::Grid &grid_lslices_offset,
|
||||
const Line &travel)
|
||||
{
|
||||
assert(ex_polygons.size() == ex_polygons_bboxes.size());
|
||||
if(!grid_lslice.bbox().contains(travel.a) || !grid_lslice.bbox().contains(travel.b))
|
||||
assert(lslices_offset.size() == lslices_offset_bboxes.size());
|
||||
if(!grid_lslices_offset.bbox().contains(travel.a) || !grid_lslices_offset.bbox().contains(travel.b))
|
||||
return false;
|
||||
|
||||
FirstIntersectionVisitor visitor(grid_lslice);
|
||||
FirstIntersectionVisitor visitor(grid_lslices_offset);
|
||||
visitor.pt_current = &travel.a;
|
||||
visitor.pt_next = &travel.b;
|
||||
grid_lslice.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
grid_lslices_offset.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
if (!visitor.intersect) {
|
||||
for (const ExPolygon &ex_polygon : ex_polygons) {
|
||||
const BoundingBox &bbox = ex_polygons_bboxes[&ex_polygon - &ex_polygons.front()];
|
||||
for (const ExPolygon &ex_polygon : lslices_offset) {
|
||||
const BoundingBox &bbox = lslices_offset_bboxes[&ex_polygon - &lslices_offset.front()];
|
||||
if (bbox.contains(travel.a) && bbox.contains(travel.b) && ex_polygon.contains(travel.a))
|
||||
return true;
|
||||
}
|
||||
@@ -670,18 +707,18 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons,
|
||||
|
||||
// Check if anyone of ExPolygons contains whole travel.
|
||||
// called by need_wipe()
|
||||
static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vector<BoundingBox> &ex_polygons_bboxes, const EdgeGrid::Grid &grid_lslice, const Polyline &travel)
|
||||
static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vector<BoundingBox> &ex_polygons_bboxes, const EdgeGrid::Grid &grid_lslice_offset, const Polyline &travel)
|
||||
{
|
||||
assert(ex_polygons.size() == ex_polygons_bboxes.size());
|
||||
if(std::any_of(travel.points.begin(), travel.points.end(), [&grid_lslice](const Point &point) { return !grid_lslice.bbox().contains(point); }))
|
||||
if(std::any_of(travel.points.begin(), travel.points.end(), [&grid_lslice_offset](const Point &point) { return !grid_lslice_offset.bbox().contains(point); }))
|
||||
return false;
|
||||
|
||||
FirstIntersectionVisitor visitor(grid_lslice);
|
||||
FirstIntersectionVisitor visitor(grid_lslice_offset);
|
||||
bool any_intersection = false;
|
||||
for (size_t line_idx = 1; line_idx < travel.size(); ++line_idx) {
|
||||
visitor.pt_current = &travel.points[line_idx - 1];
|
||||
visitor.pt_next = &travel.points[line_idx];
|
||||
grid_lslice.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
grid_lslice_offset.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor);
|
||||
any_intersection = visitor.intersect;
|
||||
if (any_intersection) break;
|
||||
}
|
||||
@@ -698,13 +735,13 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vec
|
||||
}
|
||||
|
||||
static bool need_wipe(const GCode &gcodegen,
|
||||
const EdgeGrid::Grid &grid_lslice,
|
||||
const Line &original_travel,
|
||||
const Polyline &result_travel,
|
||||
const size_t intersection_count)
|
||||
const ExPolygons &lslices_offset,
|
||||
const std::vector<BoundingBox> &lslices_offset_bboxes,
|
||||
const EdgeGrid::Grid &grid_lslices_offset,
|
||||
const Line &original_travel,
|
||||
const Polyline &result_travel,
|
||||
const size_t intersection_count)
|
||||
{
|
||||
const ExPolygons &lslices = gcodegen.layer()->lslices;
|
||||
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
|
||||
bool z_lift_enabled = gcodegen.config().z_hop.get_at(gcodegen.writer().extruder()->id()) > 0.;
|
||||
bool wipe_needed = false;
|
||||
|
||||
@@ -714,16 +751,16 @@ static bool need_wipe(const GCode &gcodegen,
|
||||
// The original layer is intersected with defined boundaries. Then it is necessary to make a detailed test.
|
||||
// If the z-lift is enabled, then a wipe is needed when the original travel leads above the holes.
|
||||
if (z_lift_enabled) {
|
||||
if (any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, original_travel)) {
|
||||
if (any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, original_travel)) {
|
||||
// Check if original_travel and result_travel are not same.
|
||||
// If both are the same, then it is possible to skip testing of result_travel
|
||||
wipe_needed = !(result_travel.size() > 2 && result_travel.first_point() == original_travel.a && result_travel.last_point() == original_travel.b) &&
|
||||
!any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, result_travel);
|
||||
!any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, result_travel);
|
||||
} else {
|
||||
wipe_needed = true;
|
||||
}
|
||||
} else {
|
||||
wipe_needed = !any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, result_travel);
|
||||
wipe_needed = !any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, result_travel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,55 +982,71 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid,
|
||||
// ExPolygons are handled one by one so returned ExPolygons could intersect.
|
||||
static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset)
|
||||
{
|
||||
double min_contour_width = 2. * offset + SCALED_EPSILON;
|
||||
double search_radius = 2. * (offset + min_contour_width);
|
||||
ExPolygons ex_poly_result = ex_polygons;
|
||||
const std::vector<double> min_contour_width_values = {offset / 2., offset, 2. * offset + SCALED_EPSILON};
|
||||
ExPolygons ex_poly_result = ex_polygons;
|
||||
resample_expolygons(ex_poly_result, offset / 2, scaled<double>(0.5));
|
||||
|
||||
for (ExPolygon &ex_poly : ex_poly_result) {
|
||||
BoundingBox bbox(get_extents(ex_poly));
|
||||
bbox.offset(SCALED_EPSILON);
|
||||
EdgeGrid::Grid grid;
|
||||
grid.set_bbox(bbox);
|
||||
grid.create(ex_poly, coord_t(0.7 * search_radius));
|
||||
|
||||
std::vector<std::vector<float>> ex_poly_distances;
|
||||
precompute_expolygon_distances(ex_poly, ex_poly_distances);
|
||||
// Filter out expolygons smaller than 0.1mm^2
|
||||
if (Vec2d bbox_size = bbox.size().cast<double>(); bbox_size.x() * bbox_size.y() < Slic3r::sqr(scale_(0.1f)))
|
||||
continue;
|
||||
|
||||
std::vector<std::vector<float>> offsets;
|
||||
offsets.reserve(ex_poly.holes.size() + 1);
|
||||
for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) {
|
||||
const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1];
|
||||
assert(poly.is_counter_clockwise() == (idx_contour == 0));
|
||||
std::vector<float> distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset, search_radius);
|
||||
for (float &distance : distances) {
|
||||
if (distance < min_contour_width)
|
||||
distance = 0.f;
|
||||
else if (distance > min_contour_width + 2. * offset)
|
||||
distance = - float(offset);
|
||||
else
|
||||
distance = - (distance - float(min_contour_width)) / 2.f;
|
||||
}
|
||||
offsets.emplace_back(distances);
|
||||
}
|
||||
for (const double &min_contour_width : min_contour_width_values) {
|
||||
const size_t min_contour_width_idx = &min_contour_width - &min_contour_width_values.front();
|
||||
const double search_radius = 2. * (offset + min_contour_width);
|
||||
|
||||
ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets);
|
||||
// If variable_offset_inner_ex produces empty result, then original ex_polygon is used
|
||||
if (offset_ex_poly.size() == 1) {
|
||||
ex_poly = std::move(offset_ex_poly.front());
|
||||
} else if (offset_ex_poly.size() > 1) {
|
||||
// fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces
|
||||
// tiny artefacts polygons, so these artefacts are removed.
|
||||
double max_area = offset_ex_poly.front().area();
|
||||
size_t max_area_idx = 0;
|
||||
for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) {
|
||||
double area = offset_ex_poly[poly_idx].area();
|
||||
if (max_area < area) {
|
||||
max_area = area;
|
||||
max_area_idx = poly_idx;
|
||||
EdgeGrid::Grid grid;
|
||||
grid.set_bbox(bbox);
|
||||
grid.create(ex_poly, coord_t(0.7 * search_radius));
|
||||
|
||||
std::vector<std::vector<float>> ex_poly_distances;
|
||||
precompute_expolygon_distances(ex_poly, ex_poly_distances);
|
||||
|
||||
std::vector<std::vector<float>> offsets;
|
||||
offsets.reserve(ex_poly.holes.size() + 1);
|
||||
for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) {
|
||||
const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1];
|
||||
assert(poly.is_counter_clockwise() == (idx_contour == 0));
|
||||
std::vector<float> distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset, search_radius);
|
||||
for (float &distance : distances) {
|
||||
if (distance < min_contour_width)
|
||||
distance = 0.f;
|
||||
else if (distance > min_contour_width + 2. * offset)
|
||||
distance = -float(offset);
|
||||
else
|
||||
distance = -(distance - float(min_contour_width)) / 2.f;
|
||||
}
|
||||
offsets.emplace_back(distances);
|
||||
}
|
||||
|
||||
ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets);
|
||||
// If variable_offset_inner_ex produces empty result, then original ex_polygon is used
|
||||
if (offset_ex_poly.size() == 1 && offset_ex_poly.front().holes.size() == ex_poly.holes.size()) {
|
||||
ex_poly = std::move(offset_ex_poly.front());
|
||||
break;
|
||||
} else if ((min_contour_width_idx + 1) < min_contour_width_values.size()) {
|
||||
continue; // Try the next round with bigger min_contour_width.
|
||||
} else if (offset_ex_poly.size() == 1) {
|
||||
ex_poly = std::move(offset_ex_poly.front());
|
||||
break;
|
||||
} else if (offset_ex_poly.size() > 1) {
|
||||
// fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces
|
||||
// tiny artefacts polygons, so these artefacts are removed.
|
||||
double max_area = offset_ex_poly.front().area();
|
||||
size_t max_area_idx = 0;
|
||||
for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) {
|
||||
double area = offset_ex_poly[poly_idx].area();
|
||||
if (max_area < area) {
|
||||
max_area = area;
|
||||
max_area_idx = poly_idx;
|
||||
}
|
||||
}
|
||||
ex_poly = std::move(offset_ex_poly[max_area_idx]);
|
||||
break;
|
||||
}
|
||||
ex_poly = std::move(offset_ex_poly[max_area_idx]);
|
||||
}
|
||||
}
|
||||
return ex_poly_result;
|
||||
@@ -1133,10 +1186,8 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
||||
Vec2d startf = start.cast<double>();
|
||||
Vec2d endf = end .cast<double>();
|
||||
|
||||
const ExPolygons &lslices = gcodegen.layer()->lslices;
|
||||
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
|
||||
bool is_support_layer = (dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr);
|
||||
if (!use_external && (is_support_layer || (!lslices.empty() && !any_expolygon_contains(lslices, lslices_bboxes, m_grid_lslice, travel)))) {
|
||||
bool is_support_layer = dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr;
|
||||
if (!use_external && (is_support_layer || (!m_lslices_offset.empty() && !any_expolygon_contains(m_lslices_offset, m_lslices_offset_bboxes, m_grid_lslices_offset, travel)))) {
|
||||
// Initialize m_internal only when it is necessary.
|
||||
if (m_internal.boundaries.empty())
|
||||
init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer())));
|
||||
@@ -1186,7 +1237,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
||||
} else if (max_detour_length_exceeded) {
|
||||
*could_be_wipe_disabled = false;
|
||||
} else
|
||||
*could_be_wipe_disabled = !need_wipe(gcodegen, m_grid_lslice, travel, result_pl, travel_intersection_count);
|
||||
*could_be_wipe_disabled = !need_wipe(gcodegen, m_lslices_offset, m_lslices_offset_bboxes, m_grid_lslices_offset, travel, result_pl, travel_intersection_count);
|
||||
|
||||
return result_pl;
|
||||
}
|
||||
@@ -1197,13 +1248,21 @@ void AvoidCrossingPerimeters::init_layer(const Layer &layer)
|
||||
{
|
||||
m_internal.clear();
|
||||
m_external.clear();
|
||||
m_lslices_offset.clear();
|
||||
m_lslices_offset_bboxes.clear();
|
||||
|
||||
float perimeter_offset = -get_external_perimeter_width(layer) / float(2.);
|
||||
m_lslices_offset = offset_ex(layer.lslices, perimeter_offset);
|
||||
|
||||
m_lslices_offset_bboxes.reserve(m_lslices_offset.size());
|
||||
for (const ExPolygon &ex_poly : m_lslices_offset)
|
||||
m_lslices_offset_bboxes.emplace_back(get_extents(ex_poly));
|
||||
|
||||
BoundingBox bbox_slice(get_extents(layer.lslices));
|
||||
bbox_slice.offset(SCALED_EPSILON);
|
||||
|
||||
m_grid_lslice.set_bbox(bbox_slice);
|
||||
//FIXME 1mm grid?
|
||||
m_grid_lslice.create(layer.lslices, coord_t(scale_(1.)));
|
||||
m_grid_lslices_offset.set_bbox(bbox_slice);
|
||||
m_grid_lslices_offset.create(m_lslices_offset, coord_t(scale_(1.)));
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
///|/ Copyright (c) Prusa Research 2020 - 2022 Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_AvoidCrossingPerimeters_hpp_
|
||||
#define slic3r_AvoidCrossingPerimeters_hpp_
|
||||
|
||||
@@ -58,8 +62,11 @@ private:
|
||||
// we enable it by default for the first travel move in print
|
||||
bool m_disabled_once { true };
|
||||
|
||||
// Lslices offseted by half an external perimeter width. Used for detection if line or polyline is inside of any polygon.
|
||||
ExPolygons m_lslices_offset;
|
||||
std::vector<BoundingBox> m_lslices_offset_bboxes;
|
||||
// Used for detection of line or polyline is inside of any polygon.
|
||||
EdgeGrid::Grid m_grid_lslice;
|
||||
EdgeGrid::Grid m_grid_lslices_offset;
|
||||
// Store all needed data for travels inside object
|
||||
Boundary m_internal;
|
||||
// Store all needed data for travels outside object
|
||||
|
||||
@@ -1038,8 +1038,7 @@ void WipeTower2::toolchange_Change(
|
||||
// The toolchange Tn command will be inserted later, only in case that the user does
|
||||
// not provide a custom toolchange gcode.
|
||||
writer.set_tool(new_tool); // This outputs nothing, the writer just needs to know the tool has changed.
|
||||
//writer.append("[start_filament_gcode]\n");
|
||||
writer.append("[filament_start_gcode]\n");
|
||||
// writer.append("[filament_start_gcode]\n");
|
||||
|
||||
|
||||
writer.flush_planner_queue();
|
||||
|
||||
@@ -320,7 +320,7 @@ std::string GCodeWriter::reset_e(bool force)
|
||||
return "";
|
||||
|
||||
if (m_extruder != nullptr) {
|
||||
if (m_extruder->E() == 0. && ! force)
|
||||
if (is_zero(m_extruder->E()) && ! force)
|
||||
return "";
|
||||
m_extruder->reset_E();
|
||||
}
|
||||
@@ -709,7 +709,7 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std
|
||||
length = 1;
|
||||
|
||||
std::string gcode;
|
||||
if (double dE = m_extruder->retract(length, restart_extra); dE != 0) {
|
||||
if (double dE = m_extruder->retract(length, restart_extra); !is_zero(dE)) {
|
||||
if (this->config.use_firmware_retraction) {
|
||||
gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n";
|
||||
}
|
||||
@@ -737,7 +737,7 @@ std::string GCodeWriter::unretract()
|
||||
if (FLAVOR_IS(gcfMakerWare))
|
||||
gcode = "M101 ; extruder on\n";
|
||||
|
||||
if (double dE = m_extruder->unretract(); dE != 0) {
|
||||
if (double dE = m_extruder->unretract(); !is_zero(dE)) {
|
||||
if (this->config.use_firmware_retraction) {
|
||||
gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n";
|
||||
gcode += this->reset_e();
|
||||
|
||||
@@ -726,7 +726,7 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle)
|
||||
static std::vector<std::string> s_Preset_print_options {
|
||||
"layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "slicing_mode",
|
||||
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
|
||||
"extra_perimeters_on_overhangs", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold",
|
||||
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold",
|
||||
"seam_position", "staggered_inner_seams", "wall_infill_order", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
|
||||
"infill_direction",
|
||||
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern",
|
||||
@@ -756,6 +756,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"flush_into_infill", "flush_into_objects", "flush_into_support",
|
||||
"tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter",
|
||||
"tree_support_branch_diameter", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall",
|
||||
"detect_narrow_internal_solid_infill",
|
||||
"gcode_add_line_number", "enable_arc_fitting", "infill_combination", /*"adaptive_layer_height",*/
|
||||
"support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",
|
||||
"initial_layer_infill_speed", "only_one_wall_top",
|
||||
@@ -818,7 +819,7 @@ static std::vector<std::string> s_Preset_printer_options {
|
||||
"printer_technology",
|
||||
"printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor",
|
||||
"fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs",
|
||||
"single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode",
|
||||
"single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode", "change_extrusion_role_gcode",
|
||||
"printer_model", "printer_variant", "printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
|
||||
"default_print_profile", "inherits",
|
||||
"silent_mode",
|
||||
|
||||
@@ -1183,6 +1183,14 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionStrings { " " });
|
||||
|
||||
def = this->add("ensure_vertical_shell_thickness", coBool);
|
||||
def->label = L("Ensure vertical shell thickness");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Add solid infill near sloping surfaces to guarantee the vertical shell thickness "
|
||||
"(top+bottom solid layers)");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
auto def_top_fill_pattern = def = this->add("top_surface_pattern", coEnum);
|
||||
def->label = L("Top surface pattern");
|
||||
def->category = L("Strength");
|
||||
@@ -3493,6 +3501,7 @@ def = this->add("filament_loading_speed", coFloats);
|
||||
def = this->add("support_top_z_distance", coFloat);
|
||||
//def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
||||
def->label = L("Top Z distance");
|
||||
def->min = 0;
|
||||
def->category = L("Support");
|
||||
def->tooltip = L("The z gap between the top support interface and object");
|
||||
def->sidetext = L("mm");
|
||||
@@ -3508,12 +3517,12 @@ def = this->add("filament_loading_speed", coFloats);
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.2));
|
||||
|
||||
// BBS:MusangKing
|
||||
def = this->add("support_bottom_z_distance", coFloat);
|
||||
def->label = L("Bottom Z distance");
|
||||
def->category = L("Support");
|
||||
def->tooltip = L("The z gap between the bottom support interface and object");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.2));
|
||||
|
||||
@@ -3962,6 +3971,15 @@ def = this->add("filament_loading_speed", coFloats);
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("change_extrusion_role_gcode", coString);
|
||||
def->label = L("Change extrusion role G-code");
|
||||
def->tooltip = L("This gcode is inserted when the extrusion role is changed");
|
||||
def->multiline = true;
|
||||
def->full_width = true;
|
||||
def->height = 5;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("top_surface_line_width", coFloatOrPercent);
|
||||
def->label = L("Top surface");
|
||||
def->category = L("Quality");
|
||||
@@ -4390,6 +4408,15 @@ def = this->add("filament_loading_speed", coFloats);
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
def = this->add("detect_narrow_internal_solid_infill", coBool);
|
||||
def->label = L("Detect narrow internal solid infill");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("This option will auto detect narrow internal solid infill area."
|
||||
" If enabled, concentric pattern will be used for the area to speed printing up."
|
||||
" Otherwise, rectilinear pattern is used defaultly.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
}
|
||||
|
||||
void PrintConfigDef::init_extruder_option_keys()
|
||||
@@ -5194,8 +5221,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
"support_transition_line_width", "support_transition_speed", "bed_temperature", "bed_temperature_initial_layer",
|
||||
"can_switch_nozzle_type", "can_add_auxiliary_fan", "extra_flush_volume", "spaghetti_detector", "adaptive_layer_height",
|
||||
"z_hop_type", "z_lift_type", "bed_temperature_difference",
|
||||
"detect_narrow_internal_solid_infill", "ensure_vertical_shell_thickness","extruder_type",
|
||||
"internal_bridge_support_thickness","ensure_vertical_shell_thickness","extruder_clearance_max_radius"
|
||||
"extruder_type",
|
||||
"internal_bridge_support_thickness","extruder_clearance_max_radius"
|
||||
};
|
||||
|
||||
if (ignore.find(opt_key) != ignore.end()) {
|
||||
|
||||
@@ -747,6 +747,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionBool, tree_support_adaptive_layer_height))
|
||||
((ConfigOptionBool, tree_support_auto_brim))
|
||||
((ConfigOptionFloat, tree_support_brim_width))
|
||||
((ConfigOptionBool, detect_narrow_internal_solid_infill))
|
||||
// ((ConfigOptionBool, adaptive_layer_height))
|
||||
((ConfigOptionFloat, support_bottom_interface_spacing))
|
||||
((ConfigOptionEnum<PerimeterGeneratorType>, wall_generator))
|
||||
@@ -797,6 +798,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, bridge_flow))
|
||||
((ConfigOptionFloat, bridge_speed))
|
||||
((ConfigOptionFloatOrPercent, internal_bridge_speed))
|
||||
((ConfigOptionBool, ensure_vertical_shell_thickness))
|
||||
((ConfigOptionEnum<InfillPattern>, top_surface_pattern))
|
||||
((ConfigOptionEnum<InfillPattern>, bottom_surface_pattern))
|
||||
((ConfigOptionEnum<InfillPattern>, internal_solid_infill_pattern))
|
||||
@@ -967,6 +969,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionBool, single_extruder_multi_material_priming))
|
||||
((ConfigOptionBool, wipe_tower_no_sparse_layers))
|
||||
((ConfigOptionString, change_filament_gcode))
|
||||
((ConfigOptionString, change_extrusion_role_gcode))
|
||||
((ConfigOptionFloat, travel_speed))
|
||||
((ConfigOptionFloat, travel_speed_z))
|
||||
((ConfigOptionBool, silent_mode))
|
||||
@@ -1121,7 +1124,6 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
// BBS: project filaments
|
||||
((ConfigOptionFloats, filament_colour_new))
|
||||
// BBS: not in any preset, calculated before slicing
|
||||
((ConfigOptionBool, has_prime_tower))
|
||||
((ConfigOptionFloat, nozzle_volume))
|
||||
((ConfigOptionPoints, start_end_points))
|
||||
((ConfigOptionEnum<TimelapseType>, timelapse_type))
|
||||
|
||||
@@ -1057,6 +1057,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "solid_infill_filament"
|
||||
|| opt_key == "sparse_infill_line_width"
|
||||
|| opt_key == "infill_direction"
|
||||
|| opt_key == "ensure_vertical_shell_thickness"
|
||||
|| opt_key == "bridge_angle"
|
||||
//BBS
|
||||
|| opt_key == "bridge_density") {
|
||||
@@ -1520,6 +1521,18 @@ void PrintObject::discover_vertical_shells()
|
||||
if (top_bottom_surfaces_all_regions) {
|
||||
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
|
||||
// is calculated over all materials.
|
||||
// Is the "ensure vertical wall thickness" applicable to any region?
|
||||
bool has_extra_layers = false;
|
||||
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
|
||||
const PrintRegionConfig &config = this->printing_region(region_id).config();
|
||||
if (config.ensure_vertical_shell_thickness.value) {
|
||||
has_extra_layers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! has_extra_layers)
|
||||
// The "ensure vertical wall thickness" feature is not applicable to any of the regions. Quit.
|
||||
return;
|
||||
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom";
|
||||
//FIXME Improve the heuristics for a grain size.
|
||||
size_t grain_size = std::max(num_layers / 16, size_t(1));
|
||||
@@ -1589,6 +1602,11 @@ void PrintObject::discover_vertical_shells()
|
||||
}
|
||||
|
||||
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||
const PrintRegion ®ion = this->printing_region(region_id);
|
||||
if (! region.config().ensure_vertical_shell_thickness.value)
|
||||
// This region will be handled by discover_horizontal_shells().
|
||||
continue;
|
||||
|
||||
//FIXME Improve the heuristics for a grain size.
|
||||
size_t grain_size = std::max(num_layers / 16, size_t(1));
|
||||
|
||||
@@ -3109,7 +3127,166 @@ void PrintObject::discover_horizontal_shells()
|
||||
surface.surface_type = type;
|
||||
}
|
||||
#endif
|
||||
// The rest has already been performed by discover_vertical_shells().
|
||||
|
||||
// If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells().
|
||||
if (region_config.ensure_vertical_shell_thickness.value)
|
||||
continue;
|
||||
|
||||
coordf_t print_z = layer->print_z;
|
||||
coordf_t bottom_z = layer->bottom_z();
|
||||
for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
|
||||
m_print->throw_if_canceled();
|
||||
SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge;
|
||||
int num_solid_layers = (type == stTop) ? region_config.top_shell_layers.value : region_config.bottom_shell_layers.value;
|
||||
if (num_solid_layers == 0)
|
||||
continue;
|
||||
// Find slices of current type for current layer.
|
||||
// Use slices instead of fill_surfaces, because they also include the perimeter area,
|
||||
// which needs to be propagated in shells; we need to grow slices like we did for
|
||||
// fill_surfaces though. Using both ungrown slices and grown fill_surfaces will
|
||||
// not work in some situations, as there won't be any grown region in the perimeter
|
||||
// area (this was seen in a model where the top layer had one extra perimeter, thus
|
||||
// its fill_surfaces were thinner than the lower layer's infill), however it's the best
|
||||
// solution so far. Growing the external slices by EXTERNAL_INFILL_MARGIN will put
|
||||
// too much solid infill inside nearly-vertical slopes.
|
||||
|
||||
// Surfaces including the area of perimeters. Everything, that is visible from the top / bottom
|
||||
// (not covered by a layer above / below).
|
||||
// This does not contain the areas covered by perimeters!
|
||||
Polygons solid;
|
||||
for (const Surface &surface : layerm->slices.surfaces)
|
||||
if (surface.surface_type == type)
|
||||
polygons_append(solid, to_polygons(surface.expolygon));
|
||||
// Infill areas (slices without the perimeters).
|
||||
for (const Surface &surface : layerm->fill_surfaces.surfaces)
|
||||
if (surface.surface_type == type)
|
||||
polygons_append(solid, to_polygons(surface.expolygon));
|
||||
if (solid.empty())
|
||||
continue;
|
||||
// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom';
|
||||
|
||||
// Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking.
|
||||
for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1;
|
||||
(type == stTop) ?
|
||||
(n >= 0 && (int(i) - n < num_solid_layers ||
|
||||
print_z - m_layers[n]->print_z < region_config.top_shell_thickness.value - EPSILON)) :
|
||||
(n < int(m_layers.size()) && (n - int(i) < num_solid_layers ||
|
||||
m_layers[n]->bottom_z() - bottom_z < region_config.bottom_shell_thickness.value - EPSILON));
|
||||
(type == stTop) ? -- n : ++ n)
|
||||
{
|
||||
// Slic3r::debugf " looking for neighbors on layer %d...\n", $n;
|
||||
// Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface.
|
||||
LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id];
|
||||
|
||||
// find intersection between neighbor and current layer's surfaces
|
||||
// intersections have contours and holes
|
||||
// we update $solid so that we limit the next neighbor layer to the areas that were
|
||||
// found on this one - in other words, solid shells on one layer (for a given external surface)
|
||||
// are always a subset of the shells found on the previous shell layer
|
||||
// this approach allows for DWIM in hollow sloping vases, where we want bottom
|
||||
// shells to be generated in the base but not in the walls (where there are many
|
||||
// narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the
|
||||
// upper perimeter as an obstacle and shell will not be propagated to more upper layers
|
||||
//FIXME How does it work for stInternalBRIDGE? This is set for sparse infill. Likely this does not work.
|
||||
Polygons new_internal_solid;
|
||||
{
|
||||
Polygons internal;
|
||||
for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces)
|
||||
if (surface.surface_type == stInternal || surface.surface_type == stInternalSolid)
|
||||
polygons_append(internal, to_polygons(surface.expolygon));
|
||||
new_internal_solid = intersection(solid, internal, ApplySafetyOffset::Yes);
|
||||
}
|
||||
if (new_internal_solid.empty()) {
|
||||
// No internal solid needed on this layer. In order to decide whether to continue
|
||||
// searching on the next neighbor (thus enforcing the configured number of solid
|
||||
// layers, use different strategies according to configured infill density:
|
||||
if (region_config.sparse_infill_density.value == 0) {
|
||||
// If user expects the object to be void (for example a hollow sloping vase),
|
||||
// don't continue the search. In this case, we only generate the external solid
|
||||
// shell if the object would otherwise show a hole (gap between perimeters of
|
||||
// the two layers), and internal solid shells are a subset of the shells found
|
||||
// on each previous layer.
|
||||
goto EXTERNAL;
|
||||
} else {
|
||||
// If we have internal infill, we can generate internal solid shells freely.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (region_config.sparse_infill_density.value == 0) {
|
||||
// if we're printing a hollow object we discard any solid shell thinner
|
||||
// than a perimeter width, since it's probably just crossing a sloping wall
|
||||
// and it's not wanted in a hollow print even if it would make sense when
|
||||
// obeying the solid shell count option strictly (DWIM!)
|
||||
float margin = float(neighbor_layerm->flow(frExternalPerimeter).scaled_width());
|
||||
Polygons too_narrow = diff(
|
||||
new_internal_solid,
|
||||
opening(new_internal_solid, margin, margin + ClipperSafetyOffset, jtMiter, 5));
|
||||
// Trim the regularized region by the original region.
|
||||
if (! too_narrow.empty())
|
||||
new_internal_solid = solid = diff(new_internal_solid, too_narrow);
|
||||
}
|
||||
|
||||
// make sure the new internal solid is wide enough, as it might get collapsed
|
||||
// when spacing is added in Fill.pm
|
||||
{
|
||||
//FIXME Vojtech: Disable this and you will be sorry.
|
||||
float margin = 3.f * layerm->flow(frSolidInfill).scaled_width(); // require at least this size
|
||||
// we use a higher miterLimit here to handle areas with acute angles
|
||||
// in those cases, the default miterLimit would cut the corner and we'd
|
||||
// get a triangle in $too_narrow; if we grow it below then the shell
|
||||
// would have a different shape from the external surface and we'd still
|
||||
// have the same angle, so the next shell would be grown even more and so on.
|
||||
Polygons too_narrow = diff(
|
||||
new_internal_solid,
|
||||
opening(new_internal_solid, margin, margin + ClipperSafetyOffset, ClipperLib::jtMiter, 5));
|
||||
if (! too_narrow.empty()) {
|
||||
// grow the collapsing parts and add the extra area to the neighbor layer
|
||||
// as well as to our original surfaces so that we support this
|
||||
// additional area in the next shell too
|
||||
// make sure our grown surfaces don't exceed the fill area
|
||||
Polygons internal;
|
||||
for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces)
|
||||
if (surface.is_internal() && !surface.is_bridge())
|
||||
polygons_append(internal, to_polygons(surface.expolygon));
|
||||
polygons_append(new_internal_solid,
|
||||
intersection(
|
||||
expand(too_narrow, +margin),
|
||||
// Discard bridges as they are grown for anchoring and we can't
|
||||
// remove such anchors. (This may happen when a bridge is being
|
||||
// anchored onto a wall where little space remains after the bridge
|
||||
// is grown, and that little space is an internal solid shell so
|
||||
// it triggers this too_narrow logic.)
|
||||
internal));
|
||||
// solid = new_internal_solid;
|
||||
}
|
||||
}
|
||||
|
||||
// internal-solid are the union of the existing internal-solid surfaces
|
||||
// and new ones
|
||||
SurfaceCollection backup = std::move(neighbor_layerm->fill_surfaces);
|
||||
polygons_append(new_internal_solid, to_polygons(backup.filter_by_type(stInternalSolid)));
|
||||
ExPolygons internal_solid = union_ex(new_internal_solid);
|
||||
// assign new internal-solid surfaces to layer
|
||||
neighbor_layerm->fill_surfaces.set(internal_solid, stInternalSolid);
|
||||
// subtract intersections from layer surfaces to get resulting internal surfaces
|
||||
Polygons polygons_internal = to_polygons(std::move(internal_solid));
|
||||
ExPolygons internal = diff_ex(backup.filter_by_type(stInternal), polygons_internal, ApplySafetyOffset::Yes);
|
||||
// assign resulting internal surfaces to layer
|
||||
neighbor_layerm->fill_surfaces.append(internal, stInternal);
|
||||
polygons_append(polygons_internal, to_polygons(std::move(internal)));
|
||||
// assign top and bottom surfaces to layer
|
||||
backup.keep_types({ stTop, stBottom, stBottomBridge });
|
||||
std::vector<SurfacesPtr> top_bottom_groups;
|
||||
backup.group(&top_bottom_groups);
|
||||
for (SurfacesPtr &group : top_bottom_groups)
|
||||
neighbor_layerm->fill_surfaces.append(
|
||||
diff_ex(group, polygons_internal),
|
||||
// Use an existing surface as a template, it carries the bridge angle etc.
|
||||
*group.front());
|
||||
}
|
||||
EXTERNAL:;
|
||||
} // foreach type (stTop, stBottom, stBottomBridge)
|
||||
} // for each layer
|
||||
} // for each region
|
||||
|
||||
|
||||
@@ -119,8 +119,6 @@ SlicingParameters SlicingParameters::create_from_config(
|
||||
//BBS
|
||||
params.gap_object_support = object_config.support_bottom_z_distance.value;
|
||||
params.gap_support_object = object_config.support_top_z_distance.value;
|
||||
if (params.gap_object_support <= 0)
|
||||
params.gap_object_support = params.gap_support_object;
|
||||
|
||||
if (!print_config.independent_support_layer_height) {
|
||||
params.gap_raft_object = std::round(params.gap_raft_object / object_config.layer_height + EPSILON) * object_config.layer_height;
|
||||
|
||||
@@ -528,14 +528,14 @@ CalibPressureAdvancePattern::CalibPressureAdvancePattern(
|
||||
};
|
||||
|
||||
void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfig &config,
|
||||
bool is_bbl_machine,
|
||||
bool is_bbl_machine,
|
||||
Model &model,
|
||||
const Vec3d &origin)
|
||||
{
|
||||
std::stringstream gcode;
|
||||
gcode << "; start pressure advance pattern for layer\n";
|
||||
|
||||
refresh_setup(config, is_bbl_machine, model, origin);
|
||||
refresh_setup(config, is_bbl_machine, model, origin);
|
||||
|
||||
gcode << move_to(Vec2d(m_starting_point.x(), m_starting_point.y()), m_writer, "Move to start XY position");
|
||||
gcode << m_writer.travel_to_z(height_first_layer(), "Move to start Z position");
|
||||
@@ -686,8 +686,6 @@ void CalibPressureAdvancePattern::refresh_setup(const DynamicPrintConfig &config
|
||||
m_config.apply(model.objects.front()->config.get(), true);
|
||||
m_config.apply(model.objects.front()->volumes.front()->config.get(), true);
|
||||
|
||||
m_is_delta = (m_config.option<ConfigOptionPoints>("printable_area")->values.size() > 4);
|
||||
|
||||
_refresh_starting_point(model);
|
||||
_refresh_writer(is_bbl_machine, model, origin);
|
||||
}
|
||||
@@ -702,11 +700,6 @@ void CalibPressureAdvancePattern::_refresh_starting_point(const Model &model)
|
||||
|
||||
m_starting_point = Vec3d(bbox.min.x(), bbox.max.y(), 0);
|
||||
m_starting_point.y() += m_handle_spacing;
|
||||
|
||||
if (m_is_delta) {
|
||||
m_starting_point.x() *= -1;
|
||||
m_starting_point.y() -= (frame_size_y() / 2);
|
||||
}
|
||||
}
|
||||
|
||||
void CalibPressureAdvancePattern::_refresh_writer(bool is_bbl_machine, const Model &model, const Vec3d &origin)
|
||||
|
||||
@@ -294,7 +294,6 @@ private:
|
||||
const Calib_Params &m_params;
|
||||
|
||||
GCodeWriter m_writer;
|
||||
bool m_is_delta;
|
||||
Vec3d m_starting_point;
|
||||
bool m_is_start_point_fixed = false;
|
||||
|
||||
|
||||
@@ -278,6 +278,12 @@ constexpr inline T sqr(T x)
|
||||
return x * x;
|
||||
}
|
||||
|
||||
template<typename Number> constexpr
|
||||
inline bool is_zero(Number value)
|
||||
{
|
||||
return std::fabs(double(value)) < 1e-6;
|
||||
}
|
||||
|
||||
template <typename T, typename Number>
|
||||
constexpr inline T lerp(const T& a, const T& b, Number t)
|
||||
{
|
||||
|
||||
@@ -277,6 +277,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
sparse_infill_density == 0 &&
|
||||
! config->opt_bool("enable_support") &&
|
||||
config->opt_int("enforce_support_layers") == 0 &&
|
||||
config->opt_bool("ensure_vertical_shell_thickness") &&
|
||||
! config->opt_bool("detect_thin_wall") &&
|
||||
! config->opt_bool("overhang_reverse") &&
|
||||
config->opt_enum<TimelapseType>("timelapse_type") == TimelapseType::tlTraditional))
|
||||
@@ -304,6 +305,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
new_conf.set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
||||
new_conf.set_key_value("enable_support", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("enforce_support_layers", new ConfigOptionInt(0));
|
||||
new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true));
|
||||
new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("timelapse_type", new ConfigOptionEnum<TimelapseType>(tlTraditional));
|
||||
@@ -524,7 +526,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
}
|
||||
|
||||
bool have_perimeters = config->opt_int("wall_loops") > 0;
|
||||
for (auto el : { "extra_perimeters_on_overhangs", "detect_thin_wall", "detect_overhang_wall",
|
||||
for (auto el : { "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "detect_thin_wall", "detect_overhang_wall",
|
||||
"seam_position", "staggered_inner_seams", "wall_infill_order", "outer_wall_line_width",
|
||||
"inner_wall_speed", "outer_wall_speed", "small_perimeter_speed", "small_perimeter_threshold" })
|
||||
toggle_field(el, have_perimeters);
|
||||
|
||||
+39
-33
@@ -2122,16 +2122,15 @@ void GUI_App::init_app_config()
|
||||
app_config = new AppConfig();
|
||||
//app_config = new AppConfig(is_editor() ? AppConfig::EAppMode::Editor : AppConfig::EAppMode::GCodeViewer);
|
||||
|
||||
m_config_corrupted = false;
|
||||
// load settings
|
||||
m_app_conf_exists = app_config->exists();
|
||||
if (m_app_conf_exists) {
|
||||
std::string error = app_config->load();
|
||||
if (!error.empty()) {
|
||||
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
|
||||
throw Slic3r::RuntimeError(
|
||||
_u8L("OrcaSlicer configuration file may be corrupted and is not abled to be parsed."
|
||||
"Please delete the file and try again.") +
|
||||
"\n\n" + app_config->config_path() + "\n\n" + error);
|
||||
// Orca: if the config file is corrupted, we will show a error dialog and create a default config file.
|
||||
m_config_corrupted = true;
|
||||
|
||||
}
|
||||
// Save orig_version here, so its empty if no app_config existed before this run.
|
||||
m_last_config_version = app_config->orig_version();//parse_semver_from_ini(app_config->config_path());
|
||||
@@ -2477,7 +2476,6 @@ bool GUI_App::on_init_inner()
|
||||
#endif // __WXMSW__
|
||||
|
||||
preset_updater = new PresetUpdater();
|
||||
#if orca_todo
|
||||
Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent& evt) {
|
||||
if (this->plater_ != nullptr) {
|
||||
// this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAvailable);
|
||||
@@ -2566,7 +2564,6 @@ bool GUI_App::on_init_inner()
|
||||
});
|
||||
dlg.ShowModal();
|
||||
});
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#ifdef __WXMSW__
|
||||
@@ -2750,6 +2747,7 @@ bool GUI_App::on_init_inner()
|
||||
|
||||
if (m_post_initialized && app_config->dirty())
|
||||
app_config->save();
|
||||
|
||||
});
|
||||
|
||||
m_initialized = true;
|
||||
@@ -2757,6 +2755,13 @@ bool GUI_App::on_init_inner()
|
||||
flush_logs();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "finished the gui app init";
|
||||
if (m_config_corrupted) {
|
||||
m_config_corrupted = false;
|
||||
show_error(nullptr,
|
||||
_u8L(
|
||||
"The OrcaSlicer configuration file may be corrupted and cannot be parsed.\nOrcaSlicer has attempted to recreate the "
|
||||
"configuration file.\nPlease note, application settings will be lost, but printer profiles will not be affected."));
|
||||
}
|
||||
//BBS: delete splash screen
|
||||
delete scrn;
|
||||
return true;
|
||||
@@ -4266,7 +4271,7 @@ void GUI_App::check_new_version_sf(bool show_tips, int by_user)
|
||||
error);
|
||||
})
|
||||
.timeout_connect(1)
|
||||
.on_complete([&](std::string body, unsigned http_status) {
|
||||
.on_complete([this,by_user](std::string body, unsigned http_status) {
|
||||
// Http response OK
|
||||
if (http_status != 200)
|
||||
return;
|
||||
@@ -4292,30 +4297,28 @@ void GUI_App::check_new_version_sf(bool show_tips, int by_user)
|
||||
std::string best_release_content;
|
||||
std::string best_pre_content;
|
||||
const std::regex reg_num("([0-9]+)");
|
||||
for (auto json_version : root) {
|
||||
std::string tag = json_version.second.get<std::string>("tag_name");
|
||||
if (tag[0] == 'v')
|
||||
tag.erase(0, 1);
|
||||
for (std::regex_iterator it = std::sregex_iterator(tag.begin(), tag.end(), reg_num);
|
||||
it != std::sregex_iterator(); ++it) {
|
||||
}
|
||||
Semver tag_version = get_version(tag, matcher);
|
||||
if (current_version == tag_version)
|
||||
i_am_pre = json_version.second.get<bool>("prerelease");
|
||||
if (json_version.second.get<bool>("prerelease")) {
|
||||
if (best_pre < tag_version) {
|
||||
best_pre = tag_version;
|
||||
best_pre_url = json_version.second.get<std::string>("html_url");
|
||||
best_pre_content = json_version.second.get<std::string>("body");
|
||||
best_pre.set_prerelease("Preview");
|
||||
}
|
||||
} else {
|
||||
if (best_release < tag_version) {
|
||||
best_release = tag_version;
|
||||
best_release_url = json_version.second.get<std::string>("html_url");
|
||||
best_release_content = json_version.second.get<std::string>("body");
|
||||
}
|
||||
}
|
||||
std::string tag = root.get<std::string>("tag_name");
|
||||
if (tag[0] == 'v')
|
||||
tag.erase(0, 1);
|
||||
for (std::regex_iterator it = std::sregex_iterator(tag.begin(), tag.end(), reg_num);
|
||||
it != std::sregex_iterator(); ++it) {
|
||||
}
|
||||
Semver tag_version = get_version(tag, matcher);
|
||||
if (current_version == tag_version)
|
||||
i_am_pre = root.get<bool>("prerelease");
|
||||
if (root.get<bool>("prerelease")) {
|
||||
if (best_pre < tag_version) {
|
||||
best_pre = tag_version;
|
||||
best_pre_url = root.get<std::string>("html_url");
|
||||
best_pre_content = root.get<std::string>("body");
|
||||
best_pre.set_prerelease("Preview");
|
||||
}
|
||||
} else {
|
||||
if (best_release < tag_version) {
|
||||
best_release = tag_version;
|
||||
best_release_url = root.get<std::string>("html_url");
|
||||
best_release_content = root.get<std::string>("body");
|
||||
}
|
||||
}
|
||||
|
||||
// if release is more recent than beta, use release anyway
|
||||
@@ -4325,8 +4328,11 @@ void GUI_App::check_new_version_sf(bool show_tips, int by_user)
|
||||
best_pre_content = best_release_content;
|
||||
}
|
||||
// if we're the most recent, don't do anything
|
||||
if ((i_am_pre ? best_pre : best_release) <= current_version)
|
||||
if ((i_am_pre ? best_pre : best_release) <= current_version) {
|
||||
if (by_user != 0)
|
||||
this->no_new_version();
|
||||
return;
|
||||
}
|
||||
|
||||
// BOOST_LOG_TRIVIAL(info) << format("Got %1% online version: `%2%`. Sending to GUI thread...",
|
||||
// SLIC3R_APP_NAME, i_am_pre ? best_pre.to_string(): best_release.to_string());
|
||||
|
||||
@@ -656,6 +656,7 @@ private:
|
||||
bool m_datadir_redefined { false };
|
||||
std::string m_older_data_dir_path;
|
||||
boost::optional<Semver> m_last_config_version;
|
||||
bool m_config_corrupted { false };
|
||||
};
|
||||
|
||||
DECLARE_APP(GUI_App)
|
||||
|
||||
@@ -37,6 +37,8 @@ protected:
|
||||
std::string get_action_snapshot_name() override { return "Paint-on seam editing"; }
|
||||
static const constexpr float CursorRadiusMin = 0.05f; // cannot be zero
|
||||
|
||||
const float get_cursor_radius_min() const override { return CursorRadiusMin; }
|
||||
|
||||
private:
|
||||
bool on_init() override;
|
||||
|
||||
|
||||
@@ -147,6 +147,17 @@ bool GLGizmoText::on_init()
|
||||
|
||||
reset_text_info();
|
||||
|
||||
m_desc["font"] = _L("Font");
|
||||
m_desc["size"] = _L("Size");
|
||||
m_desc["thickness"] = _L("Thickness");
|
||||
m_desc["text_gap"] = _L("Text Gap");
|
||||
m_desc["angle"] = _L("Angle");
|
||||
m_desc["embeded_depth"] = _L("Embeded\ndepth");
|
||||
m_desc["input_text"] = _L("Input text");
|
||||
|
||||
m_desc["surface"] = _L("Surface");
|
||||
m_desc["horizontal_text"] = _L("Horizontal text");
|
||||
|
||||
m_desc["rotate_text_caption"] = _L("Shift + Mouse move up or dowm");
|
||||
m_desc["rotate_text"] = _L("Rotate text");
|
||||
|
||||
@@ -669,13 +680,13 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 4.0f * currt_scale);
|
||||
GizmoImguiBegin("Text", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
|
||||
|
||||
float space_size = m_imgui->get_style_scaling() * 8;
|
||||
float font_cap = m_imgui->calc_text_size(_L("Font")).x;
|
||||
float size_cap = m_imgui->calc_text_size(_L("Size")).x;
|
||||
float thickness_cap = m_imgui->calc_text_size(_L("Thickness")).x;
|
||||
float input_cap = m_imgui->calc_text_size(_L("Input text")).x;
|
||||
float depth_cap = m_imgui->calc_text_size(_L("Embeded")).x;
|
||||
float caption_size = std::max(std::max(font_cap, size_cap), std::max(depth_cap, input_cap)) + space_size + ImGui::GetStyle().WindowPadding.x;
|
||||
const float space_size = m_imgui->get_style_scaling() * 8;
|
||||
const std::array<std::string, 7> cap_array = std::array<std::string, 7>{ "font", "size", "thickness", "text_gap", "angle", "embeded_depth", "input_text" };
|
||||
float caption_size = 0.0f;
|
||||
for (const auto &t : cap_array) {
|
||||
caption_size = std::max(caption_size, m_imgui->calc_text_size(m_desc[t]).x);
|
||||
}
|
||||
caption_size += space_size + ImGui::GetStyle().WindowPadding.x;
|
||||
|
||||
float input_text_size = m_imgui->scaled(10.0f);
|
||||
float button_size = ImGui::GetFrameHeight();
|
||||
@@ -702,7 +713,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
|
||||
m_imgui->text(_L("Font"));
|
||||
m_imgui->text(m_desc["font"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(list_width);
|
||||
push_combo_style(currt_scale);
|
||||
@@ -742,7 +753,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
||||
ImGui::PopStyleVar(2);
|
||||
pop_combo_style();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Size"));
|
||||
m_imgui->text(m_desc["size"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(input_size);
|
||||
if(ImGui::InputFloat("###font_size", &m_font_size, 0.0f, 0.0f, "%.2f"))
|
||||
@@ -769,7 +780,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
||||
ImGui::PopStyleVar(3);
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Thickness"));
|
||||
m_imgui->text(m_desc["thickness"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(list_width);
|
||||
float old_value = m_thickness;
|
||||
@@ -784,7 +795,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
||||
const float drag_left_width = caption_size + slider_width + space_size;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Text Gap"));
|
||||
m_imgui->text(m_desc["text_gap"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(slider_width);
|
||||
if (m_imgui->bbl_slider_float_style("##text_gap", &m_text_gap, -100, 100, "%.2f", 1.0f, true))
|
||||
@@ -795,7 +806,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
||||
m_need_update_text = true;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Angle"));
|
||||
m_imgui->text(m_desc["angle"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(slider_width);
|
||||
if (m_imgui->bbl_slider_float_style("##angle", &m_rotate_angle, 0, 360, "%.2f", 1.0f, true))
|
||||
@@ -806,7 +817,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
||||
m_need_update_text = true;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Embeded\ndepth"));
|
||||
m_imgui->text(m_desc["embeded_depth"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(list_width);
|
||||
old_value = m_embeded_depth;
|
||||
@@ -817,7 +828,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
||||
m_need_update_text = true;
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(_L("Input text"));
|
||||
m_imgui->text(m_desc["input_text"]);
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::PushItemWidth(list_width);
|
||||
|
||||
@@ -835,12 +846,12 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
|
||||
|
||||
ImGui::SameLine(caption_size);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
if (m_imgui->bbl_checkbox(_L("Surface"), m_is_surface_text))
|
||||
if (m_imgui->bbl_checkbox(m_desc["surface"], m_is_surface_text))
|
||||
m_need_update_text = true;
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
if (m_imgui->bbl_checkbox(_L("Horizontal text"), m_keep_horizontal))
|
||||
if (m_imgui->bbl_checkbox(m_desc["horizontal_text"], m_keep_horizontal))
|
||||
m_need_update_text = true;
|
||||
|
||||
//ImGui::SameLine();
|
||||
|
||||
@@ -16,7 +16,7 @@ constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2
|
||||
|
||||
static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar
|
||||
static const float HORIZONTAL_SLIDER_WINDOW_HEIGHT = 64.0f;
|
||||
static const float VERTICAL_SLIDER_WINDOW_WIDTH = 124.0f;
|
||||
static const float VERTICAL_SLIDER_WINDOW_WIDTH = 160.0f;
|
||||
static const float GROOVE_WIDTH = 12.0f;
|
||||
static const ImVec2 ONE_LAYER_MARGIN = ImVec2(20.0f, 20.0f);
|
||||
static const ImVec2 ONE_LAYER_BUTTON_SIZE = ImVec2(28.0f, 28.0f);
|
||||
|
||||
@@ -459,7 +459,7 @@ void PartPlate::calc_vertex_for_icons(int index, GeometryBuffer &buffer)
|
||||
ExPolygon poly;
|
||||
auto bed_ext = get_extents(m_shape);
|
||||
Vec2d p = bed_ext[2];
|
||||
if (m_plater->get_build_volume_type() == BuildVolume_Type::Circle)
|
||||
if (m_plater && m_plater->get_build_volume_type() == BuildVolume_Type::Circle)
|
||||
p[1] -= std::max(
|
||||
0.0, (bed_ext.size()(1) - 5 * PARTPLATE_ICON_SIZE - 4 * PARTPLATE_ICON_GAP_Y - PARTPLATE_ICON_GAP_TOP) / 2);
|
||||
|
||||
|
||||
@@ -1056,6 +1056,13 @@ void Sidebar::update_all_preset_comboboxes()
|
||||
m_bed_type_list->SelectAndNotify((int)bed_type - 1);
|
||||
}
|
||||
m_bed_type_list->Enable();
|
||||
auto str_bed_type = wxGetApp().app_config->get_printer_setting(wxGetApp().preset_bundle->printers.get_selected_preset_name(), "curr_bed_type");
|
||||
if(!str_bed_type.empty()){
|
||||
int bed_type_value = atoi(str_bed_type.c_str());
|
||||
if(bed_type_value == 0)
|
||||
bed_type_value = 1;
|
||||
m_bed_type_list->SelectAndNotify(bed_type_value - 1);
|
||||
}
|
||||
} else {
|
||||
connection_btn->Show();
|
||||
ams_btn->Hide();
|
||||
|
||||
@@ -1941,6 +1941,8 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("bridge_angle");
|
||||
optgroup->append_single_option_line("minimum_sparse_infill_area");
|
||||
optgroup->append_single_option_line("infill_combination");
|
||||
optgroup->append_single_option_line("detect_narrow_internal_solid_infill");
|
||||
optgroup->append_single_option_line("ensure_vertical_shell_thickness");
|
||||
|
||||
page = add_options_page(L("Speed"), "empty");
|
||||
optgroup = page->new_optgroup(L("Initial layer speed"), L"param_speed_first", 15);
|
||||
@@ -3229,6 +3231,17 @@ void TabPrinter::build_fff()
|
||||
option.opt.height = gcode_field_height;//150;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = page->new_optgroup(L("Change extrusion role G-code"), L"param_gcode", 0);
|
||||
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
|
||||
validate_custom_gcode_cb(this, optgroup, opt_key, value);
|
||||
};
|
||||
|
||||
option = optgroup->get_option("change_extrusion_role_gcode");
|
||||
option.opt.full_width = true;
|
||||
option.opt.is_code = true;
|
||||
option.opt.height = gcode_field_height;//150;
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = page->new_optgroup(L("Pause G-code"), L"param_gcode", 0);
|
||||
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
|
||||
validate_custom_gcode_cb(this, optgroup, opt_key, value);
|
||||
|
||||
Reference in New Issue
Block a user