mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-07 23:17:35 -06:00
Port "Extend sparse infill" from Prusa (#2134)
* Remove BambuLab's implementation of solid infill bridging enhancement , will use Prusa's instead * Port "Extend sparse infill" from Prusa * Improve anchoring by shifting the lines half-spacing * Add missing fill patterns * Improve anchoring by keeping fine details * Make sure the opposite directions do not cancel each other --------- Co-authored-by: Pavel Mikus <pavel.mikus.mail@seznam.cz>
This commit is contained in:
parent
0e785c05e5
commit
ee0e6a7227
16 changed files with 1017 additions and 506 deletions
|
@ -178,8 +178,6 @@ set(lisbslic3r_sources
|
||||||
Geometry/VoronoiUtilsCgal.hpp
|
Geometry/VoronoiUtilsCgal.hpp
|
||||||
Geometry/VoronoiVisualUtils.hpp
|
Geometry/VoronoiVisualUtils.hpp
|
||||||
Int128.hpp
|
Int128.hpp
|
||||||
InternalBridgeDetector.cpp
|
|
||||||
InternalBridgeDetector.hpp
|
|
||||||
KDTreeIndirect.hpp
|
KDTreeIndirect.hpp
|
||||||
Layer.cpp
|
Layer.cpp
|
||||||
Layer.hpp
|
Layer.hpp
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
///|/ Copyright (c) Prusa Research 2016 - 2023 Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Lukáš Hejl @hejllukas
|
||||||
|
///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill
|
||||||
|
///|/ Copyright (c) 2016 Sakari Kapanen @Flannelhead
|
||||||
|
///|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel
|
||||||
|
///|/ Copyright (c) 2013 Mark Hindess
|
||||||
|
///|/ Copyright (c) 2011 Michael Moon
|
||||||
|
///|/
|
||||||
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
|
///|/
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -46,8 +55,6 @@ struct SurfaceFillParams
|
||||||
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
|
||||||
float anchor_length = 1000.f;
|
float anchor_length = 1000.f;
|
||||||
float anchor_length_max = 1000.f;
|
float anchor_length_max = 1000.f;
|
||||||
//BBS
|
|
||||||
bool with_loop = false;
|
|
||||||
|
|
||||||
// width, height of extrusion, nozzle diameter, is bridge
|
// width, height of extrusion, nozzle diameter, is bridge
|
||||||
// For the output, for fill generator.
|
// For the output, for fill generator.
|
||||||
|
@ -79,7 +86,6 @@ struct SurfaceFillParams
|
||||||
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
||||||
RETURN_COMPARE_NON_EQUAL(anchor_length);
|
RETURN_COMPARE_NON_EQUAL(anchor_length);
|
||||||
RETURN_COMPARE_NON_EQUAL(anchor_length_max);
|
RETURN_COMPARE_NON_EQUAL(anchor_length_max);
|
||||||
RETURN_COMPARE_NON_EQUAL(with_loop);
|
|
||||||
RETURN_COMPARE_NON_EQUAL(flow.width());
|
RETURN_COMPARE_NON_EQUAL(flow.width());
|
||||||
RETURN_COMPARE_NON_EQUAL(flow.height());
|
RETURN_COMPARE_NON_EQUAL(flow.height());
|
||||||
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter());
|
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter());
|
||||||
|
@ -100,7 +106,6 @@ struct SurfaceFillParams
|
||||||
// this->dont_adjust == rhs.dont_adjust &&
|
// this->dont_adjust == rhs.dont_adjust &&
|
||||||
this->anchor_length == rhs.anchor_length &&
|
this->anchor_length == rhs.anchor_length &&
|
||||||
this->anchor_length_max == rhs.anchor_length_max &&
|
this->anchor_length_max == rhs.anchor_length_max &&
|
||||||
this->with_loop == rhs.with_loop &&
|
|
||||||
this->flow == rhs.flow &&
|
this->flow == rhs.flow &&
|
||||||
this->extrusion_role == rhs.extrusion_role;
|
this->extrusion_role == rhs.extrusion_role;
|
||||||
}
|
}
|
||||||
|
@ -151,8 +156,6 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||||
params.extruder = layerm.region().extruder(extrusion_role);
|
params.extruder = layerm.region().extruder(extrusion_role);
|
||||||
params.pattern = region_config.sparse_infill_pattern.value;
|
params.pattern = region_config.sparse_infill_pattern.value;
|
||||||
params.density = float(region_config.sparse_infill_density);
|
params.density = float(region_config.sparse_infill_density);
|
||||||
//BBS
|
|
||||||
params.with_loop = surface.surface_type == stInternalWithLoop;
|
|
||||||
|
|
||||||
if (surface.is_solid()) {
|
if (surface.is_solid()) {
|
||||||
params.density = 100.f;
|
params.density = 100.f;
|
||||||
|
@ -501,7 +504,6 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||||
params.extrusion_role = surface_fill.params.extrusion_role;
|
params.extrusion_role = surface_fill.params.extrusion_role;
|
||||||
params.using_internal_flow = using_internal_flow;
|
params.using_internal_flow = using_internal_flow;
|
||||||
params.no_extrusion_overlap = surface_fill.params.overlap;
|
params.no_extrusion_overlap = surface_fill.params.overlap;
|
||||||
params.with_loop = surface_fill.params.with_loop;
|
|
||||||
params.config = &layerm->region().config();
|
params.config = &layerm->region().config();
|
||||||
if (surface_fill.params.pattern == ipGrid)
|
if (surface_fill.params.pattern == ipGrid)
|
||||||
params.can_reverse = false;
|
params.can_reverse = false;
|
||||||
|
@ -540,6 +542,101 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) const
|
||||||
|
{
|
||||||
|
std::vector<SurfaceFill> surface_fills = group_fills(*this);
|
||||||
|
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
|
||||||
|
const auto resolution = this->object()->print()->config().resolution.value;
|
||||||
|
|
||||||
|
Polylines sparse_infill_polylines{};
|
||||||
|
|
||||||
|
for (SurfaceFill &surface_fill : surface_fills) {
|
||||||
|
if (surface_fill.surface.surface_type != stInternal) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (surface_fill.params.pattern) {
|
||||||
|
case ipCount: continue; break;
|
||||||
|
case ipSupportBase: continue; break;
|
||||||
|
//TODO: case ipEnsuring: continue; break;
|
||||||
|
case ipLightning:
|
||||||
|
case ipAdaptiveCubic:
|
||||||
|
case ipSupportCubic:
|
||||||
|
case ipRectilinear:
|
||||||
|
case ipMonotonic:
|
||||||
|
case ipMonotonicLine:
|
||||||
|
case ipAlignedRectilinear:
|
||||||
|
case ipGrid:
|
||||||
|
case ipTriangles:
|
||||||
|
case ipStars:
|
||||||
|
case ipCubic:
|
||||||
|
case ipLine:
|
||||||
|
case ipConcentric:
|
||||||
|
case ipConcentricInternal:
|
||||||
|
case ipHoneycomb:
|
||||||
|
case ip3DHoneycomb:
|
||||||
|
case ipGyroid:
|
||||||
|
case ipHilbertCurve:
|
||||||
|
case ipArchimedeanChords:
|
||||||
|
case ipOctagramSpiral: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the filler object.
|
||||||
|
std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(surface_fill.params.pattern));
|
||||||
|
f->set_bounding_box(bbox);
|
||||||
|
f->layer_id = this->id() - this->object()->get_layer(0)->id(); // We need to subtract raft layers.
|
||||||
|
f->z = this->print_z;
|
||||||
|
f->angle = surface_fill.params.angle;
|
||||||
|
// f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
|
||||||
|
// TODO: f->print_config = &this->object()->print()->config();
|
||||||
|
// TODO: f->print_object_config = &this->object()->config();
|
||||||
|
|
||||||
|
if (surface_fill.params.pattern == ipLightning)
|
||||||
|
dynamic_cast<FillLightning::Filler *>(f.get())->generator = lightning_generator;
|
||||||
|
|
||||||
|
// calculate flow spacing for infill pattern generation
|
||||||
|
double link_max_length = 0.;
|
||||||
|
if (!surface_fill.params.bridge) {
|
||||||
|
#if 0
|
||||||
|
link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
|
||||||
|
// printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length);
|
||||||
|
#else
|
||||||
|
if (surface_fill.params.density > 80.) // 80%
|
||||||
|
link_max_length = 3. * f->spacing;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
LayerRegion &layerm = *m_regions[surface_fill.region_id];
|
||||||
|
|
||||||
|
// Maximum length of the perimeter segment linking two infill lines.
|
||||||
|
f->link_max_length = (coord_t) scale_(link_max_length);
|
||||||
|
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
|
||||||
|
f->loop_clipping = coord_t(scale_(layerm.region().config().seam_gap.get_abs_value(surface_fill.params.flow.nozzle_diameter())));
|
||||||
|
|
||||||
|
// apply half spacing using this flow's own spacing and generate infill
|
||||||
|
FillParams params;
|
||||||
|
params.density = float(0.01 * surface_fill.params.density);
|
||||||
|
params.dont_adjust = false; // surface_fill.params.dont_adjust;
|
||||||
|
params.anchor_length = surface_fill.params.anchor_length;
|
||||||
|
params.anchor_length_max = surface_fill.params.anchor_length_max;
|
||||||
|
params.resolution = resolution;
|
||||||
|
params.use_arachne = false;
|
||||||
|
params.layer_height = layerm.layer()->height;
|
||||||
|
|
||||||
|
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||||
|
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||||
|
f->spacing = surface_fill.params.spacing;
|
||||||
|
surface_fill.surface.expolygon = std::move(expoly);
|
||||||
|
try {
|
||||||
|
Polylines polylines = f->fill_surface(&surface_fill.surface, params);
|
||||||
|
sparse_infill_polylines.insert(sparse_infill_polylines.end(), polylines.begin(), polylines.end());
|
||||||
|
} catch (InfillFailedException &) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sparse_infill_polylines;
|
||||||
|
}
|
||||||
|
|
||||||
// Create ironing extrusions over top surfaces.
|
// Create ironing extrusions over top surfaces.
|
||||||
void Layer::make_ironing()
|
void Layer::make_ironing()
|
||||||
{
|
{
|
||||||
|
|
|
@ -120,57 +120,13 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
|
||||||
{
|
{
|
||||||
Polylines polylines;
|
Polylines polylines;
|
||||||
ThickPolylines thick_polylines;
|
ThickPolylines thick_polylines;
|
||||||
if (!params.with_loop) {
|
try {
|
||||||
try {
|
if (params.use_arachne)
|
||||||
if (params.use_arachne)
|
thick_polylines = this->fill_surface_arachne(surface, params);
|
||||||
thick_polylines = this->fill_surface_arachne(surface, params);
|
else
|
||||||
else
|
polylines = this->fill_surface(surface, params);
|
||||||
polylines = this->fill_surface(surface, params);
|
|
||||||
}
|
|
||||||
catch (InfillFailedException&) {}
|
|
||||||
}
|
|
||||||
//BBS: add handling for infill pattern with loop
|
|
||||||
else {
|
|
||||||
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 0.5 * this->spacing)));
|
|
||||||
Polylines loop_polylines = to_polylines(expp);
|
|
||||||
{
|
|
||||||
//BBS: clip the loop
|
|
||||||
size_t j = 0;
|
|
||||||
for (size_t i = 0; i < loop_polylines.size(); ++i) {
|
|
||||||
loop_polylines[i].clip_end(this->loop_clipping);
|
|
||||||
if (loop_polylines[i].is_valid()) {
|
|
||||||
if (j < i)
|
|
||||||
loop_polylines[j] = std::move(loop_polylines[i]);
|
|
||||||
++j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (j < loop_polylines.size())
|
|
||||||
loop_polylines.erase(loop_polylines.begin() + int(j), loop_polylines.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!loop_polylines.empty()) {
|
|
||||||
if (params.use_arachne)
|
|
||||||
append(thick_polylines, to_thick_polylines(std::move(loop_polylines), scaled<coord_t>(this->spacing)));
|
|
||||||
else
|
|
||||||
append(polylines, std::move(loop_polylines));
|
|
||||||
expp = offset_ex(expp, float(scale_(0 - 0.5 * this->spacing)));
|
|
||||||
} else {
|
|
||||||
//BBS: the area is too narrow to place a loop, return to original expolygon
|
|
||||||
expp = { surface->expolygon };
|
|
||||||
}
|
|
||||||
|
|
||||||
Surface temp_surface = *surface;
|
|
||||||
for (ExPolygon& ex : expp) {
|
|
||||||
temp_surface.expolygon = ex;
|
|
||||||
try {
|
|
||||||
if (params.use_arachne)
|
|
||||||
append(thick_polylines, std::move(this->fill_surface_arachne(&temp_surface, params)));
|
|
||||||
else
|
|
||||||
append(polylines, std::move(this->fill_surface(&temp_surface, params)));
|
|
||||||
}
|
|
||||||
catch (InfillFailedException&) {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (InfillFailedException&) {}
|
||||||
|
|
||||||
if (!polylines.empty() || !thick_polylines.empty()) {
|
if (!polylines.empty() || !thick_polylines.empty()) {
|
||||||
// calculate actual flow from spacing (which might have been adjusted by the infill
|
// calculate actual flow from spacing (which might have been adjusted by the infill
|
||||||
|
|
|
@ -67,8 +67,6 @@ struct FillParams
|
||||||
bool use_arachne{ false };
|
bool use_arachne{ false };
|
||||||
// Layer height for Concentric infill with Arachne.
|
// Layer height for Concentric infill with Arachne.
|
||||||
coordf_t layer_height { 0.f };
|
coordf_t layer_height { 0.f };
|
||||||
//BBS
|
|
||||||
bool with_loop { false };
|
|
||||||
|
|
||||||
// BBS
|
// BBS
|
||||||
Flow flow;
|
Flow flow;
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
#include "InternalBridgeDetector.hpp"
|
|
||||||
#include "ClipperUtils.hpp"
|
|
||||||
#include "Geometry.hpp"
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace Slic3r {
|
|
||||||
|
|
||||||
InternalBridgeDetector::InternalBridgeDetector(
|
|
||||||
ExPolygon _internal_bridge, const ExPolygons& _fill_no_overlap, coord_t _spacing) :
|
|
||||||
fill_no_overlap(_fill_no_overlap),
|
|
||||||
spacing(_spacing)
|
|
||||||
{
|
|
||||||
this->internal_bridge_infill.push_back(std::move(_internal_bridge));
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
//#define INTERNAL_BRIDGE_DETECTOR_DEBUG_TO_SVG
|
|
||||||
|
|
||||||
void InternalBridgeDetector::initialize()
|
|
||||||
{
|
|
||||||
Polygons grown = offset(this->internal_bridge_infill, float(this->spacing));
|
|
||||||
this->m_anchor_regions = diff_ex(grown, offset(this->fill_no_overlap, 10.f));
|
|
||||||
|
|
||||||
#ifdef INTERNAL_BRIDGE_DETECTOR_DEBUG_TO_SVG
|
|
||||||
static int irun = 0;
|
|
||||||
BoundingBox bbox_svg;
|
|
||||||
|
|
||||||
bbox_svg.merge(get_extents(this->internal_bridge_infill));
|
|
||||||
bbox_svg.merge(get_extents(this->fill_no_overlap));
|
|
||||||
bbox_svg.merge(get_extents(this->m_anchor_regions));
|
|
||||||
{
|
|
||||||
std::stringstream stri;
|
|
||||||
stri << "InternalBridgeDetector_" << irun << ".svg";
|
|
||||||
SVG svg(stri.str(), bbox_svg);
|
|
||||||
svg.draw(to_polylines(this->internal_bridge_infill), "blue");
|
|
||||||
svg.draw(to_polylines(this->fill_no_overlap), "yellow");
|
|
||||||
svg.draw(to_polylines(m_anchor_regions), "red");
|
|
||||||
svg.Close();
|
|
||||||
}
|
|
||||||
++ irun;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InternalBridgeDetector::detect_angle()
|
|
||||||
{
|
|
||||||
if (this->m_anchor_regions.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::vector<InternalBridgeDirection> candidates;
|
|
||||||
std::vector<double> angles = bridge_direction_candidates();
|
|
||||||
candidates.reserve(angles.size());
|
|
||||||
for (size_t i = 0; i < angles.size(); ++ i)
|
|
||||||
candidates.emplace_back(InternalBridgeDirection(angles[i]));
|
|
||||||
|
|
||||||
Polygons clip_area = offset(this->internal_bridge_infill, 0.5f * float(this->spacing));
|
|
||||||
|
|
||||||
bool have_coverage = false;
|
|
||||||
for (size_t i_angle = 0; i_angle < candidates.size(); ++ i_angle)
|
|
||||||
{
|
|
||||||
const double angle = candidates[i_angle].angle;
|
|
||||||
|
|
||||||
Lines lines;
|
|
||||||
{
|
|
||||||
BoundingBox bbox = get_extents_rotated(this->m_anchor_regions, - angle);
|
|
||||||
// Cover the region with line segments.
|
|
||||||
lines.reserve((bbox.max(1) - bbox.min(1) + this->spacing) / this->spacing);
|
|
||||||
double s = sin(angle);
|
|
||||||
double c = cos(angle);
|
|
||||||
|
|
||||||
for (coord_t y = bbox.min(1); y <= bbox.max(1); y += this->spacing)
|
|
||||||
lines.push_back(Line(
|
|
||||||
Point((coord_t)round(c * bbox.min(0) - s * y), (coord_t)round(c * y + s * bbox.min(0))),
|
|
||||||
Point((coord_t)round(c * bbox.max(0) - s * y), (coord_t)round(c * y + s * bbox.max(0)))));
|
|
||||||
}
|
|
||||||
|
|
||||||
double total_length = 0;
|
|
||||||
double anchored_length = 0;
|
|
||||||
double max_length = 0;
|
|
||||||
{
|
|
||||||
Lines clipped_lines = intersection_ln(lines, clip_area);
|
|
||||||
for (size_t i = 0; i < clipped_lines.size(); ++i) {
|
|
||||||
const Line &line = clipped_lines[i];
|
|
||||||
double len = line.length();
|
|
||||||
total_length += len;
|
|
||||||
if (expolygons_contain(this->m_anchor_regions, line.a) && expolygons_contain(this->m_anchor_regions, line.b)) {
|
|
||||||
// This line could be anchored.
|
|
||||||
anchored_length += len;
|
|
||||||
max_length = std::max(max_length, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (anchored_length == 0.)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
have_coverage = true;
|
|
||||||
|
|
||||||
candidates[i_angle].coverage = anchored_length/total_length;
|
|
||||||
candidates[i_angle].max_length = max_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! have_coverage)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::sort(candidates.begin(), candidates.end());
|
|
||||||
size_t i_best = 0;
|
|
||||||
this->angle = candidates[i_best].angle;
|
|
||||||
if (this->angle >= PI)
|
|
||||||
this->angle -= PI;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<double> InternalBridgeDetector::bridge_direction_candidates() const
|
|
||||||
{
|
|
||||||
std::vector<double> angles;
|
|
||||||
for (int i = 0; i <= PI/this->resolution; ++i)
|
|
||||||
angles.push_back(i * this->resolution);
|
|
||||||
|
|
||||||
// we also test angles of each bridge contour
|
|
||||||
{
|
|
||||||
Lines lines = to_lines(this->internal_bridge_infill);
|
|
||||||
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
|
|
||||||
angles.push_back(line->direction());
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove duplicates
|
|
||||||
double min_resolution = PI/180.0;
|
|
||||||
std::sort(angles.begin(), angles.end());
|
|
||||||
for (size_t i = 1; i < angles.size(); ++i) {
|
|
||||||
if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) {
|
|
||||||
angles.erase(angles.begin() + i);
|
|
||||||
--i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution))
|
|
||||||
angles.pop_back();
|
|
||||||
|
|
||||||
return angles;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
#ifndef slic3r_InternalBridgeDetector_hpp_
|
|
||||||
#define slic3r_InternalBridgeDetector_hpp_
|
|
||||||
|
|
||||||
#include "libslic3r.h"
|
|
||||||
#include "ExPolygon.hpp"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Slic3r {
|
|
||||||
|
|
||||||
// BBS: InternalBridgeDetector is used to detect bridge angle for internal bridge.
|
|
||||||
// this step may enlarge internal bridge area for a little(only occupy sparse infill area) for better anchoring
|
|
||||||
class InternalBridgeDetector {
|
|
||||||
public:
|
|
||||||
// input: all fill area in LayerRegion without overlap with perimeter.
|
|
||||||
ExPolygons fill_no_overlap;
|
|
||||||
// input: internal bridge infill area.
|
|
||||||
ExPolygons internal_bridge_infill;
|
|
||||||
// input: scaled extrusion width of the infill.
|
|
||||||
coord_t spacing;
|
|
||||||
// output: the final optimal angle.
|
|
||||||
double angle = -1.;
|
|
||||||
|
|
||||||
InternalBridgeDetector(ExPolygon _internal_bridge, const ExPolygons &_fill_no_overlap, coord_t _spacing);
|
|
||||||
bool detect_angle();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initialize();
|
|
||||||
std::vector<double> bridge_direction_candidates() const;
|
|
||||||
|
|
||||||
struct InternalBridgeDirection {
|
|
||||||
InternalBridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(0.) {}
|
|
||||||
// the best direction is the one causing most lines to be bridged and the span is short
|
|
||||||
bool operator<(const InternalBridgeDirection &other) const {
|
|
||||||
double delta = this->coverage - other.coverage;
|
|
||||||
if (delta > 0.001)
|
|
||||||
return true;
|
|
||||||
else if (delta < -0.001)
|
|
||||||
return false;
|
|
||||||
else
|
|
||||||
// coverage is almost same, then compare span
|
|
||||||
return this->max_length < other.max_length;
|
|
||||||
};
|
|
||||||
double angle;
|
|
||||||
double coverage;
|
|
||||||
double max_length;
|
|
||||||
};
|
|
||||||
|
|
||||||
double resolution = PI/36.0;
|
|
||||||
ExPolygons m_anchor_regions;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -182,6 +182,9 @@ public:
|
||||||
// Phony version of make_fills() without parameters for Perl integration only.
|
// Phony version of make_fills() without parameters for Perl integration only.
|
||||||
void make_fills() { this->make_fills(nullptr, nullptr); }
|
void make_fills() { this->make_fills(nullptr, nullptr); }
|
||||||
void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator = nullptr);
|
void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator = nullptr);
|
||||||
|
Polylines generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree *adaptive_fill_octree,
|
||||||
|
FillAdaptive::Octree *support_fill_octree,
|
||||||
|
FillLightning::Generator* lightning_generator) const;
|
||||||
void make_ironing();
|
void make_ironing();
|
||||||
|
|
||||||
void export_region_slices_to_svg(const char *path) const;
|
void export_region_slices_to_svg(const char *path) const;
|
||||||
|
|
|
@ -752,7 +752,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||||
"gcode_add_line_number", "enable_arc_fitting", "infill_combination", /*"adaptive_layer_height",*/
|
"gcode_add_line_number", "enable_arc_fitting", "infill_combination", /*"adaptive_layer_height",*/
|
||||||
"support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",
|
"support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",
|
||||||
"initial_layer_infill_speed", "only_one_wall_top",
|
"initial_layer_infill_speed", "only_one_wall_top",
|
||||||
"timelapse_type", "internal_bridge_support_thickness",
|
"timelapse_type",
|
||||||
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
||||||
"wall_distribution_count", "min_feature_size", "min_bead_width", "post_process",
|
"wall_distribution_count", "min_feature_size", "min_bead_width", "post_process",
|
||||||
"small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface",
|
"small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface",
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef slic3r_Print_hpp_
|
#ifndef slic3r_Print_hpp_
|
||||||
#define slic3r_Print_hpp_
|
#define slic3r_Print_hpp_
|
||||||
|
|
||||||
|
#include "Fill/FillAdaptive.hpp"
|
||||||
|
#include "Fill/FillLightning.hpp"
|
||||||
#include "PrintBase.hpp"
|
#include "PrintBase.hpp"
|
||||||
|
|
||||||
#include "BoundingBox.hpp"
|
#include "BoundingBox.hpp"
|
||||||
|
@ -487,7 +489,8 @@ private:
|
||||||
void discover_horizontal_shells();
|
void discover_horizontal_shells();
|
||||||
void combine_infill();
|
void combine_infill();
|
||||||
void _generate_support_material();
|
void _generate_support_material();
|
||||||
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data();
|
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data(
|
||||||
|
const std::vector<std::pair<const Surface*, float>>& surfaces_w_bottom_z) const;
|
||||||
FillLightning::GeneratorPtr prepare_lightning_infill_data();
|
FillLightning::GeneratorPtr prepare_lightning_infill_data();
|
||||||
|
|
||||||
// BBS
|
// BBS
|
||||||
|
@ -517,6 +520,10 @@ private:
|
||||||
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
||||||
// so that next call to make_perimeters() performs a union() before computing loops
|
// so that next call to make_perimeters() performs a union() before computing loops
|
||||||
bool m_typed_slices = false;
|
bool m_typed_slices = false;
|
||||||
|
|
||||||
|
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> m_adaptive_fill_octrees;
|
||||||
|
FillLightning::GeneratorPtr m_lightning_generator;
|
||||||
|
|
||||||
std::vector < VolumeSlices > firstLayerObjSliceByVolume;
|
std::vector < VolumeSlices > firstLayerObjSliceByVolume;
|
||||||
std::vector<groupedVolumeSlices> firstLayerObjSliceByGroups;
|
std::vector<groupedVolumeSlices> firstLayerObjSliceByGroups;
|
||||||
// BBS: per object skirt
|
// BBS: per object skirt
|
||||||
|
|
|
@ -1097,18 +1097,6 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionBool(true));
|
def->set_default_value(new ConfigOptionBool(true));
|
||||||
|
|
||||||
def = this->add("internal_bridge_support_thickness", coFloat);
|
|
||||||
def->label = L("Internal bridge support thickness");
|
|
||||||
def->category = L("Strength");
|
|
||||||
def->tooltip = L("If enabled, support loops will be generated under the contours of internal bridges."
|
|
||||||
"These support loops could prevent internal bridges from extruding over the air and improve the top surface quality, especially when the sparse infill density is low."
|
|
||||||
"This value determines the thickness of the support loops. 0 means disable this feature");
|
|
||||||
def->sidetext = L("mm");
|
|
||||||
def->min = 0;
|
|
||||||
def->max = 2;
|
|
||||||
def->mode = comAdvanced;
|
|
||||||
def->set_default_value(new ConfigOptionFloat(0));
|
|
||||||
|
|
||||||
auto def_top_fill_pattern = def = this->add("top_surface_pattern", coEnum);
|
auto def_top_fill_pattern = def = this->add("top_surface_pattern", coEnum);
|
||||||
def->label = L("Top surface pattern");
|
def->label = L("Top surface pattern");
|
||||||
def->category = L("Strength");
|
def->category = L("Strength");
|
||||||
|
|
|
@ -711,7 +711,6 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||||
((ConfigOptionBool, detect_narrow_internal_solid_infill))
|
((ConfigOptionBool, detect_narrow_internal_solid_infill))
|
||||||
// ((ConfigOptionBool, adaptive_layer_height))
|
// ((ConfigOptionBool, adaptive_layer_height))
|
||||||
((ConfigOptionFloat, support_bottom_interface_spacing))
|
((ConfigOptionFloat, support_bottom_interface_spacing))
|
||||||
((ConfigOptionFloat, internal_bridge_support_thickness))
|
|
||||||
((ConfigOptionEnum<PerimeterGeneratorType>, wall_generator))
|
((ConfigOptionEnum<PerimeterGeneratorType>, wall_generator))
|
||||||
((ConfigOptionPercent, wall_transition_length))
|
((ConfigOptionPercent, wall_transition_length))
|
||||||
((ConfigOptionPercent, wall_transition_filter_deviation))
|
((ConfigOptionPercent, wall_transition_filter_deviation))
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,8 +15,6 @@ enum SurfaceType {
|
||||||
stBottomBridge,
|
stBottomBridge,
|
||||||
// Normal sparse infill.
|
// Normal sparse infill.
|
||||||
stInternal,
|
stInternal,
|
||||||
// Normal sparse infill.
|
|
||||||
stInternalWithLoop,
|
|
||||||
// Full infill, supporting the top surfaces and/or defining the verticall wall thickness.
|
// Full infill, supporting the top surfaces and/or defining the verticall wall thickness.
|
||||||
stInternalSolid,
|
stInternalSolid,
|
||||||
// 1st layer of dense infill over sparse infill, printed with a bridging extrusion flow.
|
// 1st layer of dense infill over sparse infill, printed with a bridging extrusion flow.
|
||||||
|
@ -112,7 +110,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<Surface> Surfaces;
|
typedef std::vector<Surface> Surfaces;
|
||||||
typedef std::vector<Surface*> SurfacesPtr;
|
typedef std::vector<const Surface*> SurfacesPtr;
|
||||||
|
|
||||||
inline Polygons to_polygons(const Surface &surface)
|
inline Polygons to_polygons(const Surface &surface)
|
||||||
{
|
{
|
||||||
|
@ -229,6 +227,7 @@ inline void polygons_append(Polygons &dst, const SurfacesPtr &src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
inline void polygons_append(Polygons &dst, SurfacesPtr &&src)
|
inline void polygons_append(Polygons &dst, SurfacesPtr &&src)
|
||||||
{
|
{
|
||||||
dst.reserve(dst.size() + number_polygons(src));
|
dst.reserve(dst.size() + number_polygons(src));
|
||||||
|
@ -238,6 +237,7 @@ inline void polygons_append(Polygons &dst, SurfacesPtr &&src)
|
||||||
(*it)->expolygon.holes.clear();
|
(*it)->expolygon.holes.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Append a vector of Surfaces at the end of another vector of polygons.
|
// Append a vector of Surfaces at the end of another vector of polygons.
|
||||||
inline void surfaces_append(Surfaces &dst, const ExPolygons &src, SurfaceType surfaceType)
|
inline void surfaces_append(Surfaces &dst, const ExPolygons &src, SurfaceType surfaceType)
|
||||||
|
|
|
@ -42,26 +42,21 @@ void SurfaceCollection::group(std::vector<SurfacesPtr> *retval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfacesPtr SurfaceCollection::filter_by_type(const SurfaceType type)
|
SurfacesPtr SurfaceCollection::filter_by_type(const SurfaceType type) const
|
||||||
{
|
{
|
||||||
SurfacesPtr ss;
|
SurfacesPtr ss;
|
||||||
for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
|
for (const Surface &surface : this->surfaces)
|
||||||
if (surface->surface_type == type) ss.push_back(&*surface);
|
if (surface.surface_type == type)
|
||||||
}
|
ss.push_back(&surface);
|
||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfacesPtr SurfaceCollection::filter_by_types(const SurfaceType *types, int ntypes)
|
SurfacesPtr SurfaceCollection::filter_by_types(std::initializer_list<SurfaceType> types) const
|
||||||
{
|
{
|
||||||
SurfacesPtr ss;
|
SurfacesPtr ss;
|
||||||
for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
|
for (const Surface &surface : this->surfaces)
|
||||||
for (int i = 0; i < ntypes; ++ i) {
|
if (std::find(types.begin(), types.end(), surface.surface_type) != types.end())
|
||||||
if (surface->surface_type == types[i]) {
|
ss.push_back(&surface);
|
||||||
ss.push_back(&*surface);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ss;
|
return ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,23 +81,15 @@ void SurfaceCollection::keep_type(const SurfaceType type)
|
||||||
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SurfaceCollection::keep_types(const SurfaceType *types, int ntypes)
|
void SurfaceCollection::keep_types(std::initializer_list<SurfaceType> types)
|
||||||
{
|
{
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
for (size_t i = 0; i < surfaces.size(); ++ i) {
|
for (size_t i = 0; i < surfaces.size(); ++ i)
|
||||||
bool keep = false;
|
if (std::find(types.begin(), types.end(), surfaces[i].surface_type) != types.end()) {
|
||||||
for (int k = 0; k < ntypes; ++ k) {
|
|
||||||
if (surfaces[i].surface_type == types[k]) {
|
|
||||||
keep = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (keep) {
|
|
||||||
if (j < i)
|
if (j < i)
|
||||||
std::swap(surfaces[i], surfaces[j]);
|
std::swap(surfaces[i], surfaces[j]);
|
||||||
++ j;
|
++ j;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (j < surfaces.size())
|
if (j < surfaces.size())
|
||||||
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
||||||
}
|
}
|
||||||
|
@ -137,23 +124,15 @@ void SurfaceCollection::remove_type(const SurfaceType type, ExPolygons *polygons
|
||||||
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SurfaceCollection::remove_types(const SurfaceType *types, int ntypes)
|
void SurfaceCollection::remove_types(std::initializer_list<SurfaceType> types)
|
||||||
{
|
{
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
for (size_t i = 0; i < surfaces.size(); ++ i) {
|
for (size_t i = 0; i < surfaces.size(); ++ i)
|
||||||
bool remove = false;
|
if (std::find(types.begin(), types.end(), surfaces[i].surface_type) == types.end()) {
|
||||||
for (int k = 0; k < ntypes; ++ k) {
|
|
||||||
if (surfaces[i].surface_type == types[k]) {
|
|
||||||
remove = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (! remove) {
|
|
||||||
if (j < i)
|
if (j < i)
|
||||||
std::swap(surfaces[i], surfaces[j]);
|
std::swap(surfaces[i], surfaces[j]);
|
||||||
++ j;
|
++ j;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (j < surfaces.size())
|
if (j < surfaces.size())
|
||||||
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
#include "Surface.hpp"
|
#include "Surface.hpp"
|
||||||
|
#include <initializer_list>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
@ -26,12 +27,12 @@ public:
|
||||||
for (const Surface &surface : this->surfaces) if (surface.is_bottom() && surface.expolygon.contains(item)) return true;
|
for (const Surface &surface : this->surfaces) if (surface.is_bottom() && surface.expolygon.contains(item)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SurfacesPtr filter_by_type(const SurfaceType type);
|
SurfacesPtr filter_by_type(const SurfaceType type) const;
|
||||||
SurfacesPtr filter_by_types(const SurfaceType *types, int ntypes);
|
SurfacesPtr filter_by_types(std::initializer_list<SurfaceType> types) const;
|
||||||
void keep_type(const SurfaceType type);
|
void keep_type(const SurfaceType type);
|
||||||
void keep_types(const SurfaceType *types, int ntypes);
|
void keep_types(std::initializer_list<SurfaceType> types);
|
||||||
void remove_type(const SurfaceType type);
|
void remove_type(const SurfaceType type);
|
||||||
void remove_types(const SurfaceType *types, int ntypes);
|
void remove_types(std::initializer_list<SurfaceType> types);
|
||||||
void filter_by_type(SurfaceType type, Polygons* polygons) const;
|
void filter_by_type(SurfaceType type, Polygons* polygons) const;
|
||||||
void remove_type(const SurfaceType type, ExPolygons *polygons);
|
void remove_type(const SurfaceType type, ExPolygons *polygons);
|
||||||
void set_type(SurfaceType type) {
|
void set_type(SurfaceType type) {
|
||||||
|
@ -54,6 +55,13 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Surfaces::const_iterator cbegin() const { return this->surfaces.cbegin(); }
|
||||||
|
Surfaces::const_iterator cend() const { return this->surfaces.cend(); }
|
||||||
|
Surfaces::const_iterator begin() const { return this->surfaces.cbegin(); }
|
||||||
|
Surfaces::const_iterator end() const { return this->surfaces.cend(); }
|
||||||
|
Surfaces::iterator begin() { return this->surfaces.begin(); }
|
||||||
|
Surfaces::iterator end() { return this->surfaces.end(); }
|
||||||
|
|
||||||
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
|
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
|
||||||
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
|
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
|
||||||
void set(const ExPolygons &src, SurfaceType surfaceType) { clear(); this->append(src, surfaceType); }
|
void set(const ExPolygons &src, SurfaceType surfaceType) { clear(); this->append(src, surfaceType); }
|
||||||
|
|
|
@ -1935,7 +1935,6 @@ void TabPrint::build()
|
||||||
optgroup->append_single_option_line("infill_combination");
|
optgroup->append_single_option_line("infill_combination");
|
||||||
optgroup->append_single_option_line("detect_narrow_internal_solid_infill");
|
optgroup->append_single_option_line("detect_narrow_internal_solid_infill");
|
||||||
optgroup->append_single_option_line("ensure_vertical_shell_thickness");
|
optgroup->append_single_option_line("ensure_vertical_shell_thickness");
|
||||||
optgroup->append_single_option_line("internal_bridge_support_thickness");
|
|
||||||
|
|
||||||
page = add_options_page(L("Speed"), "empty");
|
page = add_options_page(L("Speed"), "empty");
|
||||||
optgroup = page->new_optgroup(L("Initial layer speed"), L"param_speed_first", 15);
|
optgroup = page->new_optgroup(L("Initial layer speed"), L"param_speed_first", 15);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue