mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-01 07:12:07 +00:00
Merge branch 'main' into feat/configurable-bambu-network-lib
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ¶ms)
|
||||
{
|
||||
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 ¶ms){
|
||||
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 ¶ms)
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user