mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-01 22:40:40 +08:00
Merge branch 'master_slic3rPE_PR'
This commit is contained in:
commit
c3c514d75b
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ xs/MANIFEST.bak
|
||||
xs/assertlib*
|
||||
.init_bundle.ini
|
||||
local-lib
|
||||
build*
|
||||
|
15
t/thin.t
15
t/thin.t
@ -1,4 +1,4 @@
|
||||
use Test::More tests => 28;
|
||||
use Test::More tests => 29;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
@ -102,7 +102,9 @@ if (0) {
|
||||
is scalar(@$res), 1, 'medial axis of a semicircumference is a single line';
|
||||
|
||||
# check whether turns are all CCW or all CW
|
||||
my @lines = @{$res->[0]->lines};
|
||||
my @alllines = @{$res->[0]->lines};
|
||||
# remove lines taht are near the end.
|
||||
my @lines = grep($_->a->y >= 1578184 || $_->b->y >= 1578184, @alllines);
|
||||
my @angles = map { $lines[$_-1]->ccw($lines[$_]->b) } 1..$#lines;
|
||||
ok !!(none { $_ < 0 } @angles) || (none { $_ > 0 } @angles),
|
||||
'all medial axis segments of a semicircumference have the same orientation';
|
||||
@ -113,16 +115,25 @@ if (0) {
|
||||
[4.3, 4], [4.3, 0], [4,0], [4,4], [0,4], [0,4.5], [4,4.5], [4,10], [4.3,10], [4.3, 4.5],
|
||||
[6, 4.5], [6,10], [6.2,10], [6.2,4.5], [10,4.5], [10,4], [6.2,4], [6.2,0], [6, 0], [6, 4],
|
||||
));
|
||||
$expolygon->contour->make_counter_clockwise();
|
||||
my $res = $expolygon->medial_axis(scale 0.55, scale 0.25);
|
||||
is scalar(@$res), 2, 'medial axis of a (bit too narrow) french cross is two lines';
|
||||
ok unscale($res->[0]->length) >= (9.9) - epsilon, 'medial axis has reasonable length';
|
||||
ok unscale($res->[1]->length) >= (9.9) - epsilon, 'medial axis has reasonable length';
|
||||
my @lines1 = @{$res->[0]->lines};
|
||||
my @angles1 = map { $lines1[$_-1]->ccw($lines1[$_]->b) } 1..$#lines1;
|
||||
my @lines2 = @{$res->[1]->lines};
|
||||
my @angles2 = map { $lines2[$_-1]->ccw($lines2[$_]->b) } 1..$#lines2;
|
||||
my @angles = (@angles1, @angles2);
|
||||
ok !!(none { $_ != 0 } @angles),
|
||||
'medial axis of a (bit too narrow) french cross is two lines has only strait lines';
|
||||
}
|
||||
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_scale(
|
||||
[0.86526705,1.4509841], [0.57696039,1.8637021], [0.4502297,2.5569978], [0.45626199,3.2965596], [1.1218851,3.3049455], [0.96681072,2.8243202], [0.86328971,2.2056997], [0.85367905,1.7790778],
|
||||
));
|
||||
$expolygon->contour->make_counter_clockwise();
|
||||
my $res = $expolygon->medial_axis(scale 1, scale 0.25);
|
||||
is scalar(@$res), 1, 'medial axis of a (bit too narrow) french cross is two lines';
|
||||
ok unscale($res->[0]->length) >= (1.4) - epsilon, 'medial axis has reasonable length';
|
||||
|
@ -237,11 +237,7 @@ ExPolygon::remove_point_too_near(const coord_t tolerance) {
|
||||
|
||||
void
|
||||
ExPolygon::medial_axis(const ExPolygon &bounds, double max_width, double min_width, ThickPolylines* polylines, double height) const {
|
||||
ExPolygon simplifiedBounds = bounds;
|
||||
simplifiedBounds.remove_point_too_near(SCALED_RESOLUTION);
|
||||
ExPolygon simplifiedPolygon = *this;
|
||||
simplifiedPolygon.remove_point_too_near(SCALED_RESOLUTION);
|
||||
Slic3r::MedialAxis ma(simplifiedPolygon, simplifiedBounds, max_width, min_width, height);
|
||||
Slic3r::MedialAxis ma(*this, bounds, max_width, min_width, height);
|
||||
ma.build(polylines);
|
||||
}
|
||||
|
||||
|
@ -279,6 +279,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
||||
params.density = 0.01 * density;
|
||||
params.dont_adjust = false;
|
||||
params.fill_exactly = layerm.region()->config.enforce_full_fill_volume.getBool();
|
||||
params.dont_connect = layerm.region()->config.infill_not_connected.getBool();
|
||||
|
||||
// calculate actual flow from spacing (which might have been adjusted by the infill
|
||||
// pattern generator)
|
||||
|
@ -165,7 +165,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
if (! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON);
|
||||
@ -180,7 +180,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
||||
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||
bool first = true;
|
||||
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
|
||||
if (! first) {
|
||||
if (!params.dont_connect && !first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
const Point &first_point = it_polyline->points.front();
|
||||
|
@ -154,7 +154,7 @@ void FillGyroid::_fill_surface_single(
|
||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
if (! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, (float)SCALED_EPSILON);
|
||||
@ -169,7 +169,7 @@ void FillGyroid::_fill_surface_single(
|
||||
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chained) {
|
||||
if (! first) {
|
||||
if (!params.dont_connect && !first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
const Point &first_point = polyline.points.front();
|
||||
|
@ -1934,7 +1934,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
Point last_pos = this->last_pos();
|
||||
if (m_config.spiral_vase) {
|
||||
loop.split_at(last_pos, false);
|
||||
} else if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear) {
|
||||
} else if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear || seam_position == spHidden) {
|
||||
Polygon polygon = loop.polygon();
|
||||
const coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||
@ -1949,11 +1949,18 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
last_pos_weight = 1.f;
|
||||
}
|
||||
break;
|
||||
case spNearest:
|
||||
last_pos_weight = 5.f;
|
||||
break;
|
||||
case spRear:
|
||||
last_pos = m_layer->object()->bounding_box().center();
|
||||
last_pos.y += coord_t(3. * m_layer->object()->bounding_box().radius());
|
||||
last_pos_weight = 5.f;
|
||||
break;
|
||||
case spHidden:
|
||||
last_pos_weight = 0.1f;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Insert a projection of last_pos into the polygon.
|
||||
@ -1975,7 +1982,16 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
const float penaltySeam = 1.3f;
|
||||
const float penaltyOverhangHalf = 10.f;
|
||||
// Penalty for visible seams.
|
||||
float dist_max = 0.1f * lengths.back();// 5.f * nozzle_dmr
|
||||
if (this->config().seam_travel) {
|
||||
dist_max = 0;
|
||||
for (size_t i = 0; i < polygon.points.size(); ++i) {
|
||||
dist_max = std::max(dist_max, (float)polygon.points[i].distance_to(last_pos_proj));
|
||||
}
|
||||
}
|
||||
//TODO: ignore the angle penalty if the new point is not in an external path (bot/top/ext_peri)
|
||||
for (size_t i = 0; i < polygon.points.size(); ++i) {
|
||||
//std::cout << "check point @" << unscale(polygon.points[i].x) << ":" << unscale(polygon.points[i].y);
|
||||
float ccwAngle = penalties[i];
|
||||
if (was_clockwise)
|
||||
ccwAngle = - ccwAngle;
|
||||
@ -1996,13 +2012,15 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
// Interpolate penalty between maximum and the penalty for a convex vertex.
|
||||
penalty = penaltyConvexVertex + (penaltyFlatSurface - penaltyConvexVertex) * bspline_kernel(ccwAngle * float(PI * 2. / 3.));
|
||||
}
|
||||
if (this->config().seam_travel) {
|
||||
penalty += last_pos_weight * polygon.points[i].distance_to(last_pos_proj) / dist_max;
|
||||
}else{
|
||||
// Give a negative penalty for points close to the last point or the prefered seam location.
|
||||
//float dist_to_last_pos_proj = last_pos_proj.distance_to(polygon.points[i]);
|
||||
float dist_to_last_pos_proj = (i < last_pos_proj_idx) ?
|
||||
std::min(lengths[last_pos_proj_idx] - lengths[i], lengths.back() - lengths[last_pos_proj_idx] + lengths[i]) :
|
||||
std::min(lengths[i] - lengths[last_pos_proj_idx], lengths.back() - lengths[i] + lengths[last_pos_proj_idx]);
|
||||
float dist_max = 0.1f * lengths.back(); // 5.f * nozzle_dmr
|
||||
penalty -= last_pos_weight * bspline_kernel(dist_to_last_pos_proj / dist_max);
|
||||
}
|
||||
penalties[i] = std::max(0.f, penalty);
|
||||
}
|
||||
|
||||
@ -2385,7 +2403,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
}
|
||||
}
|
||||
if (this->on_first_layer())
|
||||
speed = m_config.get_abs_value("first_layer_speed", speed);
|
||||
if (path.role() == erInternalInfill || path.role() == erSolidInfill)
|
||||
speed = std::min(m_config.get_abs_value("first_layer_infill_speed", speed), speed);
|
||||
else
|
||||
speed = std::min(m_config.get_abs_value("first_layer_speed", speed), speed);
|
||||
if (m_volumetric_speed != 0. && speed == 0)
|
||||
speed = m_volumetric_speed / path.mm3_per_mm;
|
||||
if (m_config.max_volumetric_speed.value > 0) {
|
||||
|
@ -643,6 +643,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
||||
"\n\n");
|
||||
|
||||
// Ask our writer about how much material was consumed:
|
||||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
ToolChangeResult result;
|
||||
@ -1070,7 +1071,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
|
||||
|
||||
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
|
||||
|
||||
// Ask our writer about how much material was consumed:
|
||||
// Ask our writer about how much material was consumed.
|
||||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
ToolChangeResult result;
|
||||
@ -1168,7 +1170,6 @@ void WipeTowerPrusaMM::save_on_last_wipe()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
|
||||
// Resulting ToolChangeResults are appended into vector "result"
|
||||
void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
|
||||
@ -1258,6 +1259,4 @@ void WipeTowerPrusaMM::make_wipe_tower_square()
|
||||
lay.extra_spacing = lay.depth / lay.toolchanges_depth();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
@ -359,21 +359,21 @@ add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify)
|
||||
/// return 1 for an angle of 90° and 0 for an angle of 0° or 180°
|
||||
double
|
||||
get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t min_dist_between_point) {
|
||||
double nearestDist = point.distance_to(contour.contour.points.front());
|
||||
Point nearest = contour.contour.points.front();
|
||||
double nearest_dist = point.distance_to(contour.contour.points.front());
|
||||
Point point_nearest = contour.contour.points.front();
|
||||
size_t id_nearest = 0;
|
||||
double nearDist = nearestDist;
|
||||
Point near = nearest;
|
||||
double near_dist = nearest_dist;
|
||||
Point point_near = point_nearest;
|
||||
size_t id_near = 0;
|
||||
for (size_t id_point = 1; id_point < contour.contour.points.size(); ++id_point) {
|
||||
if (nearestDist > point.distance_to(contour.contour.points[id_point])) {
|
||||
//update near
|
||||
if (nearest_dist > point.distance_to(contour.contour.points[id_point])) {
|
||||
//update point_near
|
||||
id_near = id_nearest;
|
||||
near = nearest;
|
||||
nearDist = nearestDist;
|
||||
point_near = point_nearest;
|
||||
near_dist = nearest_dist;
|
||||
//update nearest
|
||||
nearestDist = point.distance_to(contour.contour.points[id_point]);
|
||||
nearest = contour.contour.points[id_point];
|
||||
nearest_dist = point.distance_to(contour.contour.points[id_point]);
|
||||
point_nearest = contour.contour.points[id_point];
|
||||
id_nearest = id_point;
|
||||
}
|
||||
}
|
||||
@ -381,7 +381,7 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi
|
||||
size_t id_before = id_nearest == 0 ? contour.contour.points.size() - 1 : id_nearest - 1;
|
||||
Point point_before = id_nearest == 0 ? contour.contour.points.back() : contour.contour.points[id_nearest - 1];
|
||||
//Search one point far enough to be relevant
|
||||
while (nearest.distance_to(point_before) < min_dist_between_point) {
|
||||
while (point_nearest.distance_to(point_before) < min_dist_between_point) {
|
||||
point_before = id_before == 0 ? contour.contour.points.back() : contour.contour.points[id_before - 1];
|
||||
id_before = id_before == 0 ? contour.contour.points.size() - 1 : id_before - 1;
|
||||
//don't loop
|
||||
@ -394,7 +394,7 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi
|
||||
size_t id_after = id_nearest == contour.contour.points.size() - 1 ? 0 : id_nearest + 1;
|
||||
Point point_after = id_nearest == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_nearest + 1];
|
||||
//Search one point far enough to be relevant
|
||||
while (nearest.distance_to(point_after) < min_dist_between_point) {
|
||||
while (point_nearest.distance_to(point_after) < min_dist_between_point) {
|
||||
point_after = id_after == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_after + 1];
|
||||
id_after = id_after == contour.contour.points.size() - 1 ? 0 : id_after + 1;
|
||||
//don't loop
|
||||
@ -405,15 +405,15 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi
|
||||
}
|
||||
}
|
||||
//compute angle
|
||||
angle = nearest.ccw_angle(point_before, point_after);
|
||||
angle = point_nearest.ccw_angle(point_before, point_after);
|
||||
if (angle >= PI) angle = 2 * PI - angle; // smaller angle
|
||||
//compute the diff from 90°
|
||||
angle = abs(angle - PI / 2);
|
||||
if (near.coincides_with(nearest) && max(nearestDist, nearDist) + SCALED_EPSILON < nearest.distance_to(near)) {
|
||||
if (point_near.coincides_with(point_nearest) && max(nearest_dist, near_dist) + SCALED_EPSILON < point_nearest.distance_to(point_near)) {
|
||||
//not only nearest
|
||||
Point point_before = id_near == 0 ? contour.contour.points.back() : contour.contour.points[id_near - 1];
|
||||
Point point_after = id_near == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_near + 1];
|
||||
double angle2 = min(nearest.ccw_angle(point_before, point_after), nearest.ccw_angle(point_after, point_before));
|
||||
double angle2 = min(point_nearest.ccw_angle(point_before, point_after), point_nearest.ccw_angle(point_after, point_before));
|
||||
angle2 = abs(angle - PI / 2);
|
||||
angle = (angle + angle2) / 2;
|
||||
}
|
||||
@ -613,8 +613,12 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
|
||||
// polyline, after we extend the start point it will be caught by the intersection()
|
||||
// call, so we keep the inner point until we perform the second intersection() as well
|
||||
if (polyline.endpoints.second && !bounds.has_boundary_point(polyline.points.back())) {
|
||||
Line line(*(polyline.points.end() - 2), polyline.points.back());
|
||||
|
||||
size_t first_idx = polyline.points.size() - 2;
|
||||
Line line(*(polyline.points.begin() + first_idx), polyline.points.back());
|
||||
while (line.length() < SCALED_RESOLUTION && first_idx>0) {
|
||||
first_idx--;
|
||||
line.a = *(polyline.points.begin() + first_idx);
|
||||
}
|
||||
// prevent the line from touching on the other side, otherwise intersection() might return that solution
|
||||
if (polyline.points.size() == 2) line.a = line.midpoint();
|
||||
|
||||
@ -623,16 +627,39 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
|
||||
if (this->expolygon.contour.has_boundary_point(polyline.points.back())) {
|
||||
new_back = polyline.points.back();
|
||||
} else {
|
||||
//TODO: verify also for holes.
|
||||
(void)this->expolygon.contour.first_intersection(line, &new_back);
|
||||
// safety check if no intersection
|
||||
if (new_back.x == 0 && new_back.y == 0) return;
|
||||
if (new_back.x == 0 && new_back.y == 0) {
|
||||
if (!this->expolygon.contains(line.b)) {
|
||||
//it's outside!!!
|
||||
std::cout << "Error, a line is formed that start in a polygon, end outside of it can don't cross it!\n";
|
||||
}
|
||||
new_back = line.b;
|
||||
}
|
||||
polyline.points.push_back(new_back);
|
||||
polyline.width.push_back(polyline.width.back());
|
||||
}
|
||||
Point new_bound;
|
||||
//TODO: verify also for holes.
|
||||
(void)bounds.contour.first_intersection(line, &new_bound);
|
||||
// safety check if no intersection
|
||||
if (new_bound.x == 0 && new_bound.y == 0) return;
|
||||
if (new_bound.x == 0 && new_bound.y == 0) {
|
||||
if (line.b.coincides_with_epsilon(polyline.points.back())) {
|
||||
return;
|
||||
}
|
||||
//check if we don't over-shoot inside us
|
||||
bool is_in_anchor = false;
|
||||
for (const ExPolygon& a : anchors) {
|
||||
if (a.contains(line.b)) {
|
||||
is_in_anchor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_in_anchor) std::cout << "not in anchor:\n";
|
||||
if (!is_in_anchor) return;
|
||||
new_bound = line.b;
|
||||
}
|
||||
/* if (new_bound.coincides_with_epsilon(new_back)) {
|
||||
return;
|
||||
}*/
|
||||
@ -765,7 +792,7 @@ MedialAxis::main_fusion(ThickPolylines& pp)
|
||||
coord_t biggest_main_branch_length = 0;
|
||||
for (size_t k = 0; k < pp.size(); ++k) {
|
||||
//std::cout << "try to find main : " << k << " ? " << i << " " << j << " ";
|
||||
if (k == i | k == j) continue;
|
||||
if (k == i || k == j) continue;
|
||||
ThickPolyline& main = pp[k];
|
||||
if (polyline.first_point().coincides_with(main.last_point())) {
|
||||
main.reverse();
|
||||
@ -993,20 +1020,23 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
|
||||
while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) {
|
||||
//try to split if possible
|
||||
if (polyline.width[1] > min_width) {
|
||||
double percent_can_keep = (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]);
|
||||
if (polyline.points.front().distance_to(polyline.points[1]) * percent_can_keep > this->max_width / 2
|
||||
&& polyline.points.front().distance_to(polyline.points[1])* (1 - percent_can_keep) > this->max_width / 2) {
|
||||
double percent_can_keep = 1 - (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]);
|
||||
if (polyline.points.front().distance_to(polyline.points[1]) * percent_can_keep > SCALED_RESOLUTION) {
|
||||
//Can split => move the first point and assign a new weight.
|
||||
//the update of endpoints wil be performed in concatThickPolylines
|
||||
polyline.points.front().x = polyline.points.front().x +
|
||||
(coord_t)((polyline.points[1].x - polyline.points.front().x) * percent_can_keep);
|
||||
(coord_t)((polyline.points[1].x - polyline.points.front().x) * (1 - percent_can_keep));
|
||||
polyline.points.front().y = polyline.points.front().y +
|
||||
(coord_t)((polyline.points[1].y - polyline.points.front().y) * percent_can_keep);
|
||||
(coord_t)((polyline.points[1].y - polyline.points.front().y) * (1 - percent_can_keep));
|
||||
polyline.width.front() = min_width;
|
||||
} else {
|
||||
/// almost 0-length, Remove
|
||||
polyline.points.erase(polyline.points.begin());
|
||||
polyline.width.erase(polyline.width.begin());
|
||||
}
|
||||
changes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
polyline.points.erase(polyline.points.begin());
|
||||
polyline.width.erase(polyline.width.begin());
|
||||
changes = true;
|
||||
@ -1014,25 +1044,29 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
|
||||
while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) {
|
||||
//try to split if possible
|
||||
if (polyline.width[polyline.points.size() - 2] > min_width) {
|
||||
double percent_can_keep = (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back());
|
||||
if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * percent_can_keep > this->max_width / 2
|
||||
&& polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * (1 - percent_can_keep) > this->max_width / 2) {
|
||||
double percent_can_keep = 1 - (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back());
|
||||
if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * percent_can_keep > SCALED_RESOLUTION) {
|
||||
//Can split => move the first point and assign a new weight.
|
||||
//the update of endpoints wil be performed in concatThickPolylines
|
||||
polyline.points.back().x = polyline.points.back().x +
|
||||
(coord_t)((polyline.points[polyline.points.size() - 2].x - polyline.points.back().x) * percent_can_keep);
|
||||
(coord_t)((polyline.points[polyline.points.size() - 2].x - polyline.points.back().x) * (1 - percent_can_keep));
|
||||
polyline.points.back().y = polyline.points.back().y +
|
||||
(coord_t)((polyline.points[polyline.points.size() - 2].y - polyline.points.back().y) * percent_can_keep);
|
||||
(coord_t)((polyline.points[polyline.points.size() - 2].y - polyline.points.back().y) * (1 - percent_can_keep));
|
||||
polyline.width.back() = min_width;
|
||||
} else {
|
||||
/// almost 0-length, Remove
|
||||
polyline.points.erase(polyline.points.end() - 1);
|
||||
polyline.width.erase(polyline.width.end() - 1);
|
||||
}
|
||||
changes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
polyline.points.erase(polyline.points.end() - 1);
|
||||
polyline.width.erase(polyline.width.end() - 1);
|
||||
changes = true;
|
||||
}
|
||||
if (polyline.points.size() < 2) {
|
||||
//remove points and bits that comes from a "main line"
|
||||
if (polyline.points.size() < 2 || (changes && polyline.length() < max_width && polyline.points.size() ==2)) {
|
||||
//remove self if too small
|
||||
pp.erase(pp.begin() + i);
|
||||
--i;
|
||||
@ -1056,7 +1090,6 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
|
||||
Optimisation of the old algorithm : now we select the most "strait line" choice
|
||||
when we merge with an other line at a point with more than two meet.
|
||||
*/
|
||||
bool changes = false;
|
||||
for (size_t i = 0; i < pp.size(); ++i) {
|
||||
ThickPolyline& polyline = pp[i];
|
||||
if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization
|
||||
@ -1066,8 +1099,11 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
|
||||
size_t best_idx = 0;
|
||||
|
||||
// find another polyline starting here
|
||||
for (size_t j = i + 1; j < pp.size(); ++j) {
|
||||
for (size_t j = 0; j < pp.size(); ++j) {
|
||||
if (j == i) continue;
|
||||
ThickPolyline& other = pp[j];
|
||||
if (other.endpoints.first && other.endpoints.second) continue;
|
||||
|
||||
if (polyline.last_point().coincides_with(other.last_point())) {
|
||||
other.reverse();
|
||||
} else if (polyline.first_point().coincides_with(other.last_point())) {
|
||||
@ -1091,16 +1127,14 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
|
||||
}
|
||||
}
|
||||
if (best_candidate != nullptr) {
|
||||
|
||||
polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + 1, best_candidate->points.end());
|
||||
polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + 1, best_candidate->width.end());
|
||||
polyline.endpoints.second = best_candidate->endpoints.second;
|
||||
assert(polyline.width.size() == polyline.points.size());
|
||||
changes = true;
|
||||
if (best_idx < i) i--;
|
||||
pp.erase(pp.begin() + best_idx);
|
||||
}
|
||||
}
|
||||
if (changes) concatThickPolylines(pp);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1276,15 +1310,35 @@ void
|
||||
MedialAxis::build(ThickPolylines* polylines_out)
|
||||
{
|
||||
this->id++;
|
||||
|
||||
//std::cout << layerid << "\n";
|
||||
//{
|
||||
// stringstream stri;
|
||||
// stri << "medial_axis_0_enter_" << id << ".svg";
|
||||
// SVG svg(stri.str());
|
||||
// svg.draw(this->surface);
|
||||
// svg.Close();
|
||||
//}
|
||||
this->expolygon = simplify_polygon_frontier();
|
||||
//{
|
||||
// stringstream stri;
|
||||
// stri << "medial_axis_0.5_simplified_" << id << ".svg";
|
||||
// SVG svg(stri.str());
|
||||
// svg.draw(bounds);
|
||||
// svg.draw(this->expolygon);
|
||||
// svg.Close();
|
||||
//}
|
||||
//safety check
|
||||
if (this->expolygon.area() < this->min_width * this->min_width) this->expolygon = this->surface;
|
||||
if (this->expolygon.area() < this->min_width * this->min_width) return;
|
||||
|
||||
|
||||
//std::cout << "simplify_polygon_frontier\n";
|
||||
// compute the Voronoi diagram and extract medial axis polylines
|
||||
ThickPolylines pp;
|
||||
this->polyline_from_voronoi(this->expolygon.lines(), &pp);
|
||||
|
||||
concatThickPolylines(pp);
|
||||
|
||||
//std::cout << "concatThickPolylines\n";
|
||||
//{
|
||||
// stringstream stri;
|
||||
// stri << "medial_axis_1_voronoi_" << id << ".svg";
|
||||
@ -1313,7 +1367,6 @@ MedialAxis::build(ThickPolylines* polylines_out)
|
||||
// svg.Close();
|
||||
//}
|
||||
|
||||
concatThickPolylines(pp);
|
||||
|
||||
// Aligned fusion: Fusion the bits at the end of lines by "increasing thickness"
|
||||
// For that, we have to find other lines,
|
||||
@ -1334,6 +1387,16 @@ MedialAxis::build(ThickPolylines* polylines_out)
|
||||
//fusion right-angle corners.
|
||||
fusion_corners(pp);
|
||||
|
||||
if (do_not_overextrude) {
|
||||
const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION);
|
||||
for (size_t i = 0; i < pp.size(); ++i) {
|
||||
ThickPolyline& polyline = pp[i];
|
||||
extends_line(polyline, anchors, min_width);
|
||||
polyline.reverse();
|
||||
extends_line(polyline, anchors, min_width);
|
||||
}
|
||||
}
|
||||
|
||||
//reduce extrusion when it's too thin to be printable
|
||||
remove_too_thin_extrusion(pp);
|
||||
//{
|
||||
@ -1359,6 +1422,7 @@ MedialAxis::build(ThickPolylines* polylines_out)
|
||||
|
||||
// Loop through all returned polylines in order to extend their endpoints to the
|
||||
// expolygon boundaries
|
||||
if (!do_not_overextrude) {
|
||||
const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION);
|
||||
for (size_t i = 0; i < pp.size(); ++i) {
|
||||
ThickPolyline& polyline = pp[i];
|
||||
@ -1366,6 +1430,7 @@ MedialAxis::build(ThickPolylines* polylines_out)
|
||||
polyline.reverse();
|
||||
extends_line(polyline, anchors, min_width);
|
||||
}
|
||||
}
|
||||
//{
|
||||
// stringstream stri;
|
||||
// stri << "medial_axis_5_expand_" << id << ".svg";
|
||||
|
@ -22,6 +22,7 @@ namespace Slic3r {
|
||||
const double max_width;
|
||||
const double min_width;
|
||||
const double height;
|
||||
bool do_not_overextrude = true;
|
||||
MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const double _max_width, const double _min_width, const double _height)
|
||||
: surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height) {
|
||||
};
|
||||
|
@ -455,10 +455,14 @@ void Model::adjust_min_z()
|
||||
unsigned int Model::get_auto_extruder_id(unsigned int max_extruders)
|
||||
{
|
||||
unsigned int id = s_auto_extruder_id;
|
||||
|
||||
if (++s_auto_extruder_id > max_extruders)
|
||||
if (id > max_extruders) {
|
||||
// The current counter is invalid, likely due to switching the printer profiles
|
||||
// to a profile with a lower number of extruders.
|
||||
reset_auto_extruder_id();
|
||||
|
||||
id = s_auto_extruder_id;
|
||||
} else if (++s_auto_extruder_id > max_extruders) {
|
||||
reset_auto_extruder_id();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
@ -45,9 +45,9 @@ public:
|
||||
int idx = -1;
|
||||
if (! this->points.empty()) {
|
||||
idx = 0;
|
||||
double dist_min = this->points.front().distance_to(point);
|
||||
double dist_min = this->points.front().distance_to_sq(point);
|
||||
for (int i = 1; i < int(this->points.size()); ++ i) {
|
||||
double d = this->points[i].distance_to(point);
|
||||
double d = this->points[i].distance_to_sq(point);
|
||||
if (d < dist_min) {
|
||||
dist_min = d;
|
||||
idx = i;
|
||||
|
@ -249,16 +249,18 @@ void PerimeterGenerator::process()
|
||||
|
||||
// detect edge case where a curve can be split in multiple small chunks.
|
||||
ExPolygons no_thin_onion = offset_ex(last, -(float)(ext_perimeter_width / 2));
|
||||
if (no_thin_onion.size()>0 && next_onion.size() > 3 * no_thin_onion.size()) {
|
||||
float div = 2;
|
||||
while (no_thin_onion.size() > 0 && next_onion.size() > no_thin_onion.size() && no_thin_onion.size() + next_onion.size() > 3) {
|
||||
div += 0.5;
|
||||
//use a sightly smaller spacing to try to drastically improve the split
|
||||
ExPolygons next_onion_secondTry = offset2_ex(
|
||||
last,
|
||||
-(float)(ext_perimeter_width / 2 + ext_min_spacing / 2.5 - 1),
|
||||
+(float)(ext_min_spacing / 2.5 - 1));
|
||||
if (abs(((int32_t)next_onion.size()) - ((int32_t)no_thin_onion.size())) >
|
||||
2*abs(((int32_t)next_onion_secondTry.size()) - ((int32_t)no_thin_onion.size()))) {
|
||||
-(float)(ext_perimeter_width / 2 + ext_min_spacing / div - 1),
|
||||
+(float)(ext_min_spacing / div - 1));
|
||||
if (next_onion.size() > next_onion_secondTry.size()) {
|
||||
next_onion = next_onion_secondTry;
|
||||
}
|
||||
if (div > 3) break;
|
||||
}
|
||||
|
||||
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
||||
@ -269,27 +271,32 @@ void PerimeterGenerator::process()
|
||||
// medial axis requires non-overlapping geometry
|
||||
ExPolygons thin_zones = diff_ex(last, no_thin_zone, true);
|
||||
//don't use offset2_ex, because we don't want to merge the zones that have been separated.
|
||||
ExPolygons expp = offset_ex(thin_zones, (float)(-min_width / 2));
|
||||
//a very little bit of overlap can be created here with other thin polygons, but it's more useful than worisome.
|
||||
ExPolygons half_thins = offset_ex(thin_zones, (float)(-min_width / 2));
|
||||
//simplify them
|
||||
for (ExPolygon &half_thin : half_thins) {
|
||||
half_thin.remove_point_too_near(SCALED_RESOLUTION);
|
||||
}
|
||||
//we push the bits removed and put them into what we will use as our anchor
|
||||
if (expp.size() > 0) {
|
||||
no_thin_zone = diff_ex(last, offset_ex(expp, (float)(min_width / 2)), true);
|
||||
if (half_thins.size() > 0) {
|
||||
no_thin_zone = diff_ex(last, offset_ex(half_thins, (float)(min_width / 2) - SCALED_EPSILON), true);
|
||||
}
|
||||
// compute a bit of overlap to anchor thin walls inside the print.
|
||||
for (ExPolygon &ex : expp) {
|
||||
for (ExPolygon &half_thin : half_thins) {
|
||||
//growing back the polygon
|
||||
//a very little bit of overlap can be created here with other thin polygons, but it's more useful than worisome.
|
||||
ex.remove_point_too_near(SCALED_RESOLUTION);
|
||||
ExPolygons ex_bigger = offset_ex(ex, (float)(min_width / 2));
|
||||
if (ex_bigger.size() != 1) continue; // impossible error, growing a single polygon can't create multiple or 0.
|
||||
ExPolygons anchor = intersection_ex(offset_ex(ex, (float)(min_width / 2) +
|
||||
ExPolygons thin = offset_ex(half_thin, (float)(min_width / 2));
|
||||
if (thin.size() != 1) continue; // impossible error, growing a single polygon can't create multiple or 0.
|
||||
ExPolygons anchor = intersection_ex(offset_ex(half_thin, (float)(min_width / 2) +
|
||||
(float)(ext_perimeter_width / 2), jtSquare), no_thin_zone, true);
|
||||
ExPolygons bounds = union_ex(ex_bigger, anchor, true);
|
||||
ExPolygons bounds = union_ex(thin, anchor, true);
|
||||
for (ExPolygon &bound : bounds) {
|
||||
if (!intersection_ex(ex_bigger[0], bound).empty()) {
|
||||
if (!intersection_ex(thin[0], bound).empty()) {
|
||||
//be sure it's not too small to extrude reliably
|
||||
if (ex_bigger[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) {
|
||||
thin[0].remove_point_too_near(SCALED_RESOLUTION);
|
||||
if (thin[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) {
|
||||
bound.remove_point_too_near(SCALED_RESOLUTION);
|
||||
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
|
||||
ex_bigger[0].medial_axis(bound, ext_perimeter_width + ext_perimeter_spacing2, min_width,
|
||||
thin[0].medial_axis(bound, ext_perimeter_width + ext_perimeter_spacing2, min_width,
|
||||
&thin_walls, this->layer_height);
|
||||
}
|
||||
break;
|
||||
@ -644,10 +651,10 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(
|
||||
}
|
||||
|
||||
PerimeterIntersectionPoint
|
||||
get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) {
|
||||
PerimeterGenerator::_get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) const {
|
||||
//find best points of intersections
|
||||
PerimeterIntersectionPoint intersect;
|
||||
intersect.distance = 0x7FFFFFFF;
|
||||
intersect.distance = 0x7FFFFFFF; // ! assumption on intersect type & max value
|
||||
intersect.idx_polyline_outter = -1;
|
||||
intersect.idx_children = -1;
|
||||
for (size_t idx_child = 0; idx_child < children.size(); idx_child++) {
|
||||
@ -656,24 +663,55 @@ get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPoly
|
||||
if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue;
|
||||
if (myPolylines.paths[idx_poly].length() < dist_cut + SCALED_RESOLUTION) continue;
|
||||
|
||||
if ((myPolylines.paths[idx_poly].role() == erExternalPerimeter || child.is_external() )
|
||||
&& this->object_config->seam_position.value != SeamPosition::spRandom) {
|
||||
//first, try to find 2 point near enough
|
||||
for (size_t idx_point = 0; idx_point < myPolylines.paths[idx_poly].polyline.points.size(); idx_point++) {
|
||||
const Point &p = myPolylines.paths[idx_poly].polyline.points[idx_point];
|
||||
const Point &nearest_p = *child.polygon.closest_point(p);
|
||||
const coord_t dist = (coord_t)nearest_p.distance_to(p);
|
||||
if (dist + SCALED_EPSILON / 2 < intersect.distance) {
|
||||
const double dist = nearest_p.distance_to(p);
|
||||
//Try to find a point in the far side, aligning them
|
||||
if (dist + dist_cut / 20 < intersect.distance ||
|
||||
(config->perimeter_loop_seam.value == spRear && (intersect.idx_polyline_outter <0 || p.y > intersect.outter_best.y)
|
||||
&& dist <= max_dist && intersect.distance + dist_cut / 20)) {
|
||||
//ok, copy the idx
|
||||
intersect.distance = dist;
|
||||
intersect.distance = (coord_t)nearest_p.distance_to(p);
|
||||
intersect.idx_children = idx_child;
|
||||
intersect.idx_polyline_outter = idx_poly;
|
||||
intersect.outter_best = p;
|
||||
intersect.child_best = nearest_p;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//first, try to find 2 point near enough
|
||||
for (size_t idx_point = 0; idx_point < myPolylines.paths[idx_poly].polyline.points.size(); idx_point++) {
|
||||
const Point &p = myPolylines.paths[idx_poly].polyline.points[idx_point];
|
||||
const Point &nearest_p = *child.polygon.closest_point(p);
|
||||
const double dist = nearest_p.distance_to(p);
|
||||
if (dist + SCALED_EPSILON < intersect.distance ||
|
||||
(config->perimeter_loop_seam.value == spRear && (intersect.idx_polyline_outter<0 || p.y < intersect.outter_best.y)
|
||||
&& dist <= max_dist && intersect.distance + dist_cut / 20)) {
|
||||
//ok, copy the idx
|
||||
intersect.distance = (coord_t)nearest_p.distance_to(p);
|
||||
intersect.idx_children = idx_child;
|
||||
intersect.idx_polyline_outter = idx_poly;
|
||||
intersect.outter_best = p;
|
||||
intersect.child_best = nearest_p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intersect.distance <= max_dist) {
|
||||
return intersect;
|
||||
}
|
||||
|
||||
for (size_t idx_child = 0; idx_child < children.size(); idx_child++) {
|
||||
const PerimeterGeneratorLoop &child = children[idx_child];
|
||||
for (size_t idx_poly = 0; idx_poly < myPolylines.paths.size(); idx_poly++) {
|
||||
if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue;
|
||||
if (myPolylines.paths[idx_poly].length() < dist_cut + SCALED_RESOLUTION) continue;
|
||||
|
||||
//second, try to check from one of my points
|
||||
//don't check the last point, as it's used to go outter, can't use it to go inner.
|
||||
for (size_t idx_point = 1; idx_point < myPolylines.paths[idx_poly].polyline.points.size()-1; idx_point++) {
|
||||
@ -692,9 +730,18 @@ get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPoly
|
||||
intersect.child_best = nearest_p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intersect.distance <= max_dist) {
|
||||
return intersect;
|
||||
}
|
||||
|
||||
for (size_t idx_child = 0; idx_child < children.size(); idx_child++) {
|
||||
const PerimeterGeneratorLoop &child = children[idx_child];
|
||||
for (size_t idx_poly = 0; idx_poly < myPolylines.paths.size(); idx_poly++) {
|
||||
if (myPolylines.paths[idx_poly].extruder_id == (unsigned int)-1) continue;
|
||||
if (myPolylines.paths[idx_poly].length() < dist_cut + SCALED_RESOLUTION) continue;
|
||||
|
||||
//lastly, try to check from one of his points
|
||||
for (size_t idx_point = 0; idx_point < child.polygon.points.size(); idx_point++) {
|
||||
const Point &p = child.polygon.points[idx_point];
|
||||
@ -898,17 +945,14 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop,
|
||||
//TODO change this->external_perimeter_flow.scaled_width() if it's the first one!
|
||||
const coord_t max_width_extrusion = this->perimeter_flow.scaled_width();
|
||||
ExtrusionLoop my_loop = _extrude_and_cut_loop(loop, entry_point);
|
||||
vector<bool> path_is_ccw;
|
||||
|
||||
for (size_t idx_poly = 0; idx_poly < my_loop.paths.size(); idx_poly++) {
|
||||
path_is_ccw.push_back(true);
|
||||
}
|
||||
|
||||
int child_idx = 0;
|
||||
//Polylines myPolylines = { myPolyline };
|
||||
//iterate on each point ot find the best place to go into the child
|
||||
vector<PerimeterGeneratorLoop> childs = children;
|
||||
while (!childs.empty()) {
|
||||
PerimeterIntersectionPoint nearest = get_nearest_point(childs, my_loop, this->perimeter_flow.scaled_width(), this->perimeter_flow.scaled_width()* 0.8);
|
||||
child_idx++;
|
||||
PerimeterIntersectionPoint nearest = this->_get_nearest_point(childs, my_loop, this->perimeter_flow.scaled_width(), this->perimeter_flow.scaled_width()* 1.42);
|
||||
if (nearest.idx_children == (size_t)-1) {
|
||||
//return ExtrusionEntityCollection();
|
||||
break;
|
||||
@ -920,9 +964,7 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop,
|
||||
//PerimeterGeneratorLoops less_childs = childs;
|
||||
//less_childs.erase(less_childs.begin() + nearest.idx_children);
|
||||
//create new node with recursive ask for the inner perimeter & COPY of the points, ready to be cut
|
||||
const bool cut_path_is_ccw = path_is_ccw[nearest.idx_polyline_outter];
|
||||
my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + 1, my_loop.paths[nearest.idx_polyline_outter]);
|
||||
path_is_ccw.insert(path_is_ccw.begin() + nearest.idx_polyline_outter + 1, cut_path_is_ccw);
|
||||
|
||||
ExtrusionPath *outer_start = &my_loop.paths[nearest.idx_polyline_outter];
|
||||
ExtrusionPath *outer_end = &my_loop.paths[nearest.idx_polyline_outter + 1];
|
||||
@ -981,7 +1023,6 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop,
|
||||
const size_t child_paths_size = child_loop.paths.size();
|
||||
if (child_paths_size == 0) continue;
|
||||
my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + 1, child_loop.paths.begin(), child_loop.paths.end());
|
||||
for (size_t i = 0; i < child_paths_size; i++) path_is_ccw.insert(path_is_ccw.begin() + nearest.idx_polyline_outter + 1, !cut_path_is_ccw);
|
||||
|
||||
//add paths into my_loop => need to re-get the refs
|
||||
outer_start = &my_loop.paths[nearest.idx_polyline_outter];
|
||||
@ -1005,8 +1046,8 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop,
|
||||
else
|
||||
inner_start->polyline.clip_start(inner_start->polyline.length()/2);
|
||||
} else {
|
||||
coord_t length_poly_1 = outer_start->polyline.length();
|
||||
coord_t length_poly_2 = outer_end->polyline.length();
|
||||
double length_poly_1 = outer_start->polyline.length();
|
||||
double length_poly_2 = outer_end->polyline.length();
|
||||
coord_t length_trim_1 = outer_start_spacing / 2;
|
||||
coord_t length_trim_2 = outer_end_spacing / 2;
|
||||
if (length_poly_1 < length_trim_1) {
|
||||
@ -1065,9 +1106,9 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop,
|
||||
my_loop.paths[idx].reverse();
|
||||
}
|
||||
outer_start = &my_loop.paths[nearest.idx_polyline_outter];
|
||||
outer_end = &my_loop.paths[nearest.idx_polyline_outter + child_paths_size + 1];
|
||||
inner_start = &my_loop.paths[nearest.idx_polyline_outter + 1];
|
||||
inner_end = &my_loop.paths[nearest.idx_polyline_outter + child_paths_size];
|
||||
outer_end = &my_loop.paths[nearest.idx_polyline_outter + child_paths_size + 1];
|
||||
}
|
||||
|
||||
}
|
||||
@ -1152,7 +1193,8 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop,
|
||||
travel_path_end[0].polyline.append(outer_end->polyline.points.front());
|
||||
}
|
||||
//check if we add path or reuse bits
|
||||
if (outer_start->polyline.points.size() == 1) {
|
||||
//FIXME
|
||||
/*if (outer_start->polyline.points.size() == 1) {
|
||||
outer_start->polyline = travel_path_begin.front().polyline;
|
||||
travel_path_begin.erase(travel_path_begin.begin());
|
||||
outer_start->extruder_id = -1;
|
||||
@ -1160,15 +1202,13 @@ PerimeterGenerator::_traverse_and_join_loops(const PerimeterGeneratorLoop &loop,
|
||||
outer_end->polyline = travel_path_end.back().polyline;
|
||||
travel_path_end.erase(travel_path_end.end() - 1);
|
||||
outer_end->extruder_id = -1;
|
||||
}
|
||||
}*/
|
||||
//add paths into my_loop => after that all ref are wrong!
|
||||
for (int i = travel_path_end.size() - 1; i >= 0; i--) {
|
||||
my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + child_paths_size + 1, travel_path_end[i]);
|
||||
path_is_ccw.insert(path_is_ccw.begin() + nearest.idx_polyline_outter + child_paths_size + 1, cut_path_is_ccw);
|
||||
}
|
||||
for (int i = travel_path_begin.size() - 1; i >= 0; i--) {
|
||||
my_loop.paths.insert(my_loop.paths.begin() + nearest.idx_polyline_outter + 1, travel_path_begin[i]);
|
||||
path_is_ccw.insert(path_is_ccw.begin() + nearest.idx_polyline_outter + 1, cut_path_is_ccw);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,7 @@ private:
|
||||
ThickPolylines &thin_walls) const;
|
||||
ExtrusionLoop _traverse_and_join_loops(const PerimeterGeneratorLoop &loop, const PerimeterGeneratorLoops &childs, const Point entryPoint) const;
|
||||
ExtrusionLoop _extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, const Point entryPoint, const Line &direction = Line(Point(0,0),Point(0,0))) const;
|
||||
PerimeterIntersectionPoint _get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) const;
|
||||
ExtrusionEntityCollection _variable_width
|
||||
(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const;
|
||||
};
|
||||
|
@ -296,6 +296,11 @@ void concatThickPolylines(ThickPolylines& pp) {
|
||||
//concat polyline if only 2 polyline at a point
|
||||
for (size_t i = 0; i < pp.size(); ++i) {
|
||||
ThickPolyline *polyline = &pp[i];
|
||||
if (polyline->first_point().coincides_with(polyline->last_point())) {
|
||||
polyline->endpoints.first = false;
|
||||
polyline->endpoints.second = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t id_candidate_first_point = -1;
|
||||
size_t id_candidate_last_point = -1;
|
||||
@ -305,38 +310,37 @@ void concatThickPolylines(ThickPolylines& pp) {
|
||||
for (size_t j = 0; j < pp.size(); ++j) {
|
||||
if (j == i) continue;
|
||||
ThickPolyline *other = &pp[j];
|
||||
if (other->first_point().coincides_with(other->last_point())) continue;
|
||||
if (polyline->last_point().coincides_with(other->last_point())) {
|
||||
other->reverse();
|
||||
id_candidate_last_point = j;
|
||||
nbCandidate_last_point++;
|
||||
} else if (polyline->first_point().coincides_with(other->last_point())) {
|
||||
id_candidate_first_point = j;
|
||||
nbCandidate_first_point++;
|
||||
} else if (polyline->first_point().coincides_with(other->first_point())) {
|
||||
id_candidate_first_point = j;
|
||||
nbCandidate_first_point++;
|
||||
other->reverse();
|
||||
} else if (polyline->last_point().coincides_with(other->first_point())) {
|
||||
}
|
||||
if (polyline->last_point().coincides_with(other->first_point())) {
|
||||
id_candidate_last_point = j;
|
||||
nbCandidate_last_point++;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (polyline->first_point().coincides_with(other->last_point())) {
|
||||
id_candidate_first_point = j;
|
||||
nbCandidate_first_point++;
|
||||
}
|
||||
if (polyline->first_point().coincides_with(other->first_point())) {
|
||||
id_candidate_first_point = j;
|
||||
nbCandidate_first_point++;
|
||||
}
|
||||
}
|
||||
if (id_candidate_last_point == id_candidate_first_point && nbCandidate_first_point == 1 && nbCandidate_last_point == 1) {
|
||||
if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse();
|
||||
// it's a trap! it's a loop!
|
||||
if (pp[id_candidate_first_point].points.size() > 2) {
|
||||
polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin() + 1, pp[id_candidate_first_point].points.end() - 1);
|
||||
polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin() + 1, pp[id_candidate_first_point].width.end() - 1);
|
||||
}
|
||||
polyline->points.insert(polyline->points.end(), pp[id_candidate_first_point].points.begin() + 1, pp[id_candidate_first_point].points.end());
|
||||
polyline->width.insert(polyline->width.end(), pp[id_candidate_first_point].width.begin() + 1, pp[id_candidate_first_point].width.end());
|
||||
pp.erase(pp.begin() + id_candidate_first_point);
|
||||
changes = true;
|
||||
polyline->endpoints.first = false;
|
||||
polyline->endpoints.second = false;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (nbCandidate_first_point == 1) {
|
||||
if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse();
|
||||
//concat at front
|
||||
polyline->width[0] = std::max(polyline->width.front(), pp[id_candidate_first_point].width.back());
|
||||
polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin(), pp[id_candidate_first_point].points.end() - 1);
|
||||
@ -351,11 +355,12 @@ void concatThickPolylines(ThickPolylines& pp) {
|
||||
if (id_candidate_last_point > id_candidate_first_point) {
|
||||
id_candidate_last_point--;
|
||||
}
|
||||
} else if (nbCandidate_first_point == 0 && !polyline->endpoints.first && !polyline->first_point().coincides_with(polyline->last_point())) {
|
||||
} else if (nbCandidate_first_point == 0) {
|
||||
//update endpoint
|
||||
polyline->endpoints.first = true;
|
||||
}
|
||||
if (nbCandidate_last_point == 1) {
|
||||
if (polyline->last_point().coincides_with(pp[id_candidate_last_point].last_point())) pp[id_candidate_last_point].reverse();
|
||||
//concat at back
|
||||
polyline->width[polyline->width.size() - 1] = std::max(polyline->width.back(), pp[id_candidate_last_point].width.front());
|
||||
polyline->points.insert(polyline->points.end(), pp[id_candidate_last_point].points.begin() + 1, pp[id_candidate_last_point].points.end());
|
||||
@ -367,7 +372,7 @@ void concatThickPolylines(ThickPolylines& pp) {
|
||||
i--;
|
||||
polyline = &pp[i];
|
||||
}
|
||||
} else if (nbCandidate_last_point == 0 && !polyline->endpoints.second && !polyline->first_point().coincides_with(polyline->last_point())) {
|
||||
} else if (nbCandidate_last_point == 0) {
|
||||
//update endpoint
|
||||
polyline->endpoints.second = true;
|
||||
}
|
||||
@ -380,5 +385,6 @@ void concatThickPolylines(ThickPolylines& pp) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -126,6 +126,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
||||
"first_layer_acceleration",
|
||||
"first_layer_bed_temperature",
|
||||
"first_layer_speed",
|
||||
"first_layer_infill_speed",
|
||||
"gcode_comments",
|
||||
"gcode_flavor",
|
||||
"infill_acceleration",
|
||||
|
@ -34,6 +34,14 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->cli = "avoid-crossing-perimeters!";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("remove_small_gaps", coBool);
|
||||
def->label = L("Remove small gaps");
|
||||
def->tooltip = L("Remove the small gaps in the 3D model when slicing. Disable it if you "
|
||||
"are very confident on your model, or you want to print an item with a geometry "
|
||||
"designed for vase mode.");
|
||||
def->cli = "remove-small-gaps!";
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("bed_shape", coPoints);
|
||||
def->label = L("Bed shape");
|
||||
def->default_value = new ConfigOptionPoints { Pointf(0,0), Pointf(200,0), Pointf(200,200), Pointf(0,200) };
|
||||
@ -301,7 +309,7 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("top_fill_pattern", coEnum);
|
||||
def->label = L(" Top");
|
||||
def->label = L("Top Pattern");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("Fill pattern for top infill. This only affects the top external visible layer, and not its adjacent solid shells.");
|
||||
def->cli = "top-fill-pattern|solid-fill-pattern=s";
|
||||
@ -325,7 +333,7 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
|
||||
|
||||
def = this->add("bottom_fill_pattern", coEnum);
|
||||
def->label = L("Bottom");
|
||||
def->label = L("Bottom Pattern");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom external visible layer, and not its adjacent solid shells.");
|
||||
def->cli = "bottom-fill-pattern|solid-fill-pattern=s";
|
||||
@ -401,13 +409,25 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("perimeter_loop", coBool);
|
||||
def->label = L("Looping perimeters");
|
||||
def->label = L(" ");
|
||||
def->category = L("Layers and Perimeters");
|
||||
def->tooltip = L("Join the perimeters to create only one continuous extrusion without any z-hop."
|
||||
" Long inside travel (from external to holes) are not extruded to give some place to the infill.");
|
||||
def->cli = "loop-perimeter!";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("perimeter_loop_seam", coEnum);
|
||||
def->label = L("Seam position");
|
||||
def->category = L("Layers and Perimeters");
|
||||
def->tooltip = L("Position of perimeters starting points.");
|
||||
def->cli = "perimeter-seam-position=s";
|
||||
def->enum_keys_map = &ConfigOptionEnum<SeamPosition>::get_enum_values();
|
||||
def->enum_values.push_back("nearest");
|
||||
def->enum_values.push_back("rear");
|
||||
def->enum_labels.push_back(L("Nearest"));
|
||||
def->enum_labels.push_back(L("Rear"));
|
||||
def->default_value = new ConfigOptionEnum<SeamPosition>(spRear);
|
||||
|
||||
def = this->add("extra_perimeters", coBool);
|
||||
def->label = L("Extra perimeters if needed");
|
||||
def->category = L("Layers and Perimeters");
|
||||
@ -787,8 +807,8 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->default_value = new ConfigOptionPercent(18);
|
||||
|
||||
def = this->add("fill_pattern", coEnum);
|
||||
def->label = L("Inside");
|
||||
def->category = L("Sparse fill pattern");
|
||||
def->label = L("Pattern");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("Fill pattern for general low-density infill.");
|
||||
def->cli = "fill-pattern=s";
|
||||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
@ -863,15 +883,29 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->default_value = new ConfigOptionFloatOrPercent(0.35, false);
|
||||
|
||||
def = this->add("first_layer_speed", coFloatOrPercent);
|
||||
def->label = L("First layer speed");
|
||||
def->label = L("default");
|
||||
def->tooltip = L("If expressed as absolute value in mm/s, this speed will be applied to all the print moves "
|
||||
"of the first layer, regardless of their type. If expressed as a percentage "
|
||||
"(for example: 40%) it will scale the default speeds.");
|
||||
"but infill of the first layer, it can be overwrite by the 'default' (default depends of the type of the path) "
|
||||
"speed if it's lower than that. If expressed as a percentage "
|
||||
"(for example: 40%) it will scale the 'default' speeds . "
|
||||
"If expressed as absolute value, it can be overwrite by the 'default' speed if it's lower than that.");
|
||||
def->sidetext = L("mm/s or %");
|
||||
def->cli = "first-layer-speed=s";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(30, false);
|
||||
|
||||
def = this->add("first_layer_infill_speed", coFloatOrPercent);
|
||||
def->label = L("infill");
|
||||
def->tooltip = L("If expressed as absolute value in mm/s, this speed will be applied to infill moves "
|
||||
"of the first layer, it can be overwrite by the 'default' (solid infill or infill if not bottom) "
|
||||
"speed if it's lower than that. If expressed as a percentage "
|
||||
"(for example: 40%) it will scale the 'default' speed. "
|
||||
"If expressed as absolute value, it can be overwrite by the 'default' speed if it's lower than that.");
|
||||
def->sidetext = L("mm/s or %");
|
||||
def->cli = "first-layer-infill-speed=s";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(30, false);
|
||||
|
||||
def = this->add("first_layer_temperature", coInts);
|
||||
def->label = L("First layer");
|
||||
def->tooltip = L("Extruder temperature for first layer. If you want to control temperature manually "
|
||||
@ -964,6 +998,13 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->cli = "infill-dense!";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("infill_not_connected", coBool);
|
||||
def->label = ("Do not connect infill lines to each other.");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("If checked, the infill algorithm will try to not connect the lines near the infill. Can be useful for art or with high infill/perimeter overlap.");
|
||||
def->cli = "infill-not-connected!";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("infill_dense_algo", coEnum);
|
||||
def->label = L("Algorithm");
|
||||
def->tooltip = L("Choose the way the dense layer is lay out."
|
||||
@ -1644,12 +1685,21 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->enum_values.push_back("nearest");
|
||||
def->enum_values.push_back("aligned");
|
||||
def->enum_values.push_back("rear");
|
||||
def->enum_values.push_back("hidden");
|
||||
def->enum_labels.push_back(L("Random"));
|
||||
def->enum_labels.push_back(L("Nearest"));
|
||||
def->enum_labels.push_back(L("Aligned"));
|
||||
def->enum_labels.push_back(L("Rear"));
|
||||
def->enum_labels.push_back(L("Hidden"));
|
||||
def->default_value = new ConfigOptionEnum<SeamPosition>(spAligned);
|
||||
|
||||
def = this->add("seam_travel", coBool);
|
||||
def->label = L("Travel move reduced");
|
||||
def->category = L("Layers and Perimeters");
|
||||
def->tooltip = L("Add a big cost to travel paths when possible (when going into a loop), so it will prefer a less optimal seam posistion if it's nearer.");
|
||||
def->cli = "seam-travel!";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
#if 0
|
||||
def = this->add("seam_preferred_direction", coFloat);
|
||||
// def->gui_type = "slider";
|
||||
|
@ -42,7 +42,7 @@ enum SupportMaterialPattern {
|
||||
};
|
||||
|
||||
enum SeamPosition {
|
||||
spRandom, spNearest, spAligned, spRear
|
||||
spRandom, spNearest, spAligned, spRear, spHidden
|
||||
};
|
||||
|
||||
enum FilamentType {
|
||||
@ -120,6 +120,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum
|
||||
keys_map["nearest"] = spNearest;
|
||||
keys_map["aligned"] = spAligned;
|
||||
keys_map["rear"] = spRear;
|
||||
keys_map["hidden"] = spHidden;
|
||||
}
|
||||
return keys_map;
|
||||
}
|
||||
@ -340,6 +341,7 @@ class PrintObjectConfig : public StaticPrintConfig
|
||||
STATIC_PRINT_CONFIG_CACHE(PrintObjectConfig)
|
||||
public:
|
||||
ConfigOptionBool clip_multipart_objects;
|
||||
ConfigOptionBool remove_small_gaps;
|
||||
ConfigOptionBool dont_support_bridges;
|
||||
ConfigOptionFloat elefant_foot_compensation;
|
||||
ConfigOptionFloatOrPercent extrusion_width;
|
||||
@ -351,6 +353,7 @@ public:
|
||||
ConfigOptionBool exact_last_layer_height;
|
||||
ConfigOptionInt raft_layers;
|
||||
ConfigOptionEnum<SeamPosition> seam_position;
|
||||
ConfigOptionBool seam_travel;
|
||||
// ConfigOptionFloat seam_preferred_direction;
|
||||
// ConfigOptionFloat seam_preferred_direction_jitter;
|
||||
ConfigOptionBool support_material;
|
||||
@ -387,6 +390,7 @@ protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
OPT_PTR(clip_multipart_objects);
|
||||
OPT_PTR(remove_small_gaps);
|
||||
OPT_PTR(dont_support_bridges);
|
||||
OPT_PTR(elefant_foot_compensation);
|
||||
OPT_PTR(extrusion_width);
|
||||
@ -397,6 +401,7 @@ protected:
|
||||
OPT_PTR(exact_last_layer_height);
|
||||
OPT_PTR(raft_layers);
|
||||
OPT_PTR(seam_position);
|
||||
OPT_PTR(seam_travel);
|
||||
// OPT_PTR(seam_preferred_direction);
|
||||
// OPT_PTR(seam_preferred_direction_jitter);
|
||||
OPT_PTR(support_material);
|
||||
@ -446,6 +451,7 @@ public:
|
||||
ConfigOptionFloatOrPercent external_perimeter_speed;
|
||||
ConfigOptionBool external_perimeters_first;
|
||||
ConfigOptionBool perimeter_loop;
|
||||
ConfigOptionEnum<SeamPosition> perimeter_loop_seam;
|
||||
ConfigOptionBool extra_perimeters;
|
||||
ConfigOptionBool only_one_perimeter_top;
|
||||
ConfigOptionFloat fill_angle;
|
||||
@ -458,6 +464,7 @@ public:
|
||||
ConfigOptionInt infill_every_layers;
|
||||
ConfigOptionFloatOrPercent infill_overlap;
|
||||
ConfigOptionFloat infill_speed;
|
||||
ConfigOptionBool infill_not_connected;
|
||||
ConfigOptionBool infill_dense;
|
||||
ConfigOptionEnum<DenseInfillAlgo> infill_dense_algo;
|
||||
ConfigOptionBool infill_first;
|
||||
@ -502,6 +509,7 @@ protected:
|
||||
OPT_PTR(external_perimeter_speed);
|
||||
OPT_PTR(external_perimeters_first);
|
||||
OPT_PTR(perimeter_loop);
|
||||
OPT_PTR(perimeter_loop_seam);
|
||||
OPT_PTR(extra_perimeters);
|
||||
OPT_PTR(only_one_perimeter_top);
|
||||
OPT_PTR(fill_angle);
|
||||
@ -515,6 +523,7 @@ protected:
|
||||
OPT_PTR(infill_overlap);
|
||||
OPT_PTR(infill_speed);
|
||||
OPT_PTR(infill_dense);
|
||||
OPT_PTR(infill_not_connected);
|
||||
OPT_PTR(infill_dense_algo);
|
||||
OPT_PTR(infill_first);
|
||||
OPT_PTR(overhangs);
|
||||
@ -755,6 +764,7 @@ public:
|
||||
ConfigOptionInts first_layer_bed_temperature;
|
||||
ConfigOptionFloatOrPercent first_layer_extrusion_width;
|
||||
ConfigOptionFloatOrPercent first_layer_speed;
|
||||
ConfigOptionFloatOrPercent first_layer_infill_speed;
|
||||
ConfigOptionInts first_layer_temperature;
|
||||
ConfigOptionFloat infill_acceleration;
|
||||
ConfigOptionInts max_fan_speed;
|
||||
@ -826,6 +836,7 @@ protected:
|
||||
OPT_PTR(first_layer_bed_temperature);
|
||||
OPT_PTR(first_layer_extrusion_width);
|
||||
OPT_PTR(first_layer_speed);
|
||||
OPT_PTR(first_layer_infill_speed);
|
||||
OPT_PTR(first_layer_temperature);
|
||||
OPT_PTR(infill_acceleration);
|
||||
OPT_PTR(max_fan_speed);
|
||||
|
@ -158,6 +158,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
||||
|| opt_key == "thin_walls"
|
||||
|| opt_key == "external_perimeters_first"
|
||||
|| opt_key == "perimeter_loop"
|
||||
|| opt_key == "perimeter_loop_seam"
|
||||
|| opt_key == "no_perimeter_unsupported"
|
||||
|| opt_key == "min_perimeter_unsupported"
|
||||
|| opt_key == "noperi_bridge_only") {
|
||||
@ -207,6 +208,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
||||
|| opt_key == "infill_every_layers"
|
||||
|| opt_key == "solid_infill_every_layers"
|
||||
|| opt_key == "infill_dense"
|
||||
|| opt_key == "infill_not_connected"
|
||||
|| opt_key == "infill_dense_algo"
|
||||
|| opt_key == "bottom_solid_layers"
|
||||
|| opt_key == "top_solid_layers"
|
||||
@ -243,6 +245,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
||||
steps.emplace_back(posInfill);
|
||||
} else if (
|
||||
opt_key == "seam_position"
|
||||
|| opt_key == "seam_travel"
|
||||
|| opt_key == "seam_preferred_direction"
|
||||
|| opt_key == "seam_preferred_direction_jitter"
|
||||
|| opt_key == "support_material_speed"
|
||||
@ -1856,6 +1859,7 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
|
||||
mesh.translate(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
|
||||
// perform actual slicing
|
||||
TriangleMeshSlicer mslicer(&mesh);
|
||||
mslicer.safety_offset = (this->config.remove_small_gaps ? scale_(0.0499) : SCALED_EPSILON);
|
||||
mslicer.slice(z, &layers);
|
||||
}
|
||||
}
|
||||
|
@ -1792,7 +1792,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
|
||||
//}
|
||||
|
||||
// perform a safety offset to merge very close facets (TODO: find test case for this)
|
||||
double safety_offset = scale_(0.0499);
|
||||
//double safety_offset = scale_(0.0499); // now a config value
|
||||
//FIXME see https://github.com/prusa3d/Slic3r/issues/520
|
||||
// double safety_offset = scale_(0.0001);
|
||||
|
||||
|
@ -168,6 +168,7 @@ public:
|
||||
const float min_z, const float max_z, IntersectionLine *line_out) const;
|
||||
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
|
||||
|
||||
double safety_offset = scale_(0.0499);
|
||||
private:
|
||||
const TriangleMesh *mesh;
|
||||
// Map from a facet to an edge index.
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#define SLIC3R_FORK_NAME "Slic3r++"
|
||||
#define SLIC3R_VERSION "1.41.1"
|
||||
#define SLIC3R_VERSION "1.41.2
|
||||
#define SLIC3R_BUILD "UNKNOWN"
|
||||
|
||||
typedef int32_t coord_t;
|
||||
|
@ -527,6 +527,27 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
||||
}
|
||||
else
|
||||
val = 0;
|
||||
} else if (m_opt_id.compare("perimeter_loop_seam") == 0) {
|
||||
if (!m_opt.enum_values.empty()) {
|
||||
std::string key;
|
||||
t_config_enum_values map_names = ConfigOptionEnum<SeamPosition>::get_enum_values();
|
||||
for (auto it : map_names) {
|
||||
if (val == it.second) {
|
||||
key = it.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
for (auto el : m_opt.enum_values) {
|
||||
if (el.compare(key) == 0)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
|
||||
val = idx == m_opt.enum_values.size() ? 0 : idx;
|
||||
} else
|
||||
val = 3;
|
||||
}
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(val);
|
||||
break;
|
||||
@ -591,7 +612,15 @@ boost::any& Choice::get_value()
|
||||
m_value = static_cast<SupportMaterialPattern>(ret_enum);
|
||||
else if (m_opt_id.compare("seam_position") == 0)
|
||||
m_value = static_cast<SeamPosition>(ret_enum);
|
||||
else if (m_opt_id.compare("host_type") == 0)
|
||||
else if (m_opt_id.compare("perimeter_loop_seam") == 0) {
|
||||
if (!m_opt.enum_values.empty()) {
|
||||
std::string key = m_opt.enum_values[ret_enum];
|
||||
t_config_enum_values map_names = ConfigOptionEnum<SeamPosition>::get_enum_values();
|
||||
int value = map_names.at(key);
|
||||
m_value = static_cast<SeamPosition>(value);
|
||||
} else
|
||||
m_value = static_cast<SeamPosition>(3);
|
||||
} else if (m_opt_id.compare("host_type") == 0)
|
||||
m_value = static_cast<PrintHostType>(ret_enum);
|
||||
else if (m_opt_id.compare("infill_dense_algo") == 0)
|
||||
m_value = static_cast<DenseInfillAlgo>(ret_enum);
|
||||
|
@ -3436,8 +3436,14 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
|
||||
{
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
_resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
|
||||
if (m_canvas != nullptr)
|
||||
m_canvas->Refresh();
|
||||
|
||||
// Because of performance problems on macOS, where PaintEvents are not delivered
|
||||
// frequently enough, we call render() here directly when we can.
|
||||
// We can't do that when m_force_zoom_to_bed_enabled == true, because then render()
|
||||
// ends up calling back here via _force_zoom_to_bed(), causing a stack overflow.
|
||||
if (m_canvas != nullptr) {
|
||||
m_force_zoom_to_bed_enabled ? m_canvas->Refresh() : render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -603,7 +603,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<GCodeFlavor>(boost::any_cast<GCodeFlavor>(value)));
|
||||
else if (opt_key.compare("support_material_pattern") == 0)
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value)));
|
||||
else if (opt_key.compare("seam_position") == 0)
|
||||
else if (opt_key.compare("seam_position") == 0 || opt_key.compare("perimeter_loop_seam") == 0)
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value)));
|
||||
else if (opt_key.compare("host_type") == 0)
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<PrintHostType>(boost::any_cast<PrintHostType>(value)));
|
||||
|
@ -459,7 +459,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
||||
else if (opt_key.compare("support_material_pattern") == 0){
|
||||
ret = static_cast<int>(config.option<ConfigOptionEnum<SupportMaterialPattern>>(opt_key)->value);
|
||||
}
|
||||
else if (opt_key.compare("seam_position") == 0){
|
||||
else if (opt_key.compare("seam_position") == 0 || opt_key.compare("perimeter_loop_seam") == 0) {
|
||||
ret = static_cast<int>(config.option<ConfigOptionEnum<SeamPosition>>(opt_key)->value);
|
||||
}
|
||||
else if (opt_key.compare("host_type") == 0){
|
||||
|
@ -307,7 +307,14 @@ const std::vector<std::string>& Preset::print_options()
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging",
|
||||
"only_one_perimeter_top", "single_extruder_multi_material_priming", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
"infill_dense", "infill_dense_algo", "no_perimeter_unsupported", "min_perimeter_unsupported", "noperi_bridge_only",
|
||||
"support_material_solid_first_layer", "exact_last_layer_height", "perimeter_loop"
|
||||
"support_material_solid_first_layer"
|
||||
, "exact_last_layer_height"
|
||||
, "perimeter_loop",
|
||||
, "perimeter_loop_seam"
|
||||
, "seam_travel"
|
||||
, "remove_small_gaps"
|
||||
, "infill_not_connected"
|
||||
,"first_layer_infill_speed"
|
||||
};
|
||||
return s_opts;
|
||||
}
|
||||
|
@ -106,6 +106,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||
const auto &support_material_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("support_material_extrusion_width");
|
||||
const auto &top_infill_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("top_infill_extrusion_width");
|
||||
const auto &first_layer_speed = *print_config.option<ConfigOptionFloatOrPercent>("first_layer_speed");
|
||||
const auto &first_layer_infill_speed = *print_config.option<ConfigOptionFloatOrPercent>("first_layer_infill_speed");
|
||||
|
||||
// Index of an extruder assigned to a feature. If set to 0, an active extruder will be used for a multi-material print.
|
||||
// If different from idx_extruder, it will not be taken into account for this hint.
|
||||
@ -142,6 +143,12 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||
speed_normal = first_layer_speed.get_abs_value(speed_normal);
|
||||
return (speed_normal > 0.) ? speed_normal : speed_max;
|
||||
};
|
||||
auto limit_infill_by_first_layer_speed = [&first_layer_infill_speed, first_layer](double speed_normal, double speed_max) {
|
||||
if (first_layer && first_layer_infill_speed.value > 0)
|
||||
// Apply the first layer limit.
|
||||
speed_normal = first_layer_infill_speed.get_abs_value(speed_normal);
|
||||
return (speed_normal > 0.) ? speed_normal : speed_max;
|
||||
};
|
||||
if (perimeter_extruder_active) {
|
||||
double external_perimeter_rate = Flow::new_from_config_width(frExternalPerimeter,
|
||||
first_positive(first_layer_extrusion_width_ptr, external_perimeter_extrusion_width, extrusion_width),
|
||||
@ -165,7 +172,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||
if (! bridging && infill_extruder_active) {
|
||||
double infill_rate = Flow::new_from_config_width(frInfill,
|
||||
first_positive(first_layer_extrusion_width_ptr, infill_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(infill_speed, max_print_speed);
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_infill_by_first_layer_speed(infill_speed, max_print_speed);
|
||||
if (max_flow < infill_rate) {
|
||||
max_flow = infill_rate;
|
||||
max_flow_extrusion_type = _CHB(L("infill"));
|
||||
@ -175,7 +182,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||
double solid_infill_rate = Flow::new_from_config_width(frInfill,
|
||||
first_positive(first_layer_extrusion_width_ptr, solid_infill_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, 0).mm3_per_mm() *
|
||||
(bridging ? bridge_speed : limit_by_first_layer_speed(solid_infill_speed, max_print_speed));
|
||||
(bridging ? bridge_speed : limit_infill_by_first_layer_speed(solid_infill_speed, max_print_speed));
|
||||
if (max_flow < solid_infill_rate) {
|
||||
max_flow = solid_infill_rate;
|
||||
max_flow_extrusion_type = _CHB(L("solid infill"));
|
||||
@ -183,7 +190,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||
if (! bridging) {
|
||||
double top_solid_infill_rate = Flow::new_from_config_width(frInfill,
|
||||
first_positive(first_layer_extrusion_width_ptr, top_infill_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(top_solid_infill_speed, max_print_speed);
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_infill_by_first_layer_speed(top_solid_infill_speed, max_print_speed);
|
||||
if (max_flow < top_solid_infill_rate) {
|
||||
max_flow = top_solid_infill_rate;
|
||||
max_flow_extrusion_type = _CHB(L("top solid infill"));
|
||||
|
@ -821,19 +821,28 @@ void TabPrint::build()
|
||||
optgroup->append_line(line);
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Advanced")));
|
||||
optgroup->append_single_option_line("seam_position");
|
||||
optgroup->append_single_option_line("remove_small_gaps");
|
||||
line = { _(L("Avoid unsupported perimeters")), "" };
|
||||
line.append_option(optgroup->get_option("seam_position"));
|
||||
line.append_option(optgroup->get_option("seam_travel"));
|
||||
optgroup->append_line(line);
|
||||
optgroup->append_single_option_line("external_perimeters_first");
|
||||
optgroup->append_single_option_line("perimeter_loop");
|
||||
line = { _(L("Looping perimeter")), "" };
|
||||
line.append_option(optgroup->get_option("perimeter_loop"));
|
||||
line.append_option(optgroup->get_option("perimeter_loop_seam"));
|
||||
optgroup->append_line(line);
|
||||
|
||||
page = add_options_page(_(L("Infill")), "infill.png");
|
||||
optgroup = page->new_optgroup(_(L("Infill")));
|
||||
optgroup->append_single_option_line("fill_density");
|
||||
optgroup->append_single_option_line("fill_pattern");
|
||||
line = { _(L("Fill external pattern")), "" };
|
||||
line = { _(L("Fill internal")), "" };
|
||||
line.append_option(optgroup->get_option("fill_pattern"));
|
||||
line.append_option(optgroup->get_option("infill_not_connected"));
|
||||
optgroup->append_line(line);
|
||||
line = { _(L("Fill external")), "" };
|
||||
line.append_option(optgroup->get_option("top_fill_pattern"));
|
||||
line.append_option(optgroup->get_option("bottom_fill_pattern"));
|
||||
optgroup->append_line(line);
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Reducing printing time")));
|
||||
optgroup->append_single_option_line("infill_every_layers");
|
||||
optgroup->append_single_option_line("infill_only_where_needed");
|
||||
@ -916,7 +925,10 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("travel_speed");
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Modifiers")));
|
||||
optgroup->append_single_option_line("first_layer_speed");
|
||||
line = { _(L("First layer speed")), "" };
|
||||
line.append_option(optgroup->get_option("first_layer_speed"));
|
||||
line.append_option(optgroup->get_option("first_layer_infill_speed"));
|
||||
optgroup->append_line(line);
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Acceleration control (advanced)")));
|
||||
optgroup->append_single_option_line("perimeter_acceleration");
|
||||
@ -1196,9 +1208,11 @@ void TabPrint::update()
|
||||
bool have_perimeters = m_config->opt_int("perimeters") > 0;
|
||||
for (auto el : { "extra_perimeters", "only_one_perimeter_top", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
|
||||
"seam_position", "external_perimeters_first", "external_perimeter_extrusion_width",
|
||||
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "perimeter_loop" })
|
||||
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "perimeter_loop", "perimeter_loop_seam" })
|
||||
get_field(el)->toggle(have_perimeters);
|
||||
|
||||
get_field("perimeter_loop_seam")->toggle(m_config->opt_bool("perimeter_loop"));
|
||||
|
||||
bool have_no_perimeter_unsupported = have_perimeters && m_config->opt_bool("no_perimeter_unsupported");
|
||||
for (auto el : { "min_perimeter_unsupported", "noperi_bridge_only" })
|
||||
get_field(el)->toggle(have_no_perimeter_unsupported);
|
||||
|
@ -330,7 +330,7 @@ public:
|
||||
size_t m_sys_extruders_count;
|
||||
|
||||
TabPrinter() {}
|
||||
TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {}
|
||||
TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Hardware Settings")), "printer", no_controller) {}
|
||||
~TabPrinter(){}
|
||||
|
||||
void build() override;
|
||||
|
Loading…
x
Reference in New Issue
Block a user