Merge branch 'dev' of https://github.com/prusa3d/PrusaSlicer into dev
This commit is contained in:
commit
bdbaecb7f8
23 changed files with 438 additions and 329 deletions
|
@ -13,103 +13,200 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
struct SurfaceGroupAttrib
|
||||
struct SurfaceFillParams
|
||||
{
|
||||
SurfaceGroupAttrib() : is_solid(false), flow_width(0.f), pattern(-1) {}
|
||||
bool operator==(const SurfaceGroupAttrib &other) const
|
||||
{ return is_solid == other.is_solid && flow_width == other.flow_width && pattern == other.pattern; }
|
||||
bool is_solid;
|
||||
float flow_width;
|
||||
// pattern is of type InfillPattern, -1 for an unset pattern.
|
||||
int pattern;
|
||||
SurfaceFillParams() : flow(0.f, 0.f, 0.f, false) { memset(this, 0, sizeof(*this)); }
|
||||
// Zero based extruder ID.
|
||||
unsigned int extruder;
|
||||
// Infill pattern, adjusted for the density etc.
|
||||
InfillPattern pattern;
|
||||
|
||||
// FillBase
|
||||
// in unscaled coordinates
|
||||
coordf_t spacing;
|
||||
// infill / perimeter overlap, in unscaled coordinates
|
||||
coordf_t overlap;
|
||||
// Angle as provided by the region config, in radians.
|
||||
float angle;
|
||||
// Non-negative for a bridge.
|
||||
float bridge_angle;
|
||||
|
||||
// FillParams
|
||||
float density;
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect;
|
||||
// Don't adjust spacing to fill the space evenly.
|
||||
bool dont_adjust;
|
||||
|
||||
// width, height of extrusion, nozzle diameter, is bridge
|
||||
// For the output, for fill generator.
|
||||
Flow flow;
|
||||
|
||||
// For the output
|
||||
ExtrusionRole extrusion_role;
|
||||
|
||||
// Various print settings?
|
||||
|
||||
// Index of this entry in a linear vector.
|
||||
size_t idx;
|
||||
|
||||
|
||||
bool operator<(const SurfaceFillParams &rhs) const {
|
||||
#define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false;
|
||||
#define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false;
|
||||
|
||||
// Sort first by decreasing bridging angle, so that the bridges are processed with priority when trimming one layer by the other.
|
||||
if (this->bridge_angle > rhs.bridge_angle) return true;
|
||||
if (this->bridge_angle < rhs.bridge_angle) return false;
|
||||
|
||||
RETURN_COMPARE_NON_EQUAL(extruder);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, pattern);
|
||||
RETURN_COMPARE_NON_EQUAL(spacing);
|
||||
RETURN_COMPARE_NON_EQUAL(overlap);
|
||||
RETURN_COMPARE_NON_EQUAL(angle);
|
||||
RETURN_COMPARE_NON_EQUAL(density);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_connect);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.width);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.height);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(const SurfaceFillParams &rhs) const {
|
||||
return this->extruder == rhs.extruder &&
|
||||
this->pattern == rhs.pattern &&
|
||||
this->pattern == rhs.pattern &&
|
||||
this->spacing == rhs.spacing &&
|
||||
this->overlap == rhs.overlap &&
|
||||
this->angle == rhs.angle &&
|
||||
this->density == rhs.density &&
|
||||
this->dont_connect == rhs.dont_connect &&
|
||||
this->dont_adjust == rhs.dont_adjust &&
|
||||
this->flow == rhs.flow &&
|
||||
this->extrusion_role == rhs.extrusion_role;
|
||||
}
|
||||
};
|
||||
|
||||
// Generate infills for Slic3r::Layer::Region.
|
||||
// The Slic3r::Layer::Region at this point of time may contain
|
||||
// surfaces of various types (internal/bridge/top/bottom/solid).
|
||||
// The infills are generated on the groups of surfaces with a compatible type.
|
||||
// Returns an array of Slic3r::ExtrusionPath::Collection objects containing the infills generaed now
|
||||
// and the thin fills generated by generate_perimeters().
|
||||
void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
||||
{
|
||||
// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
|
||||
|
||||
double fill_density = layerm.region()->config().fill_density;
|
||||
Flow infill_flow = layerm.flow(frInfill);
|
||||
Flow solid_infill_flow = layerm.flow(frSolidInfill);
|
||||
Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill);
|
||||
struct SurfaceFill {
|
||||
SurfaceFill(const SurfaceFillParams& params) : region_id(size_t(-1)), surface(stCount, ExPolygon()), params(params) {}
|
||||
|
||||
Surfaces surfaces;
|
||||
|
||||
// merge adjacent surfaces
|
||||
// in case of bridge surfaces, the ones with defined angle will be attached to the ones
|
||||
// without any angle (shouldn't this logic be moved to process_external_surfaces()?)
|
||||
{
|
||||
Polygons polygons_bridged;
|
||||
polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size());
|
||||
for (Surfaces::iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it)
|
||||
if (it->bridge_angle >= 0)
|
||||
polygons_append(polygons_bridged, *it);
|
||||
|
||||
// group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle)
|
||||
// group is of type Slic3r::SurfaceCollection
|
||||
//FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions.
|
||||
std::vector<SurfacesPtr> groups;
|
||||
layerm.fill_surfaces.group(&groups);
|
||||
|
||||
// merge compatible groups (we can generate continuous infill for them)
|
||||
{
|
||||
// cache flow widths and patterns used for all solid groups
|
||||
// (we'll use them for comparing compatible groups)
|
||||
std::vector<SurfaceGroupAttrib> group_attrib(groups.size());
|
||||
for (size_t i = 0; i < groups.size(); ++ i) {
|
||||
// we can only merge solid non-bridge surfaces, so discard
|
||||
// non-solid surfaces
|
||||
const Surface &surface = *groups[i].front();
|
||||
if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
|
||||
group_attrib[i].is_solid = true;
|
||||
group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
|
||||
group_attrib[i].pattern = surface.is_external() ?
|
||||
size_t region_id;
|
||||
Surface surface;
|
||||
ExPolygons expolygons;
|
||||
SurfaceFillParams params;
|
||||
};
|
||||
|
||||
std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
{
|
||||
std::vector<SurfaceFill> surface_fills;
|
||||
|
||||
// Fill in a map of a region & surface to SurfaceFillParams.
|
||||
std::set<SurfaceFillParams> set_surface_params;
|
||||
std::vector<std::vector<const SurfaceFillParams*>> region_to_surface_params(layer.regions().size(), std::vector<const SurfaceFillParams*>());
|
||||
SurfaceFillParams params;
|
||||
bool has_internal_voids = false;
|
||||
for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
|
||||
const LayerRegion &layerm = *layer.regions()[region_id];
|
||||
region_to_surface_params[region_id].assign(layerm.fill_surfaces.size(), nullptr);
|
||||
for (const Surface &surface : layerm.fill_surfaces.surfaces)
|
||||
if (surface.surface_type == stInternalVoid)
|
||||
has_internal_voids = true;
|
||||
else {
|
||||
FlowRole extrusion_role = (surface.surface_type == stTop) ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
|
||||
bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
|
||||
params.extruder = layerm.region()->extruder(extrusion_role);
|
||||
params.pattern = layerm.region()->config().fill_pattern.value;
|
||||
params.density = float(layerm.region()->config().fill_density);
|
||||
|
||||
if (surface.is_solid()) {
|
||||
params.density = 100.f;
|
||||
params.pattern = (surface.is_external() && ! is_bridge) ?
|
||||
(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
|
||||
ipRectilinear;
|
||||
}
|
||||
}
|
||||
// Loop through solid groups, find compatible groups and append them to this one.
|
||||
for (size_t i = 0; i < groups.size(); ++ i) {
|
||||
if (! group_attrib[i].is_solid)
|
||||
continue;
|
||||
for (size_t j = i + 1; j < groups.size();) {
|
||||
if (group_attrib[i] == group_attrib[j]) {
|
||||
// groups are compatible, merge them
|
||||
groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end());
|
||||
groups.erase(groups.begin() + j);
|
||||
group_attrib.erase(group_attrib.begin() + j);
|
||||
} else
|
||||
++ j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round.
|
||||
for (size_t round = 0; round < 2; ++ round) {
|
||||
for (std::vector<SurfacesPtr>::iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) {
|
||||
const SurfacesPtr &group = *it_group;
|
||||
bool is_bridge = group.front()->bridge_angle >= 0;
|
||||
if (is_bridge != (round == 0))
|
||||
continue;
|
||||
// Make a union of polygons defining the infiill regions of a group, use a safety offset.
|
||||
Polygons union_p = union_(to_polygons(*it_group), true);
|
||||
// Subtract surfaces having a defined bridge_angle from any other, use a safety offset.
|
||||
if (! polygons_bridged.empty() && ! is_bridge)
|
||||
union_p = diff(union_p, polygons_bridged, true);
|
||||
// subtract any other surface already processed
|
||||
//FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
|
||||
// Using group.front() as a template.
|
||||
surfaces_append(surfaces, diff_ex(union_p, to_polygons(surfaces), true), *group.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ipRectilinear;
|
||||
} else if (params.density <= 0)
|
||||
continue;
|
||||
|
||||
params.extrusion_role =
|
||||
is_bridge ?
|
||||
erBridgeInfill :
|
||||
(surface.is_solid() ?
|
||||
((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
|
||||
erInternalInfill);
|
||||
params.bridge_angle = float(surface.bridge_angle);
|
||||
params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
||||
|
||||
// calculate the actual flow we'll be using for this infill
|
||||
params.flow = layerm.region()->flow(
|
||||
extrusion_role,
|
||||
(surface.thickness == -1) ? layerm.layer()->height : surface.thickness, // extrusion height
|
||||
is_bridge || Fill::use_bridge_flow(params.pattern), // bridge flow?
|
||||
layerm.layer()->id() == 0, // first layer?
|
||||
-1, // auto width
|
||||
*layerm.layer()->object()
|
||||
);
|
||||
|
||||
// Calculate flow spacing for infill pattern generation.
|
||||
if (! surface.is_solid() && ! is_bridge) {
|
||||
// it's internal infill, so we can calculate a generic flow spacing
|
||||
// for all layers, for avoiding the ugly effect of
|
||||
// misaligned infill on first layer because of different extrusion width and
|
||||
// layer height
|
||||
params.spacing = layerm.region()->flow(
|
||||
frInfill,
|
||||
layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers?
|
||||
false, // no bridge
|
||||
false, // no first layer
|
||||
-1, // auto width
|
||||
*layer.object()
|
||||
).spacing();
|
||||
} else
|
||||
params.spacing = params.flow.spacing();
|
||||
|
||||
auto it_params = set_surface_params.find(params);
|
||||
if (it_params == set_surface_params.end())
|
||||
it_params = set_surface_params.insert(it_params, params);
|
||||
region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()] = &(*it_params);
|
||||
}
|
||||
}
|
||||
|
||||
surface_fills.reserve(set_surface_params.size());
|
||||
for (const SurfaceFillParams ¶ms : set_surface_params) {
|
||||
const_cast<SurfaceFillParams&>(params).idx = surface_fills.size();
|
||||
surface_fills.emplace_back(params);
|
||||
}
|
||||
|
||||
for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
|
||||
const LayerRegion &layerm = *layer.regions()[region_id];
|
||||
for (const Surface &surface : layerm.fill_surfaces.surfaces)
|
||||
if (surface.surface_type != stInternalVoid) {
|
||||
const SurfaceFillParams *params = region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()];
|
||||
if (params != nullptr) {
|
||||
SurfaceFill &fill = surface_fills[params->idx];
|
||||
if (fill.region_id = size_t(-1)) {
|
||||
fill.region_id = region_id;
|
||||
fill.surface = surface;
|
||||
fill.expolygons.emplace_back(std::move(fill.surface.expolygon));
|
||||
} else
|
||||
fill.expolygons.emplace_back(surface.expolygon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Polygons all_polygons;
|
||||
for (SurfaceFill &fill : surface_fills)
|
||||
if (! fill.expolygons.empty() && (fill.expolygons.size() > 1 || ! all_polygons.empty())) {
|
||||
Polygons polys = to_polygons(std::move(fill.expolygons));
|
||||
// Make a union of polygons, use a safety offset, subtract the preceding polygons.
|
||||
// Bridges are processed first (see SurfaceFill::operator<())
|
||||
fill.expolygons = all_polygons.empty() ? union_ex(polys, true) : diff_ex(polys, all_polygons, true);
|
||||
append(all_polygons, std::move(polys));
|
||||
}
|
||||
}
|
||||
|
||||
// we need to detect any narrow surfaces that might collapse
|
||||
// when adding spacing below
|
||||
// such narrow surfaces are often generated in sloping walls
|
||||
|
@ -119,155 +216,170 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
|||
// we are going to grow such regions by overlapping them with the void (if any)
|
||||
// TODO: detect and investigate whether there could be narrow regions without
|
||||
// any void neighbors
|
||||
{
|
||||
coord_t distance_between_surfaces = std::max(
|
||||
std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()),
|
||||
top_solid_infill_flow.scaled_spacing());
|
||||
Polygons surfaces_polygons = to_polygons(surfaces);
|
||||
Polygons collapsed = diff(
|
||||
surfaces_polygons,
|
||||
offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2),
|
||||
true);
|
||||
Polygons to_subtract;
|
||||
to_subtract.reserve(collapsed.size() + number_polygons(surfaces));
|
||||
for (Surfaces::const_iterator it_surface = surfaces.begin(); it_surface != surfaces.end(); ++ it_surface)
|
||||
if (it_surface->surface_type == stInternalVoid)
|
||||
polygons_append(to_subtract, *it_surface);
|
||||
polygons_append(to_subtract, collapsed);
|
||||
surfaces_append(
|
||||
surfaces,
|
||||
intersection_ex(
|
||||
offset(collapsed, (float)distance_between_surfaces),
|
||||
to_subtract,
|
||||
true),
|
||||
stInternalSolid);
|
||||
if (has_internal_voids) {
|
||||
// Internal voids are generated only if "infill_only_where_needed" or "infill_every_layers" are active.
|
||||
coord_t distance_between_surfaces = 0;
|
||||
Polygons surfaces_polygons;
|
||||
Polygons voids;
|
||||
int region_internal_infill = -1;
|
||||
int region_solid_infill = -1;
|
||||
int region_some_infill = -1;
|
||||
for (SurfaceFill &surface_fill : surface_fills)
|
||||
if (! surface_fill.expolygons.empty()) {
|
||||
distance_between_surfaces = std::max(distance_between_surfaces, surface_fill.params.flow.scaled_spacing());
|
||||
append((surface_fill.surface.surface_type == stInternalVoid) ? voids : surfaces_polygons, to_polygons(surface_fill.expolygons));
|
||||
if (surface_fill.surface.surface_type == stInternalSolid)
|
||||
region_internal_infill = (int)surface_fill.region_id;
|
||||
if (surface_fill.surface.is_solid())
|
||||
region_solid_infill = (int)surface_fill.region_id;
|
||||
if (surface_fill.surface.surface_type != stInternalVoid)
|
||||
region_some_infill = (int)surface_fill.region_id;
|
||||
}
|
||||
if (! voids.empty() && ! surfaces_polygons.empty()) {
|
||||
// First clip voids by the printing polygons, as the voids were ignored by the loop above during mutual clipping.
|
||||
voids = diff(voids, surfaces_polygons);
|
||||
// Corners of infill regions, which would not be filled with an extrusion path with a radius of distance_between_surfaces/2
|
||||
Polygons collapsed = diff(
|
||||
surfaces_polygons,
|
||||
offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2),
|
||||
true);
|
||||
//FIXME why the voids are added to collapsed here? First it is expensive, second the result may lead to some unwanted regions being
|
||||
// added if two offsetted void regions merge.
|
||||
// polygons_append(voids, collapsed);
|
||||
ExPolygons extensions = intersection_ex(offset(collapsed, (float)distance_between_surfaces), voids, true);
|
||||
// Now find an internal infill SurfaceFill to add these extrusions to.
|
||||
SurfaceFill *internal_solid_fill = nullptr;
|
||||
unsigned int region_id = 0;
|
||||
if (region_internal_infill != -1)
|
||||
region_id = region_internal_infill;
|
||||
else if (region_solid_infill != -1)
|
||||
region_id = region_solid_infill;
|
||||
else if (region_some_infill != -1)
|
||||
region_id = region_some_infill;
|
||||
const LayerRegion& layerm = *layer.regions()[region_id];
|
||||
for (SurfaceFill &surface_fill : surface_fills)
|
||||
if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layerm.layer()->height - surface_fill.params.flow.height) < EPSILON) {
|
||||
internal_solid_fill = &surface_fill;
|
||||
break;
|
||||
}
|
||||
if (internal_solid_fill == nullptr) {
|
||||
// Produce another solid fill.
|
||||
params.extruder = layerm.region()->extruder(frSolidInfill);
|
||||
params.pattern = ipRectilinear;
|
||||
params.density = 100.f;
|
||||
params.extrusion_role = erInternalInfill;
|
||||
params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
||||
// calculate the actual flow we'll be using for this infill
|
||||
params.flow = layerm.region()->flow(
|
||||
frSolidInfill,
|
||||
layerm.layer()->height, // extrusion height
|
||||
false, // bridge flow?
|
||||
layerm.layer()->id() == 0, // first layer?
|
||||
-1, // auto width
|
||||
*layer.object()
|
||||
);
|
||||
params.spacing = params.flow.spacing();
|
||||
surface_fills.emplace_back(params);
|
||||
surface_fills.back().surface.surface_type = stInternalSolid;
|
||||
surface_fills.back().surface.thickness = layer.height;
|
||||
surface_fills.back().expolygons = std::move(extensions);
|
||||
} else {
|
||||
append(extensions, std::move(internal_solid_fill->expolygons));
|
||||
internal_solid_fill->expolygons = union_ex(extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0) {
|
||||
// require "Slic3r/SVG.pm";
|
||||
// Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
|
||||
// expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
|
||||
// red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ],
|
||||
// );
|
||||
}
|
||||
return surface_fills;
|
||||
}
|
||||
|
||||
// friend to Layer
|
||||
void Layer::make_fills()
|
||||
{
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
layerm->fills.clear();
|
||||
|
||||
std::vector<SurfaceFill> surface_fills = group_fills(*this);
|
||||
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
|
||||
|
||||
for (SurfaceFill &surface_fill : surface_fills) {
|
||||
// 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();
|
||||
f->z = this->print_z;
|
||||
f->angle = surface_fill.params.angle;
|
||||
f->spacing = surface_fill.params.spacing;
|
||||
|
||||
for (const Surface &surface : surfaces) {
|
||||
if (surface.surface_type == stInternalVoid)
|
||||
continue;
|
||||
InfillPattern fill_pattern = layerm.region()->config().fill_pattern.value;
|
||||
double density = fill_density;
|
||||
FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill :
|
||||
(surface.is_solid() ? frSolidInfill : frInfill);
|
||||
bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
|
||||
|
||||
if (surface.is_solid()) {
|
||||
density = 100.;
|
||||
fill_pattern = (surface.is_external() && ! is_bridge) ?
|
||||
(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
|
||||
ipRectilinear;
|
||||
} else if (density <= 0)
|
||||
continue;
|
||||
|
||||
// get filler object
|
||||
std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(fill_pattern));
|
||||
f->set_bounding_box(layerm.layer()->object()->bounding_box());
|
||||
|
||||
// calculate the actual flow we'll be using for this infill
|
||||
coordf_t h = (surface.thickness == -1) ? layerm.layer()->height : surface.thickness;
|
||||
Flow flow = layerm.region()->flow(
|
||||
role,
|
||||
h,
|
||||
is_bridge || f->use_bridge_flow(), // bridge flow?
|
||||
layerm.layer()->id() == 0, // first layer?
|
||||
-1, // auto width
|
||||
*layerm.layer()->object()
|
||||
);
|
||||
|
||||
// calculate flow spacing for infill pattern generation
|
||||
bool using_internal_flow = false;
|
||||
if (! surface.is_solid() && ! is_bridge) {
|
||||
// it's internal infill, so we can calculate a generic flow spacing
|
||||
// for all layers, for avoiding the ugly effect of
|
||||
// misaligned infill on first layer because of different extrusion width and
|
||||
// layer height
|
||||
Flow internal_flow = layerm.region()->flow(
|
||||
frInfill,
|
||||
layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers?
|
||||
false, // no bridge
|
||||
false, // no first layer
|
||||
-1, // auto width
|
||||
*layerm.layer()->object()
|
||||
);
|
||||
f->spacing = internal_flow.spacing();
|
||||
using_internal_flow = true;
|
||||
} else {
|
||||
f->spacing = flow.spacing();
|
||||
}
|
||||
|
||||
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge;
|
||||
double link_max_length = 0.;
|
||||
if (! is_bridge) {
|
||||
if (! surface_fill.params.flow.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 (density > 80.) // 80%
|
||||
if (surface_fill.params.density > 80.) // 80%
|
||||
link_max_length = 3. * f->spacing;
|
||||
#endif
|
||||
}
|
||||
|
||||
f->layer_id = layerm.layer()->id();
|
||||
f->z = layerm.layer()->print_z;
|
||||
f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
||||
// 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_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
|
||||
// f->layer_height = h;
|
||||
f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
|
||||
|
||||
// apply half spacing using this flow's own spacing and generate infill
|
||||
FillParams params;
|
||||
params.density = float(0.01 * density);
|
||||
// params.dont_adjust = true;
|
||||
params.dont_adjust = false;
|
||||
Polylines polylines = f->fill_surface(&surface, params);
|
||||
if (polylines.empty())
|
||||
continue;
|
||||
params.density = float(0.01 * surface_fill.params.density);
|
||||
params.dont_adjust = surface_fill.params.dont_adjust; // false
|
||||
|
||||
// calculate actual flow from spacing (which might have been adjusted by the infill
|
||||
// pattern generator)
|
||||
if (using_internal_flow) {
|
||||
// if we used the internal flow we're not doing a solid infill
|
||||
// so we can safely ignore the slight variation that might have
|
||||
// been applied to $f->flow_spacing
|
||||
} else {
|
||||
flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, (float)h, is_bridge || f->use_bridge_flow());
|
||||
}
|
||||
|
||||
// Save into layer.
|
||||
auto *eec = new ExtrusionEntityCollection();
|
||||
out.entities.push_back(eec);
|
||||
// Only concentric fills are not sorted.
|
||||
eec->no_sort = f->no_sort();
|
||||
extrusion_entities_append_paths(
|
||||
eec->entities, std::move(polylines),
|
||||
is_bridge ?
|
||||
erBridgeInfill :
|
||||
(surface.is_solid() ?
|
||||
((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
|
||||
erInternalInfill),
|
||||
flow.mm3_per_mm(), flow.width, flow.height);
|
||||
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||
surface_fill.surface.expolygon = std::move(expoly);
|
||||
Polylines polylines = f->fill_surface(&surface_fill.surface, params);
|
||||
if (! polylines.empty()) {
|
||||
// calculate actual flow from spacing (which might have been adjusted by the infill
|
||||
// pattern generator)
|
||||
double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm();
|
||||
double flow_width = surface_fill.params.flow.width;
|
||||
if (using_internal_flow) {
|
||||
// if we used the internal flow we're not doing a solid infill
|
||||
// so we can safely ignore the slight variation that might have
|
||||
// been applied to f->spacing
|
||||
} else {
|
||||
Flow new_flow = Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter, surface_fill.params.flow.height, surface_fill.params.flow.bridge);
|
||||
flow_mm3_per_mm = new_flow.mm3_per_mm();
|
||||
flow_width = new_flow.width;
|
||||
}
|
||||
// Save into layer.
|
||||
auto *eec = new ExtrusionEntityCollection();
|
||||
m_regions[surface_fill.region_id]->fills.entities.push_back(eec);
|
||||
// Only concentric fills are not sorted.
|
||||
eec->no_sort = f->no_sort();
|
||||
extrusion_entities_append_paths(
|
||||
eec->entities, std::move(polylines),
|
||||
surface_fill.params.extrusion_role,
|
||||
flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add thin fill regions
|
||||
// thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
|
||||
// Unpacks the collection, creates multiple collections per path.
|
||||
// The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
|
||||
// Why the paths are unpacked?
|
||||
for (const ExtrusionEntity *thin_fill : layerm.thin_fills.entities) {
|
||||
ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
|
||||
out.entities.push_back(&collection);
|
||||
collection.entities.push_back(thin_fill->clone());
|
||||
}
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
for (const ExtrusionEntity *thin_fill : layerm->thin_fills.entities) {
|
||||
ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
|
||||
layerm->fills.entities.push_back(&collection);
|
||||
collection.entities.push_back(thin_fill->clone());
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
for (size_t i = 0; i < layerm->fills.entities.size(); ++ i)
|
||||
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -34,7 +34,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
|||
case ipArchimedeanChords: return new FillArchimedeanChords();
|
||||
case ipHilbertCurve: return new FillHilbertCurve();
|
||||
case ipOctagramSpiral: return new FillOctagramSpiral();
|
||||
default: throw std::invalid_argument("unknown type");;
|
||||
default: throw std::invalid_argument("unknown type");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,24 @@ Fill* Fill::new_from_type(const std::string &type)
|
|||
return (it == enum_keys_map.end()) ? nullptr : new_from_type(InfillPattern(it->second));
|
||||
}
|
||||
|
||||
// Force initialization of the Fill::use_bridge_flow() internal static map in a thread safe fashion even on compilers
|
||||
// not supporting thread safe non-static data member initializers.
|
||||
static bool use_bridge_flow_initializer = Fill::use_bridge_flow(ipGrid);
|
||||
|
||||
bool Fill::use_bridge_flow(const InfillPattern type)
|
||||
{
|
||||
static std::vector<unsigned char> cached;
|
||||
if (cached.empty()) {
|
||||
cached.assign(size_t(ipCount), 0);
|
||||
for (size_t i = 0; i < cached.size(); ++ i) {
|
||||
auto *fill = Fill::new_from_type((InfillPattern)i);
|
||||
cached[i] = fill->use_bridge_flow();
|
||||
delete fill;
|
||||
}
|
||||
}
|
||||
return cached[type] != 0;
|
||||
}
|
||||
|
||||
Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
// Perform offset.
|
||||
|
|
|
@ -70,6 +70,7 @@ public:
|
|||
|
||||
static Fill* new_from_type(const InfillPattern type);
|
||||
static Fill* new_from_type(const std::string &type);
|
||||
static bool use_bridge_flow(const InfillPattern type);
|
||||
|
||||
void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; }
|
||||
|
||||
|
|
|
@ -56,6 +56,8 @@ public:
|
|||
// Enable some perimeter squish (see INSET_OVERLAP_TOLERANCE).
|
||||
// Here an overlap of 0.2x external perimeter spacing is allowed for by the elephant foot compensation.
|
||||
coord_t scaled_elephant_foot_spacing() const { return coord_t(0.5f * float(this->scaled_width() + 0.6f * this->scaled_spacing())); }
|
||||
|
||||
bool operator==(const Flow &rhs) const { return this->width == rhs.width && this->height == rhs.height && this->nozzle_diameter == rhs.nozzle_diameter && this->bridge == rhs.bridge; }
|
||||
|
||||
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
|
||||
// Create a flow from the spacing of extrusion lines.
|
||||
|
|
|
@ -1742,13 +1742,9 @@ void GCode::process_layer(
|
|||
|
||||
// This extrusion is part of certain Region, which tells us which extruder should be used for it:
|
||||
int correct_extruder_id = Print::get_extruder(*fill, region);
|
||||
//FIXME what is this?
|
||||
entity_type=="infills" ?
|
||||
std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) :
|
||||
std::max<int>(region.config().perimeter_extruder.value - 1, 0);
|
||||
|
||||
// Let's recover vector of extruder overrides:
|
||||
const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, (int)layer_to_print.object()->copies().size());
|
||||
const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size());
|
||||
|
||||
// Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
|
||||
for (unsigned int extruder : layer_tools.extruders)
|
||||
|
@ -3031,7 +3027,7 @@ const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtru
|
|||
|
||||
// This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter)
|
||||
// It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy.
|
||||
void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, unsigned int object_copies_num)
|
||||
void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, size_t object_copies_num)
|
||||
{
|
||||
// We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves:
|
||||
ExtrusionEntityCollection* perimeters_or_infills = &infills;
|
||||
|
|
|
@ -246,7 +246,7 @@ protected:
|
|||
std::vector<const ExtruderPerCopy*> perimeters_overrides;
|
||||
|
||||
// Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping
|
||||
void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, unsigned int object_copies_num);
|
||||
void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, size_t object_copies_num);
|
||||
};
|
||||
|
||||
std::vector<Region> by_region; // all extrusions for this island, grouped by regions
|
||||
|
|
|
@ -458,14 +458,14 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
|||
continue;
|
||||
}
|
||||
|
||||
const auto& object = object_list[i];
|
||||
const PrintObject* object = object_list[i];
|
||||
|
||||
// Finds this layer:
|
||||
auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
|
||||
if (this_layer_it == object->layers().end())
|
||||
continue;
|
||||
const Layer* this_layer = *this_layer_it;
|
||||
unsigned int num_of_copies = object->copies().size();
|
||||
size_t num_of_copies = object->copies().size();
|
||||
|
||||
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
|
||||
|
||||
|
@ -494,7 +494,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
|||
|
||||
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
|
||||
set_extruder_override(fill, copy, new_extruder, num_of_copies);
|
||||
volume_to_wipe -= fill->total_volume();
|
||||
volume_to_wipe -= float(fill->total_volume());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -512,7 +512,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
|||
|
||||
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
|
||||
set_extruder_override(fill, copy, new_extruder, num_of_copies);
|
||||
volume_to_wipe -= fill->total_volume();
|
||||
volume_to_wipe -= float(fill->total_volume());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -540,9 +540,9 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
|
|||
if (this_layer_it == object->layers().end())
|
||||
continue;
|
||||
const Layer* this_layer = *this_layer_it;
|
||||
unsigned int num_of_copies = object->copies().size();
|
||||
size_t num_of_copies = object->copies().size();
|
||||
|
||||
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
|
||||
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
|
||||
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
|
||||
const auto& region = *object->print()->regions()[region_id];
|
||||
|
||||
|
@ -598,7 +598,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
|
|||
// so -1 was used as "print as usual".
|
||||
// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
|
||||
// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
|
||||
const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies)
|
||||
const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies)
|
||||
{
|
||||
auto entity_map_it = entity_map.find(entity);
|
||||
if (entity_map_it == entity_map.end())
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
}
|
||||
|
||||
// This is called from GCode::process_layer - see implementation for further comments:
|
||||
const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies);
|
||||
const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies);
|
||||
|
||||
// This function goes through all infill entities, decides which ones will be used for wiping and
|
||||
// marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
|
||||
|
@ -44,7 +44,7 @@ private:
|
|||
void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
|
||||
|
||||
// Returns true in case that entity is not printed with its usual extruder for a given copy:
|
||||
bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const {
|
||||
bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const {
|
||||
return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1);
|
||||
}
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ public:
|
|||
|
||||
if (f != 0.f && f != m_current_feedrate) {
|
||||
if (limit_volumetric_flow) {
|
||||
float e_speed = e / (((len == 0) ? std::abs(e) : len) / f * 60.f);
|
||||
float e_speed = e / (((len == 0.f) ? std::abs(e) : len) / f * 60.f);
|
||||
f /= std::max(1.f, e_speed / m_filpar[m_current_tool].max_e_speed);
|
||||
}
|
||||
m_gcode += set_format_F(f);
|
||||
|
@ -194,7 +194,7 @@ public:
|
|||
m_current_pos.y() = y;
|
||||
|
||||
// Update the elapsed time with a rough estimate.
|
||||
m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f;
|
||||
m_elapsed_time += ((len == 0.f) ? std::abs(e) : len) / m_current_feedrate * 60.f;
|
||||
m_gcode += "\n";
|
||||
return *this;
|
||||
}
|
||||
|
@ -387,14 +387,14 @@ public:
|
|||
}
|
||||
|
||||
|
||||
WipeTowerWriter& set_fan(unsigned int speed)
|
||||
WipeTowerWriter& set_fan(unsigned speed)
|
||||
{
|
||||
if (speed == m_last_fan_speed)
|
||||
return *this;
|
||||
if (speed == 0)
|
||||
m_gcode += "M107\n";
|
||||
else
|
||||
m_gcode += "M106 S" + std::to_string((size_t)(255.0 * speed / 100.0)) + "\n";
|
||||
m_gcode += "M106 S" + std::to_string(unsigned(255.0 * speed / 100.0)) + "\n";
|
||||
m_last_fan_speed = speed;
|
||||
return *this;
|
||||
}
|
||||
|
@ -417,7 +417,7 @@ private:
|
|||
float m_y_shift = 0.f;
|
||||
float m_wipe_tower_width = 0.f;
|
||||
float m_wipe_tower_depth = 0.f;
|
||||
float m_last_fan_speed = 0.f;
|
||||
unsigned m_last_fan_speed = 0.f;
|
||||
int current_temp = -1;
|
||||
const float m_default_analyzer_line_width;
|
||||
float m_used_filament_length = 0.f;
|
||||
|
@ -466,12 +466,12 @@ private:
|
|||
WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) :
|
||||
m_semm(config.single_extruder_multi_material.value),
|
||||
m_wipe_tower_pos(config.wipe_tower_x, config.wipe_tower_y),
|
||||
m_wipe_tower_width(config.wipe_tower_width),
|
||||
m_wipe_tower_rotation_angle(config.wipe_tower_rotation_angle),
|
||||
m_wipe_tower_width(float(config.wipe_tower_width)),
|
||||
m_wipe_tower_rotation_angle(float(config.wipe_tower_rotation_angle)),
|
||||
m_y_shift(0.f),
|
||||
m_z_pos(0.f),
|
||||
m_is_first_layer(false),
|
||||
m_bridging(config.wipe_tower_bridging),
|
||||
m_bridging(float(config.wipe_tower_bridging)),
|
||||
m_gcode_flavor(config.gcode_flavor),
|
||||
m_current_tool(initial_tool),
|
||||
wipe_volumes(wiping_matrix)
|
||||
|
@ -479,16 +479,16 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl
|
|||
// If this is a single extruder MM printer, we will use all the SE-specific config values.
|
||||
// Otherwise, the defaults will be used to turn off the SE stuff.
|
||||
if (m_semm) {
|
||||
m_cooling_tube_retraction = config.cooling_tube_retraction;
|
||||
m_cooling_tube_length = config.cooling_tube_length;
|
||||
m_parking_pos_retraction = config.parking_pos_retraction;
|
||||
m_extra_loading_move = config.extra_loading_move;
|
||||
m_set_extruder_trimpot = config.high_current_on_filament_swap;
|
||||
m_cooling_tube_retraction = float(config.cooling_tube_retraction);
|
||||
m_cooling_tube_length = float(config.cooling_tube_length);
|
||||
m_parking_pos_retraction = float(config.parking_pos_retraction);
|
||||
m_extra_loading_move = float(config.extra_loading_move);
|
||||
m_set_extruder_trimpot = config.high_current_on_filament_swap;
|
||||
}
|
||||
// Calculate where the priming lines should be - very naive test not detecting parallelograms or custom shapes
|
||||
const std::vector<Vec2d>& bed_points = config.bed_shape.values;
|
||||
m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed);
|
||||
m_bed_width = BoundingBoxf(bed_points).size().x();
|
||||
m_bed_width = float(BoundingBoxf(bed_points).size().x());
|
||||
}
|
||||
|
||||
|
||||
|
@ -505,21 +505,21 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
|
|||
// If this is a single extruder MM printer, we will use all the SE-specific config values.
|
||||
// Otherwise, the defaults will be used to turn off the SE stuff.
|
||||
if (m_semm) {
|
||||
m_filpar[idx].loading_speed = config.filament_loading_speed.get_at(idx);
|
||||
m_filpar[idx].loading_speed_start = config.filament_loading_speed_start.get_at(idx);
|
||||
m_filpar[idx].unloading_speed = config.filament_unloading_speed.get_at(idx);
|
||||
m_filpar[idx].unloading_speed_start = config.filament_unloading_speed_start.get_at(idx);
|
||||
m_filpar[idx].delay = config.filament_toolchange_delay.get_at(idx);
|
||||
m_filpar[idx].loading_speed = float(config.filament_loading_speed.get_at(idx));
|
||||
m_filpar[idx].loading_speed_start = float(config.filament_loading_speed_start.get_at(idx));
|
||||
m_filpar[idx].unloading_speed = float(config.filament_unloading_speed.get_at(idx));
|
||||
m_filpar[idx].unloading_speed_start = float(config.filament_unloading_speed_start.get_at(idx));
|
||||
m_filpar[idx].delay = float(config.filament_toolchange_delay.get_at(idx));
|
||||
m_filpar[idx].cooling_moves = config.filament_cooling_moves.get_at(idx);
|
||||
m_filpar[idx].cooling_initial_speed = config.filament_cooling_initial_speed.get_at(idx);
|
||||
m_filpar[idx].cooling_final_speed = config.filament_cooling_final_speed.get_at(idx);
|
||||
m_filpar[idx].cooling_initial_speed = float(config.filament_cooling_initial_speed.get_at(idx));
|
||||
m_filpar[idx].cooling_final_speed = float(config.filament_cooling_final_speed.get_at(idx));
|
||||
}
|
||||
|
||||
m_filpar[idx].filament_area = float((M_PI/4.f) * pow(config.filament_diameter.get_at(idx), 2)); // all extruders are assumed to have the same filament diameter at this point
|
||||
float nozzle_diameter = config.nozzle_diameter.get_at(idx);
|
||||
float nozzle_diameter = float(config.nozzle_diameter.get_at(idx));
|
||||
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
|
||||
|
||||
float max_vol_speed = config.filament_max_volumetric_speed.get_at(idx);
|
||||
float max_vol_speed = float(config.filament_max_volumetric_speed.get_at(idx));
|
||||
if (max_vol_speed!= 0.f)
|
||||
m_filpar[idx].max_e_speed = (max_vol_speed / filament_area());
|
||||
|
||||
|
@ -548,7 +548,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
|
|||
const std::vector<unsigned int> &tools,
|
||||
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
|
||||
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
|
||||
bool last_wipe_inside_wipe_tower)
|
||||
bool /*last_wipe_inside_wipe_tower*/)
|
||||
{
|
||||
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
|
||||
this->m_current_tool = tools.front();
|
||||
|
@ -683,7 +683,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_
|
|||
box_coordinates cleaning_box(
|
||||
Vec2f(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
|
||||
m_wipe_tower_width - m_perimeter_width,
|
||||
(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width
|
||||
(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5f*m_perimeter_width
|
||||
: m_wipe_tower_depth-m_perimeter_width));
|
||||
|
||||
WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
|
||||
|
@ -789,7 +789,7 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of
|
|||
// Extrude 4 rounds of a brim around the future wipe tower.
|
||||
box_coordinates box(wipeTower_box);
|
||||
for (size_t i = 0; i < 4; ++ i) {
|
||||
box.expand(m_perimeter_width - m_layer_height*(1.f-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space
|
||||
box.expand(m_perimeter_width - m_layer_height*float(1.-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space
|
||||
writer.travel (box.ld, 7000)
|
||||
.extrude(box.lu, 2100).extrude(box.ru)
|
||||
.extrude(box.rd ).extrude(box.ld);
|
||||
|
@ -898,8 +898,8 @@ void WipeTower::toolchange_Unload(
|
|||
const float x = volume_to_length(m_filpar[m_current_tool].ramming_speed[i] * 0.25f, line_width, m_layer_height);
|
||||
const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / filament_area(); // transform volume per sec to E move;
|
||||
const float dist = std::min(x - e_done, remaining); // distance to travel for either the next 0.25s, or to the next turnaround
|
||||
const float actual_time = dist/x * 0.25;
|
||||
writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), dist / (actual_time / 60.));
|
||||
const float actual_time = dist/x * 0.25f;
|
||||
writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0.f, 0.f, e * (dist / x), dist / (actual_time / 60.f));
|
||||
remaining -= dist;
|
||||
|
||||
if (remaining < WT_EPSILON) { // we reached a turning point
|
||||
|
@ -1078,21 +1078,21 @@ void WipeTower::toolchange_Wipe(
|
|||
|
||||
float traversed_x = writer.x();
|
||||
if (m_left_to_right)
|
||||
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
|
||||
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
|
||||
else
|
||||
writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
|
||||
writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
|
||||
|
||||
if (writer.y()+EPSILON > cleaning_box.lu.y()-0.5f*m_perimeter_width)
|
||||
if (writer.y()+float(EPSILON) > cleaning_box.lu.y()-0.5f*m_perimeter_width)
|
||||
break; // in case next line would not fit
|
||||
|
||||
traversed_x -= writer.x();
|
||||
x_to_wipe -= fabs(traversed_x);
|
||||
x_to_wipe -= std::abs(traversed_x);
|
||||
if (x_to_wipe < WT_EPSILON) {
|
||||
writer.travel(m_left_to_right ? xl + 1.5*m_perimeter_width : xr - 1.5*m_perimeter_width, writer.y(), 7200);
|
||||
writer.travel(m_left_to_right ? xl + 1.5f*m_perimeter_width : xr - 1.5f*m_perimeter_width, writer.y(), 7200);
|
||||
break;
|
||||
}
|
||||
// stepping to the next line:
|
||||
writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5*m_perimeter_width, writer.y() + dy);
|
||||
writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5f*m_perimeter_width, writer.y() + dy);
|
||||
m_left_to_right = !m_left_to_right;
|
||||
}
|
||||
|
||||
|
@ -1175,7 +1175,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
|
|||
writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f))
|
||||
.extrude(fill_box.lu + Vec2f(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
|
||||
|
||||
const int n = 1+(right-left)/(m_bridging);
|
||||
const int n = 1+int((right-left)/m_bridging);
|
||||
const float dx = (right-left)/n;
|
||||
for (int i=1;i<=n;++i) {
|
||||
float x=left+dx*i;
|
||||
|
@ -1254,7 +1254,7 @@ void WipeTower::plan_tower()
|
|||
for (auto& layer : m_plan)
|
||||
layer.depth = 0.f;
|
||||
|
||||
for (int layer_index = m_plan.size() - 1; layer_index >= 0; --layer_index)
|
||||
for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index)
|
||||
{
|
||||
float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth());
|
||||
m_plan[layer_index].depth = this_layer_depth;
|
||||
|
|
|
@ -171,21 +171,6 @@ void Layer::make_perimeters()
|
|||
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << " - Done";
|
||||
}
|
||||
|
||||
void Layer::make_fills()
|
||||
{
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("Making fills for layer " PRINTF_ZU "\n", this->id());
|
||||
#endif
|
||||
for (LayerRegion *layerm : m_regions) {
|
||||
layerm->fills.clear();
|
||||
make_fill(*layerm, layerm->fills);
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 0; i < layerm->fills.entities.size(); ++ i)
|
||||
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Layer::export_region_slices_to_svg(const char *path) const
|
||||
{
|
||||
BoundingBox bbox;
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
config(config), object_config(object_config), print_config(print_config),
|
||||
loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces),
|
||||
_ext_mm3_per_mm(-1), _mm3_per_mm(-1), _mm3_per_mm_overhang(-1)
|
||||
{};
|
||||
{}
|
||||
void process();
|
||||
|
||||
private:
|
||||
|
|
|
@ -108,7 +108,6 @@ static inline bool opts_equal(const DynamicConfig &config_old, const DynamicConf
|
|||
|
||||
std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig &rhs)
|
||||
{
|
||||
const ConfigDef *def = rhs.def();
|
||||
std::vector<std::string> diff_keys;
|
||||
for (const t_config_option_key &opt_key : rhs.keys())
|
||||
if (! opts_equal(m_config, rhs, opt_key))
|
||||
|
@ -124,7 +123,6 @@ std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig
|
|||
// a current extruder ID is used.
|
||||
bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs)
|
||||
{
|
||||
const ConfigDef *def = rhs.def();
|
||||
bool modified = false;
|
||||
for (const t_config_option_key &opt_key : rhs.keys()) {
|
||||
if (! opts_equal(m_config, rhs, opt_key)) {
|
||||
|
|
|
@ -39,6 +39,8 @@ class PrintRegion
|
|||
public:
|
||||
const Print* print() const { return m_print; }
|
||||
const PrintRegionConfig& config() const { return m_config; }
|
||||
// 1-based extruder identifier for this region and role.
|
||||
unsigned int extruder(FlowRole role) const;
|
||||
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
|
||||
// Average diameter of nozzles participating on extruding this region.
|
||||
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
|
||||
|
|
|
@ -2049,7 +2049,7 @@ void PrintConfigDef::init_fff_params()
|
|||
{
|
||||
int threads = (unsigned int)boost::thread::hardware_concurrency();
|
||||
def->set_default_value(new ConfigOptionInt(threads > 0 ? threads : 2));
|
||||
def->cli == ConfigOptionDef::nocli;
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
}
|
||||
|
||||
def = this->add("toolchange_gcode", coString);
|
||||
|
|
|
@ -35,7 +35,7 @@ enum PrintHostType {
|
|||
|
||||
enum InfillPattern {
|
||||
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
|
||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount,
|
||||
};
|
||||
|
||||
enum SupportMaterialPattern {
|
||||
|
|
|
@ -2,6 +2,21 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
// 1-based extruder identifier for this region and role.
|
||||
unsigned int PrintRegion::extruder(FlowRole role) const
|
||||
{
|
||||
size_t extruder = 0;
|
||||
if (role == frPerimeter || role == frExternalPerimeter)
|
||||
extruder = m_config.perimeter_extruder;
|
||||
else if (role == frInfill)
|
||||
extruder = m_config.infill_extruder;
|
||||
else if (role == frSolidInfill || role == frTopSolidInfill)
|
||||
extruder = m_config.solid_infill_extruder;
|
||||
else
|
||||
throw std::invalid_argument("Unknown role");
|
||||
return extruder;
|
||||
}
|
||||
|
||||
Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const
|
||||
{
|
||||
ConfigOptionFloatOrPercent config_width;
|
||||
|
@ -28,24 +43,13 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir
|
|||
throw std::invalid_argument("Unknown role");
|
||||
}
|
||||
}
|
||||
if (config_width.value == 0) {
|
||||
|
||||
if (config_width.value == 0)
|
||||
config_width = object.config().extrusion_width;
|
||||
}
|
||||
|
||||
// get the configured nozzle_diameter for the extruder associated
|
||||
// to the flow role requested
|
||||
size_t extruder = 0; // 1-based
|
||||
if (role == frPerimeter || role == frExternalPerimeter) {
|
||||
extruder = m_config.perimeter_extruder;
|
||||
} else if (role == frInfill) {
|
||||
extruder = m_config.infill_extruder;
|
||||
} else if (role == frSolidInfill || role == frTopSolidInfill) {
|
||||
extruder = m_config.solid_infill_extruder;
|
||||
} else {
|
||||
throw std::invalid_argument("Unknown role");
|
||||
}
|
||||
double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1);
|
||||
|
||||
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
|
||||
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
|
||||
double nozzle_diameter = m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1);
|
||||
return Flow::new_from_config_width(role, config_width, (float)nozzle_diameter, (float)layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0f);
|
||||
}
|
||||
|
||||
|
@ -79,12 +83,14 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con
|
|||
|
||||
void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const
|
||||
{
|
||||
auto num_extruders = (int)print()->config().nozzle_diameter.size();
|
||||
// PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder.
|
||||
// If not, then there must be something wrong with the Print::apply() function.
|
||||
#ifndef NDEBUG
|
||||
auto num_extruders = (int)print()->config().nozzle_diameter.size();
|
||||
assert(this->config().perimeter_extruder <= num_extruders);
|
||||
assert(this->config().infill_extruder <= num_extruders);
|
||||
assert(this->config().solid_infill_extruder <= num_extruders);
|
||||
#endif
|
||||
collect_object_printing_extruders(print()->config(), this->config(), object_extruders);
|
||||
}
|
||||
|
||||
|
|
|
@ -185,8 +185,6 @@ private:
|
|||
|
||||
SLAAutoSupports::Config m_config;
|
||||
|
||||
float m_supports_force_total = 0.f;
|
||||
|
||||
void process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights);
|
||||
void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false);
|
||||
void project_onto_mesh(std::vector<sla::SupportPoint>& points) const;
|
||||
|
|
|
@ -676,7 +676,7 @@ std::string SLAPrint::validate() const
|
|||
if(supports_en && !builtinpad.enabled && elv < pinhead_width )
|
||||
return L(
|
||||
"Elevation is too low for object. Use the \"Pad around "
|
||||
"obect\" feature to print the object without elevation.");
|
||||
"object\" feature to print the object without elevation.");
|
||||
|
||||
if(supports_en && builtinpad.enabled &&
|
||||
cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) {
|
||||
|
|
|
@ -24,9 +24,8 @@ enum SurfaceType {
|
|||
stInternalVoid,
|
||||
// Inner/outer perimeters.
|
||||
stPerimeter,
|
||||
// Last surface type, if the SurfaceType is used as an index into a vector.
|
||||
stLast,
|
||||
stCount = stLast + 1
|
||||
// Number of SurfaceType enums.
|
||||
stCount,
|
||||
};
|
||||
|
||||
class Surface
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
|
||||
void clear() { surfaces.clear(); }
|
||||
bool empty() const { return surfaces.empty(); }
|
||||
size_t size() const { return surfaces.size(); }
|
||||
bool has(SurfaceType type) const {
|
||||
for (const Surface &surface : this->surfaces)
|
||||
if (surface.surface_type == type) return true;
|
||||
|
|
|
@ -308,7 +308,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
}
|
||||
else {
|
||||
render_color[3] = 1.f;
|
||||
if ((m_hover_id == i && m_editing_mode)) { // ignore hover state unless editing mode is active
|
||||
if ((size_t(m_hover_id) == i && m_editing_mode)) { // ignore hover state unless editing mode is active
|
||||
render_color[0] = 0.f;
|
||||
render_color[1] = 1.0f;
|
||||
render_color[2] = 1.0f;
|
||||
|
@ -330,7 +330,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
|
||||
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2)));
|
||||
glsafe(::glTranslatef(support_point.pos(0), support_point.pos(1), support_point.pos(2)));
|
||||
glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data()));
|
||||
|
||||
if (vol->is_left_handed())
|
||||
|
@ -347,16 +347,16 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
Eigen::AngleAxisd aa(q);
|
||||
glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)));
|
||||
|
||||
const float cone_radius = 0.25f; // mm
|
||||
const float cone_height = 0.75f;
|
||||
const double cone_radius = 0.25; // mm
|
||||
const double cone_height = 0.75;
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslatef(0.f, 0.f, support_point.head_front_radius * RenderPointScale));
|
||||
::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 24, 1);
|
||||
::gluCylinder(m_quadric, 0., cone_radius, cone_height, 24, 1);
|
||||
glsafe(::glTranslatef(0.f, 0.f, cone_height));
|
||||
::gluDisk(m_quadric, 0.0, cone_radius, 24, 1);
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
::gluSphere(m_quadric, support_point.head_front_radius * RenderPointScale, 24, 12);
|
||||
::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12);
|
||||
if (vol->is_left_handed())
|
||||
glFrontFace(GL_CCW);
|
||||
|
||||
|
@ -777,7 +777,7 @@ std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const st
|
|||
}
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
|
||||
void GLGizmoSlaSupports::update_cache_entry_normal(size_t i) const
|
||||
{
|
||||
int idx = 0;
|
||||
Eigen::Matrix<float, 1, 3> pp = m_editing_cache[i].support_point.pos;
|
||||
|
|
|
@ -30,7 +30,7 @@ private:
|
|||
ObjectID m_model_object_id = 0;
|
||||
int m_active_instance = -1;
|
||||
float m_active_instance_bb_radius; // to cache the bb
|
||||
mutable float m_z_shift = 0.f;
|
||||
mutable double m_z_shift = 0.f;
|
||||
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
|
||||
|
||||
const float RenderPointScale = 1.f;
|
||||
|
@ -99,11 +99,9 @@ private:
|
|||
void render_clipping_plane(const Selection& selection) const;
|
||||
bool is_mesh_update_necessary() const;
|
||||
void update_mesh();
|
||||
void update_cache_entry_normal(unsigned int i) const;
|
||||
void update_cache_entry_normal(size_t i) const;
|
||||
bool unsaved_changes() const;
|
||||
|
||||
EState m_no_hover_state = Off;
|
||||
EState m_no_hover_old_state = Off;
|
||||
bool m_lock_unique_islands = false;
|
||||
bool m_editing_mode = false; // Is editing mode active?
|
||||
bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes).
|
||||
|
|
|
@ -65,13 +65,6 @@ new_from_type(CLASS, type)
|
|||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
void
|
||||
make_fill(CLASS, layer_region, out_append)
|
||||
char* CLASS;
|
||||
LayerRegion* layer_region;
|
||||
ExtrusionEntityCollection* out_append;
|
||||
CODE:
|
||||
make_fill(*layer_region, *out_append);
|
||||
%}
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue