Merge branch 'main' into feat/configurable-bambu-network-lib

This commit is contained in:
SoftFever
2025-12-27 23:40:35 +08:00
committed by GitHub
216 changed files with 3875 additions and 529 deletions

View File

@@ -35,6 +35,11 @@ Clipper2Lib::Paths64 Slic3rPoints_to_Paths64(const Container& in)
return out;
}
Clipper2Lib::Paths64 Slic3rPolylines_to_Paths64(const Polylines& in)
{
return Slic3rPoints_to_Paths64(in);
}
Points Path64ToPoints(const Clipper2Lib::Path64& path64)
{
Points points;

View File

@@ -4,9 +4,12 @@
#include "ExPolygon.hpp"
#include "Polygon.hpp"
#include "Polyline.hpp"
#include "clipper2/clipper.h"
namespace Slic3r {
Clipper2Lib::Paths64 Slic3rPolylines_to_Paths64(const Slic3r::Polylines& in);
Slic3r::Polylines Paths64_to_polylines(const Clipper2Lib::Paths64& in);
Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
ExPolygons union_ex_2(const Polygons &expolygons);

View File

@@ -200,6 +200,10 @@ void Fill3DHoneycomb::_fill_surface_single(
if (std::abs(infill_angle) >= EPSILON) expolygon.rotate(-infill_angle);
BoundingBox bb = expolygon.contour.bounding_box();
// Expand the bounding box to avoid artifacts at the edges
coord_t expand = 5 * (scale_(this->spacing));
bb.offset(expand);
// Note: with equally-scaled X/Y/Z, the pattern will create a vertically-stretched
// truncated octahedron; so Z is pre-adjusted first by scaling by sqrt(2)
coordf_t zScale = sqrt(2);

View File

@@ -1371,42 +1371,47 @@ void Filler::_fill_surface_single(
all_polylines.reserve(lines.size());
std::transform(lines.begin(), lines.end(), std::back_inserter(all_polylines), [](const Line& l) { return Polyline{ l.a, l.b }; });
// Apply multiline offset if needed
multiline_fill(all_polylines, params, spacing);
// Apply multiline offset if needed
multiline_fill(all_polylines, params, spacing);
// Crop all polylines
all_polylines = intersection_pl(std::move(all_polylines), expolygon);
#endif
}
// After intersection_pl some polylines with only one line are split into more lines
for (Polyline &polyline : all_polylines) {
//FIXME assert that all the points are collinear and in between the start and end point.
if (polyline.points.size() > 2)
polyline.points.erase(polyline.points.begin() + 1, polyline.points.end() - 1);
}
// assert(has_no_collinear_lines(all_polylines));
if (params.multiline == 1) {
// After intersection_pl some polylines with only one line are split into more lines
for (Polyline& polyline : all_polylines) {
// FIXME assert that all the points are collinear and in between the start and end point.
if (polyline.points.size() > 2)
polyline.points.erase(polyline.points.begin() + 1, polyline.points.end() - 1);
}
// assert(has_no_collinear_lines(all_polylines));
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
{
static int iRun = 0;
export_infill_lines_to_svg(expolygon, all_polylines, debug_out_path("FillAdaptive-initial-%d.svg", iRun++));
}
{
static int iRun = 0;
export_infill_lines_to_svg(expolygon, all_polylines, debug_out_path("FillAdaptive-initial-%d.svg", iRun++));
}
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
const auto hook_length = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length)));
const auto hook_length_max = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length_max)));
const auto hook_length = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length)));
const auto hook_length_max = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length_max)));
Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length, hook_length_max) : std::move(all_polylines);
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
{
static int iRun = 0;
export_infill_lines_to_svg(expolygon, all_polylines_with_hooks, debug_out_path("FillAdaptive-hooks-%d.svg", iRun++));
}
{
static int iRun = 0;
export_infill_lines_to_svg(expolygon, all_polylines_with_hooks, debug_out_path("FillAdaptive-hooks-%d.svg", iRun++));
}
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
chain_or_connect_infill(std::move(all_polylines_with_hooks), expolygon, polylines_out, this->spacing, params);
chain_or_connect_infill(std::move(all_polylines_with_hooks), expolygon, polylines_out, this->spacing, params);
} else {
// if multiline is > 1 infill is ready to connect
chain_or_connect_infill(std::move(all_polylines), expolygon, polylines_out, this->spacing, params);
}
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
{
@@ -1443,6 +1448,17 @@ static std::vector<CubeProperties> make_cubes_properties(double max_cube_edge_le
if (edge_length > max_cube_edge_length)
break;
}
// Orca: Ensure at least 2 levels so build_octree() will insert triangles.
// Fixes scenario where adaptive fill is disconnected from walls on low densities
if (cubes_properties.size() == 1) {
CubeProperties p = cubes_properties.back();
p.edge_length *= 2.0;
p.height = p.edge_length * sqrt(3);
p.diagonal_length = p.edge_length * sqrt(2);
p.line_z_distance = p.edge_length / sqrt(3);
p.line_xy_distance = p.edge_length / sqrt(6);
cubes_properties.push_back(p);
}
return cubes_properties;
}

