mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-04 14:20:58 +00:00
* Add Missing Includes Across src/libslic3r Every libslic3r source and header now directly includes the headers declaring what it uses, rather than relying on the precompiled header or transitive includes. Generated with clang-tidy misc-include-cleaner, with libslic3r headers spelled libslic3r/... so they resolve outside the library's private include paths. MultiMaterialSegmentation.hpp, Support/SupportParameters.hpp and Format/STEP.hpp are made self-contained by hand. * Make the libslic3r Headers Compile on Their Own Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Left out: I18N.hpp, which errors on purpose when included from GUI code, and VoxelizeCSGMesh.hpp and SLA/bicubic.h, which nothing includes and which no longer compile at all. * Add the Includes Missing From the Hand-Fixed libslic3r Headers clang-tidy would not edit these headers while they failed to compile on their own, so the first pass skipped them. With the headers now self-contained, a second pass adds the rest. * Keep Windows Setup Ahead of the Added libslic3r Includes Print.cpp and Thread.cpp open with a _WIN32 block that has to come first; without the precompiled header, Print.cpp otherwise reaches windows.h through OCCT with NONLS defined and boost/regex fails. OpenVDBUtils.cpp and SLA/SupportTreeBuilder.cpp had includes inside #ifndef NOMINMAX, which libslic3r defines on Windows, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory. * Re-Add libslic3r Includes After the Clipper2 2.0.1 Migration Rebasing onto main took main's version of the files the Clipper2 migration rewrote, so their added includes are restored here, along with includes for main's new code. Clipper2's individual headers are now ignored by clang-tidy: they only build the Z variant through clipper2_z.hpp, which defines USINGZ first, so including clipper.core.h and the like directly broke ClipperZUtils.cpp.
204 lines
9.1 KiB
C++
204 lines
9.1 KiB
C++
#include "../ClipperUtils.hpp"
|
|
#include "../ExPolygon.hpp"
|
|
#include "../Surface.hpp"
|
|
#include "../VariableWidth.hpp"
|
|
#include "Arachne/WallToolPaths.hpp"
|
|
|
|
#include "FillConcentric.hpp"
|
|
#include "libslic3r/Fill/FillBase.hpp"
|
|
#include "libslic3r/BoundingBox.hpp"
|
|
#include "libslic3r/Arachne/utils/ExtrusionLine.hpp"
|
|
#include "FillCornerSmoothing.hpp"
|
|
#include "libslic3r/Point.hpp"
|
|
#include "libslic3r/Polyline.hpp"
|
|
#include "libslic3r/libslic3r.h"
|
|
#include "libslic3r/Polygon.hpp"
|
|
#include <algorithm>
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
#include <cstddef>
|
|
#include <cassert>
|
|
#include <libslic3r/ShortestPath.hpp>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace Slic3r {
|
|
|
|
void FillConcentric::_fill_surface_single(
|
|
const FillParams ¶ms,
|
|
unsigned int thickness_layers,
|
|
const std::pair<float, Point> &direction,
|
|
ExPolygon expolygon,
|
|
Polylines &polylines_out)
|
|
{
|
|
// no rotation is supported for this infill pattern
|
|
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
|
|
|
coord_t min_spacing = scale_(this->spacing) * params.multiline;
|
|
coord_t distance = coord_t(min_spacing / params.density);
|
|
// A non-positive step never shrinks the region, so the inset loop below would not end.
|
|
if (min_spacing <= 0 || distance <= 0)
|
|
return;
|
|
|
|
if (params.density > 0.9999f && !params.dont_adjust) {
|
|
distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance);
|
|
this->spacing = unscale<double>(distance);
|
|
}
|
|
|
|
// Contract surface polygon by half line width to avoid excesive overlap with perimeter
|
|
ExPolygons contracted = offset_ex(expolygon, -float(scale_(0.5 * (params.multiline - 1) * this->spacing )));
|
|
|
|
Polygons loops = to_polygons(contracted);
|
|
|
|
ExPolygons last { contracted };
|
|
while (! last.empty()) {
|
|
last = offset2_ex(last, -(distance + min_spacing/2), +min_spacing/2);
|
|
append(loops, to_polygons(last));
|
|
}
|
|
|
|
// Orca: round the corners of the loops. Unlike the other patterns these are never clipped to the
|
|
// fill region - they are its offsets - so a corner may only be rounded where the curve replacing it
|
|
// stays inside. Rounding cuts toward the inside of the turn, which around a hole, at a concave
|
|
// feature or across a thin region is outside the fill and would put the extrusion over a wall.
|
|
// The reach is capped at half the distance between two loops as well: a loop is as long as the
|
|
// object, and a corner cut by half of its side would swallow the neighbouring loops.
|
|
auto corner_stays_inside = [&contracted](const Vec2d &from, const Vec2d &to) {
|
|
// The straight chord between the ends of the curve is the deepest the curve can cut.
|
|
for (const double t : { 0.25, 0.5, 0.75 }) {
|
|
const Vec2d sample = from + t * (to - from);
|
|
const Point point(coord_t(sample.x()), coord_t(sample.y()));
|
|
if (std::none_of(contracted.begin(), contracted.end(),
|
|
[&point](const ExPolygon ®ion) { return region.contains(point); }))
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
smooth_polygons_corners(loops, params.smooth_factor, scaled<double>(params.resolution), 0.5 * distance,
|
|
corner_stays_inside);
|
|
|
|
// generate paths from the outermost to the innermost, to avoid
|
|
// adhesion problems of the first central tiny loops
|
|
loops = union_pt_chained_outside_in(loops);
|
|
|
|
// Orca: an outward fill order prints the innermost loops first instead.
|
|
if (params.fill_order == SurfaceFillOrder::Outward)
|
|
std::reverse(loops.begin(), loops.end());
|
|
|
|
// split paths using a nearest neighbor search
|
|
size_t iPathFirst = polylines_out.size();
|
|
Point last_pos(0, 0);
|
|
for (const Polygon &loop : loops) {
|
|
polylines_out.emplace_back(loop.split_at_index(last_pos.nearest_point_index(loop.points)));
|
|
last_pos = polylines_out.back().last_point();
|
|
}
|
|
|
|
// Apply multiline offset if needed
|
|
multiline_fill(polylines_out, params, spacing);
|
|
|
|
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
|
|
// Keep valid paths only.
|
|
size_t j = iPathFirst;
|
|
for (size_t i = iPathFirst; i < polylines_out.size(); ++ i) {
|
|
polylines_out[i].clip_end(this->loop_clipping);
|
|
if (polylines_out[i].is_valid()) {
|
|
if (j < i)
|
|
polylines_out[j] = std::move(polylines_out[i]);
|
|
++ j;
|
|
}
|
|
}
|
|
if (j < polylines_out.size())
|
|
polylines_out.erase(polylines_out.begin() + j, polylines_out.end());
|
|
//TODO: return ExtrusionLoop objects to get better chained paths,
|
|
// otherwise the outermost loop starts at the closest point to (0, 0).
|
|
// We want the loops to be split inside the G-code generator to get optimum path planning.
|
|
}
|
|
|
|
void FillConcentric::_fill_surface_single(const FillParams& params,
|
|
unsigned int thickness_layers,
|
|
const std::pair<float, Point>& direction,
|
|
ExPolygon expolygon,
|
|
ThickPolylines& thick_polylines_out)
|
|
{
|
|
assert(params.use_arachne);
|
|
assert(this->print_config != nullptr && this->print_object_config != nullptr);
|
|
|
|
// no rotation is supported for this infill pattern
|
|
Point bbox_size = expolygon.contour.bounding_box().size();
|
|
coord_t min_spacing = scaled<coord_t>(this->spacing);
|
|
if (min_spacing <= 0)
|
|
return;
|
|
|
|
if (params.density > 0.9999f && !params.dont_adjust) {
|
|
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
|
|
Polygons polygons = offset(expolygon, float(min_spacing) / 2.f);
|
|
|
|
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
|
|
Arachne::WallToolPathsParams input_params;
|
|
input_params.min_bead_width = 0.85 * min_nozzle_diameter;
|
|
input_params.min_feature_size = 0.25 * min_nozzle_diameter;
|
|
input_params.wall_transition_length = 1.0 * min_nozzle_diameter;
|
|
input_params.wall_transition_angle = 10;
|
|
input_params.wall_transition_filter_deviation = 0.25 * min_nozzle_diameter;
|
|
input_params.wall_distribution_count = 1;
|
|
|
|
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, params.layer_height, input_params);
|
|
|
|
std::vector<Arachne::VariableWidthLines> loops = wallToolPaths.getToolPaths();
|
|
std::vector<const Arachne::ExtrusionLine*> all_extrusions;
|
|
for (Arachne::VariableWidthLines& loop : loops) {
|
|
if (loop.empty())
|
|
continue;
|
|
for (const Arachne::ExtrusionLine& wall : loop)
|
|
all_extrusions.emplace_back(&wall);
|
|
}
|
|
|
|
// Orca: a forced fill order prints the loops in strictly monotonic depth order so
|
|
// that surfaces broken up by holes or slots cannot hop outward and back inward.
|
|
const bool forced_fill_order = params.fill_order != SurfaceFillOrder::Default;
|
|
if (forced_fill_order) {
|
|
const bool outward = params.fill_order == SurfaceFillOrder::Outward;
|
|
std::stable_sort(all_extrusions.begin(), all_extrusions.end(),
|
|
[outward](const Arachne::ExtrusionLine *a, const Arachne::ExtrusionLine *b) {
|
|
return outward ? a->inset_idx > b->inset_idx : a->inset_idx < b->inset_idx;
|
|
});
|
|
}
|
|
|
|
// Split paths using a nearest neighbor search.
|
|
size_t firts_poly_idx = thick_polylines_out.size();
|
|
Point last_pos(0, 0);
|
|
for (const Arachne::ExtrusionLine* extrusion : all_extrusions) {
|
|
if (extrusion->empty())
|
|
continue;
|
|
|
|
ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion);
|
|
if (extrusion->is_closed)
|
|
thick_polyline.start_at_index(last_pos.nearest_point_index(thick_polyline.points));
|
|
thick_polylines_out.emplace_back(std::move(thick_polyline));
|
|
last_pos = thick_polylines_out.back().last_point();
|
|
}
|
|
|
|
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
|
|
// Keep valid paths only.
|
|
size_t j = firts_poly_idx;
|
|
for (size_t i = firts_poly_idx; i < thick_polylines_out.size(); ++i) {
|
|
thick_polylines_out[i].clip_end(this->loop_clipping);
|
|
if (thick_polylines_out[i].is_valid()) {
|
|
if (j < i)
|
|
thick_polylines_out[j] = std::move(thick_polylines_out[i]);
|
|
++j;
|
|
}
|
|
}
|
|
if (j < thick_polylines_out.size())
|
|
thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end());
|
|
|
|
if (!forced_fill_order)
|
|
reorder_by_shortest_traverse(thick_polylines_out);
|
|
}
|
|
else {
|
|
Polylines polylines;
|
|
this->_fill_surface_single(params, thickness_layers, direction, expolygon, polylines);
|
|
append(thick_polylines_out, to_thick_polylines(std::move(polylines), min_spacing));
|
|
}
|
|
}
|
|
|
|
} // namespace Slic3r
|