Merge main

This commit is contained in:
Lam Wei Lun
2026-08-24 14:19:49 +08:00
19 changed files with 397 additions and 240 deletions
+2 -2
View File
@@ -154,8 +154,8 @@ void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const
//h^2 = L^2 / b^2 [factor the divisor]
const int64_t height_2 = double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2);
// Orca: The value of `height_2` is squared, so we need to compare it with the squared value
if ((height_2 <= Slic3r::sqr(scaled<coord_t>(0.005)) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current, previous, next) <= scaled<double>(0.005))) // make sure that height_2 is not small because of cancellation of positive and negative areas
if ((height_2 <= Slic3r::sqr(colinear_vertex_tolerance()) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current, previous, next) <= double(colinear_vertex_tolerance()))) // make sure that height_2 is not small because of cancellation of positive and negative areas
continue;
if (length2 < smallest_line_segment_squared
@@ -133,8 +133,8 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
const auto height_2 = int64_t(double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2));
const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next);
// Orca: The value of `height_2` is squared, so we need to compare it with the squared value
if ((height_2 <= Slic3r::sqr(scaled<coord_t>(0.005)) // Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled<double>(0.005)) // Make sure that height_2 is not small because of cancellation of positive and negative areas
if ((height_2 <= Slic3r::sqr(colinear_vertex_tolerance()) // Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current.p, previous.p, next.p) <= double(colinear_vertex_tolerance())) // Make sure that height_2 is not small because of cancellation of positive and negative areas
// We shouldn't remove middle junctions of colinear segments if the area changed for the C-P segment is exceeding the maximum allowed
&& extrusion_area_error <= maximum_extrusion_area_deviation)
{
@@ -32,6 +32,14 @@ class Flow;
namespace Slic3r::Arachne
{
// ORCA: Tolerance of the "almost exactly colinear" early-out shared by the two simplify() passes
// (this file and WallToolPaths.cpp). That test drops a vertex regardless of the user's Maximum wall
// resolution/deviation, so it has to stay at the scale of coordinate rounding noise. A larger value
// silently decimates finely tessellated curves: on a circle, one vertex may be removed whenever the
// sagitta of the resulting chord falls below the tolerance, which halves the point count and turns
// smooth arcs into corners the firmware has to decelerate through.
inline coord_t colinear_vertex_tolerance() { return coord_t(SCALED_EPSILON); }
/*!
* Represents a polyline (not just a line) that is to be extruded with variable
* line width.
+14 -5
View File
@@ -682,7 +682,7 @@ Polygon apply_fuzzy_skin(const Polygon& polygon, const PerimeterGenerator& perim
return fuzzified;
}
void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerator& perimeter_generator, const bool is_contour)
void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerator& perimeter_generator, const bool is_contour, const bool closed)
{
const auto slice_z = perimeter_generator.slice_z;
const auto& regions = perimeter_generator.regions_by_fuzzify;
@@ -690,7 +690,7 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
const auto& config = regions.begin()->first;
const bool fuzzify = should_fuzzify(config, perimeter_generator.layer_id, extrusion->inset_idx, is_contour);
if (fuzzify)
fuzzy_extrusion_line(extrusion->junctions, slice_z, config);
fuzzy_extrusion_line(extrusion->junctions, slice_z, config, closed);
} else {
// Merge regions that produce identical fuzzy effects (differ only in type).
// When the style (e.g. External) and a painted region (All) both fuzzify this loop
@@ -701,10 +701,19 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
// Fast path: single merged region — apply directly without splitting
if (merged_regions.size() == 1 && merged_regions.front().expolygons.empty()) {
fuzzy_extrusion_line(extrusion->junctions, slice_z, *merged_regions.front().config);
fuzzy_extrusion_line(extrusion->junctions, slice_z, *merged_regions.front().config, closed);
return;
}
// Open path means this is a thin wall that collapsed into a single thick line, in this case the path will go exactly
// between the middle two sides of the object. And since the paint segmentation never goes beyond the middle line because
// it uses voronoi diagram, we need to expand the segmentation a little bit to make sure it covers the path.
if (!closed) {
for (auto& r : merged_regions) {
r.expolygons = offset_ex(r.expolygons, perimeter_generator.ext_perimeter_flow.scaled_width() / 10);
}
}
#ifdef DEBUG_FUZZY
{
int i = 0;
@@ -752,7 +761,7 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
// Fuzzy splitted extrusion
if (std::all_of(splitted.begin(), splitted.end(), [](const Algorithm::SplitLineJunction& j) { return j.clipped; })) {
// The entire polygon is fuzzified
fuzzy_extrusion_line(extrusion->junctions, slice_z, *r.config);
fuzzy_extrusion_line(extrusion->junctions, slice_z, *r.config, closed);
continue;
} else {
const auto current_ext = extrusion->junctions;
@@ -803,7 +812,7 @@ void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerato
}
//Orca: ensure the loop is closed after fuzzy
if (!extrusion->junctions.empty() && extrusion->junctions.front().p != extrusion->junctions.back().p) {
if (closed && !extrusion->junctions.empty() && extrusion->junctions.front().p != extrusion->junctions.back().p) {
extrusion->junctions.back().p = extrusion->junctions.front().p;
extrusion->junctions.back().w = extrusion->junctions.front().w;
}
@@ -16,7 +16,7 @@ void group_region_by_fuzzify(PerimeterGenerator& g);
bool should_fuzzify(const FuzzySkinConfig& config, int layer_id, size_t loop_idx, bool is_contour);
Polygon apply_fuzzy_skin(const Polygon& polygon, const PerimeterGenerator& perimeter_generator, size_t loop_idx, bool is_contour);
void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerator& perimeter_generator, bool is_contour);
void apply_fuzzy_skin(Arachne::ExtrusionLine* extrusion, const PerimeterGenerator& perimeter_generator, bool is_contour, bool closed = true);
} // namespace Slic3r::Feature::FuzzySkin
+9 -5
View File
@@ -351,19 +351,23 @@ void Node::convertToPolylines(Polylines &output, const coord_t line_overlap) con
{
Polylines result;
result.emplace_back();
convertToPolylines(0, result);
// Orca: the layers are filled in parallel, so they would consume a shared generator in a
// different order every run, and a model would not slice the same way twice. Each tree seeds
// its own from where it is rooted; one constant seed would start them all on the same pick.
std::mt19937_64 rng { uint64_t(PointHash{}(m_p)) };
convertToPolylines(0, result, rng);
removeJunctionOverlap(result, line_overlap);
append(output, std::move(result));
}
void Node::convertToPolylines(size_t long_line_idx, Polylines &output) const
void Node::convertToPolylines(size_t long_line_idx, Polylines &output, std::mt19937_64 &rng) const
{
if (m_children.empty()) {
output[long_line_idx].points.push_back(m_p);
return;
}
size_t first_child_idx = rand() % m_children.size();
m_children[first_child_idx]->convertToPolylines(long_line_idx, output);
const size_t first_child_idx = rng() % m_children.size();
m_children[first_child_idx]->convertToPolylines(long_line_idx, output, rng);
output[long_line_idx].points.push_back(m_p);
for (size_t idx_offset = 1; idx_offset < m_children.size(); idx_offset++) {
@@ -371,7 +375,7 @@ void Node::convertToPolylines(size_t long_line_idx, Polylines &output) const
const Node& child = *m_children[child_idx];
output.emplace_back();
size_t child_line_idx = output.size() - 1;
child.convertToPolylines(child_line_idx, output);
child.convertToPolylines(child_line_idx, output, rng);
output[child_line_idx].points.emplace_back(m_p);
}
}
+3 -1
View File
@@ -7,6 +7,7 @@
#include <functional>
#include <memory>
#include <optional>
#include <random>
#include <vector>
#include "../../EdgeGrid.hpp"
@@ -259,8 +260,9 @@ protected:
*
* \param long_line a reference to a polyline in \p output which to continue building on in the recursion
* \param output all branches in this tree connected into polylines
* \param rng the generator the junctions draw from, carried through the recursion
*/
void convertToPolylines(size_t long_line_idx, Polylines &output) const;
void convertToPolylines(size_t long_line_idx, Polylines &output, std::mt19937_64 &rng) const;
void removeJunctionOverlap(Polylines &polylines, coord_t line_overlap) const;
+1 -1
View File
@@ -32,7 +32,7 @@ using ThumbnailsList = std::vector<ThumbnailData>;
struct ThumbnailsParams
{
const Vec2ds sizes;
const Vec2ds sizes{};
bool printable_only;
bool parts_only;
bool show_bed;
+16
View File
@@ -229,6 +229,22 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
// Append thin walls to the nearest-neighbor search (only for first iteration)
if (! thin_walls.empty()) {
// Orca: apply fuzzy skin to thin walls as well
for (auto& thin_wall : thin_walls) {
// First, we convert the ThickPolyline into Arachne::ExtrusionLine so we could reuse our existing fuzzy code
Arachne::ExtrusionLine el(0, true);
el.junctions.reserve(thin_wall.points.size());
for (int i = 0; i < thin_wall.points.size(); i++) {
el.junctions.emplace_back(thin_wall.points[i], thin_wall.width[i], 0);
}
// Then we fuzzy it
apply_fuzzy_skin(&el, perimeter_generator, true, thin_wall.is_closed());
// Then convert the result back to ThickPolyline
thin_wall = Arachne::to_thick_polyline(el);
}
variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow, coll.entities);
thin_walls.clear();
}
+7
View File
@@ -1499,6 +1499,13 @@ static std::vector<Polygons> make_loops(
Polygons &polygons = layers[line_idx];
polygons = make_loops(lines[line_idx]);
// Orca: A planar quad represented by two triangles contributes a point where the
// slicing plane crosses the shared diagonal. After rounding to coord_t this
// point may be very slightly off the otherwise straight contour edge. Apart
// from being redundant, such points make the subsequent contour
// simplification depend on the slice height (and may move seam candidates).
remove_collinear(polygons);
auto this_mode = line_idx < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode;
if (! polygons.empty()) {
if (this_mode == MeshSlicingParams::SlicingMode::Positive) {
+22 -1
View File
@@ -420,7 +420,7 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
if (properties_shown) {
float label_w = 0.0f;
float value_w = 0.0f;
properties_rows.reserve(13);
properties_rows.reserve(14);
auto add_row = [&properties_rows, &label_w, &value_w](std::string label, std::string value) {
label_w = std::max(label_w, ImGui::CalcTextSize(label.c_str()).x);
value_w = std::max(value_w, ImGui::CalcTextSize(value.c_str()).x);
@@ -433,6 +433,27 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
add_row(_u8L("Width"), buff);
if (is_extrusion) sprintf(buff, ("%.3f " + _u8L("mm")).c_str(), vertex.height); else strcpy(buff, NA_CSTR);
add_row(_u8L("Height"), buff);
// ORCA: Length of the move ending at the current vertex. Arc moves (G2/G3) are discretized
// into several vertices sharing the same gcode line id, so accumulate the whole run to report
// the arc length instead of the length of a single chord.
if (vertex_id > 0 && (is_extrusion || vertex.is_travel() || vertex.is_wipe())) {
const size_t vertices_count = viewer->get_vertices_count();
size_t first_id = vertex_id;
while (first_id > 0 && viewer->get_vertex_at(first_id - 1).gcode_id == vertex.gcode_id)
--first_id;
size_t last_id = vertex_id;
while (last_id + 1 < vertices_count && viewer->get_vertex_at(last_id + 1).gcode_id == vertex.gcode_id)
++last_id;
float length = 0.0f;
for (size_t i = std::max<size_t>(first_id, 1); i <= last_id; ++i) {
length += (libvgcode::convert(viewer->get_vertex_at(i).position) -
libvgcode::convert(viewer->get_vertex_at(i - 1).position)).norm();
}
sprintf(buff, ("%.3f " + _u8L("mm")).c_str(), length);
}
else
strcpy(buff, NA_CSTR);
add_row(_u8L("Length"), buff);
sprintf(buff, "%d", vertex.layer_id + 1);
add_row(_u8L("Layer"), buff);
sprintf(buff, ("%.1f " + _u8L("mm/s")).c_str(), vertex.feedrate);