mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-05 23:01:17 +00:00
@@ -2673,10 +2673,10 @@ void CadDocument::apply_feature(TopoDS_Shape& result, bool& have_body,
|
||||
ridge = pipe.Shape();
|
||||
have_ridge = !ridge.IsNull();
|
||||
}
|
||||
} catch (const Standard_Failure&) {
|
||||
have_ridge = false; // OCCT failure — on OCCT >= 8 Standard_Failure derives from std::exception, so this handler must come first
|
||||
} catch (const std::exception&) {
|
||||
have_ridge = false; // fall back to the bare cylinder/bore below
|
||||
} catch (const Standard_Failure&) {
|
||||
have_ridge = false; // OCCT failure (not a std::exception) — must be caught here too
|
||||
}
|
||||
|
||||
if (f.thread_internal) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <TopExp.hxx>
|
||||
#include <TopTools.hxx>
|
||||
#include <TopTools_IndexedMapOfShape.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
|
||||
#include <Poly_Triangulation.hxx>
|
||||
#include <gp_Ax2.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "XCAFDoc_DocumentTool.hxx"
|
||||
#include "XCAFDoc_ShapeTool.hxx"
|
||||
#include "XCAFApp_Application.hxx"
|
||||
#include "TDF_LabelSequence.hxx"
|
||||
#include "TopoDS_Solid.hxx"
|
||||
#include "TopoDS_Compound.hxx"
|
||||
#include "TopoDS_Builder.hxx"
|
||||
|
||||
+38
-28
@@ -2504,7 +2504,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = path;
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -2556,7 +2557,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
}
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = std::string(path) + "\n" + err_msg;
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -2580,7 +2582,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = std::string(path) + "\n" + ex.what();
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -2693,7 +2696,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
if (ret) {
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = std::string(path) + "\nFailed to rename the output G-code file: " + ret.message();
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -2712,7 +2716,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.id = std::to_string(print->model().id().id);
|
||||
ctx.name = print->get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = path;
|
||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||
@@ -7964,6 +7969,20 @@ double GCode::calc_max_volumetric_speed(const double layer_height, const double
|
||||
return res;
|
||||
}
|
||||
|
||||
// ORCA: Overlap at or below which the overhang fan switches on; negative when it does not depend on overlap
|
||||
// (Overhang_threshold_none cools every external perimeter).
|
||||
static float overhang_fan_overlap_threshold(int overhang_fan_threshold)
|
||||
{
|
||||
switch (overhang_fan_threshold) {
|
||||
case (int) Overhang_threshold_1_4: return 0.9f;
|
||||
case (int) Overhang_threshold_2_4: return 0.75f;
|
||||
case (int) Overhang_threshold_3_4: return 0.5f;
|
||||
case (int) Overhang_threshold_4_4: return 0.25f;
|
||||
case (int) Overhang_threshold_bridge: return 0.05f;
|
||||
default: return -1.f;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed)
|
||||
{
|
||||
std::string gcode;
|
||||
@@ -8306,6 +8325,13 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
|
||||
ConfigOptionPercents overhang_overlap_levels({90, 75, 50, 25, 13, 0});
|
||||
|
||||
// ORCA: Lets the path be split where the overhang fan switches, not only where the speed changes.
|
||||
// Bridges and overhang perimeters are cooled regardless of overlap.
|
||||
float fan_overlap_threshold = -1.f;
|
||||
if (FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers && path.role() != erBridgeInfill &&
|
||||
path.role() != erOverhangPerimeter)
|
||||
fan_overlap_threshold = overhang_fan_overlap_threshold(FILAMENT_CONFIG(overhang_fan_threshold));
|
||||
|
||||
if (NOZZLE_CONFIG(slowdown_for_curled_perimeters)){
|
||||
ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
|
||||
{FloatOrPercent{100, true},
|
||||
@@ -8326,7 +8352,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
FloatOrPercent{NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true}});
|
||||
|
||||
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds,
|
||||
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters));
|
||||
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters),
|
||||
fan_overlap_threshold);
|
||||
}else{
|
||||
ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
|
||||
{FloatOrPercent{100, true},
|
||||
@@ -8345,7 +8372,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
FloatOrPercent{NOZZLE_CONFIG(bridge_speed) * 100 / ref_speed, true}});
|
||||
|
||||
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds,
|
||||
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters));
|
||||
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters),
|
||||
fan_overlap_threshold);
|
||||
}
|
||||
variable_speed = std::any_of(new_points.begin(), new_points.end(),
|
||||
[speed](const ProcessedPoint &p) { return fabs(double(p.speed) - speed) > 1; }); // Ignore small speed variations (under 1mm/sec)
|
||||
@@ -8497,28 +8525,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
if (role == erBridgeInfill || role == erOverhangPerimeter) { // ORCA: Split out bridge infill to internal and external to apply separate fan settings
|
||||
return true;
|
||||
}
|
||||
switch (overhang_fan_threshold) {
|
||||
case (int)Overhang_threshold_1_4:
|
||||
return overlap <= 0.9f;
|
||||
break;
|
||||
case (int)Overhang_threshold_2_4:
|
||||
return overlap <= 0.75f;
|
||||
break;
|
||||
case (int)Overhang_threshold_3_4:
|
||||
return overlap <= 0.5f;
|
||||
break;
|
||||
case (int)Overhang_threshold_4_4:
|
||||
return overlap <= 0.25f;
|
||||
break;
|
||||
case (int)Overhang_threshold_bridge:
|
||||
return overlap <= 0.05f;
|
||||
break;
|
||||
case (int)Overhang_threshold_none:
|
||||
if (overhang_fan_threshold == Overhang_threshold_none)
|
||||
return is_external_perimeter(role);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
const float overlap_threshold = overhang_fan_overlap_threshold(overhang_fan_threshold);
|
||||
return overlap_threshold >= 0.f && overlap <= overlap_threshold;
|
||||
};
|
||||
|
||||
std::string comment;
|
||||
|
||||
@@ -41,10 +41,12 @@ std::vector<ExtendedPoint<L::Dim>> estimate_points_properties(const POINTS&
|
||||
float flow_width,
|
||||
float max_line_length = -1.0f,
|
||||
float min_distance = -1.0f,
|
||||
// Maps an overhang distance onto the speed it will be printed at. Interior sampling
|
||||
// needs it to tell which of the points it could add would change the G-code, and is
|
||||
// skipped without it.
|
||||
const std::function<float(float)>& distance_to_speed = {})
|
||||
// Speed an overhang distance prints at. Without it, interior sampling
|
||||
// is skipped and every line over 4mm is split.
|
||||
const std::function<float(float)>& distance_to_speed = {},
|
||||
// Overlap (1 - distance / flow_width) at or below which the overhang
|
||||
// fan switches on; negative when the fan does not depend on overlap.
|
||||
float fan_overlap_threshold = -1.0f)
|
||||
{
|
||||
bool looped = input_points.front() == input_points.back();
|
||||
std::function<size_t(size_t,size_t)> get_prev_index = [](size_t idx, size_t count) {
|
||||
@@ -125,29 +127,43 @@ std::vector<ExtendedPoint<L::Dim>> estimate_points_properties(const POINTS&
|
||||
points.push_back(next_point);
|
||||
}
|
||||
|
||||
// ORCA: How an overhang distance prints, which is what the passes below compare. A segment is printed at the lower
|
||||
// of the speeds at its two ends, and with the overhang fan on if the overlap at either end turns it on. A point
|
||||
// added to a path can therefore only change the G-code where it prints at a different speed or fan state from the
|
||||
// points either side of it, and the passes below add points there and nowhere else.
|
||||
const float width_inv = 1.f / flow_width;
|
||||
// Whether an overhang distance turns the overhang fan on. The overlap test check_overhang_fan applies in GCode.cpp.
|
||||
auto fan_on = [fan_overlap_threshold, width_inv](float distance) {
|
||||
return fan_overlap_threshold >= 0.f && 1.f - distance * width_inv <= fan_overlap_threshold;
|
||||
};
|
||||
// Whether two overhang distances are interchangeable ie have the same speed (beyond a 1mm/sec threshold that gcode.cpp filters out on)
|
||||
// and the same fan state.
|
||||
auto same_speed_and_fan = [&distance_to_speed, &fan_on](float a, float b) {
|
||||
return std::abs(distance_to_speed(a) - distance_to_speed(b)) <= 1.f && fan_on(a) == fan_on(b);
|
||||
};
|
||||
// Whether the first overhang distance prints slower than the second, beyond the 1mm/sec gcode.cpp tolerance, or turns the overhang
|
||||
// fan on where the second does not. Against a supported point (overhang distance 0) it tells whether an end is
|
||||
// affected by the overhang.
|
||||
auto slower_or_cooled = [&distance_to_speed, &fan_on](float a, float b) {
|
||||
return distance_to_speed(a) < distance_to_speed(b) - 1.f || (fan_on(a) && !fan_on(b));
|
||||
};
|
||||
|
||||
// ORCA: Interior sampling
|
||||
// The passes below infer the support under a span from its endpoints alone, so an interior that is supported
|
||||
// differently from both ends is invisible to them: the outer perimeter of an overhang whose ends are caged by
|
||||
// full height walls reads as supported along its whole length. Probe the interior, keep the samples the
|
||||
// endpoint interpolation fails to predict, and bisect either side of each one, so a span that is only partly
|
||||
// unsupported gets points where its support actually changes instead of one reading spread across all of it.
|
||||
if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS && min_distance > 0 && distance_to_speed) {
|
||||
// The passes below infer the support under a span from its endpoints alone, so a part that is supported
|
||||
// differently from both ends is invisible to them. The outer perimeter of an overhang whose ends are supported by
|
||||
// reads as supported along its whole length. Probe the interior, keep the samples the endpoint interpolation fails to predict,
|
||||
// and bisect either side of each one, so a span that is only partly unsupported gets points where its support actually changes
|
||||
// instead of one reading being spread across all of it.
|
||||
// Skipped where there is nothing to find: min_distance <= 0 when no overhang can slow this path down, and
|
||||
// fan_overlap_threshold < 0 when no overhang switches the fan on. It also needs distance_to_speed to tell which of
|
||||
// the points it could add would change the G-code.
|
||||
if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS && distance_to_speed && (min_distance > 0 || fan_overlap_threshold >= 0.f)) {
|
||||
// Probe at least this densely before treating matching samples as evidence that a span is uniform. The
|
||||
// segmentation pass below only splits lines of 2mm or more, and every pass here drops points closer
|
||||
// together than min_spacing, so finer discovery would not produce a more precise speed transition.
|
||||
// together than min_spacing, so finer discovery would not produce a more precise transition.
|
||||
const double max_probe_spacing = std::max(2., 4. * min_spacing);
|
||||
// A backstop for that length test, which on a non-finite length would never be met.
|
||||
constexpr int max_bisection_depth = 10;
|
||||
// Whether two readings are interchangeable. A segment is printed at the lower of the speeds its ends
|
||||
// read, so a sample that agrees on speed with what is already known cannot change the G-code, whatever
|
||||
// its distance says. The distances themselves are far too coarse a stand-in for this: the speed sections
|
||||
// interpolate, so readings a small fraction of min_distance apart can still be tens of mm/s apart.
|
||||
// The tolerance matches the one GCode.cpp applies when it decides a path has a variable speed at all.
|
||||
auto same_speed = [&distance_to_speed](float a, float b) {
|
||||
return std::abs(distance_to_speed(a) - distance_to_speed(b)) <= 1.f;
|
||||
};
|
||||
// Whether the first reading is printed slower than the second, once they are known to differ.
|
||||
auto prints_slower = [&distance_to_speed](float a, float b) { return distance_to_speed(a) < distance_to_speed(b); };
|
||||
|
||||
// Part of a segment still to bisect: its positions along the segment and bisections left.
|
||||
struct Subspan { double t0, t1; int depth; };
|
||||
@@ -185,8 +201,8 @@ std::vector<ExtendedPoint<L::Dim>> estimate_points_properties(const POINTS&
|
||||
if (!interior.empty()) {
|
||||
std::sort(interior.begin(), interior.end(),
|
||||
[](const std::pair<double, float>& l, const std::pair<double, float>& r) { return l.first < r.first; });
|
||||
// Coarse probing keeps every sample it took until this pass can see which ones bracket a speed
|
||||
// transition. Matching samples cannot be discarded during discovery: one may be the last
|
||||
// Coarse probing keeps every sample it took until this pass can see which ones bracket a speed or
|
||||
// fan transition. Matching samples cannot be discarded during discovery: one may be the last
|
||||
// supported point before a narrow unsupported pocket found by a later probe.
|
||||
size_t kept = 0;
|
||||
for (size_t i = 0; i < interior.size(); ++i) {
|
||||
@@ -195,15 +211,15 @@ std::vector<ExtendedPoint<L::Dim>> estimate_points_properties(const POINTS&
|
||||
const bool at_end = i + 1 == interior.size(); // And nothing follows the last sample but the segment's end
|
||||
const float before = at_start ? curr.distance : interior[kept - 1].second;
|
||||
const float after = at_end ? next.distance : interior[i + 1].second;
|
||||
// A sample is worth a point in the path only where it prints at a different speed from the
|
||||
// readings either side of it. Differing from one of the segment's own ends is not enough on
|
||||
// its own where the sample is the faster of the two: the segmentation pass below already
|
||||
// ends the slowdown an end reads, at a distance taken from how far out that end is rather
|
||||
// than from wherever bisection happened to stop, and a point here would leave the span
|
||||
// beside the end too short for that pass to run at all. Support an end cannot account for,
|
||||
// where the interior is the slower reading, is exactly what this pass is here to find.
|
||||
const bool worth_before = !same_speed(sample, before) && (!at_start || prints_slower(sample, before));
|
||||
const bool worth_after = !same_speed(sample, after) && (!at_end || prints_slower(sample, after));
|
||||
// A sample is worth a point in the path only where it prints differently, in speed or fan state,
|
||||
// from the points either side of it. Differing from one of the segment's own ends is not enough on
|
||||
// its own where the sample is not the slower or cooled of the two: the segmentation pass below
|
||||
// already confines the slowdown and cooling at an end, at a distance taken from how far out that
|
||||
// end is rather than from wherever bisection happened to stop, and a point here would leave the
|
||||
// span beside the end too short for that pass to run at all. Support an end cannot account for,
|
||||
// where the interior is the slower or cooled of the two, is exactly what this pass is here to find.
|
||||
const bool worth_before = !same_speed_and_fan(sample, before) && (!at_start || slower_or_cooled(sample, before));
|
||||
const bool worth_after = !same_speed_and_fan(sample, after) && (!at_end || slower_or_cooled(sample, after));
|
||||
if (worth_before || worth_after)
|
||||
interior[kept++] = interior[i];
|
||||
}
|
||||
@@ -238,52 +254,60 @@ std::vector<ExtendedPoint<L::Dim>> estimate_points_properties(const POINTS&
|
||||
if ((curr.distance > -boundary_offset && curr.distance < boundary_offset + 2.0f) ||
|
||||
(next.distance > -boundary_offset && next.distance < boundary_offset + 2.0f)) {
|
||||
double line_len = (next.position - curr.position).norm();
|
||||
|
||||
// ORCA: Segment path to smaller lines by adding additional points only if the path has an overhang that
|
||||
// will trigger a slowdown and the path is also reasonably large, i.e. 2mm in length or more
|
||||
// If there is no overhang in the start/end point, dont segment it.
|
||||
// Ignore this check if the control of segmentation for overhangs is disabled (min_distance=-1)
|
||||
if ((min_distance > 0 && ((std::abs(curr.distance) > min_distance) || (std::abs(next.distance) > min_distance)) && line_len >= 2.f) ||
|
||||
(min_distance <= 0 && line_len > 4.0f)) {
|
||||
|
||||
// ORCA: A line prints as slow as its slower end and is cooled if either end is, so an overhang at one
|
||||
// end would otherwise slow down or cool the whole line. Split the line so that only the part beside
|
||||
// that end prints that way, if the line is reasonably long (2mm or more) and at least one end prints
|
||||
// slower or cooled compared with a supported point (overhang distance 0). Deciding on how the end
|
||||
// prints, rather than on its overhang distance against min_distance, also catches an end whose overhang
|
||||
// distance is exactly where the slowdown begins, such as an outline crossing at half a line width.
|
||||
// Without distance_to_speed, split every line over 4mm.
|
||||
const bool split_line = distance_to_speed ?
|
||||
line_len >= 2.f && (slower_or_cooled(curr.distance, 0.f) || slower_or_cooled(next.distance, 0.f)) :
|
||||
line_len > 4.0f;
|
||||
if (split_line) {
|
||||
// Each end's piece is that end's overhang distance plus 1.5 line widths (3 * boundary_offset) long:
|
||||
// a0 ends the piece beside curr, a1 starts the piece beside next.
|
||||
double a0 = std::clamp((curr.distance + 3 * boundary_offset) / line_len, 0.0, 1.0);
|
||||
double a1 = std::clamp(1.0f - (next.distance + 3 * boundary_offset) / line_len, 0.0, 1.0);
|
||||
double t0 = std::min(a0, a1);
|
||||
double t1 = std::max(a0, a1);
|
||||
|
||||
if (t0 < 1.0) {
|
||||
Vec p0 = curr.position + t0 * (next.position - curr.position);
|
||||
auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(
|
||||
p0.template cast<AABBScalar>());
|
||||
ExtendedPoint<L::Dim> new_p{};
|
||||
new_p.position = p0;
|
||||
new_p.distance = float(p0_dist + boundary_offset);
|
||||
// ORCA: only create a new point in the path if the new point overhang distance will be used to generate a speed change
|
||||
// or if this option is disabled (min_distance<=0)
|
||||
if( (std::abs(p0_dist) > min_distance) || (min_distance<=0)){
|
||||
// ORCA: also filter out points that are introduced to the start of the path when their distance from the start point is
|
||||
// not meaningful
|
||||
if ((p0 - curr.position).norm() > min_spacing && (next.position - p0).norm() > min_spacing) {
|
||||
new_points.push_back(new_p);
|
||||
}
|
||||
}
|
||||
// Up to two cut points, in order along the line. Each takes its own overhang distance, so every
|
||||
// piece prints by the overhang distances at its own two ends. t0 >= 1 or t1 <= 0 falls on the
|
||||
// line's own end, so there is no cut. A cut closer than min_spacing to either end of the line is
|
||||
// not meaningful and is filtered out (#6714).
|
||||
ExtendedPoint<L::Dim> cut[2]{};
|
||||
bool keep[2] = {false, false};
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
const double t = k == 0 ? t0 : t1;
|
||||
if (k == 0 ? t >= 1.0 : t <= 0.0)
|
||||
continue;
|
||||
const Vec p = curr.position + t * (next.position - curr.position);
|
||||
auto [p_dist, p_near_l, p_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(
|
||||
p.template cast<AABBScalar>());
|
||||
cut[k].position = p;
|
||||
cut[k].distance = float(p_dist + boundary_offset);
|
||||
keep[k] = (p - curr.position).norm() > min_spacing && (next.position - p).norm() > min_spacing;
|
||||
}
|
||||
if (t1 > 0.0) {
|
||||
Vec p1 = curr.position + t1 * (next.position - curr.position);
|
||||
auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(
|
||||
p1.template cast<AABBScalar>());
|
||||
ExtendedPoint<L::Dim> new_p{};
|
||||
new_p.position = p1;
|
||||
new_p.distance = float(p1_dist + boundary_offset);
|
||||
// ORCA: only create a new point in the path if the new point overhang distance will be used to generate a speed change
|
||||
// or if this option is disabled (min_distance<=0)
|
||||
if( (std::abs(p1_dist) > min_distance) || (min_distance<=0)){
|
||||
// ORCA: filter out points that are introduced to the end of the path when their distance from the end point is
|
||||
// not meaningful
|
||||
if ((p1 - curr.position).norm() > min_spacing && (next.position - p1).norm() > min_spacing) {
|
||||
new_points.push_back(new_p);
|
||||
}
|
||||
}
|
||||
if (distance_to_speed) {
|
||||
// Only keep a cut that changes the G-code: one that prints differently from at least one of the
|
||||
// points either side of it, which are the line's ends or the other cut where that is kept. A cut
|
||||
// that prints like both would only split a move into two identical ones.
|
||||
if (keep[0])
|
||||
keep[0] = !same_speed_and_fan(cut[0].distance, curr.distance) ||
|
||||
!same_speed_and_fan(cut[0].distance, keep[1] ? cut[1].distance : next.distance);
|
||||
if (keep[1])
|
||||
keep[1] = !same_speed_and_fan(cut[1].distance, keep[0] ? cut[0].distance : curr.distance) ||
|
||||
!same_speed_and_fan(cut[1].distance, next.distance);
|
||||
// Two cuts closer together than min_spacing would leave a micro segment between them, so only the
|
||||
// first is kept.
|
||||
if (keep[0] && keep[1] && (cut[1].position - cut[0].position).norm() <= min_spacing)
|
||||
keep[1] = false;
|
||||
}
|
||||
for (int k = 0; k < 2; ++k)
|
||||
if (keep[k])
|
||||
new_points.push_back(cut[k]);
|
||||
}
|
||||
}
|
||||
new_points.push_back(next);
|
||||
@@ -423,7 +447,10 @@ public:
|
||||
const ConfigOptionFloatsOrPercents &speeds,
|
||||
float ext_perimeter_speed,
|
||||
float original_speed,
|
||||
bool slowdown_for_curled_edges)
|
||||
bool slowdown_for_curled_edges,
|
||||
// Overlap at or below which the overhang fan switches on; negative when the fan
|
||||
// does not depend on overlap.
|
||||
float fan_overlap_threshold = -1.0f)
|
||||
{
|
||||
size_t speed_sections_count = std::min(overlaps.values.size(), speeds.values.size());
|
||||
std::vector<std::pair<float, float>> speed_sections;
|
||||
@@ -463,7 +490,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// If a meaningful (i.e. needing slowdown) overhang distance was not found, then we shouldn't split the lines
|
||||
// If no overhang distance slows this path down, -1 turns interior sampling off unless the overhang fan can switch.
|
||||
// Lines are only split where an end prints slower or cooled, so here only a fan switch splits them.
|
||||
if (!found)
|
||||
smallest_distance_with_lower_speed=-1.f;
|
||||
|
||||
@@ -487,9 +515,17 @@ public:
|
||||
return round(final_speed);
|
||||
};
|
||||
|
||||
// ORCA: The speed sections are built from ext_perimeter_speed, which can be above the speed this path prints at
|
||||
// (original_speed, e.g. held down by resonance avoidance). Every segment is capped at original_speed below, so
|
||||
// overhang distances whose speeds differ only above it print the same and must not count as a speed change when
|
||||
// the path is split.
|
||||
auto effective_speed = [&calculate_speed, original_speed](float distance) {
|
||||
return std::min(calculate_speed(distance), original_speed);
|
||||
};
|
||||
|
||||
std::vector<ExtendedPoint<3>> extended_points =
|
||||
estimate_points_properties<true, true, true, true>(path.polyline.points, prev_layer_boundaries[current_object], path.width, -1,
|
||||
smallest_distance_with_lower_speed, calculate_speed);
|
||||
smallest_distance_with_lower_speed, effective_speed, fan_overlap_threshold);
|
||||
const auto width_inv = 1.0f / path.width;
|
||||
std::vector<ProcessedPoint> processed_points;
|
||||
processed_points.reserve(extended_points.size());
|
||||
|
||||
+10
-5
@@ -2717,7 +2717,8 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.id = std::to_string(m_model.id().id);
|
||||
ctx.name = get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.cancellation_check = [this]() { return canceled(); };
|
||||
fire_lifecycle_event(LifecycleEvent::SliceStarted, ctx);
|
||||
@@ -3344,7 +3345,8 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.id = std::to_string(m_model.id().id);
|
||||
ctx.name = get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.cancellation_check = [this]() { return canceled(); };
|
||||
fire_lifecycle_event(LifecycleEvent::SliceGeometryFinished, ctx);
|
||||
@@ -4950,7 +4952,8 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
|
||||
{
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.id = std::to_string(m_model.id().id);
|
||||
ctx.name = get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = file;
|
||||
ctx.cancellation_check = [this]() { return canceled(); };
|
||||
@@ -4980,7 +4983,8 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found errors when process gcode file %1%") %file.c_str();
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.id = std::to_string(m_model.id().id);
|
||||
ctx.name = get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = file + "\n" + ex.what();
|
||||
ctx.cancellation_check = [this]() { return canceled(); };
|
||||
@@ -4994,7 +4998,8 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.id = std::to_string(m_model.id().id);
|
||||
ctx.name = get_model_name();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = file;
|
||||
ctx.cancellation_check = [this]() { return canceled(); };
|
||||
|
||||
@@ -34,6 +34,10 @@ static const char* Segments_Vertex_Shader =
|
||||
// ORCA: 0 during the shadow caster pass - the bias below shifts eye_position but not
|
||||
// world_position, so the caster would write a depth the receiver never looks up.
|
||||
"uniform float bias_scale;\n"
|
||||
// draw the instances last to first, top layers before the ones they hide, so that early depth
|
||||
// rejection discards most of the hidden fragments; set when the camera looks down on the print
|
||||
"uniform int reverse_order;\n"
|
||||
"uniform int instance_count;\n"
|
||||
"in int vertex_id;\n"
|
||||
"out vec3 color;\n"
|
||||
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
|
||||
@@ -59,7 +63,8 @@ static const char* Segments_Vertex_Shader =
|
||||
" return top_diffuse + front_diffuse + top_specular;\n"
|
||||
"}\n"
|
||||
"void main() {\n"
|
||||
" int id_a = int(texelFetch(segment_index_tex, gl_InstanceID).r);\n"
|
||||
" int instance = (reverse_order != 0) ? instance_count - 1 - gl_InstanceID : gl_InstanceID;\n"
|
||||
" int id_a = int(texelFetch(segment_index_tex, instance).r);\n"
|
||||
" int id_b = id_a + 1;\n"
|
||||
" vec3 pos_a = texelFetch(position_tex, id_a).xyz;\n"
|
||||
" vec3 pos_b = texelFetch(position_tex, id_b).xyz;\n"
|
||||
|
||||
@@ -763,6 +763,8 @@ void ViewerImpl::init(const std::string& opengl_context_version)
|
||||
m_uni_segments_height_width_angle_tex_id = glGetUniformLocation(m_segments_shader_id, "height_width_angle_tex");
|
||||
m_uni_segments_colors_tex_id = glGetUniformLocation(m_segments_shader_id, "color_tex");
|
||||
m_uni_segments_segment_index_tex_id = glGetUniformLocation(m_segments_shader_id, "segment_index_tex");
|
||||
m_uni_segments_reverse_order_id = glGetUniformLocation(m_segments_shader_id, "reverse_order");
|
||||
m_uni_segments_instance_count_id = glGetUniformLocation(m_segments_shader_id, "instance_count");
|
||||
// ORCA: realistic view
|
||||
m_uni_segments_shadow_map_id = glGetUniformLocation(m_segments_shader_id, "shadow_map");
|
||||
m_uni_segments_shadow_light_vp_id = glGetUniformLocation(m_segments_shader_id, "shadow_light_vp");
|
||||
@@ -2090,6 +2092,15 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
||||
glsafe(glUniformMatrix4fv(m_uni_segments_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
|
||||
glsafe(glUniformMatrix4fv(m_uni_segments_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
|
||||
glsafe(glUniform3fv(m_uni_segments_camera_position_id, 1, camera_position.data()));
|
||||
// The segments come in print order, bottom layer first. Seen from above, that is back to front,
|
||||
// and every hidden fragment is shaded before the one that covers it. Drawing them last to first
|
||||
// lets the depth test reject the hidden ones instead. The camera looks down when the world's
|
||||
// up axis points towards it, which is the view matrix's (2, 2) entry being positive.
|
||||
const bool top_down = !m_rendering_shadow_casters && view_matrix[10] > 0.0f;
|
||||
glsafe(glUniform1i(m_uni_segments_reverse_order_id, top_down ? 1 : 0));
|
||||
#ifndef ENABLE_OPENGL_ES
|
||||
glsafe(glUniform1i(m_uni_segments_instance_count_id, static_cast<int>(m_enabled_segments_count)));
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
||||
// with the lookup off.
|
||||
glsafe(glUniform1i(m_uni_segments_shadow_map_id, m_shadow_map_texture_unit));
|
||||
|
||||
@@ -362,6 +362,8 @@ private:
|
||||
int m_uni_segments_height_width_angle_tex_id{ -1 };
|
||||
int m_uni_segments_colors_tex_id{ -1 };
|
||||
int m_uni_segments_segment_index_tex_id{ -1 };
|
||||
int m_uni_segments_reverse_order_id{ -1 };
|
||||
int m_uni_segments_instance_count_id{ -1 };
|
||||
int m_uni_segments_shadow_map_id{ -1 };
|
||||
int m_uni_segments_shadow_light_vp_id{ -1 };
|
||||
int m_uni_segments_shadow_intensity_id{ -1 };
|
||||
|
||||
@@ -4862,13 +4862,13 @@ void DesignPanel::on_import_mesh()
|
||||
try {
|
||||
shape = GeometryEngine::mesh_to_brep(mesh.its, MESH_IMPORT_TOLERANCE,
|
||||
MESH_IMPORT_MERGE_ANGLE_DEG, stats);
|
||||
} catch (const std::exception& e) {
|
||||
fail(_L("Mesh conversion failed: ") + wxString::FromUTF8(e.what()));
|
||||
return;
|
||||
} catch (const Standard_Failure& e) { // OCCT throws outside std::exception
|
||||
} catch (const Standard_Failure& e) { // on OCCT >= 8 Standard_Failure derives from std::exception — must precede that handler
|
||||
fail(_L("Mesh conversion failed: ") + wxString::FromUTF8(
|
||||
e.GetMessageString() ? e.GetMessageString() : "OCCT error"));
|
||||
return;
|
||||
} catch (const std::exception& e) {
|
||||
fail(_L("Mesh conversion failed: ") + wxString::FromUTF8(e.what()));
|
||||
return;
|
||||
}
|
||||
if (shape.IsNull()) { fail(_L("Mesh conversion produced no geometry")); return; }
|
||||
|
||||
|
||||
@@ -12807,8 +12807,6 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
notification_manager->set_slicing_progress_export_possible();
|
||||
|
||||
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
|
||||
const std::string lifecycle_job_name = this->background_process.fff_print() ?
|
||||
this->background_process.fff_print()->output_filename() : std::string();
|
||||
this->background_process.reset_export();
|
||||
// This bool stops showing export finished notification even when process_completed_with_error is false
|
||||
bool has_error = false;
|
||||
@@ -12855,8 +12853,18 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = lifecycle_job_name;
|
||||
ctx.code = evt.cancelled() ? Slic3r::LifecycleEvtCode::Warn : (has_error ? Slic3r::LifecycleEvtCode::Error : Slic3r::LifecycleEvtCode::Ok);
|
||||
if (const PrintBase* print = this->background_process.current_print()) {
|
||||
const Model& model = print->model();
|
||||
ctx.id = std::to_string(model.id().id);
|
||||
if (model.model_info)
|
||||
ctx.name = model.model_info->model_name;
|
||||
} else {
|
||||
// Realistically Printbase* print will never be null because select_technology already asserts an active print
|
||||
// and the worker thread asserts it before processing.
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": slicing completed without an active print; lifecycle event has no model ID";
|
||||
}
|
||||
ctx.code = evt.cancelled() ? Slic3r::LifecycleEvtCode::Warn :
|
||||
(has_error ? Slic3r::LifecycleEvtCode::Error : Slic3r::LifecycleEvtCode::Ok);
|
||||
ctx.msg = evt.cancelled() ? "cancelled" : (has_error ? lifecycle_error_msg : std::string());
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::SlicingJobComplete, ctx);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/app.h>
|
||||
@@ -115,10 +116,67 @@ std::string PrintHost::get_print_host_webui(DynamicPrintConfig* config)
|
||||
return webui_url;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Moonraker (Klipper's API server) reports a raised exception as { "error": { "code", "message", "traceback" } }
|
||||
// under every host type that connects to it, often with the cause only in the traceback. Returns the reason to show,
|
||||
// or empty for any other body.
|
||||
std::string moonraker_error_reason(const std::string &body)
|
||||
{
|
||||
const auto root = nlohmann::json::parse(body, nullptr, false);
|
||||
const auto err = root.find("error");
|
||||
if (err == root.end())
|
||||
return {};
|
||||
const auto message = err->find("message");
|
||||
const auto traceback = err->find("traceback");
|
||||
if (message == err->end() || traceback == err->end() || !message->is_string() || !traceback->is_string())
|
||||
return {};
|
||||
|
||||
const auto &msg = message->get_ref<const std::string &>();
|
||||
const auto &tb = traceback->get_ref<const std::string &>();
|
||||
if (msg.empty())
|
||||
return {};
|
||||
const auto end = tb.find_last_not_of(" \t\r\n");
|
||||
if (end == std::string::npos)
|
||||
return msg;
|
||||
|
||||
// Chained exceptions each start a new traceback; the one that failed the request is the last.
|
||||
const auto header = tb.rfind("Traceback (most recent call last):", end);
|
||||
|
||||
// Tornado renders a raised HTTPError as "HTTP <code>: <reason>[ (<detail>)]", and the detail may span lines.
|
||||
const auto code = err->find("code");
|
||||
if (code != err->end() && code->is_number_integer()) {
|
||||
const std::string marker = "HTTP " + std::to_string(code->get<int>()) + ": ";
|
||||
const auto pos = tb.rfind(marker, end);
|
||||
if (pos != std::string::npos && (header == std::string::npos || pos > header) && pos + marker.size() <= end) {
|
||||
const std::string reason = tb.substr(pos + marker.size(), end + 1 - pos - marker.size());
|
||||
// An HTTPError whose detail equals its reason, like HTTPError(401, "Unauthorized"), renders the phrase twice.
|
||||
return reason == msg + " (" + msg + ")" ? msg : reason;
|
||||
}
|
||||
}
|
||||
|
||||
// Any other exception's type and message are everything from the first unindented line after its frames.
|
||||
auto begin = (header == std::string::npos) ? std::string::npos : tb.find('\n', header);
|
||||
while (begin != std::string::npos && begin < end) {
|
||||
++begin;
|
||||
if (tb[begin] != ' ' && tb[begin] != '\r' && tb[begin] != '\n')
|
||||
break;
|
||||
begin = tb.find('\n', begin);
|
||||
}
|
||||
if (begin == std::string::npos || begin > end) {
|
||||
const auto nl = tb.rfind('\n', end);
|
||||
begin = (nl == std::string::npos) ? 0 : nl + 1;
|
||||
}
|
||||
return msg + " (" + tb.substr(begin, end + 1 - begin) + ")";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
wxString PrintHost::format_error(const std::string &body, const std::string &error, unsigned status) const
|
||||
{
|
||||
if (status != 0) {
|
||||
auto wxbody = wxString::FromUTF8(body.data());
|
||||
const std::string reason = moonraker_error_reason(body);
|
||||
auto wxbody = wxString::FromUTF8(reason.empty() ? body : reason);
|
||||
return wxString::Format("HTTP %u: %s", status, wxbody);
|
||||
} else {
|
||||
if (error.find("curl:Timeout was reached") != std::string::npos) {
|
||||
|
||||
Reference in New Issue
Block a user