View File

@@ -3,6 +3,7 @@
#include <cmath>
#include "../ClipperUtils.hpp"
#include "../Clipper2Utils.hpp"
#include "../EdgeGrid.hpp"
#include "../Geometry.hpp"
#include "../Geometry/Circle.hpp"
@@ -2699,60 +2700,77 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const Polygons &boun
connect_base_support(std::move(infill_ordered), polygons_src, bbox, polylines_out, spacing, params);
}
//Fill Multiline
// Fill Multiline -Clipper2 version
void multiline_fill(Polylines& polylines, const FillParams& params, float spacing)
{
if (params.multiline > 1) {
const int n_lines = params.multiline;
const int n_polylines = static_cast<int>(polylines.size());
Polylines all_polylines;
all_polylines.reserve(n_lines * n_polylines);
if (params.multiline <= 1)
return;
const float center = (n_lines - 1) / 2.0f;
const int n_lines = params.multiline;
const int n_polylines = static_cast<int>(polylines.size());
Polylines all_polylines;
all_polylines.reserve(n_lines * n_polylines);
for (int line = 0; line < n_lines; ++line) {
float offset = scale_((static_cast<float>(line) - center) * spacing);
// Remove invalid polylines
polylines.erase(std::remove_if(polylines.begin(), polylines.end(),
[](const Polyline& p) { return p.size() < 2; }),
polylines.end());
for (const Polyline& pl : polylines) {
const size_t n = pl.points.size();
if (n < 2) {
all_polylines.emplace_back(pl);
continue;
}
if (polylines.empty())
return;
// Convert source polylines to Clipper2 paths
Clipper2Lib::Paths64 subject_paths = Slic3rPolylines_to_Paths64(polylines);
Points new_points;
new_points.reserve(n);
for (size_t i = 0; i < n; ++i) {
Vec2f tangent;
// For the first and last point, if the polyline is a
// closed loop, get the tangent from the points on either
// side of the join, otherwise just use the first or last
// line.
if (i == 0) {
if (pl.points[0] == pl.points[n-1]) {
tangent = (pl.points[1] - pl.points[n-2]).template cast<float>().normalized();
} else {
tangent = (pl.points[1] - pl.points[0]).template cast<float>().normalized();
}
} else if (i == n - 1) {
if (pl.points[0] == pl.points[n-1]) {
tangent = (pl.points[1] - pl.points[n-2]).template cast<float>().normalized();
} else {
tangent = (pl.points[n-1] - pl.points[n-2]).template cast<float>().normalized();
}
} else
tangent = (pl.points[i+1] - pl.points[i-1]).template cast<float>().normalized();
Vec2f normal(-tangent.y(), tangent.x());
const double miter_limit = 2.0;
const int rings = n_lines / 2;
Point p = pl.points[i] + (normal * offset).template cast<coord_t>();
new_points.push_back(p);
}
// Compute offsets (in units of spacing)
std::vector<double> offsets;
offsets.reserve(n_lines);
all_polylines.emplace_back(std::move(new_points));
}
}
polylines = std::move(all_polylines);
if (n_lines % 2 != 0) {
// Odd: center line at offset = 0
offsets.push_back(0.0);
for (int i = 1; i <= rings; ++i)
offsets.push_back(i * spacing);
} else {
// Even: no center, start at 0.5 * spacing
double start = 0.5 * spacing;
for (int i = 0; i < rings; ++i)
offsets.push_back(start + i * spacing);
}
// Process each offset
Clipper2Lib::ClipperOffset offsetter(miter_limit);
offsetter.AddPaths(subject_paths, Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Round);
for (double t : offsets) {
if (t == 0.0) {
// Center line (only applies when n_lines is odd)
all_polylines.insert(all_polylines.end(), polylines.begin(), polylines.end());
continue;
}
// ClipperOffset with current offset distance (union is not needed here)
Clipper2Lib::Paths64 offset_paths;
offsetter.Execute(scale_(t), offset_paths);
if (offset_paths.empty())
continue;
// Convert back to polylines
Polylines new_polylines = Paths64_to_polylines(offset_paths);
for (Polyline& pl : new_polylines) {
if (pl.points.size() < 3)
continue;
if (pl.points.front() != pl.points.back())
pl.points.push_back(pl.points.front());
all_polylines.emplace_back(std::move(pl));
}
}
polylines = std::move(all_polylines);
}
} // namespace Slic3r

