mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-01 04:41:32 +00:00
Merge remote-tracking branch 'origin/main' into feat/printer-agent-impl
This commit is contained in:
@@ -35,6 +35,7 @@ jobs:
|
||||
// kind of change
|
||||
'crash',
|
||||
'bug-fix',
|
||||
'SECURITY',
|
||||
'enhancement',
|
||||
'QoL',
|
||||
'optimization',
|
||||
@@ -193,6 +194,7 @@ jobs:
|
||||
// kind of change
|
||||
'crash',
|
||||
'bug-fix',
|
||||
'SECURITY',
|
||||
'enhancement',
|
||||
'QoL',
|
||||
'optimization',
|
||||
|
||||
+2
-1
@@ -1913,7 +1913,8 @@ int CLI::run(int argc, char **argv)
|
||||
old_printable_width = static_cast<int>(old_printable_bbox.size().x());
|
||||
old_printable_depth = static_cast<int>(old_printable_bbox.size().y());
|
||||
}
|
||||
old_printable_height = (int)(config.opt_float("printable_height"));
|
||||
if (config.option<ConfigOptionFloat>("printable_height"))
|
||||
old_printable_height = (int)(config.opt_float("printable_height"));
|
||||
|
||||
if (config.option<ConfigOptionFloat>("extruder_clearance_height_to_rod"))
|
||||
old_height_to_rod = config.opt_float("extruder_clearance_height_to_rod");
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include "FillBase.hpp"
|
||||
#include "FillGyroid.hpp"
|
||||
|
||||
@@ -123,44 +124,41 @@ static inline double f(double x, double z_sin, double z_cos, bool vertical, bool
|
||||
}
|
||||
}
|
||||
|
||||
// Repeats one period of a wave from the last sample at or before x_min to the first one at or after x_max.
|
||||
static inline Polyline make_wave(
|
||||
const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
|
||||
double z_cos, double z_sin, bool vertical, bool flip)
|
||||
const std::vector<Vec2d>& one_period, double x_min, double x_max, double offset, double scaleFactor, bool vertical)
|
||||
{
|
||||
std::vector<Vec2d> points = one_period;
|
||||
double period = points.back()(0);
|
||||
if (width != period) // do not extend if already truncated
|
||||
{
|
||||
points.reserve(one_period.size() * size_t(floor(width / period)));
|
||||
points.pop_back();
|
||||
const double period = one_period.back().x();
|
||||
// The last sample of a period is the first one of the next.
|
||||
const size_t n = one_period.size() - 1;
|
||||
double x0 = std::floor(x_min / period) * period;
|
||||
size_t i = 0;
|
||||
while (i + 1 < n && x0 + one_period[i + 1].x() <= x_min)
|
||||
++i;
|
||||
|
||||
size_t n = points.size();
|
||||
do {
|
||||
points.emplace_back(points[points.size()-n].x() + period, points[points.size()-n].y());
|
||||
} while (points.back()(0) < width - EPSILON);
|
||||
|
||||
points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip)));
|
||||
}
|
||||
|
||||
// and construct the final polyline to return:
|
||||
Polyline polyline;
|
||||
polyline.points.reserve(points.size());
|
||||
for (auto& point : points) {
|
||||
point(1) += offset;
|
||||
point(1) = std::clamp(double(point.y()), 0., height);
|
||||
polyline.points.reserve(size_t((x_max - x0) / period + 1.) * n + 1);
|
||||
for (;;) {
|
||||
Vec2d point(x0 + one_period[i].x(), one_period[i].y() + offset);
|
||||
const bool last = point.x() >= x_max;
|
||||
if (vertical)
|
||||
std::swap(point(0), point(1));
|
||||
polyline.points.emplace_back((point * scaleFactor).cast<coord_t>());
|
||||
if (last)
|
||||
break;
|
||||
if (++i == n) {
|
||||
i = 0;
|
||||
x0 += period;
|
||||
}
|
||||
}
|
||||
|
||||
return polyline;
|
||||
}
|
||||
|
||||
static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance)
|
||||
static std::vector<Vec2d> make_one_period(double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance)
|
||||
{
|
||||
std::vector<Vec2d> points;
|
||||
double dx = M_PI_2; // exact coordinates on main inflexion lobes
|
||||
double limit = std::min(2*M_PI, width);
|
||||
double limit = 2*M_PI;
|
||||
points.reserve(coord_t(ceil(limit / tolerance / 3)));
|
||||
|
||||
for (double x = 0.; x < limit - EPSILON; x += dx) {
|
||||
@@ -239,7 +237,8 @@ static inline double compute_omega_factor(double density_adjusted, double line_s
|
||||
return std::clamp(raw, 1.0, 2.0);
|
||||
}
|
||||
|
||||
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
|
||||
// Waves covering bbox, with the pattern anchored at origin.
|
||||
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, const BoundingBox &bbox, const Point &origin)
|
||||
{
|
||||
const double scaleFactor = scale_(line_spacing) / density_adjusted;
|
||||
|
||||
@@ -254,29 +253,36 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double
|
||||
const double z_cos = cos(z);
|
||||
|
||||
bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
|
||||
// Range to cover in pattern units, with the waves running along x.
|
||||
Vec2d lo = (bbox.min - origin).cast<double>() / scaleFactor;
|
||||
Vec2d hi = (bbox.max - origin).cast<double>() / scaleFactor;
|
||||
double lower_bound = 0.;
|
||||
double upper_bound = height;
|
||||
bool flip = true;
|
||||
if (vertical) {
|
||||
flip = false;
|
||||
lower_bound = -M_PI;
|
||||
upper_bound = width - M_PI_2;
|
||||
std::swap(width,height);
|
||||
std::swap(lo(0), lo(1));
|
||||
std::swap(hi(0), hi(1));
|
||||
}
|
||||
|
||||
std::vector<Vec2d> one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time
|
||||
std::vector<Vec2d> one_period_odd = make_one_period(scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time
|
||||
flip = !flip; // even polylines are a bit shifted
|
||||
std::vector<Vec2d> one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance);
|
||||
Polylines result;
|
||||
std::vector<Vec2d> one_period_even = make_one_period(scaleFactor, z_cos, z_sin, vertical, flip, tolerance);
|
||||
|
||||
for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) {
|
||||
// creates odd polylines
|
||||
result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
||||
// creates even polylines
|
||||
y0 += M_PI;
|
||||
if (y0 < upper_bound + EPSILON) {
|
||||
result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
||||
// Every wave spans [offset + f_min, offset + f_max] across.
|
||||
double f_min = std::numeric_limits<double>::max();
|
||||
double f_max = std::numeric_limits<double>::lowest();
|
||||
for (const std::vector<Vec2d> *one_period : { &one_period_odd, &one_period_even })
|
||||
for (const Vec2d &point : *one_period) {
|
||||
f_min = std::min(f_min, point.y());
|
||||
f_max = std::max(f_max, point.y());
|
||||
}
|
||||
|
||||
Polylines result;
|
||||
for (int i = int(std::ceil((lo.y() - f_max - lower_bound) / M_PI)); lower_bound + i * M_PI + f_min <= hi.y(); ++i) {
|
||||
Polyline &wave = result.emplace_back(make_wave(i % 2 == 0 ? one_period_odd : one_period_even, lo.x(), hi.x(),
|
||||
lower_bound + i * M_PI, scaleFactor, vertical));
|
||||
wave.translate(origin);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -302,12 +308,12 @@ void FillGyroid::_fill_surface_single(
|
||||
// Distance between the gyroid waves in scaled coordinates.
|
||||
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
||||
|
||||
// align bounding box to a multiple of our grid module
|
||||
bb.merge(align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
|
||||
// Anchor the pattern to our grid module; the 10-line shift keeps its established phase.
|
||||
const coord_t shift = coord_t(10 * scale_(this->spacing));
|
||||
const Point origin = align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)) - Point(shift, shift);
|
||||
|
||||
// Expand the bounding box to avoid artifacts at the edges
|
||||
coord_t expand = 10 * (scale_(this->spacing));
|
||||
bb.offset(expand);
|
||||
// Keep the pattern ends and the multiline copies outside the contour.
|
||||
bb.offset(scale_(this->spacing * params.multiline));
|
||||
|
||||
// generate pattern
|
||||
Polylines polylines;
|
||||
@@ -327,23 +333,14 @@ void FillGyroid::_fill_surface_single(
|
||||
const float density_factor = std::max(0.001f, float(params.density * DensityAdjust / params.multiline));
|
||||
const float period = float(2.0 * M_PI) * float(this->spacing) / density_factor;
|
||||
|
||||
// bb is already expanded above by 10 * scale_(spacing) for edge artifacts;
|
||||
// skip a second offset here to avoid raster-area bloat in the marching squares pass.
|
||||
// A cell of margin for the rings closed along the raster border, and a fixed sampling grid for every region.
|
||||
const coord_t cell = scaled(marchsq::GyroidField::gsizef);
|
||||
bb.offset(cell);
|
||||
bb.merge(align_to_grid(bb.min, Point(cell, cell)));
|
||||
marchsq::GyroidField sf(bb, this->z, period, float(omega));
|
||||
polylines = marchsq::get_gyroid_polylines(sf, SCALED_SPARSE_INFILL_RESOLUTION);
|
||||
} else {
|
||||
polylines = make_gyroid_waves(
|
||||
scale_(this->z),
|
||||
density_adjusted,
|
||||
this->spacing,
|
||||
ceil(bb.size()(0) / distance) + 1.,
|
||||
ceil(bb.size()(1) / distance) + 1.);
|
||||
|
||||
// The parametric generator produces wave coords relative to the grid origin;
|
||||
// shift them into absolute layer coords. The marching-squares branch above
|
||||
// already emits absolute coords via GyroidField::to_Point, so it skips this.
|
||||
for (Polyline &pl : polylines)
|
||||
pl.translate(bb.min);
|
||||
polylines = make_gyroid_waves(scale_(this->z), density_adjusted, this->spacing, bb, origin);
|
||||
}
|
||||
|
||||
// Apply multiline offset if needed
|
||||
|
||||
@@ -307,7 +307,7 @@ public:
|
||||
bool ams_support_virtual_tray { true };
|
||||
time_t ams_user_setting_start = 0;
|
||||
time_t ams_switch_filament_start = 0;
|
||||
AmsStatusMain ams_status_main;
|
||||
AmsStatusMain ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_IDLE;
|
||||
int ams_status_sub;
|
||||
int ams_version = 0;
|
||||
|
||||
|
||||
@@ -2291,9 +2291,7 @@ void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector<wxSt
|
||||
// Fill the real per-printer max color count into the %s template.
|
||||
if (!params.empty())
|
||||
msg = wxString::Format(m_pre_print_checker.get_pre_state_msg(status), params[0], params[0]);
|
||||
}
|
||||
|
||||
else if (status == PrintDialogStatus::PrintStatusAmsMappingU0Invalid) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusAmsMappingU0Invalid) {
|
||||
wxString msg_text;
|
||||
if (params.size() > 1)
|
||||
msg_text = wxString::Format(_L("Filament %s does not match the filament in AMS slot %s. Please update the printer firmware to support AMS slot assignment."), params[0], params[1]);
|
||||
@@ -2319,7 +2317,7 @@ void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector<wxSt
|
||||
} else if (status == PrintDialogStatus::PrintStatusNoSdcard) {
|
||||
Enable_Refresh_Button(true);
|
||||
Enable_Send_Button(false);
|
||||
}else if (status == PrintDialogStatus::PrintStatusUnsupportedPrinter ||
|
||||
} else if (status == PrintDialogStatus::PrintStatusUnsupportedPrinter ||
|
||||
status == PrintDialogStatus::PrintStatusOptionalPrinterModel) {
|
||||
wxString msg_text;
|
||||
const bool block_send = status == PrintDialogStatus::PrintStatusUnsupportedPrinter;
|
||||
|
||||
@@ -1410,74 +1410,60 @@ void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector<wxSt
|
||||
update_print_status_msg(wxEmptyString, false, false);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusInvalidPrinter) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusInvalidPrinter) {
|
||||
update_print_status_msg(wxEmptyString, true, true);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusConnectingServer) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusConnectingServer) {
|
||||
wxString msg_text = _L("Connecting to server...");
|
||||
update_print_status_msg(msg_text, true, true);
|
||||
Enable_Send_Button(true);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusReading) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusReading) {
|
||||
wxString msg_text = _L("Synchronizing device information...");
|
||||
update_print_status_msg(msg_text, false, true);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(false);
|
||||
}
|
||||
|
||||
else if (status == PrintDialogStatus::PrintStatusReadingFinished) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusReadingFinished) {
|
||||
update_print_status_msg(wxEmptyString, false, true);
|
||||
Enable_Send_Button(true);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusReadingTimeout) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusReadingTimeout) {
|
||||
wxString msg_text = _L("Synchronizing device information timed out.");
|
||||
update_print_status_msg(msg_text, true, true);
|
||||
Enable_Send_Button(true);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusInUpgrading) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusInUpgrading) {
|
||||
wxString msg_text = _L("Cannot send print tasks when an update is in progress");
|
||||
update_print_status_msg(msg_text, true, true);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusUnsupportedPrinter) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusUnsupportedPrinter) {
|
||||
wxString msg_text = _L("The selected printer is incompatible with the chosen printer presets.");
|
||||
update_print_status_msg(msg_text, true, true);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Send_Button(true);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusRefreshingMachineList) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusRefreshingMachineList) {
|
||||
update_print_status_msg(wxEmptyString, false, true);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(false);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusSending) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusSending) {
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(false);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusSendingCanceled) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusSendingCanceled) {
|
||||
Enable_Send_Button(true);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusNoSdcard) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusNoSdcard) {
|
||||
wxString msg_text = _L("Storage needs to be inserted before send to printer.");
|
||||
update_print_status_msg(msg_text, true, true);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusNotOnTheSameLAN) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusNotOnTheSameLAN) {
|
||||
wxString msg_text = _L("The printer is required to be on the same LAN as Orca Slicer.");
|
||||
update_print_status_msg(msg_text, true, true);
|
||||
Enable_Send_Button(false);
|
||||
Enable_Refresh_Button(true);
|
||||
}
|
||||
else if (status == PrintDialogStatus::PrintStatusNotSupportedSendToSDCard) {
|
||||
} else if (status == PrintDialogStatus::PrintStatusNotSupportedSendToSDCard) {
|
||||
wxString msg_text = _L("The printer does not support sending to printer storage.");
|
||||
update_print_status_msg(msg_text, true, true);
|
||||
Enable_Send_Button(false);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "libslic3r/AABBTreeLines.hpp"
|
||||
#include "libslic3r/Fill/Fill.hpp"
|
||||
#include "libslic3r/Fill/FillAdaptive.hpp"
|
||||
#include "libslic3r/Fill/FillGyroid.hpp"
|
||||
#include "libslic3r/Flow.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/IntersectionPoints.hpp"
|
||||
@@ -1257,6 +1258,61 @@ TEST_CASE("Multiline infill of an object matches the infill of a larger object w
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Gyroid infill of an object matches the infill of a larger object with the same center", "[Fill]")
|
||||
{
|
||||
const bool optimized = GENERATE(false, true);
|
||||
const int multiline = GENERATE(1, 2);
|
||||
const float density = GENERATE(0.05f, 0.2f);
|
||||
const double spacing = 0.45;
|
||||
CAPTURE(optimized, multiline, density);
|
||||
|
||||
auto circle = [](double radius) {
|
||||
Polygon contour = make_circle_num_segments(scale_(radius), 120);
|
||||
contour.translate(Point::new_scale(100., 60.));
|
||||
return ExPolygon(std::move(contour));
|
||||
};
|
||||
const ExPolygon object = circle(20.);
|
||||
const ExPolygon larger = circle(30.);
|
||||
auto fill = [optimized, multiline, density, spacing](const ExPolygon ®ion, double z) {
|
||||
std::unique_ptr<Fill> filler(Fill::new_from_type(ipGyroid));
|
||||
filler->spacing = spacing;
|
||||
filler->angle = float(M_PI / 7.);
|
||||
filler->z = z;
|
||||
|
||||
FillParams params;
|
||||
params.density = density;
|
||||
params.multiline = multiline;
|
||||
params.gyroid_optimized = optimized;
|
||||
params.dont_adjust = true;
|
||||
Surface surface(stInternal, region);
|
||||
return filler->fill_surface(&surface, params);
|
||||
};
|
||||
// Away from the boundary of the object, where both are clipped and connected the same way.
|
||||
const Polygons inner = shrink(to_polygons(object), scale_(1.));
|
||||
auto farthest = [&inner](const Polylines &from, const Polylines &to) {
|
||||
const AABBTreeLines::LinesDistancer<Line> tree(to_lines(to));
|
||||
double distance = 0.;
|
||||
for (const Polyline &path : intersection_pl(from, inner))
|
||||
for (const Point &point : path.equally_spaced_points(scale_(0.2)))
|
||||
distance = std::max(distance, tree.distance_from_lines<false>(point));
|
||||
return unscale<double>(distance);
|
||||
};
|
||||
|
||||
// Marching squares simplifies rings that start elsewhere in each object.
|
||||
const double tolerance = optimized ? SPARSE_INFILL_RESOLUTION + 0.01 : 0.01;
|
||||
// Half a z period of the waves, through both switches between horizontal and vertical waves.
|
||||
const double wave_distance = spacing * multiline / (density * FillGyroid::DensityAdjust);
|
||||
for (int step = 0; step <= 8; ++step) {
|
||||
const double z = wave_distance * M_PI * step / 8.;
|
||||
CAPTURE(z);
|
||||
const Polylines paths = fill(object, z);
|
||||
REQUIRE_FALSE(paths.empty());
|
||||
const Polylines reference = fill(larger, z);
|
||||
CHECK(farthest(reference, paths) < tolerance);
|
||||
CHECK(farthest(paths, reference) < tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Multiline cubic infill follows the cubic lines without crossing itself", "[Fill]")
|
||||
{
|
||||
const int multiline = GENERATE(2, 3);
|
||||
|
||||
Reference in New Issue
Block a user