View File

@@ -19,7 +19,7 @@ void FillConcentric::_fill_surface_single(
// no rotation is supported for this infill pattern
BoundingBox bounding_box = expolygon.contour.bounding_box();
coord_t min_spacing = scale_(this->spacing);
coord_t min_spacing = scale_(this->spacing) * params.multiline;
coord_t distance = coord_t(min_spacing / params.density);
if (params.density > 0.9999f && !params.dont_adjust) {
@@ -27,8 +27,12 @@ void FillConcentric::_fill_surface_single(
this->spacing = unscale<double>(distance);
}
Polygons loops = to_polygons(expolygon);
ExPolygons last { std::move(expolygon) };
// 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 { std::move(contracted) };
while (! last.empty()) {
last = offset2_ex(last, -(distance + min_spacing/2), +min_spacing/2);
append(loops, to_polygons(last));
@@ -46,6 +50,9 @@ void FillConcentric::_fill_surface_single(
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;

View File

@@ -74,7 +74,7 @@ void FillHoneycomb::_fill_surface_single(
}
}
// Apply multiline offset if needed
multiline_fill(all_polylines, params, 1.1 * spacing);
multiline_fill(all_polylines, params, spacing);
all_polylines = intersection_pl(std::move(all_polylines), expolygon);
chain_or_connect_infill(std::move(all_polylines), expolygon, polylines_out, this->spacing, params);

View File

@@ -81,6 +81,9 @@ void FillPlanePath::_fill_surface_single(
BoundingBox snug_bounding_box = get_extents(expolygon).inflated(SCALED_EPSILON);
// Expand the bounding box to avoid artifacts at the edges
snug_bounding_box.offset(scale_(this->spacing)*params.multiline);
// Rotated bounding box of the area to fill in with the pattern.
BoundingBox bounding_box = align ?
// Sparse infill needs to be aligned across layers. Align infill across layers using the object's bounding box.
@@ -97,7 +100,7 @@ void FillPlanePath::_fill_surface_single(
Polyline polyline;
{
auto distance_between_lines = scaled<double>(this->spacing) / params.density;
auto distance_between_lines = scaled<double>(this->spacing) * params.multiline / params.density;
auto min_x = coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines));
auto min_y = coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines));
auto max_x = coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines));
@@ -117,8 +120,13 @@ void FillPlanePath::_fill_surface_single(
}
}
Polylines polylines = {polyline};
// Apply multiline offset if needed
multiline_fill(polylines, params, spacing);
if (polyline.size() >= 2) {
Polylines polylines = intersection_pl(polyline, expolygon);
polylines = intersection_pl(std::move(polylines), expolygon);
if (!polylines.empty()) {
Polylines chained;
if (params.dont_connect() || params.density > 0.5) {

View File

@@ -3000,7 +3000,7 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar
params.density /= double(sweep_params.size());
assert(params.density > 0.0001f && params.density <= 1.f);
ExPolygonWithOffset poly_with_offset_base(surface->expolygon, 0, float(scale_(this->overlap - 0.5 * this->spacing)));
ExPolygonWithOffset poly_with_offset_base(surface->expolygon, 0, float(scale_(this->overlap + 0.5 * params.multiline * this->spacing)));//increase offset to crop infill lines when using multiline infill
if (poly_with_offset_base.n_contours == 0)
// Not a single infill line fits.
return true;
@@ -3012,19 +3012,24 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar
for (const SweepParams &sweep : sweep_params) {
// Rotate polygons so that we can work with vertical lines here
float angle = rotate_vector.first + sweep.angle_base;
//Fill Multiline
for (int i = 0; i < params.multiline; ++i) {
coord_t group_offset = i * line_spacing;
coord_t internal_offset = (i - (params.multiline - 1) / 2.0f) * line_width;
coord_t total_offset = group_offset + internal_offset;
coord_t pattern_shift = scale_(sweep.pattern_shift + unscale_(total_offset));
make_fill_lines(ExPolygonWithOffset(poly_with_offset_base, -angle), rotate_vector.second.rotated(-angle), angle,
line_width + coord_t(SCALED_EPSILON), line_spacing, pattern_shift, fill_lines);
}
make_fill_lines(ExPolygonWithOffset(poly_with_offset_base, -angle), rotate_vector.second.rotated(-angle), angle,
line_width + coord_t(SCALED_EPSILON), line_spacing, coord_t(scale_(sweep.pattern_shift)), fill_lines);
}
// Apply multiline offset if needed
multiline_fill(fill_lines, params, spacing);
// Contract surface polygon by half line width to avoid excesive overlap with perimeter
ExPolygons contracted = offset_ex(surface->expolygon, -float(scale_(0.5 * this->spacing)));
// if contraction results in empty polygon, use original surface
const ExPolygon &intersection_surface = contracted.empty() ? surface->expolygon : contracted.front();
if ((params.pattern == ipLateralLattice || params.pattern == ipLateralHoneycomb ) && params.multiline >1 )
// Intersect polylines with perimeter
fill_lines = intersection_pl(std::move(fill_lines), intersection_surface);
if ((params.pattern == ipLateralLattice || params.pattern == ipLateralHoneycomb ) && params.multiline >1 )
remove_overlapped(fill_lines, line_width);
if (!fill_lines.empty()) {
@@ -3033,7 +3038,260 @@ if ((params.pattern == ipLateralLattice || params.pattern == ipLateralHoneycomb
fill_lines = chain_polylines(std::move(fill_lines));
append(polylines_out, std::move(fill_lines));
} else
connect_infill(std::move(fill_lines), poly_with_offset_base.polygons_outer, get_extents(surface->expolygon.contour), polylines_out, this->spacing, params);
connect_infill(std::move(fill_lines), intersection_surface, polylines_out, this->spacing, params);
}
return true;
}
bool FillRectilinear::fill_surface_trapezoidal(
const Surface* surface,
FillParams params,
const std::initializer_list<SweepParams>& sweep_params,
Polylines& polylines_out,
int Pattern_type) // 0=grid, 1=triangular
{
assert(params.multiline > 1);
Polylines polylines;
// Common parameters
const coord_t d1 = coord_t(scale_(this->spacing)) * params.multiline; // Infill total wall thickness
// Pattern-specific parameters
coord_t period;
double base_angle;
std::pair<double, Point> rotate_vector = this->_infill_direction(surface);
if (Pattern_type == 0) {
// Grid pattern parameters
period = coord_t((2.0 * d1 / params.density) * std::sqrt(2.0));
base_angle = rotate_vector.first + M_PI_4; // 45
} else {
// Triangular pattern parameters
period = coord_t(( 2.0 * d1 / params.density) * std::sqrt(3.0));
base_angle = rotate_vector.first + M_PI_2; //90
}
// Obtain the expolygon and rotate to align with pattern base angle
ExPolygon expolygon = surface->expolygon;
if (std::abs(base_angle) >= EPSILON) {
expolygon.rotate(-base_angle, rotate_vector.second);
}
// Use extended object bounding box for consistent pattern across layers
BoundingBox bb = this->extended_object_bounding_box();
switch (Pattern_type) {
case 0: // Grid / Trapezoidal
{
// Generate a non-crossing trapezoidal pattern to avoid overextrusion at intersections when `multiline > 1`.
// P1--P2
// / \
// P0/ \P3__P4
//
// P1x-P2x=P3x-P4x=d1
// P0y-P1y=P2y-P3y=d2
const coord_t d2 = coord_t(0.5 * period - d1);
// Align bounding box to the grid
bb.merge(align_to_grid(bb.min, Point(period, period)));
const coord_t xmin = bb.min.x();
const coord_t xmax = bb.max.x();
const coord_t ymin = bb.min.y();
const coord_t ymax = bb.max.y();
// Create the two base row patterns once
Polyline base_row_normal;
base_row_normal.points.reserve(((xmax - xmin) / period + 1) * 5); // 5 points per trapezoid
Polyline base_row_flipped;
base_row_flipped.points.reserve(((xmax - xmin) / period + 1) * 5); // 5 points per trapezoid
// Build complete rows from xmin to xmax
for (coord_t x = xmin; x < xmax; x += period) {
// Normal row
base_row_normal.points.emplace_back(Point(x, d1 / 2)); // P0
base_row_normal.points.emplace_back(Point(x + d1, d1 / 2)); // P1
base_row_normal.points.emplace_back(Point(x + d1 + d2, d1 / 2 + d2)); // P2
base_row_normal.points.emplace_back(Point(x + 2 * d1 + d2, d1 / 2 + d2)); // P3
base_row_normal.points.emplace_back(Point(x + 2 * d1 + 2 * d2, d1 / 2)); // P4
}
// Flipped row (mirrored vertically)
base_row_flipped.points = base_row_normal.points;
for (auto& p : base_row_flipped.points) {
p.y() = period / 2 - p.y();
}
// Pre-allocate polylines
const size_t estimated_rows = ((ymax - ymin) / (period / 2) + 1);
polylines.reserve(estimated_rows);
bool flip_vertical = false;
// Now just copy and translate vertically
for (coord_t y = ymin; y < ymax; y += period / 2) {
Polyline pl_row = flip_vertical ? base_row_flipped : base_row_normal;
// Translate all points vertically
for (Point& p : pl_row.points) {
p.y() += y;
}
polylines.emplace_back(std::move(pl_row));
flip_vertical = !flip_vertical;
}
// transpose points for odd layers
if (layer_id % 2 == 1) {
for (Polyline& pl : polylines) {
for (Point& p : pl.points) {
std::swap(p.x(), p.y());
p.x() += d1 / 2;
p.y() -= d1 / 2;
}
}
}
break;
}
case 1: // Triangular
{
// Generate a non-crossing trapezoidal pattern with a base line below.
// P1-P2
// / \
// P0/ \P3_P4
// ----------------
// P1x-P2x=P3x-P4x=d2
// P0y-P1y=P2y-P3y=h-2d1
//
// Triangular pattern density adjustment:
const coord_t d2_tri = coord_t(2.0 / std::sqrt(3.0) * d1);
const coord_t h = coord_t(0.5 * std::sqrt(3.0) * period); // height of triangle
// Align bounding box to the grid
bb.merge(align_to_grid(bb.center(), Point(period,h)));
const int layer_mod = layer_id % 3;
const double angle = layer_mod * 2.0 * M_PI / 3.0;
const Point rotation_center = bb.center();
const coord_t half_w = bb.size().x() / 2;
const coord_t half_h = bb.size().y() / 2;
// Compute how many full periods fit in each direction
const coord_t num_periods_x = coord_t(std::ceil(half_w / double(period)));
coord_t num_periods_y =coord_t(std::ceil(half_h / double(h)));
// Ensure an even number of rows so the pattern stays centered
if ((num_periods_y % 2) != 0)
++num_periods_y;
// Compute aligned limits (symmetric around the origin)
const coord_t x_min_aligned = -num_periods_x * period;
const coord_t x_max_aligned = num_periods_x * period;
const coord_t y_min_aligned = -num_periods_y * h;
const coord_t y_max_aligned = num_periods_y * h;
// Pre-allocate estimated number of polylines
const size_t estimated_rows = (y_max_aligned - y_min_aligned) / h + 2;
const size_t estimated_polylines = (estimated_rows + 1) * 2; // base line + trapezoid line per row
polylines.reserve(estimated_polylines);
// Create the two base row templates once
Polyline base_line_template;
base_line_template.points.reserve(2); // 2 points for base line
Polyline trapezoid_row_normal;
trapezoid_row_normal.points.reserve(((x_max_aligned - x_min_aligned) / period + 1) * 5); // 5 points per trapezoid
Polyline trapezoid_row_shifted;
trapezoid_row_shifted.points.reserve(((x_max_aligned - x_min_aligned) / period + 1) * 5); // 5 points per trapezoid
// Build base line template (from x_min_aligned to x_max_aligned)
base_line_template.points.emplace_back(Point(x_min_aligned, 0));
base_line_template.points.emplace_back(Point(x_max_aligned, 0));
// Build complete trapezoid rows once
// Normal row (no shift)
for (coord_t x = x_min_aligned; x < x_max_aligned; x += period) {
trapezoid_row_normal.points.emplace_back(Point(x + d2_tri / 2, d1)); // P0
trapezoid_row_normal.points.emplace_back(Point(x + period / 2 - d2_tri / 2, h - d1)); // P1
trapezoid_row_normal.points.emplace_back(Point(x + period / 2 + d2_tri / 2, h - d1)); // P2
trapezoid_row_normal.points.emplace_back(Point(x + period - d2_tri / 2, d1)); // P3
trapezoid_row_normal.points.emplace_back(Point(x + period, d1)); // P4
}
// Shifted row (mirrored vertically)
trapezoid_row_shifted.points = trapezoid_row_normal.points;
for (auto& p : trapezoid_row_shifted.points)
p.y() = h - p.y();
bool shift_row = false;
// Generate pattern by copying and translating templates vertically
for (coord_t y = y_min_aligned; y < y_max_aligned; y += h) {
// Base line - copy and translate
Polyline base_line = base_line_template;
for (Point& p : base_line.points) {
p.y() += y;
}
polylines.emplace_back(std::move(base_line));
// Trapezoid line - copy and translate the appropriate template
Polyline trapezoid_line = shift_row ? trapezoid_row_shifted : trapezoid_row_normal;
for (Point& p : trapezoid_line.points) {
p.y() += y;
}
if (!trapezoid_line.points.empty()) {
polylines.emplace_back(std::move(trapezoid_line));
}
shift_row = !shift_row;
}
// Rotate around origin (0,0)
if (layer_mod)
for (auto& pl : polylines)
pl.rotate(angle, Point(0,0));
break;
}
default:
// Handle unknown pattern type
break;
}
// Apply multiline fill
multiline_fill(polylines, params, spacing);
// Contract surface polygon by half line width to avoid excesive overlap with perimeter
ExPolygons contracted = offset_ex(expolygon, -float(scale_(0.5 * this->spacing)));
// if contraction results in empty polygon, use original surface
const ExPolygon &intersection_surface = contracted.empty() ? expolygon : contracted.front();
// Intersect polylines with offset expolygon
polylines = intersection_pl(std::move(polylines), intersection_surface);
// Remove very short segments that may cause connection issues
const double minlength = scale_(0.8 * this->spacing);
if (minlength > 0 && !polylines.empty()) {
polylines.erase(std::remove_if(polylines.begin(), polylines.end(),
[minlength](const Polyline& pl) { return pl.length() < minlength; }),
polylines.end());
}
// Connect infill lines using offset expolygon
int infill_start_idx = polylines_out.size();
if (!polylines.empty()) {
Slic3r::Fill::chain_or_connect_infill(std::move(polylines), intersection_surface, polylines_out, this->spacing, params);
// Rotate back the infill lines to original orientation
if (std::abs(base_angle) >= EPSILON) {
for (auto it = polylines_out.begin() + infill_start_idx; it != polylines_out.end(); ++it) {
it->rotate(base_angle, rotate_vector.second);
}
}
}
return true;
@@ -3077,15 +3335,27 @@ Polylines FillMonotonicLine::fill_surface(const Surface* surface, const FillPara
Polylines FillGrid::fill_surface(const Surface *surface, const FillParams &params)
{
Polylines polylines_out;
if (! this->fill_surface_by_multilines(
surface, params,
{ { 0.f, 0.f }, { float(M_PI / 2.), 0.f } },
polylines_out))
BOOST_LOG_TRIVIAL(error) << "FillGrid::fill_surface() failed to fill a region.";
if (this->layer_id % 2 == 1)
for (int i = 0; i < polylines_out.size(); i++)
std::reverse(polylines_out[i].begin(), polylines_out[i].end());
if (params.multiline > 1) {
// Experimental trapezoidal grid
if (!this->fill_surface_trapezoidal(
surface, params,
{ { 0.f, 0.f }, { float(M_PI / 2.), 0.f } },
polylines_out,0))
BOOST_LOG_TRIVIAL(error) << "FillGrid::fill_surface_trapezoidal() failed.";
} else {
if (!this->fill_surface_by_multilines(
surface, params,
{ { 0.f, 0.f }, { float(M_PI / 2.), 0.f } },
polylines_out))
BOOST_LOG_TRIVIAL(error) << "FillGrid::fill_surface() failed to fill a region.";
if (this->layer_id % 2 == 1)
for (int i = 0; i < polylines_out.size(); i++)
std::reverse(polylines_out[i].begin(), polylines_out[i].end());
}
return polylines_out;
}
@@ -3108,12 +3378,23 @@ Polylines FillLateralLattice::fill_surface(const Surface *surface, const FillPar
Polylines FillTriangles::fill_surface(const Surface *surface, const FillParams &params){
Polylines polylines_out;
if (params.multiline > 1) {
// Experimental trapezoidal grid
if (!this->fill_surface_trapezoidal(
surface, params,
{ { 0.f, 0.f }, { float(M_PI / 2.), 0.f } },
polylines_out,1))
BOOST_LOG_TRIVIAL(error) << "FillGrid::fill_surface_trapezoidal() failed.";
} else {
if (! this->fill_surface_by_multilines(
surface, params,
{ { 0.f, 0.f }, { float(M_PI / 3.), 0.f }, { float(2. * M_PI / 3.), 0. } },
polylines_out))
BOOST_LOG_TRIVIAL(error) << "FillTriangles::fill_surface() failed to fill a region.";
}
return polylines_out;
}
Polylines FillStars::fill_surface(const Surface *surface, const FillParams &params)
@@ -3144,8 +3425,8 @@ Polylines FillQuarterCubic::fill_surface(const Surface* surface, const FillParam
using namespace boost::math::float_constants;
Polylines polylines_out;
coord_t line_width = coord_t(scale_(this->spacing));
coord_t period = coord_t(scale_(this->spacing) / params.density) * 4;
coord_t line_width = coord_t(scale_(this->spacing)) * params.multiline;
coord_t period = coord_t(scale_(this->spacing) *params.multiline / params.density) * 4;
// First half tetrahedral fill
double pattern_z_shift = 0.0;

View File

@@ -29,6 +29,7 @@ protected:
float pattern_shift;
};
bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out);
bool fill_surface_trapezoidal(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out,int Pattern_type);
// The extended bounding box of the whole object that covers any rotation of every layer.
BoundingBox extended_object_bounding_box() const;

View File

@@ -2727,8 +2727,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
auto probe_dist_x = std::max(1., m_config.bed_mesh_probe_distance.value.x());
auto probe_dist_y = std::max(1., m_config.bed_mesh_probe_distance.value.y());
int probe_count_x = std::max(3, (int) std::ceil(mesh_bbox.size().x() / probe_dist_x));
int probe_count_y = std::max(3, (int) std::ceil(mesh_bbox.size().y() / probe_dist_y));
int probe_count_x = std::max(3, (int) std::ceil(mesh_bbox.size().x() / probe_dist_x) + 1);
int probe_count_y = std::max(3, (int) std::ceil(mesh_bbox.size().y() / probe_dist_y) + 1);
auto bed_mesh_algo = "bicubic";
if (probe_count_x * probe_count_y <= 6) { // lagrange needs up to a total of 6 mesh points
bed_mesh_algo = "lagrange";
@@ -6186,10 +6186,16 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
);
}
// if still in avoidance mode and under max, clamp to “min”
if (m_resonance_avoidance
&& speed <= m_config.max_resonance_avoidance_speed.value) {
speed = std::min(speed, m_config.min_resonance_avoidance_speed.value);
// if still in avoidance mode and under "max", adjust speed:
// - speeds in lower half of range: clamp down to "min"
// - speeds in upper half of range: boost up to "max"
if (m_resonance_avoidance && speed < m_config.max_resonance_avoidance_speed.value) {
if (speed < m_config.min_resonance_avoidance_speed.value +
((m_config.max_resonance_avoidance_speed.value - m_config.min_resonance_avoidance_speed.value) / 2)) {
speed = std::min(speed, m_config.min_resonance_avoidance_speed.value);
} else {
speed = m_config.max_resonance_avoidance_speed.value;
}
}
// reset flag for next segment

View File

@@ -1045,8 +1045,8 @@ namespace client
case coFloatOrPercent:
{
std::string opt_key(opt.it_range.begin(), opt.it_range.end());
if (boost::ends_with(opt_key, "extrusion_width")) {
// Extrusion width supports defaults and a complex graph of dependencies.
if (boost::ends_with(opt_key, "line_width")) {
// Line width supports defaults and a complex graph of dependencies.
output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast<unsigned int>(ctx->current_extruder_id)));
} else if (! static_cast<const ConfigOptionFloatOrPercent*>(opt.opt)->percent) {
// Not a percent, just return the value.
@@ -1060,8 +1060,8 @@ namespace client
const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over);
if (opt_parent == nullptr)
ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range);
if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) {
// Extrusion width supports defaults and a complex graph of dependencies.
if (boost::ends_with(opt_def->ratio_over, "line_width")) {
// Line width supports defaults and a complex graph of dependencies.
assert(opt_parent->type() == coFloatOrPercent);
v *= Flow::extrusion_width(opt_def->ratio_over, static_cast<const ConfigOptionFloatOrPercent*>(opt_parent), *ctx, static_cast<unsigned int>(ctx->current_extruder_id));
break;
@@ -2197,9 +2197,8 @@ namespace client
initializer_list(_r1)[px::bind(&MyContext::vector_variable_new_from_initializer_list, _r1, _a, _b, _1)]
// Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index.
// Only process such variable references, which return a naked vector variable.
// Orca todo: following code cause strange build errors with MSVC C++17
// | eps(px::bind(&MyContext::could_be_vector_variable_reference, _b)) >>
// variable_reference(_r1)[px::val(qi::_pass) = px::bind(&MyContext::vector_variable_new_from_copy, _r1, _a, _b, _1)]
| eps(px::bind(&MyContext::could_be_vector_variable_reference, _b)) >>
variable_reference(_r1)[qi::_pass = px::bind(&MyContext::vector_variable_new_from_copy, _r1, _a, _b, _1)]
// Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above.
| conditional_expression(_r1)
[px::bind(&MyContext::scalar_variable_new_from_scalar_expression, _r1, _a, _b, _1)]

View File

@@ -2751,6 +2751,8 @@ Preset *PresetBundle::get_similar_printer_preset(std::string printer_model, std:
{
if (printer_model.empty())
printer_model = printers.get_selected_preset().config.opt_string("printer_model");
if (printer_model.empty()) // ORCA ensure a compatible model exist. fixes switches to blank preset if preset has no inherited value
return nullptr;
auto printer_variant_old = printers.get_selected_preset().config.opt_string("printer_variant");
std::map<std::string, Preset*> printer_presets;
for (auto &preset : printers.m_presets) {
@@ -2761,7 +2763,8 @@ Preset *PresetBundle::get_similar_printer_preset(std::string printer_model, std:
}
if (printer_presets.empty())
return nullptr;
auto prefer_printer = printers.get_selected_preset().name;
auto prefer_printer = printers.get_selected_preset().alias; //.name ORCA use alias instead "name" for calling system presets. otherwise nozzle combo will not change printer presets if they custom named
if (!printer_variant.empty())
boost::replace_all(prefer_printer, printer_variant_old, printer_variant);
else if (auto n = prefer_printer.find(printer_variant_old); n != std::string::npos)

View File

@@ -2733,7 +2733,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Fill Multiline");
def->tooltip = L("Using multiple lines for the infill pattern, if supported by infill pattern.");
def->min = 1;
def->max = 5; // Maximum number of lines for infill pattern
def->max = 10; // Maximum number of lines for infill pattern
def->set_default_value(new ConfigOptionInt(1));
def = this->add("sparse_infill_pattern", coEnum);
@@ -7646,6 +7646,9 @@ std::set<std::string> filament_options_with_variant = {
"filament_retraction_length",
"filament_z_hop",
"filament_z_hop_types",
"filament_retract_lift_above",
"filament_retract_lift_below",
"filament_retract_lift_enforce",
"filament_retract_restart_extra",
"filament_retraction_speed",
"filament_deretraction_speed",
@@ -7664,7 +7667,11 @@ std::set<std::string> filament_options_with_variant = {
"filament_flush_volumetric_speed",
"filament_flush_temp",
"volumetric_speed_coefficients",
"filament_adaptive_volumetric_speed"
"filament_adaptive_volumetric_speed",
"filament_ironing_flow",
"filament_ironing_spacing",
"filament_ironing_inset",
"filament_ironing_speed"
};
// Parameters that are the same as the number of extruders