This commit is contained in:
EiNSTeiN- 2023-07-01 18:02:12 -04:00
parent e0f7263a4c
commit 22c42b9844
40 changed files with 1611 additions and 95 deletions

View File

@ -221,6 +221,10 @@ set(SLIC3R_SOURCES
MutablePriorityQueue.hpp
NormalUtils.cpp
NormalUtils.hpp
NonplanarFacet.cpp
NonplanarFacet.hpp
NonplanarSurface.cpp
NonplanarSurface.hpp
NSVGUtils.cpp
NSVGUtils.hpp
ObjectID.cpp

View File

@ -418,7 +418,7 @@ bool has_duplicate_points(const ExPolygons &expolys)
{
#if 1
// Check globally.
#if 0
#if 1
// Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
Points allpts;
allpts.reserve(count_points(expolys));

View File

@ -10,6 +10,8 @@
#include <string_view>
#include <numeric>
#include <boost/variant.hpp>
namespace Slic3r {
class ExPolygon;
@ -67,6 +69,8 @@ public:
float width;
// Height of the extrusion, used for visualization purposes.
float height;
/// distance to surface layer in nonplanar extrusions -1.0 if not part of nonplanar extrusion
float distance_to_top = -1.0;
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {}
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}
@ -122,6 +126,11 @@ private:
ExtrusionRole m_role;
};
class ExtrusionPath3 : public ExtrusionPath
{
Polyline3 polyline;
};
class ExtrusionPathOriented : public ExtrusionPath
{
public:

View File

@ -1,5 +1,7 @@
#include "ExtrusionEntityCollection.hpp"
#include "ShortestPath.hpp"
#include "BoundingBox.hpp"
#include "SVG.hpp"
#include <algorithm>
#include <cmath>
#include <map>
@ -152,4 +154,17 @@ double ExtrusionEntityCollection::min_mm3_per_mm() const
return min_mm3_per_mm;
}
void ExtrusionEntityCollection::export_to_svg(const char *path) const
{
BoundingBox bbox;
for (const ExtrusionEntity *entity : this->entities)
bbox.merge(get_extents(entity->as_polylines()));
SVG svg(path, bbox);
for (const ExtrusionEntity *entity : this->entities)
svg.draw(entity->as_polylines(), "black", scale_(0.1f));
svg.Close();
}
}

View File

@ -124,6 +124,7 @@ public:
ExtrusionEntityCollection flatten(bool preserve_ordering = false) const;
double min_mm3_per_mm() const override;
double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
void export_to_svg(const char *path) const;
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const override {

View File

@ -19,7 +19,9 @@ GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role)
}
if (role == ExtrusionRole::InternalInfill) return GCodeExtrusionRole::InternalInfill;
if (role == ExtrusionRole::SolidInfill) return GCodeExtrusionRole::SolidInfill;
if (role == ExtrusionRole::SolidInfillNonplanar) return GCodeExtrusionRole::SolidInfillNonplanar;
if (role == ExtrusionRole::TopSolidInfill) return GCodeExtrusionRole::TopSolidInfill;
if (role == ExtrusionRole::TopSolidInfillNonplanar) return GCodeExtrusionRole::TopSolidInfillNonplanar;
if (role == ExtrusionRole::Ironing) return GCodeExtrusionRole::Ironing;
if (role == ExtrusionRole::BridgeInfill) return GCodeExtrusionRole::BridgeInfill;
if (role == ExtrusionRole::GapFill) return GCodeExtrusionRole::GapFill;
@ -40,7 +42,9 @@ std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role)
case GCodeExtrusionRole::OverhangPerimeter : return L("Overhang perimeter");
case GCodeExtrusionRole::InternalInfill : return L("Internal infill");
case GCodeExtrusionRole::SolidInfill : return L("Solid infill");
case GCodeExtrusionRole::SolidInfillNonplanar : return L("Nonplanar solid infill");
case GCodeExtrusionRole::TopSolidInfill : return L("Top solid infill");
case GCodeExtrusionRole::TopSolidInfillNonplanar : return L("Nonplanar top solid infill");
case GCodeExtrusionRole::Ironing : return L("Ironing");
case GCodeExtrusionRole::BridgeInfill : return L("Bridge infill");
case GCodeExtrusionRole::GapFill : return L("Gap fill");
@ -66,8 +70,12 @@ GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role)
return GCodeExtrusionRole::InternalInfill;
else if (role == L("Solid infill"))
return GCodeExtrusionRole::SolidInfill;
else if (role == L("Nonplanar solid infill"))
return GCodeExtrusionRole::SolidInfill;
else if (role == L("Top solid infill"))
return GCodeExtrusionRole::TopSolidInfill;
else if (role == L("Nonplanar top solid infill"))
return GCodeExtrusionRole::TopSolidInfillNonplanar;
else if (role == L("Ironing"))
return GCodeExtrusionRole::Ironing;
else if (role == L("Bridge infill"))

View File

@ -26,6 +26,7 @@ enum class ExtrusionRoleModifier : uint16_t {
Solid,
Ironing,
Bridge,
Nonplanar,
// 3) Special types
// Indicator that the extrusion role was mixed from multiple differing extrusion roles,
// for example from Support and SupportInterface.
@ -55,9 +56,11 @@ struct ExtrusionRole : public ExtrusionRoleModifiers
static constexpr const ExtrusionRoleModifiers InternalInfill{ ExtrusionRoleModifier::Infill };
// Solid internal infill.
static constexpr const ExtrusionRoleModifiers SolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid };
static constexpr const ExtrusionRoleModifiers SolidInfillNonplanar{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Nonplanar };
// Top solid infill (visible).
//FIXME why there is no bottom solid infill type?
static constexpr const ExtrusionRoleModifiers TopSolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::External };
static constexpr const ExtrusionRoleModifiers TopSolidInfillNonplanar{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::External | ExtrusionRoleModifier::Nonplanar };
// Ironing infill at the top surfaces.
static constexpr const ExtrusionRoleModifiers Ironing{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Ironing | ExtrusionRoleModifier::External };
// Visible bridging infill at the bottom of an object.
@ -84,6 +87,7 @@ struct ExtrusionRole : public ExtrusionRoleModifiers
bool is_solid_infill() const { return this->is_infill() && this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); }
bool is_external() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::External); }
bool is_bridge() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Bridge); }
bool is_nonplanar() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Nonplanar); }
bool is_support() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Support); }
bool is_support_base() const { return this->is_support() && ! this->is_external(); }
@ -108,7 +112,9 @@ enum class GCodeExtrusionRole : uint8_t {
OverhangPerimeter,
InternalInfill,
SolidInfill,
SolidInfillNonplanar,
TopSolidInfill,
TopSolidInfillNonplanar,
Ironing,
BridgeInfill,
GapFill,

View File

@ -2,6 +2,8 @@
#include <stdio.h>
#include <memory>
#include "SVG.hpp"
#include "../ClipperUtils.hpp"
#include "../Geometry.hpp"
#include "../Layer.hpp"
@ -144,7 +146,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
if (surface.is_solid()) {
params.density = 100.f;
//FIXME for non-thick bridges, shall we allow a bottom surface pattern?
params.pattern = (surface.is_external() && ! is_bridge) ?
params.pattern = (surface.is_external() && ! is_bridge && !surface.is_nonplanar()) ?
(surface.is_top() ? region_config.top_fill_pattern.value : region_config.bottom_fill_pattern.value) :
fill_type_monotonic(region_config.top_fill_pattern) ? ipMonotonic : ipRectilinear;
} else if (params.density <= 0)
@ -154,7 +156,10 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
is_bridge ?
ExtrusionRole::BridgeInfill :
(surface.is_solid() ?
(surface.is_top() ? ExtrusionRole::TopSolidInfill : ExtrusionRole::SolidInfill) :
(surface.is_top() ?
(surface.is_nonplanar() ? ExtrusionRole::TopSolidInfillNonplanar : ExtrusionRole::TopSolidInfill) :
(surface.is_nonplanar() ? ExtrusionRole::SolidInfillNonplanar : ExtrusionRole::SolidInfill)
) :
ExtrusionRole::InternalInfill);
params.bridge_angle = float(surface.bridge_angle);
params.angle = float(Geometry::deg2rad(region_config.fill_angle.value));
@ -252,7 +257,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
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)
if (surface_fill.surface.is_internal_solid())
region_internal_infill = (int)surface_fill.region_id;
if (surface_fill.surface.is_solid())
region_solid_infill = (int)surface_fill.region_id;
@ -281,10 +286,10 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
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(layer.height - surface_fill.params.flow.height()) < EPSILON) {
internal_solid_fill = &surface_fill;
break;
}
if ((surface_fill.surface.is_internal_solid()) && std::abs(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);
@ -309,7 +314,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
// Use ipEnsuring pattern for all internal Solids.
{
for (size_t surface_fill_id = 0; surface_fill_id < surface_fills.size(); ++surface_fill_id)
if (SurfaceFill &fill = surface_fills[surface_fill_id]; fill.surface.surface_type == stInternalSolid) {
if (SurfaceFill &fill = surface_fills[surface_fill_id]; fill.surface.is_internal_solid()) {
fill.params.pattern = ipEnsuring;
}
}
@ -449,10 +454,12 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0;
export_group_fills_to_svg(debug_out_path("Layer-fill_surfaces-10_fill-final-%d.svg", iRun ++).c_str(), surface_fills);
export_group_fills_to_svg(debug_out_path("Layer-%d-fill_surfaces-10_fill-final-%d.svg", id(), iRun ++).c_str(), surface_fills);
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
BOOST_LOG_TRIVIAL(trace) << "make_fills: found " << surface_fills.size() << " surfaces to fill for layer " << id();
size_t first_object_layer_id = this->object()->get_layer(0)->id();
for (SurfaceFill &surface_fill : surface_fills) {
// Create the filler object.
@ -519,6 +526,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
else
polylines = f->fill_surface(&surface_fill.surface, params);
} catch (InfillFailedException &) {
BOOST_LOG_TRIVIAL(trace) << "make_fills: InfillFailedException!";
}
if (!polylines.empty() || !thick_polylines.empty()) {
// calculate actual flow from spacing (which might have been adjusted by the infill
@ -562,7 +570,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height());
}
insert_fills_into_islands(*this, uint32_t(surface_fill.region_id), fill_begin, uint32_t(layerm.fills().size()));
}
} else {
BOOST_LOG_TRIVIAL(trace) << "make_fills: no infill was generated for layer " << id();
}
}
}
@ -854,7 +864,7 @@ void Layer::make_ironing()
polygons_append(polys, surface.expolygon);
} else {
for (const Surface &surface : ironing_params.layerm->slices())
if (surface.surface_type == stTop || (iron_everything && surface.surface_type == stBottom))
if (surface.is_top() || (iron_everything && surface.surface_type == stBottom))
// stBottomBridge is not being ironed on purpose, as it would likely destroy the bridges.
polygons_append(polys, surface.expolygon);
}
@ -862,7 +872,7 @@ void Layer::make_ironing()
// Add solid fill surfaces. This may not be ideal, as one will not iron perimeters touching these
// solid fill surfaces, but it is likely better than nothing.
for (const Surface &surface : ironing_params.layerm->fill_surfaces())
if (surface.surface_type == stInternalSolid)
if (surface.is_internal_solid())
polygons_append(infills, surface.expolygon);
}
}

View File

@ -2459,7 +2459,10 @@ void GCode::process_layer_single_object(
};
ExtrusionEntitiesPtr temp_fill_extrusions;
if (const Layer *layer = layer_to_print.object_layer; layer)
if (const Layer *layer = layer_to_print.object_layer; layer) {
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layer->export_lslices_polygons_to_svg_debug("process_gcode_layer");
#endif
for (size_t idx : layer->lslice_indices_sorted_by_print_order) {
const LayerSlice &lslice = layer->lslices_ex[idx];
auto extrude_infill_range = [&](
@ -2472,15 +2475,25 @@ void GCode::process_layer_single_object(
for (auto it_fill_range = it_fill_ranges_begin; it_fill_range != it_fill_ranges_end; ++ it_fill_range) {
assert(it_fill_range->region() == it_fill_ranges_begin->region());
for (uint32_t fill_id : *it_fill_range) {
assert(dynamic_cast<ExtrusionEntityCollection*>(fills.entities[fill_id]));
if (auto *eec = static_cast<ExtrusionEntityCollection*>(fills.entities[fill_id]);
(eec->role() == ExtrusionRole::Ironing) == ironing && shall_print_this_extrusion_collection(eec, region)) {
auto *eec = static_cast<ExtrusionEntityCollection*>(fills.entities[fill_id]);
assert(eec);
if ((eec->role() == ExtrusionRole::Ironing) == ironing && shall_print_this_extrusion_collection(eec, region)) {
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static int extrude_infill_range_run = 0;
eec->export_to_svg(debug_out_path("Layer-%d_gcode-extrude_infill_range-shall-be-printed-%u.svg", layerm.layer()->id(), extrude_infill_range_run++).c_str());
#endif
if (eec->can_reverse())
// Flatten the infill collection for better path planning.
for (auto *ee : eec->entities)
for (auto *ee : eec->entities) {
temp_fill_extrusions.emplace_back(ee);
}
else
temp_fill_extrusions.emplace_back(eec);
} else {
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static int iRun = 0;
eec->export_to_svg(debug_out_path("Layer-%d_gcode-extrude_infill_range-shall-not-be-printed-%u.svg", layerm.layer()->id(), iRun++).c_str());
#endif
}
}
}
@ -2491,17 +2504,30 @@ void GCode::process_layer_single_object(
// Will parallel access of initial G-code preview to these extrusions while reordering them at backend cause issues?
chain_and_reorder_extrusion_entities(temp_fill_extrusions, &m_last_pos);
const auto extrusion_name = ironing ? "ironing"sv : "infill"sv;
for (const ExtrusionEntity *fill : temp_fill_extrusions)
for (const ExtrusionEntity *fill : temp_fill_extrusions) {
if (auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill); eec) {
for (const ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities)
for (const ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) {
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static int iRun = 0;
eec->export_to_svg(debug_out_path("Layer-%d_gcode-extrude_infill_range-collection-%u.svg", layerm.layer()->id(), iRun++).c_str());
#endif
gcode += this->extrude_entity(*ee, extrusion_name);
} else
}
} else {
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static int iRun = 0;
export_to_svg(debug_out_path("Layer-%d_gcode-extrude_infill_range-entity-%u.svg", layerm.layer()->id(), iRun++).c_str(),
fill->as_polyline(), get_extents(layerm.slices().surfaces), scale_(0.1f));
#endif
gcode += this->extrude_entity(*fill, extrusion_name);
}
}
}
};
//FIXME order islands?
// Sequential tool path ordering of multiple parts within the same object, aka. perimeter tracking (#5511)
BOOST_LOG_TRIVIAL(trace) << "Generating GCode for layer " << layer->id() << " - slice " << idx << " has " << lslice.islands.size() << " islands";
for (const LayerIsland &island : lslice.islands) {
auto process_perimeters = [&]() {
const LayerRegion &layerm = *layer->get_region(island.perimeters.region());
@ -2510,9 +2536,13 @@ void GCode::process_layer_single_object(
const PrintRegion &region = print.get_print_region(layerm.region().print_region_id());
bool first = true;
for (uint32_t perimeter_id : island.perimeters) {
assert(dynamic_cast<const ExtrusionEntityCollection*>(layerm.perimeters().entities[perimeter_id]));
if (const auto *eec = static_cast<const ExtrusionEntityCollection*>(layerm.perimeters().entities[perimeter_id]);
shall_print_this_extrusion_collection(eec, region)) {
const auto *eec = static_cast<const ExtrusionEntityCollection*>(layerm.perimeters().entities[perimeter_id]);
assert(eec);
if (shall_print_this_extrusion_collection(eec, region)) {
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static int iRun = 0;
eec->export_to_svg(debug_out_path("Layer-%d_gcode-process_perimeters-%u.svg", layerm.layer()->id(), iRun++).c_str());
#endif
// This may not apply to Arachne, but maybe the Arachne gap fill should disable reverse as well?
// assert(! eec->can_reverse());
if (first) {
@ -2520,8 +2550,14 @@ void GCode::process_layer_single_object(
init_layer_delayed();
m_config.apply(region.config());
}
for (const ExtrusionEntity *ee : *eec)
for (const ExtrusionEntity *ee : *eec) {
gcode += this->extrude_entity(*ee, comment_perimeter, -1.);
}
} else {
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static int iRun = 0;
eec->export_to_svg(debug_out_path("Layer-%d_gcode-process_perimeters-not-printed-%u.svg", layerm.layer()->id(), iRun++).c_str());
#endif
}
}
};
@ -2535,6 +2571,7 @@ void GCode::process_layer_single_object(
it = it_end;
}
};
BOOST_LOG_TRIVIAL(trace) << "Generating GCode for layer " << layer->id() << " island - " << island.fills.size() << " fills, " << island.perimeters.size() << " perimeters.";
if (print.config().infill_first) {
process_infill();
process_perimeters();
@ -2558,6 +2595,7 @@ void GCode::process_layer_single_object(
}
}
}
}
if (! first && this->config().gcode_label_objects)
gcode += std::string("; stop printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n";
}
@ -2793,6 +2831,11 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, const std::s
std::string GCode::extrude_entity(const ExtrusionEntity &entity, const std::string_view description, double speed)
{
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static int extrude_infill_range_run = 0;
export_to_svg(debug_out_path("gcode-extrude_entity-%u.svg", extrude_infill_range_run++).c_str(),
entity.as_polyline(), get_extents(entity.as_polyline()), scale_(0.1f));
#endif
if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity))
return this->extrude_path(*path, description, speed);
else if (const ExtrusionMultiPath* multipath = dynamic_cast<const ExtrusionMultiPath*>(&entity))
@ -2943,7 +2986,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
acceleration = m_config.first_layer_acceleration_over_raft.value;
} else if (m_config.bridge_acceleration.value > 0 && path.role().is_bridge()) {
acceleration = m_config.bridge_acceleration.value;
} else if (m_config.top_solid_infill_acceleration > 0 && path.role() == ExtrusionRole::TopSolidInfill) {
} else if (m_config.top_solid_infill_acceleration > 0 && (path.role() == ExtrusionRole::TopSolidInfill || path.role() == ExtrusionRole::TopSolidInfillNonplanar)) {
acceleration = m_config.top_solid_infill_acceleration.value;
} else if (m_config.solid_infill_acceleration > 0 && path.role().is_solid_infill()) {
acceleration = m_config.solid_infill_acceleration.value;
@ -2976,9 +3019,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
speed = m_config.get_abs_value("bridge_speed");
} else if (path.role() == ExtrusionRole::InternalInfill) {
speed = m_config.get_abs_value("infill_speed");
} else if (path.role() == ExtrusionRole::SolidInfill) {
} else if (path.role() == ExtrusionRole::SolidInfill || path.role() == ExtrusionRole::SolidInfillNonplanar) {
speed = m_config.get_abs_value("solid_infill_speed");
} else if (path.role() == ExtrusionRole::TopSolidInfill) {
} else if (path.role() == ExtrusionRole::TopSolidInfill || path.role() == ExtrusionRole::TopSolidInfillNonplanar) {
speed = m_config.get_abs_value("top_solid_infill_speed");
} else if (path.role() == ExtrusionRole::Ironing) {
speed = m_config.get_abs_value("ironing_speed");
@ -3118,15 +3161,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
comment = description;
comment += description_bridge;
}
Vec2d prev = this->point_to_gcode_quantized(path.polyline.points.front());
Vec3d prev3 = this->point3_to_gcode_quantized(path.polyline.points.front());
auto it = path.polyline.points.begin();
auto end = path.polyline.points.end();
for (++ it; it != end; ++ it) {
Vec2d p = this->point_to_gcode_quantized(*it);
const double line_length = (p - prev).norm();
Vec3d p3 = this->point3_to_gcode_quantized(*it);
const double line_length = (p3 - prev3).norm();
path_length += line_length;
gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment);
prev = p;
gcode += m_writer.extrude_to_xyz(p3, e_per_mm * line_length, comment);
prev3 = p3;
}
} else {
std::string marked_comment;
@ -3138,13 +3181,13 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
double last_set_fan_speed = new_points[0].fan_speed;
gcode += m_writer.set_speed(last_set_speed, "", cooling_marker_setspeed_comments);
gcode += "\n;_SET_FAN_SPEED" + std::to_string(int(last_set_fan_speed)) + "\n";
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
Vec3d prev3 = this->point3_to_gcode_quantized(new_points[0].p);
for (size_t i = 1; i < new_points.size(); i++) {
const ProcessedPoint &processed_point = new_points[i];
Vec2d p = this->point_to_gcode_quantized(processed_point.p);
const double line_length = (p - prev).norm();
gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, marked_comment);
prev = p;
Vec3d p3 = this->point3_to_gcode_quantized(processed_point.p);
const double line_length = (p3 - prev3).norm();
gcode += m_writer.extrude_to_xyz(p3, e_per_mm * line_length, marked_comment);
prev3 = p3;
double new_speed = processed_point.speed * 60.0;
if (last_set_speed != new_speed) {
gcode += m_writer.set_speed(new_speed, "", cooling_marker_setspeed_comments);
@ -3186,6 +3229,8 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
// check whether a straight travel move would need retraction
bool needs_retraction = this->needs_retraction(travel, role);
// check whether we need to move to a different z layer
bool needs_zmove = this->needs_zmove(travel);
// check whether wipe could be disabled without causing visible stringing
bool could_be_wipe_disabled = false;
// Save state of use_external_mp_once for the case that will be needed to call twice m_avoid_crossing_perimeters.travel_to.
@ -3232,10 +3277,20 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
// use G1 because we rely on paths being straight (G0 may make round paths)
if (travel.size() >= 2) {
// Move Z up if necessary
if (needs_zmove) {
gcode += m_writer.travel_to_z(this->layer()->print_z, "Move up for non planar extrusion");
}
gcode += m_writer.set_travel_acceleration((unsigned int)(m_config.travel_acceleration.value + 0.5));
for (size_t i = 1; i < travel.size(); ++ i)
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
for (size_t i = 1; i < travel.size(); ++ i) {
if (needs_zmove) {
gcode += m_writer.travel_to_xyz(this->point3_to_gcode(travel.points[i]), comment);
} else {
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
}
}
if (! GCodeWriter::supports_separate_travel_acceleration(config().gcode_flavor)) {
// In case that this flavor does not support separate print and travel acceleration,
@ -3243,8 +3298,18 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
gcode += m_writer.set_travel_acceleration((unsigned int)(m_config.travel_acceleration.value + 0.5));
}
if (needs_zmove) {
float move_z = unscale<double>(point.nonplanar_z);
if(point.nonplanar_z == -1) {
move_z = this->layer()->print_z;
}
gcode += m_writer.travel_to_z(move_z, "Move down for non planar extrusion");
}
this->set_last_pos(travel.points.back());
}
return gcode;
}
@ -3287,6 +3352,24 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
return true;
}
bool
GCode::needs_zmove(const Polyline &travel)
{
if (travel.length() < scale_(1.0)) {
// skip zmove if the move is shorter 1 mm
return false;
}
//check if any point in travel is below the layer z
for (Point p : travel.points)
{
if ((p.nonplanar_z != -1) && (p.nonplanar_z < scale_(this->layer()->print_z)))
return true;
}
return false;
}
std::string GCode::retract(bool toolchange)
{
std::string gcode;
@ -3421,12 +3504,28 @@ Vec2d GCode::point_to_gcode(const Point &point) const
return unscaled<double>(point) + m_origin - extruder_offset;
}
// convert a model-space scaled point into G-code coordinates
Vec3d GCode::point3_to_gcode(const Point &point) const
{
Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset);
double p_x = unscaled<double>(point.x()) + m_origin.x() - extruder_offset.x();
double p_y = unscaled<double>(point.y()) + m_origin.y() - extruder_offset.y();
double p_z = point.nonplanar_z == -1 ? this->layer()->print_z : unscale<double>(point.nonplanar_z);
return { p_x, p_y, p_z };
}
Vec2d GCode::point_to_gcode_quantized(const Point &point) const
{
Vec2d p = this->point_to_gcode(point);
return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) };
}
Vec3d GCode::point3_to_gcode_quantized(const Point &point) const
{
Vec3d p = this->point3_to_gcode(point);
return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()), GCodeFormatter::quantize_xyzf(p.z()) };
}
// convert a model-space scaled point into G-code coordinates
Point GCode::gcode_to_point(const Vec2d &point) const
{
@ -3435,7 +3534,6 @@ Point GCode::gcode_to_point(const Vec2d &point) const
// This function may be called at the very start from toolchange G-code when the extruder is not assigned yet.
pt += m_config.extruder_offset.get_at(extruder->id());
return scaled<coord_t>(pt);
}
} // namespace Slic3r

View File

@ -166,8 +166,10 @@ public:
const Point& last_pos() const { return m_last_pos; }
// Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset.
Vec2d point_to_gcode(const Point &point) const;
Vec3d point3_to_gcode(const Point &point) const;
// Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset and quantized to G-code resolution.
Vec2d point_to_gcode_quantized(const Point &point) const;
Vec3d point3_to_gcode_quantized(const Point &point) const;
Point gcode_to_point(const Vec2d &point) const;
const FullPrintConfig &config() const { return m_config; }
const Layer* layer() const { return m_layer; }
@ -326,6 +328,7 @@ private:
std::string travel_to(const Point &point, ExtrusionRole role, std::string comment);
bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None);
bool needs_zmove(const Polyline &travel);
std::string retract(bool toolchange = false);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z);

View File

@ -274,7 +274,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
// FIXME: This function was not being used when travel_speed_z was separated (bd6badf).
// Calculation of feedrate was not updated accordingly. If you want to use
// this function, fix it first.
std::terminate();
// std::terminate();
/* If target Z is lower than current Z but higher than nominal Z we
don't perform the Z move but we only move in the XY plane and
@ -360,16 +360,16 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std:
return w.string();
}
#if 0
#if 1
std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment)
{
m_pos = point;
m_lifted = 0;
m_extruder->extrude(dE);
//m_extruder->extrude(dE);
GCodeG1Formatter w;
w.emit_xyz(point);
w.emit_e(m_extrusion_axis, m_extruder->E());
w.emit_e(m_extrusion_axis, m_extruder->extrude(dE).second);
w.emit_comment(this->config.gcode_comments, comment);
return w.string();
}

View File

@ -62,7 +62,7 @@ public:
std::string travel_to_z(double z, const std::string &comment = std::string());
bool will_move_z(double z) const;
std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string());
// std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string());
std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string());
std::string retract(bool before_wipe = false);
std::string retract_for_toolchange(bool before_wipe = false);
std::string unretract();

View File

@ -746,4 +746,74 @@ bool trafos_differ_in_rotation_by_z_and_mirroring_by_xy_only(const Transform3d &
return std::abs(d * d) < EPSILON * lx2 * ly2;
}
bool
Point_in_triangle(Vec2f pt, Vec2f v1, Vec2f v2, Vec2f v3)
{
//Check if point is right of every edge
if (sign(pt, v1, v2) <= 0.0f) return false;
if (sign(pt, v2, v3) <= 0.0f) return false;
if (sign(pt, v3, v1) <= 0.0f) return false;
return true;
}
//https://graphics.stanford.edu/~mdfisher/Code/Engine/Plane.cpp.html
coord_t
Project_point_on_plane(Vec3f v1, Vec3f n, Point pt)
{
//if no intersection leave point unchanged (should never happen)
if(n.z() == 0) {
return -1;
}
//unscale point for calculations
float px = unscale<float>(pt.x());
float py = unscale<float>(pt.y());
float pz = 0;
//Calculate space plane
float d = -(v1.x() * n.x() + v1.y() * n.y() + v1.z() * n.z());
float u = -(n.x() * px + n.y() * py + n.z() * pz + d) / n.z();
//scale up again
return scale_(u);
}
// http://paulbourke.net/geometry/pointlineplane/index.html
Vec3d*
Line_intersection(Vec3d p1, Vec3d p2, Point p3, Point p4) {
float denom = ((p4.y() - p3.y())*(p2.x() - p1.x())) -
((p4.x() - p3.x())*(p2.y() - p1.y()));
float nume_a = ((p4.x() - p3.x())*(p1.y() - p3.y())) -
((p4.y() - p3.y())*(p1.x() - p3.x()));
float nume_b = ((p2.x() - p1.x())*(p1.y() - p3.y())) -
((p2.y() - p1.y())*(p1.x() - p3.x()));
if(denom == 0.0f)
{
return NULL;
}
float ua = nume_a / denom;
float ub = nume_b / denom;
if(ua >= 0.0f && ua <= 1.0f && ub >= 0.0f && ub <= 1.0f)
{
// Get the intersection point anc calculate z component
Vec3d* ret = new Vec3d();
ret->x() = p1.x() + ua*(p2.x() - p1.x());
ret->y() = p1.y() + ua*(p2.y() - p1.y());
ret->z() = p1.z() - ((sqrt((p1.x()-ret->x())*(p1.x()-ret->x()) + (p1.y()-ret->y())*(p1.y()-ret->y()))
/ sqrt((p1.x()-p2.x())*(p1.x()-p2.x()) + (p1.y()-p2.y())*(p1.y()-p2.y())))
* (p1.z() - p2.z()));
return ret;
}
return NULL;
}
}} // namespace Slic3r::Geometry

View File

@ -529,6 +529,23 @@ Vec<3, T> spheric_to_dir(const Pair &v)
return spheric_to_dir<T>(plr, azm);
}
inline float sign(Vec2f p1, Vec2f p2, Vec2f p3)
{
return (p1.x() - p3.x()) * (p2.y() - p3.y()) - (p2.x() - p3.x()) * (p1.y() - p3.y());
}
bool Point_in_triangle(Vec2f pt, Vec2f v1, Vec2f v2, Vec2f v3);
//https://graphics.stanford.edu/~mdfisher/Code/Engine/Plane.cpp.html
coord_t Project_point_on_plane(Vec3f v1, Vec3f n, Point pt);
// http://paulbourke.net/geometry/pointlineplane/index.html
Vec3d *Line_intersection(Vec3d p1, Vec3d p2, Point p3, Point p4);
inline float triangle_surface(Point p1, Point p2, Point p3) {
return 0.5 * ((p2.x()-p1.x()) * (p3.y()-p1.y()) - (p2.y()-p1.y()) * (p3.x()-p1.x()));
}
} } // namespace Slicer::Geometry
#endif

View File

@ -54,6 +54,8 @@ void Layer::make_slices()
}
// lslices are sorted by topological order from outside to inside from the clipper union used above
this->lslices = slices;
BOOST_LOG_TRIVIAL(trace) << "make_slices layer " << id() << " found " << slices.size() << " slices";
}
this->lslice_indices_sorted_by_print_order = chain_expolygons(this->lslices);
@ -1066,7 +1068,7 @@ void Layer::export_region_slices_to_svg(const char *path) const
void Layer::export_region_slices_to_svg_debug(const char *name) const
{
static size_t idx = 0;
this->export_region_slices_to_svg(debug_out_path("Layer-slices-%s-%d.svg", name, idx ++).c_str());
this->export_region_slices_to_svg(debug_out_path("Layer-%d-slices-%s-%d.svg", id(), name, idx ++).c_str());
}
void Layer::export_region_fill_surfaces_to_svg(const char *path) const
@ -1092,7 +1094,57 @@ void Layer::export_region_fill_surfaces_to_svg(const char *path) const
void Layer::export_region_fill_surfaces_to_svg_debug(const char *name) const
{
static size_t idx = 0;
this->export_region_fill_surfaces_to_svg(debug_out_path("Layer-fill_surfaces-%s-%d.svg", name, idx ++).c_str());
this->export_region_fill_surfaces_to_svg(debug_out_path("Layer-%d-fill_surfaces-%s-%d.svg", id(), name, idx ++).c_str());
}
void Layer::export_lslices_polygons_to_svg(const char *path) const
{
BoundingBox bbox;
for (const auto *region : m_regions)
for (const auto &surface : region->slices())
bbox.merge(get_extents(surface.expolygon));
SVG svg(path, bbox);
for (int i = 0; i< lslices.size(); i++)
svg.draw(lslices[i], "black", 1.0);
svg.Close();
}
// Export to "out/Layer-%name-%idx.svg" with an increasing index with every export.
void Layer::export_lslices_polygons_to_svg_debug(const char *name) const
{
static size_t idx = 0;
this->export_lslices_polygons_to_svg(debug_out_path("Layer-%d-lslice_polygons-%s-%d.svg", id(), name, idx ++).c_str());
}
void
LayerRegion::remove_nonplanar_slices(SurfaceCollection topNonplanar) {
Surfaces layerm_slices_surfaces(m_slices.surfaces);
//save previously detected nonplanar surfaces
SurfaceCollection polyNonplanar;
for(Surface s : m_slices.surfaces) {
if (s.is_nonplanar()) {
polyNonplanar.surfaces.push_back(s);
}
}
// clear internal surfaces
m_slices.clear();
// append internal surfaces again without the found topNonplanar surfaces
m_slices.append(
diff_ex(
union_ex(layerm_slices_surfaces),
topNonplanar.surfaces,
ApplySafetyOffset::No),
stInternal
);
}
void
LayerRegion::append_top_nonplanar_slices(SurfaceCollection topNonplanar) {
m_slices.append(std::move(topNonplanar));
}
BoundingBox get_extents(const LayerRegion &layer_region)

View File

@ -7,6 +7,7 @@
#include "Flow.hpp"
#include "SurfaceCollection.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "NonplanarSurface.hpp"
#include <boost/container/small_vector.hpp>
@ -132,6 +133,9 @@ public:
// ordered collection of extrusion paths to fill surfaces
// (this collection contains only ExtrusionEntityCollection objects)
[[nodiscard]] const ExtrusionEntityCollection& fills() const { return m_fills; }
// Vector of nonplanar_surfaces which are homed in this layer
[[nodiscard]] const NonplanarSurfaces& nonplanar_surfaces() const { return m_nonplanar_surfaces; }
Flow flow(FlowRole role) const;
Flow flow(FlowRole role, double layer_height) const;
@ -167,6 +171,20 @@ public:
// Is there any valid extrusion assigned to this LayerRegion?
bool has_extrusions() const { return ! this->perimeters().empty() || ! this->fills().empty(); }
//append a new nonplanar surface to the list skip if already in list
void append_nonplanar_surface(NonplanarSurface& surface);
// Projects the paths of a collection regarding the structure of a stl mesh
void project_nonplanar_extrusion(ExtrusionEntityCollection* collection);
/// Projects nonplanar surfaces downwards regarding the structure of the stl mesh.
void project_nonplanar_surfaces();
///project a nonplanar path
void project_nonplanar_path(ExtrusionPath* path);
///sets the z coordinates correctly for all points TODO move to generation of points
void correct_z_on_path(ExtrusionPath *path);
//
void remove_nonplanar_slices(SurfaceCollection topNonplanar);
void append_top_nonplanar_slices(SurfaceCollection topNonplanar);
protected:
friend class Layer;
friend class PrintObject;
@ -225,6 +243,8 @@ private:
// (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection m_fills;
NonplanarSurfaces m_nonplanar_surfaces;
// collection of expolygons representing the bridged areas (thus not
// needing support material)
// Polygons bridged;
@ -379,9 +399,11 @@ public:
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
void export_lslices_polygons_to_svg(const char *name) const;
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
void export_lslices_polygons_to_svg_debug(const char *name) const;
// Is there any valid extrusion assigned to this LayerRegion?
virtual bool has_extrusions() const { for (auto layerm : m_regions) if (layerm->has_extrusions()) return true; return false; }

View File

@ -463,10 +463,16 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
Surfaces tops = expand_merge_surfaces(m_fill_surfaces.surfaces, stTop, shells,
RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps),
sparse, expansion_params_into_sparse_infill, closing_radius);
Surfaces nonplanarTops = expand_merge_surfaces(m_fill_surfaces.surfaces, stTopNonplanar, shells,
RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps),
sparse, expansion_params_into_sparse_infill, closing_radius);
Surfaces nonplanarInternals = expand_merge_surfaces(m_fill_surfaces.surfaces, stInternalSolidNonplanar, shells,
RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps),
sparse, expansion_params_into_sparse_infill, closing_radius);
// m_fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternal, stInternalSolid });
m_fill_surfaces.clear();
reserve_more(m_fill_surfaces.surfaces, shells.size() + sparse.size() + bridges.size() + bottoms.size() + tops.size());
reserve_more(m_fill_surfaces.surfaces, shells.size() + sparse.size() + bridges.size() + bottoms.size() + tops.size() + nonplanarTops.size() + nonplanarInternals.size());
{
Surface solid_templ(stInternalSolid, {});
solid_templ.thickness = layer_thickness;
@ -480,6 +486,8 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
m_fill_surfaces.append(std::move(bridges.surfaces));
m_fill_surfaces.append(std::move(bottoms));
m_fill_surfaces.append(std::move(tops));
m_fill_surfaces.append(std::move(nonplanarTops));
m_fill_surfaces.append(std::move(nonplanarInternals));
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug("4_process_external_surfaces-final");
@ -876,7 +884,7 @@ void LayerRegion::export_region_slices_to_svg_debug(const char *name) const
{
static std::map<std::string, size_t> idx_map;
size_t &idx = idx_map[name];
this->export_region_slices_to_svg(debug_out_path("LayerRegion-slices-%s-%d.svg", name, idx ++).c_str());
this->export_region_slices_to_svg(debug_out_path("LayerRegion-%d-slices-%s-%d.svg", layer()->id(), name, idx ++).c_str());
}
void LayerRegion::export_region_fill_surfaces_to_svg(const char *path) const
@ -903,7 +911,246 @@ void LayerRegion::export_region_fill_surfaces_to_svg_debug(const char *name) con
{
static std::map<std::string, size_t> idx_map;
size_t &idx = idx_map[name];
this->export_region_fill_surfaces_to_svg(debug_out_path("LayerRegion-fill_surfaces-%s-%d.svg", name, idx ++).c_str());
this->export_region_fill_surfaces_to_svg(debug_out_path("LayerRegion-%d-fill_surfaces-%s-%d.svg", layer()->id(), name, idx ++).c_str());
}
void
LayerRegion::append_nonplanar_surface(NonplanarSurface& surface)
{
for(auto & s : m_nonplanar_surfaces){
if (s == surface){
return;
}
}
m_nonplanar_surfaces.push_back(surface);
}
void
LayerRegion::project_nonplanar_extrusion(ExtrusionEntityCollection *collection)
{
for (auto& entity : collection->entities) {
if (ExtrusionLoop* loop = dynamic_cast<ExtrusionLoop*>(entity)) {
BOOST_LOG_TRIVIAL(trace) << "Projecting extrusion loop with " << loop->paths.size() << " paths";
for(auto& path : loop->paths) {
project_nonplanar_path(&path);
correct_z_on_path(&path);
}
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0;
export_to_svg(debug_out_path("Layer-%d_project_extrusion_loop-%u-after.svg", layer()->id(), iRun ++).c_str(),
loop->as_polyline(), get_extents(this->slices().surfaces), scale_(0.1f));
}
#endif
} else if (ExtrusionMultiPath* multipath = dynamic_cast<ExtrusionMultiPath*>(entity)) {
BOOST_LOG_TRIVIAL(trace) << "Projecting extrusion multipath with " << multipath->paths.size() << " paths";
for (auto& path : multipath->paths) {
project_nonplanar_path(&path);
correct_z_on_path(&path);
}
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0;
export_to_svg(debug_out_path("Layer-%d_project_extrusion_multipath-%u-after.svg", layer()->id(), iRun ++).c_str(),
multipath->as_polyline(), get_extents(this->slices().surfaces), scale_(0.1f));
}
#endif
} else if (ExtrusionPath* path = dynamic_cast<ExtrusionPath*>(entity)) {
BOOST_LOG_TRIVIAL(trace) << "Projecting 1 extrusion path";
project_nonplanar_path(path);
correct_z_on_path(path);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static int iRun = 0;
export_to_svg(debug_out_path("Layer-%d_project_extrusion_path-%u-after.svg", layer()->id(), iRun ++).c_str(),
path->as_polyline(), get_extents(this->slices().surfaces), scale_(0.1f));
}
#endif
} else {
BOOST_LOG_TRIVIAL(trace) << "Unknown entity class " << typeid(entity).name();
assert(false);
}
}
}
void
LayerRegion::project_nonplanar_surfaces()
{
// skip if there are no nonplanar_surfaces on this LayerRegion
if (m_nonplanar_surfaces.size() == 0) {
return;
}
BOOST_LOG_TRIVIAL(trace) << "Projecting " << m_nonplanar_surfaces.size() << " nonplanar surfaces for region " <<
region().print_region_id() << " and layer " << layer()->id() << " (z = " << layer()->print_z << ")";
// for all perimeters do path projection
BOOST_LOG_TRIVIAL(trace) << "Projecting " << m_perimeters.size() << " nonplanar perimeters";
for (auto& col : m_perimeters.entities) {
ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(col);
this->project_nonplanar_extrusion(collection);
}
// and all fill paths do path projection
BOOST_LOG_TRIVIAL(trace) << "Projecting " << m_fills.size() << " nonplanar fills";
for (auto& col : m_fills.entities) {
ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(col);
this->project_nonplanar_extrusion(collection);
}
}
//Sorting functions for soring of paths
bool
greaterX(const Vec3d &a, const Vec3d &b)
{
return (a.x() < b.x());
}
bool
smallerX(const Vec3d &a, const Vec3d &b)
{
return (a.x() > b.x());
}
bool
greaterY(const Vec3d &a, const Vec3d &b)
{
return (a.y() < b.y());
}
bool
smallerY(const Vec3d &a, const Vec3d &b)
{
return (a.y() > b.y());
}
void
LayerRegion::project_nonplanar_path(ExtrusionPath *path)
{
//First check all points and project them regarding the triangle mesh
for (Point& point : path->polyline.points) {
for (auto& surface : m_nonplanar_surfaces) {
float distance_to_top = surface.stats.max.z - this->layer()->print_z;
for(auto& facet : surface.mesh) {
// skip if point is outside of the bounding box of the triangle
if (unscale(point).x() < std::min({facet.second.vertex[0].x, facet.second.vertex[1].x, facet.second.vertex[2].x}) ||
unscale(point).x() > std::max({facet.second.vertex[0].x, facet.second.vertex[1].x, facet.second.vertex[2].x}) ||
unscale(point).y() < std::min({facet.second.vertex[0].y, facet.second.vertex[1].y, facet.second.vertex[2].y}) ||
unscale(point).y() > std::max({facet.second.vertex[0].y, facet.second.vertex[1].y, facet.second.vertex[2].y}))
{
continue;
}
//check if point is inside of Triangle
if (Slic3r::Geometry::Point_in_triangle(
Vec2f(unscale(point).x(), unscale(point).y()),
Vec2f(facet.second.vertex[0].x, facet.second.vertex[0].y),
Vec2f(facet.second.vertex[1].x, facet.second.vertex[1].y),
Vec2f(facet.second.vertex[2].x, facet.second.vertex[2].y))
&& (facet.second.normal.z != 0))
{
coord_t z = Slic3r::Geometry::Project_point_on_plane(Vec3f(facet.second.vertex[0].x,facet.second.vertex[0].y,facet.second.vertex[0].z),
Vec3f(facet.second.normal.x,facet.second.normal.y,facet.second.normal.z),
point);
//Shift down when on lower layer
point.nonplanar_z = z - scale_(distance_to_top);
//break;
}
}
}
}
// Then check all line intersections, cut line on intersection and project new point
std::vector<Vec3crd>::size_type size = path->polyline.points.size();
for (std::vector<Vec3crd>::size_type i = 0; i < size-1; ++i)
{
Pointf3s intersections;
// check against every facet if lines intersect
for (auto& surface : m_nonplanar_surfaces) {
float distance_to_top = surface.stats.max.z - this->layer()->print_z;
for(auto& facet : surface.mesh) {
for(int j= 0; j < 3; j++) {
// TODO precheck for faster computation
Vec3d p1 = Vec3d(scale_(facet.second.vertex[j].x), scale_(facet.second.vertex[j].y), scale_(facet.second.vertex[j].z));
Vec3d p2 = Vec3d(scale_(facet.second.vertex[(j+1) % 3].x), scale_(facet.second.vertex[(j+1) % 3].y), scale_(facet.second.vertex[(j+1) % 3].z));
Vec3d* p = Slic3r::Geometry::Line_intersection(p1, p2, path->polyline.points[i], path->polyline.points[i+1]);
if (p) {
// add distance to top for every added point
p->z() = p->z() - scale_(distance_to_top);
intersections.push_back(*p);
}
}
}
}
// Stop if no intersections are found
if (intersections.size() == 0) continue;
// sort found intersectons if there are more than 1
if ( intersections.size() > 1 ){
if (abs(path->polyline.points[i+1].x() - path->polyline.points[i].x()) >= abs(path->polyline.points[i+1].y() - path->polyline.points[i].y()) ) {
// sort by X
if(path->polyline.points[i].x() < path->polyline.points[i+1].x()) {
std::sort(intersections.begin(), intersections.end(), smallerX);
}else {
std::sort(intersections.begin(), intersections.end(), greaterX);
}
} else {
// sort by Y
if(path->polyline.points[i].y() < path->polyline.points[i+1].y()) {
std::sort(intersections.begin(), intersections.end(), smallerY);
}else {
std::sort(intersections.begin(), intersections.end(), greaterY);
}
}
}
// remove duplicates
Pointf3s::iterator point = intersections.begin();
while (point != intersections.end()-1) {
bool delete_point = false;
Pointf3s::iterator point2 = point;
++point2;
//compare with next point if they are the same, delete current point
if ((*point).x() == (*point2).x() && (*point).y() == (*point2).y()) {
//remove duplicate point
delete_point = true;
point = intersections.erase(point);
}
//continue loop when no point is removed. Otherwise the new point is set while deleting the old one.
if (!delete_point) {
++point;
}
}
//insert new points into array
for (Vec3d p : intersections)
{
Point *pt = new Point(p.x(), p.y());
pt->nonplanar_z = p.z();
path->polyline.points.insert(path->polyline.points.begin()+i+1, *pt);
}
//modifiy array boundary
i = i + intersections.size();
size = size + intersections.size();
}
}
void
LayerRegion::correct_z_on_path(ExtrusionPath *path)
{
for (Point& point : path->polyline.points) {
if(point.nonplanar_z == -1) {
point.nonplanar_z = scale_(this->layer()->print_z);
}
}
}
}

View File

@ -103,8 +103,28 @@ bool MultiPoint::remove_duplicate_points()
return false;
}
bool
MultiPoint::needs_zmove(const Points &pts)
{
coord_t z = -1;
//check if any point in travel is below the layer z
for (Point p : pts)
{
if(z == -1) {
z = p.nonplanar_z;
}
else if ((p.nonplanar_z != -1) && (p.nonplanar_z != z))
return true;
}
return false;
}
Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance)
{
if(MultiPoint::needs_zmove(pts))
return pts;
Points result_pts;
auto tolerance_sq = int64_t(sqr(tolerance));
if (! pts.empty()) {

View File

@ -81,6 +81,7 @@ public:
}
}
static bool needs_zmove(const Points &pts);
static Points douglas_peucker(const Points &points, const double tolerance);
static Points visivalingam(const Points& pts, const double& tolerance);

View File

@ -0,0 +1,72 @@
#include "NonplanarFacet.hpp"
namespace Slic3r {
void
NonplanarFacet::calculate_stats() {
//calculate min and max values
this->stats.min.x = this->vertex[0].x;
this->stats.min.y = this->vertex[0].y;
this->stats.min.z = this->vertex[0].z;
this->stats.max.x = this->vertex[0].x;
this->stats.max.y = this->vertex[0].y;
this->stats.max.z = this->vertex[0].z;
for(int j = 1; j < 3; j++) {
this->stats.min.x = std::min(this->stats.min.x, this->vertex[j].x);
this->stats.min.y = std::min(this->stats.min.y, this->vertex[j].y);
this->stats.min.z = std::min(this->stats.min.z, this->vertex[j].z);
this->stats.max.x = std::max(this->stats.max.x, this->vertex[j].x);
this->stats.max.y = std::max(this->stats.max.y, this->vertex[j].y);
this->stats.max.z = std::max(this->stats.max.z, this->vertex[j].z);
}
}
void
NonplanarFacet::translate(float x, float y, float z)
{
//translate facet
for(int j = 0; j < 3; j++) {
this->vertex[j].x += x;
this->vertex[j].y += y;
this->vertex[j].z += z;
}
//translate min and max values
this->stats.min.x += x;
this->stats.min.y += y;
this->stats.min.z += z;
this->stats.max.x += x;
this->stats.max.y += y;
this->stats.max.z += z;
}
void
NonplanarFacet::scale(float versor[3])
{
//scale facet
for(int j = 0; j < 3; j++) {
this->vertex[j].x *= versor[0];
this->vertex[j].y *= versor[1];
this->vertex[j].z *= versor[2];
}
//scale min and max values
this->stats.min.x *= versor[0];
this->stats.min.y *= versor[1];
this->stats.min.z *= versor[2];
this->stats.max.x *= versor[0];
this->stats.max.y *= versor[1];
this->stats.max.z *= versor[2];
}
float
NonplanarFacet::calculate_surface_area()
{
return Slic3r::Geometry::triangle_surface(
Point(this->vertex[0].x, this->vertex[0].y),
Point(this->vertex[1].x, this->vertex[1].y),
Point(this->vertex[2].x, this->vertex[2].y)
);
}
}

View File

@ -0,0 +1,39 @@
#ifndef slic3r_NonplanarFacet_hpp_
#define slic3r_NonplanarFacet_hpp_
#include "libslic3r.h"
#include "Geometry.hpp"
namespace Slic3r {
typedef struct {
float x;
float y;
float z;
} facet_vertex;
typedef struct {
facet_vertex max;
facet_vertex min;
} facet_stats;
class NonplanarFacet
{
public:
facet_vertex vertex[3];
facet_vertex normal;
int neighbor[3];
facet_stats stats;
bool marked = false;
NonplanarFacet() {};
~NonplanarFacet() {};
void calculate_stats();
void translate(float x, float y, float z);
void scale(float versor[3]);
float calculate_surface_area();
};
};
#endif

View File

@ -0,0 +1,240 @@
#include "NonplanarSurface.hpp"
namespace Slic3r {
NonplanarSurface::NonplanarSurface(std::map<int, NonplanarFacet> &_mesh)
{
this->mesh = _mesh;
this->calculate_stats();
}
bool
NonplanarSurface::operator==(const NonplanarSurface& other) const {
return (stats.min.x == other.stats.min.x &&
stats.min.y == other.stats.min.y &&
stats.min.z == other.stats.min.z &&
stats.max.x == other.stats.max.x &&
stats.max.y == other.stats.max.y &&
stats.max.z == other.stats.max.z);
}
void
NonplanarSurface::calculate_stats()
{
//calculate min and max values
this->stats.min.x = 10000000;
this->stats.min.y = 10000000;
this->stats.min.z = 10000000;
this->stats.max.x = -10000000;
this->stats.max.y = -10000000;
this->stats.max.z = -10000000;
for(auto& facet : this->mesh) {
this->stats.min.x = std::min(this->stats.min.x, facet.second.stats.min.x);
this->stats.min.y = std::min(this->stats.min.y, facet.second.stats.min.y);
this->stats.min.z = std::min(this->stats.min.z, facet.second.stats.min.z);
this->stats.max.x = std::max(this->stats.max.x, facet.second.stats.max.x);
this->stats.max.y = std::max(this->stats.max.y, facet.second.stats.max.y);
this->stats.max.z = std::max(this->stats.max.z, facet.second.stats.max.z);
}
}
void
NonplanarSurface::translate(float x, float y, float z)
{
//translate all facets
for(auto& facet : this->mesh) {
facet.second.translate(x, y, z);
}
//translate min and max values
this->stats.min.x += x;
this->stats.min.y += y;
this->stats.min.z += z;
this->stats.max.x += x;
this->stats.max.y += y;
this->stats.max.z += z;
}
void
NonplanarSurface::scale(float factor)
{
float versor[3];
versor[0] = factor;
versor[1] = factor;
versor[2] = factor;
this->scale(versor);
}
void
NonplanarSurface::scale(float versor[3])
{
//scale all facets
for(auto& facet : this->mesh) {
facet.second.scale(versor);
}
//scale min and max values
this->stats.min.x *= versor[0];
this->stats.min.y *= versor[1];
this->stats.min.z *= versor[2];
this->stats.max.x *= versor[0];
this->stats.max.y *= versor[1];
this->stats.max.z *= versor[2];
}
void
NonplanarSurface::rotate_z(float angle) {
double radian_angle = (angle / 180.0) * 3.1415927;
double c = cos(radian_angle);
double s = sin(radian_angle);
for(auto& facet : this->mesh) {
for(int j = 0; j < 3; j++) {
double xold = facet.second.vertex[j].x;
double yold = facet.second.vertex[j].y;
facet.second.vertex[j].x = c * xold - s * yold;
facet.second.vertex[j].y = s * xold + c * yold;
}
facet.second.calculate_stats();
}
this->calculate_stats();
}
void
NonplanarSurface::debug_output()
{
std::cout << "Facets(" << this->mesh.size() << "): (min:X:" << this->stats.min.x << " Y:" << this->stats.min.y << " Z:" << this->stats.min.z <<
" max:X:" << this->stats.max.x << " Y:" << this->stats.max.y << " Z:" << this->stats.max.z << ")" <<
"Height " << this->stats.max.z - this->stats.min.z << std::endl;
for(auto& facet : this->mesh) {
std::cout << "triangle: (" << facet.first << ")(" << facet.second.marked << ") ";
std::cout << " (" << (180*std::acos(facet.second.normal.z))/3.14159265 << "°)";
std::cout << " | V0:";
std::cout << " X:"<< facet.second.vertex[0].x;
std::cout << " Y:"<< facet.second.vertex[0].y;
std::cout << " Z:"<< facet.second.vertex[0].z;
std::cout << " | V1:";
std::cout << " X:"<< facet.second.vertex[1].x;
std::cout << " Y:"<< facet.second.vertex[1].y;
std::cout << " Z:"<< facet.second.vertex[1].z;
std::cout << " | V2:";
std::cout << " X:"<< facet.second.vertex[2].x;
std::cout << " Y:"<< facet.second.vertex[2].y;
std::cout << " Z:"<< facet.second.vertex[2].z;
std::cout << " | Normal:";
std::cout << " X:"<< facet.second.normal.x;
std::cout << " Y:"<< facet.second.normal.y;
std::cout << " Z:"<< facet.second.normal.z;
//TODO check if neighbors exist
// stl_neighbors* neighbors = mesh.stl.neighbors_start + facet.first;
std::cout << " | Neighbors:";
std::cout << " 0:"<< facet.second.neighbor[0];
std::cout << " 1:"<< facet.second.neighbor[1];
std::cout << " 2:"<< facet.second.neighbor[2];
std::cout << std::endl;
}
}
NonplanarSurfaces
NonplanarSurface::group_surfaces()
{
std::pair<int, NonplanarFacet> begin = *this->mesh.begin();
this->mark_neighbor_surfaces(begin.first);
NonplanarSurface newSurface;
for (std::map<int, NonplanarFacet>::iterator it=this->mesh.begin(); it!=this->mesh.end();) {
if((*it).second.marked == false) {
newSurface.mesh[(*it).first] = (*it).second;
it = this->mesh.erase(it);
}
else {
++it;
}
}
this->calculate_stats();
if (newSurface.mesh.size() == 0) {
//return only this
NonplanarSurfaces nonplanar_surfaces;
nonplanar_surfaces.push_back(*this);
return nonplanar_surfaces;
}
else {
//return union of this and recursion
NonplanarSurfaces nonplanar_surfaces = newSurface.group_surfaces();
nonplanar_surfaces.push_back(*this);
return nonplanar_surfaces;
}
}
void
NonplanarSurface::mark_neighbor_surfaces(int id)
{
//if already marked return
if(this->mesh.find(id) == this->mesh.end() || this->mesh[id].marked == true) return;
//mark this facet
this->mesh[id].marked = true;
//mark all neighbors
for(int j = 0; j < 3; j++) {
this->mark_neighbor_surfaces(this->mesh[id].neighbor[j]);
}
}
bool
NonplanarSurface::check_max_printing_height(float height)
{
if ((this->stats.max.z - this->stats.min.z) > height ) {
std::cout << "Surface removed: printheight too heigh (" << (this->stats.max.z - this->stats.min.z) << " mm)" << '\n';
return true;
} else {
return false;
}
}
bool
NonplanarSurface::check_surface_area()
{
//calculate surface area of nonplanar surface.
float area = 0.0f;
for(auto& facet : this->mesh) {
area += facet.second.calculate_surface_area();
}
if (area < 20.0f) {
std::cout << "Surface removed: area too small (" << area << " mm²)" << '\n';
return true;
} else {
return false;
}
}
void
NonplanarSurface::check_printable_surfaces(float max_angle)
{
//TODO do something
}
/* this will return scaled ExPolygons */
ExPolygons
NonplanarSurface::horizontal_projection() const
{
Polygons pp;
pp.reserve(this->mesh.size());
for(auto& facet : this->mesh) {
Polygon p;
p.points.resize(3);
p.points[0] = Point(scale_(facet.second.vertex[0].x), scale_(facet.second.vertex[0].y));
p.points[1] = Point(scale_(facet.second.vertex[1].x), scale_(facet.second.vertex[1].y));
p.points[2] = Point(scale_(facet.second.vertex[2].x), scale_(facet.second.vertex[2].y));
p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that
pp.push_back(p);
}
// the offset factor was tuned using groovemount.stl
return union_ex(offset(pp, scale_(0.01)));
}
}

View File

@ -0,0 +1,54 @@
#ifndef slic3r_NonplanarSurface_hpp_
#define slic3r_NonplanarSurface_hpp_
#include "libslic3r.h"
#include "NonplanarFacet.hpp"
#include "Point.hpp"
#include "Polygon.hpp"
#include "ExPolygon.hpp"
#include "Geometry.hpp"
#include "ClipperUtils.hpp"
namespace Slic3r {
typedef struct {
float x;
float y;
float z;
} mesh_vertex;
typedef struct {
mesh_vertex max;
mesh_vertex min;
} mesh_stats;
class NonplanarSurface;
typedef std::vector<NonplanarSurface> NonplanarSurfaces;
class NonplanarSurface
{
public:
std::map<int, NonplanarFacet> mesh;
mesh_stats stats;
NonplanarSurface() {};
~NonplanarSurface() {};
NonplanarSurface(std::map<int, NonplanarFacet> &_mesh);
bool operator==(const NonplanarSurface& other) const;
void calculate_stats();
void translate(float x, float y, float z);
void scale(float factor);
void scale(float versor[3]);
void rotate_z(float angle);
void debug_output();
NonplanarSurfaces group_surfaces();
void mark_neighbor_surfaces(int id);
bool check_max_printing_height(float height);
void check_printable_surfaces(float max_angle);
bool check_surface_area();
ExPolygons horizontal_projection() const;
};
};
#endif

View File

@ -159,11 +159,14 @@ class Point : public Vec2crd
public:
using coord_type = coord_t;
coord_t nonplanar_z { -1 };
Point() : Vec2crd(0, 0) {}
Point(int32_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
Point(int64_t x, int64_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
Point(double x, double y) : Vec2crd(coord_t(std::round(x)), coord_t(std::round(y))) {}
Point(const Point &rhs) { *this = rhs; }
Point(Vec3crd &p) : Vec2crd(p.x(), p.y()) {}
explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(std::round(rhs.x())), coord_t(std::round(rhs.y()))) {}
// This constructor allows you to construct Point from Eigen expressions
template<typename OtherDerived>

View File

@ -402,7 +402,7 @@ bool has_duplicate_points(const Polygons &polys)
{
#if 1
// Check globally.
#if 0
#if 1
// Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
Points allpts;
allpts.reserve(count_points(polys));

View File

@ -4,6 +4,7 @@
#include "ExPolygon.hpp"
#include "Line.hpp"
#include "Polygon.hpp"
#include "SVG.hpp"
#include <iostream>
#include <utility>
@ -332,4 +333,29 @@ Lines3 Polyline3::lines() const
return lines;
}
bool export_to_svg(const char *path, const Polyline &polyline, BoundingBox bbox, const float stroke_width)
{
SVG svg(path, bbox);
svg.draw(polyline, "black", stroke_width);
svg.Close();
return true;
}
bool export_to_svg(const char *path, const Polylines &polylines, BoundingBox bbox, const float stroke_width)
{
SVG svg(path, bbox);
svg.draw(polylines, "black", stroke_width);
svg.Close();
return true;
}
bool export_to_svg(const char *path, const ThickPolylines &polylines, BoundingBox bbox, const float stroke_width)
{
SVG svg(path, bbox);
svg.draw(polylines, "black", stroke_width);
svg.Close();
return true;
}
}

View File

@ -234,6 +234,10 @@ public:
typedef std::vector<Polyline3> Polylines3;
extern bool export_to_svg(const char *path, const Polyline &polyline, BoundingBox bbox, const float stroke_width = 1.f);
extern bool export_to_svg(const char *path, const Polylines &polylines, BoundingBox bbox, const float stroke_width = 1.f);
extern bool export_to_svg(const char *path, const ThickPolylines &polylines, BoundingBox bbox, const float stroke_width = 1.f);
}
#endif

View File

@ -432,6 +432,7 @@ static std::vector<std::string> s_Preset_print_options {
"ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
"max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
"use_nonplanar_layers", "nonplanar_layers_angle", "nonplanar_layers_height",
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"enable_dynamic_overhang_speeds", "overhang_speed_0", "overhang_speed_1", "overhang_speed_2", "overhang_speed_3",

View File

@ -17,6 +17,8 @@
#include "GCode/ThumbnailData.hpp"
#include "GCode/GCodeProcessor.hpp"
#include "MultiMaterialSegmentation.hpp"
#include "NonplanarSurface.hpp"
#include "NonplanarFacet.hpp"
#include "libslic3r.h"
@ -67,7 +69,9 @@ enum PrintStep : unsigned int {
enum PrintObjectStep : unsigned int {
posSlice, posPerimeters, posPrepareInfill,
posInfill, posIroning, posSupportSpotsSearch, posSupportMaterial, posEstimateCurledExtrusions, posCount,
posInfill, posIroning, posSupportSpotsSearch,
posSupportMaterial, posNonplanarProjection, posEstimateCurledExtrusions,
posCount,
};
// A PrintRegion object represents a group of volumes to print
@ -264,6 +268,8 @@ public:
coord_t height() const { return m_size.z(); }
// Centering offset of the sliced mesh from the scaled and rotated mesh of the model.
const Point& center_offset() const { return m_center_offset; }
//
NonplanarSurfaces nonplanar_surfaces() { return m_nonplanar_surfaces; }
bool has_brim() const {
return this->config().brim_type != btNoBrim
@ -378,8 +384,16 @@ private:
void estimate_curled_extrusions();
void slice_volumes();
void make_slices();
void lslices_were_updated();
// Has any support (not counting the raft).
void detect_surfaces_type();
void merge_nonplanar_surfaces();
void debug_svg_print();
bool check_nonplanar_collisions(NonplanarSurface &surface);
void project_nonplanar_surfaces();
void find_nonplanar_surfaces();
void detect_nonplanar_surfaces();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
@ -414,6 +428,8 @@ private:
// so that next call to make_perimeters() performs a union() before computing loops
bool m_typed_slices = false;
NonplanarSurfaces m_nonplanar_surfaces;
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> m_adaptive_fill_octrees;
FillLightning::GeneratorPtr m_lightning_generator;
};

View File

@ -1369,6 +1369,33 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.8));
def = this->add("use_nonplanar_layers", coBool);
def->label = L("Use nonplanar layers");
def->category = L("Nonplanar layers");
def->tooltip = L("Generate nonplanar layers on top of the object");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("nonplanar_layers_angle", coFloat);
def->label = L("Maximum nonplanar angle");
def->category = L("Nonplanar layers");
def->tooltip = L("The maximum angle the printer can print without collisions.");
def->sidetext = L("°");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(10.0));
def = this->add("nonplanar_layers_height", coFloat);
def->label = L("Maximum nonplanar height");
def->category = L("Nonplanar layers");
def->tooltip = L("The maximum height the printer can print without collisions.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(10.0));
def = this->add("gap_fill_enabled", coBool);
def->label = L("Fill gaps");
def->category = L("Layers and Perimeters");

View File

@ -562,6 +562,10 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, thick_bridges))
((ConfigOptionFloat, xy_size_compensation))
((ConfigOptionBool, wipe_into_objects))
((ConfigOptionBool, use_nonplanar_layers))
((ConfigOptionFloat, nonplanar_layers_angle))
((ConfigOptionFloat, nonplanar_layers_height))
)
PRINT_CONFIG_CLASS_DEFINE(

View File

@ -33,6 +33,7 @@
#include "TriangleSelectorWrapper.hpp"
#include "format.hpp"
#include "libslic3r.h"
#include "SVG.hpp"
#include <algorithm>
#include <cmath>
@ -53,6 +54,7 @@
#include <utility>
#include <boost/log/trivial.hpp>
#include <boost/bind.hpp>
#include <tbb/parallel_for.h>
#include <vector>
@ -178,6 +180,8 @@ void PrintObject::make_perimeters()
}
m_typed_slices = false;
}
this->detect_nonplanar_surfaces();
// compare each layer to the one below, and mark those slices needing
// one additional inner perimeter, like the top of domed objects-
@ -275,16 +279,17 @@ void PrintObject::prepare_infill()
m_print->set_status(30, _u8L("Preparing infill"));
if (m_typed_slices) {
// To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442.
// The preceding step (perimeter generator) only modifies extra_perimeters and the extra perimeters are only used by discover_vertical_shells()
// with more than a single region. If this step does not use Surface::extra_perimeters or Surface::extra_perimeters is always zero, it is safe
// to reset to the untyped slices before re-runnning detect_surfaces_type().
for (Layer* layer : m_layers) {
layer->restore_untyped_slices_no_extra_perimeters();
m_print->throw_if_canceled();
}
}
// if (m_typed_slices) {
// // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442.
// // The preceding step (perimeter generator) only modifies extra_perimeters and the extra perimeters are only used by discover_vertical_shells()
// // with more than a single region. If this step does not use Surface::extra_perimeters or Surface::extra_perimeters is always zero, it is safe
// // to reset to the untyped slices before re-runnning detect_surfaces_type().
// for (Layer* layer : m_layers) {
// layer->restore_untyped_slices_no_extra_perimeters();
// m_print->throw_if_canceled();
// }
// this->detect_nonplanar_surfaces();
// }
// This will assign a type (top/bottom/internal) to $layerm->slices.
// Then the classifcation of $layerm->slices is transfered onto
@ -434,6 +439,9 @@ void PrintObject::infill()
}
}
);
this->project_nonplanar_surfaces();
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end";
/* we could free memory now, but this would make this step not idempotent
@ -792,6 +800,13 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posInfill);
steps.emplace_back(posSupportMaterial);
}
} else if (
opt_key == "use_nonplanar_layers"
|| opt_key == "nonplanar_layers_angle"
|| opt_key == "nonplanar_layers_height") {
// steps.emplace_back(posPerimeters);
// steps.emplace_back(posInfill);
steps.emplace_back(posSlice);
} else if (
opt_key == "perimeter_generator"
|| opt_key == "wall_transition_length"
@ -899,6 +914,8 @@ void PrintObject::cleanup()
// stBottomBridge - Part of a region, which is not fully supported, but it hangs in the air, or it hangs losely on a support or a raft.
// stBottom - Part of a region, which is not supported by the same region, but it is supported either by another region, or by a soluble interface layer.
// stInternal - Part of a region, which is supported by the same region type.
// stTopNonplanar -
// stInternalSolidNonplanar -
// If a part of a region is of stBottom and stTop, the stBottom wins.
void PrintObject::detect_surfaces_type()
{
@ -952,6 +969,22 @@ void PrintObject::detect_surfaces_type()
// collapse very narrow parts (using the safety offset in the diff is not enough)
float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f;
//Find mark nonplanar surfaces
Surfaces nonplanar_surfaces;
for(auto& surface : layerm->nonplanar_surfaces()) {
surfaces_append(
nonplanar_surfaces,
intersection_ex(surface.horizontal_projection(), union_ex(layerm->slices().surfaces)),
(surface.stats.max.z <= layer->slice_z + layer->height ? stTopNonplanar : stInternalSolidNonplanar)
);
BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces - layer " << (idx_layer+1) << "/" << m_layers.size() << " is " <<
(surface.stats.max.z <= layer->slice_z + layer->height ? "top nonplanar" : "internal nonplanar") <<
", surface max z=" << surface.stats.max.z << ", slice_z=" << layer->slice_z << ", layer height=" << layer->height;
}
//remove non planar surfaces form all surfaces to get planar surfaces
ExPolygons planar_surfaces = diff_ex(layerm->slices().surfaces, nonplanar_surfaces, ApplySafetyOffset::Yes);
// find top surfaces (difference between current surfaces
// of current layer and upper one)
Surfaces top;
@ -959,13 +992,11 @@ void PrintObject::detect_surfaces_type()
ExPolygons upper_slices = interface_shells ?
diff_ex(layerm->slices().surfaces, upper_layer->m_regions[region_id]->slices().surfaces, ApplySafetyOffset::Yes) :
diff_ex(layerm->slices().surfaces, upper_layer->lslices, ApplySafetyOffset::Yes);
surfaces_append(top, opening_ex(upper_slices, offset), stTop);
surfaces_append(top, diff_ex(opening_ex(upper_slices, offset), nonplanar_surfaces, ApplySafetyOffset::Yes), stTop);
} else {
// if no upper layer, all surfaces of this one are solid
// we clone surfaces because we're going to clear the slices collection
top = layerm->slices().surfaces;
for (Surface &surface : top)
surface.surface_type = stTop;
surfaces_append(top, union_ex(planar_surfaces), stTop);
}
// Find bottom surfaces (difference between current surfaces of current layer and lower one).
@ -984,7 +1015,11 @@ void PrintObject::detect_surfaces_type()
surfaces_append(
bottom,
opening_ex(
diff_ex(layerm->slices().surfaces, lower_layer->lslices, ApplySafetyOffset::Yes),
diff_ex(
diff_ex(layerm->slices().surfaces, lower_layer->lslices, ApplySafetyOffset::Yes),
nonplanar_surfaces,
ApplySafetyOffset::Yes
),
offset),
surface_type_bottom_other);
// if user requested internal shells, we need to identify surfaces
@ -996,8 +1031,11 @@ void PrintObject::detect_surfaces_type()
bottom,
opening_ex(
diff_ex(
intersection(layerm->slices().surfaces, lower_layer->lslices), // supported
lower_layer->m_regions[region_id]->slices().surfaces,
diff_ex(
intersection(layerm->slices().surfaces, lower_layer->lslices), // supported
lower_layer->m_regions[region_id]->slices().surfaces,
ApplySafetyOffset::Yes),
nonplanar_surfaces,
ApplySafetyOffset::Yes),
offset),
stBottom);
@ -1029,6 +1067,7 @@ void PrintObject::detect_surfaces_type()
std::vector<std::pair<Slic3r::ExPolygons, SVG::ExPolygonAttributes>> expolygons_with_attributes;
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green")));
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown")));
expolygons_with_attributes.emplace_back(std::make_pair(union_ex(nonplanar_surfaces), SVG::ExPolygonAttributes("red")));
expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices().surfaces), SVG::ExPolygonAttributes("black")));
SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes);
}
@ -1045,13 +1084,15 @@ void PrintObject::detect_surfaces_type()
// find internal surfaces (difference between top/bottom surfaces and others)
{
Polygons topbottom = to_polygons(top);
polygons_append(topbottom, to_polygons(bottom));
surfaces_append(surfaces_out, diff_ex(surfaces_prev, topbottom), stInternal);
Polygons solid_surfaces = to_polygons(top);
polygons_append(solid_surfaces, to_polygons(bottom));
polygons_append(solid_surfaces, to_polygons(nonplanar_surfaces));
surfaces_append(surfaces_out, diff_ex(surfaces_prev, solid_surfaces), stInternal);
}
surfaces_append(surfaces_out, std::move(top));
surfaces_append(surfaces_out, std::move(bottom));
surfaces_append(surfaces_out, std::move(nonplanar_surfaces));
// Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n",
// $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
@ -1101,6 +1142,240 @@ void PrintObject::detect_surfaces_type()
m_typed_slices = true;
}
bool
PrintObject::check_nonplanar_collisions(NonplanarSurface &surface)
{
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
Polygons collider;
Polygons nonplanar_polygon = to_polygons(surface.horizontal_projection());
//check each layer
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) {
m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Check nonplanar collisions for region " << region_id << " and layer " << layer->print_z;
Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->m_regions[region_id];
//skip if below minimum nonplanar surface
if (surface.stats.min.z-layer->height > layer->slice_z) continue;
//break if above nonplanar surface
if (surface.stats.max.z < layer->slice_z) break;
float angle_rad = m_config.nonplanar_layers_angle.value * 3.14159265/180.0;
float angle_offset = scale_(layer->height*std::sin(1.57079633-angle_rad)/std::sin(angle_rad));
//debug
// SVG svg("svg/collider" + std::to_string(layer->id()) + ".svg");
// svg.draw(layerm_slices_surfaces, "blue");
// svg.draw(union_ex(diff(collider,nonplanar_polygon)), "red", 0.7f);
// svg.draw_outline(collider);
// svg.arrows = false;
// svg.Close();
//check if current surface collides with previous collider
ExPolygons collisions = union_ex(intersection(layerm->slices().surfaces, diff(collider, nonplanar_polygon)));
if (!collisions.empty()){
double area = 0;
for (auto& c : collisions){
area += c.area();
}
//collsion found abort when area > 1.0 mm²
if (1.0 < unscale<double>(unscale<double>(area))) {
std::cout << "Surface removed: collision on layer " << layer->print_z << "mm (" << unscale<double>(unscale<double>(area)) << " mm²)" << '\n';
return true;
}
}
if (layer->upper_layer != NULL) {
Layer* upper_layer = layer->upper_layer;
LayerRegion *upper_layerm = upper_layer->m_regions[region_id];
//merge the ofsetted surface to the collider
collider= offset(
union_(
intersection(
diff_ex(layerm->slices().surfaces, upper_layerm->slices().surfaces, ApplySafetyOffset::No),
nonplanar_polygon,
ApplySafetyOffset::No),
collider),
angle_offset);
}
}
}
return false;
}
void
PrintObject::detect_nonplanar_surfaces()
{
//skip if not active
if(!m_config.use_nonplanar_layers.value) return;
bool moved_surfaces = false;
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(trace) << "detect_nonplanar_surfaces for region " << region_id;
const PrintRegion &region = this->printing_region(region_id);
//repeat detection for every nonplanar_surface
for (auto& nonplanar_surface: this->nonplanar_surfaces()) {
float distance_to_top = 0.0f;
for (int shell_thickness = 0; region.config().top_solid_layers > shell_thickness; ++shell_thickness){
//search home layer where the area is projected to
for (LayerPtrs::reverse_iterator home_layer_it = this->m_layers.rbegin(); home_layer_it != this->m_layers.rend(); ++home_layer_it){
Layer* home_layer = *home_layer_it;
LayerRegion &home_layerm = *home_layer->m_regions[region_id];
//continue if home layer is not maximum height of nonplanar_surface - the desired distance to the top of the surface for more than one top solid layer
if (home_layer->slice_z > nonplanar_surface.stats.max.z - distance_to_top) continue;
//process layers
for (LayerPtrs::iterator layer_it = this->m_layers.begin(); layer_it != this->m_layers.end(); ++layer_it){
Layer* layer = *layer_it;
LayerRegion &layerm = *layer->m_regions[region_id];
//skip if below minimum nonplanar surface and below the last possible surface layer
if (nonplanar_surface.stats.min.z-layer->height-distance_to_top > layer->slice_z) continue;
//break if above home layer
if (home_layer->slice_z < layer->slice_z) break;
//skip if bottom layer because we dont want to project the bottom layers up
if (layer->lower_layer == NULL) continue;
BOOST_LOG_TRIVIAL(trace) << "detect_nonplanar_surfaces for region " << region_id << " and layer " << layer->print_z;
Surfaces layerm_slices_surfaces = layerm.slices().surfaces;
SurfaceCollection topNonplanar;
if (layer->upper_layer != NULL) {
//append layers where nothing is above
Layer* upper_layer = layer->upper_layer;
LayerRegion &upper_layerm = *upper_layer->m_regions[region_id];
Surfaces upper_surfaces = upper_layerm.slices().surfaces;
topNonplanar.append(
intersection_ex(
nonplanar_surface.horizontal_projection(),
union_ex(
diff_ex(
layerm_slices_surfaces,
upper_surfaces,
ApplySafetyOffset::No)),
ApplySafetyOffset::No),
(shell_thickness == 0 ? stTopNonplanar : stInternalSolidNonplanar),
distance_to_top
);
// append layers where nonplanar areas with a lower distance_to_top are above
SurfaceCollection upper_nonplanar;
for (auto& s : upper_surfaces){
if (s.is_nonplanar() && s.distance_to_top < distance_to_top) {
upper_nonplanar.surfaces.push_back(s);
}
}
if (upper_nonplanar.size() > 0)
topNonplanar.append(
intersection_ex(
nonplanar_surface.horizontal_projection(),
to_expolygons(upper_nonplanar.surfaces),
ApplySafetyOffset::No),
(shell_thickness == 0 ? stTopNonplanar : stInternalSolidNonplanar),
distance_to_top
);
}
else {
topNonplanar.append(
intersection_ex(
nonplanar_surface.horizontal_projection(),
union_ex(to_expolygons(layerm_slices_surfaces)),
ApplySafetyOffset::No),
(shell_thickness == 0 ? stTopNonplanar : stInternalSolidNonplanar),
distance_to_top
);
}
if (topNonplanar.size() > 0) {
// layerm.export_region_slices_to_svg_debug("home_layer_append-layerm");
// home_layerm.export_region_slices_to_svg_debug("home_layer_append-home_layerm");
BOOST_LOG_TRIVIAL(trace) << "Removing " << topNonplanar.size() << " nonplanar surfaces from layer " << layer->print_z;
layerm.remove_nonplanar_slices(topNonplanar);
BOOST_LOG_TRIVIAL(trace) << "Adding " << topNonplanar.size() << " nonplanar surfaces to layer " << home_layer->print_z;
// move nonplanar surfaces to home layer
home_layerm.append_top_nonplanar_slices(topNonplanar);
//save nonplanar_surface to home_layers nonplanar_surface list
home_layerm.append_nonplanar_surface(nonplanar_surface);
moved_surfaces = true;
}
}
//increase distance to the top layer
distance_to_top += home_layer->height;
break;
}
}
}
}
if(moved_surfaces) {
// After changing a layer's slices, we must rebuild its lslices into islands
this->make_slices();
this->lslices_were_updated();
}
// Debugging output.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("0_detect_nonplanar_surfaces");
layerm->export_region_fill_surfaces_to_svg_debug("0_detect_nonplanar_surfaces");
} // for each layer
} // for each region
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
//set typed_slices to true to force merge
m_typed_slices = true;
}
void
PrintObject::project_nonplanar_surfaces()
{
if(!m_config.use_nonplanar_layers.value) return;
//TODO check when steps should be invalidated
if (is_step_done(posNonplanarProjection)) return;
set_started(posNonplanarProjection);
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
BOOST_LOG_TRIVIAL(debug) << "Processing nonplanar surfaces for region " << region_id << " in parallel - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this, region_id](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
BOOST_LOG_TRIVIAL(trace) << "Processing nonplanar surfaces for region " << region_id << " and layer " << \
idx_layer << " (z = " << layerm->layer()->print_z << ")";
layerm->project_nonplanar_surfaces();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_fill_surfaces_to_svg_debug("11_project_nonplanar_surfaces-final");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} // for each layer of a region
});
}
BOOST_LOG_TRIVIAL(debug) << "Processing nonplanar surfaces - end";
set_done(posNonplanarProjection);
}
void PrintObject::process_external_surfaces()
{
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info();
@ -1223,6 +1498,7 @@ void PrintObject::discover_vertical_shells()
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge };
const std::initializer_list<SurfaceType> surfaces_top { stTop, stTopNonplanar };
const size_t num_regions = this->num_printing_regions();
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
@ -1239,7 +1515,7 @@ void PrintObject::discover_vertical_shells()
LayerRegion &layerm = *layer.m_regions[region_id];
float top_bottom_expansion = float(layerm.flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff;
// Top surfaces.
append(cache.top_surfaces, offset(layerm.slices().filter_by_type(stTop), top_bottom_expansion));
append(cache.top_surfaces, offset(layerm.slices().filter_by_types(surfaces_top), top_bottom_expansion));
// append(cache.top_surfaces, offset(layerm.fill_surfaces().filter_by_type(stTop), top_bottom_expansion));
// Bottom surfaces.
append(cache.bottom_surfaces, offset(layerm.slices().filter_by_types(surfaces_bottom), top_bottom_expansion));
@ -1297,6 +1573,7 @@ void PrintObject::discover_vertical_shells()
[this, region_id, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge };
const std::initializer_list<SurfaceType> surfaces_top { stTop, stTopNonplanar };
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
Layer &layer = *m_layers[idx_layer];
@ -1304,7 +1581,7 @@ void PrintObject::discover_vertical_shells()
float top_bottom_expansion = float(layerm.flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff;
// Top surfaces.
auto &cache = cache_top_botom_regions[idx_layer];
cache.top_surfaces = offset(layerm.slices().filter_by_type(stTop), top_bottom_expansion);
cache.top_surfaces = offset(layerm.slices().filter_by_types(surfaces_top), top_bottom_expansion);
// append(cache.top_surfaces, offset(layerm.fill_surfaces().filter_by_type(stTop), top_bottom_expansion));
// Bottom surfaces.
cache.bottom_surfaces = offset(layerm.slices().filter_by_types(surfaces_bottom), top_bottom_expansion);
@ -1485,7 +1762,7 @@ void PrintObject::discover_vertical_shells()
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
}
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces().filter_by_type(stInternalVoid), "yellow", 0.5);
@ -1493,7 +1770,7 @@ void PrintObject::discover_vertical_shells()
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
}
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalsolid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces().filter_by_type(stInternalSolid), "yellow", 0.5);
@ -1501,18 +1778,32 @@ void PrintObject::discover_vertical_shells()
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
}
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalsolidnonplanar-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces().filter_by_type(stInternalSolidNonplanar), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces().filter_by_type(stInternalSolidNonplanar), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
}
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-holes-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw_outline(to_expolygons(holes), "black", "blue", scale_(0.05));
svg.Close();
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Trim the shells region by the internal & internal void surfaces.
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces().filter_by_types({ stInternal, stInternalVoid, stInternalSolid }));
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces().filter_by_types({ stInternal, stInternalVoid, stInternalSolid, stInternalSolidNonplanar }));
const Polygons polygonsInternalNonplanar = to_polygons(layerm->fill_surfaces().filter_by_type(stInternalSolidNonplanar));
shell = intersection(shell, polygonsInternal, ApplySafetyOffset::Yes);
polygons_append(shell, diff(polygonsInternal, holes));
if (shell.empty())
continue;
// Append the internal solids, so they will be merged with the new ones.
polygons_append(shell, to_polygons(layerm->fill_surfaces().filter_by_type(stInternalSolid)));
polygons_append(shell, to_polygons(layerm->fill_surfaces().filter_by_types({ stInternalSolid, stInternalSolidNonplanar })));
// These regions will be filled by a rectilinear full infill. Currently this type of infill
// only fills regions, which fit at least a single line. To avoid gaps in the sparse infill,
@ -1560,7 +1851,8 @@ void PrintObject::discover_vertical_shells()
if (regularized_shell.empty())
continue;
ExPolygons new_internal_solid = intersection_ex(polygonsInternal, regularized_shell);
ExPolygons new_internal_solid = intersection_ex(diff_ex(polygonsInternal, polygonsInternalNonplanar), regularized_shell);
ExPolygons new_internal_solid_nonplanar = intersection_ex(polygonsInternalNonplanar, regularized_shell);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before));
@ -1583,14 +1875,16 @@ void PrintObject::discover_vertical_shells()
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", debug_idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", debug_idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", debug_idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid_nonplanar-%d.svg", debug_idx), get_extents(shell), new_internal_solid_nonplanar, "black", "blue", scale_(0.05));
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Assign resulting internal surfaces to layer.
layerm->m_fill_surfaces.keep_types({ stTop, stBottom, stBottomBridge });
layerm->m_fill_surfaces.keep_types({ stTop, stTopNonplanar, stBottom, stBottomBridge });
layerm->m_fill_surfaces.append(new_internal, stInternal);
layerm->m_fill_surfaces.append(new_internal_void, stInternalVoid);
layerm->m_fill_surfaces.append(new_internal_solid, stInternalSolid);
layerm->m_fill_surfaces.append(new_internal_solid_nonplanar, stInternalSolidNonplanar);
} // for each layer
});
m_print->throw_if_canceled();
@ -1688,7 +1982,7 @@ void PrintObject::bridge_over_infill()
unsupported_area = diff(unsupported_area, lower_layer_solids);
for (const LayerRegion *region : layer->regions()) {
SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid);
SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_types({stInternalSolid, stInternalSolidNonplanar});
for (const Surface *s : region_internal_solids) {
Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area);
// The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported.

View File

@ -5,6 +5,7 @@
#include "MultiMaterialSegmentation.hpp"
#include "Print.hpp"
#include "ShortestPath.hpp"
#include "AABBMesh.hpp"
#include <boost/log/trivial.hpp>
@ -518,6 +519,14 @@ void PrintObject::slice()
if (! warning.empty())
BOOST_LOG_TRIVIAL(info) << warning;
#endif
this->lslices_were_updated();
if (m_layers.empty())
throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
this->set_done(posSlice);
}
void PrintObject::lslices_were_updated()
{
// Update bounding boxes, back up raw slices of complex models.
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
@ -541,9 +550,6 @@ void PrintObject::slice()
Layer::build_up_down_graph(*m_layers[layer_idx - 1], *m_layers[layer_idx]);
}
});
if (m_layers.empty())
throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
this->set_done(posSlice);
}
template<typename ThrowOnCancel>
@ -708,7 +714,7 @@ void PrintObject::slice_volumes()
m_layers[layer_id]->regions()[region_id]->m_slices.append(std::move(by_layer[layer_id]), stInternal);
}
region_slices.clear();
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - removing top empty layers";
while (! m_layers.empty()) {
const Layer *layer = m_layers.back();
@ -740,7 +746,16 @@ void PrintObject::slice_volumes()
apply_mm_segmentation(*this, [print]() { print->throw_if_canceled(); });
}
this->find_nonplanar_surfaces();
this->make_slices();
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - end";
}
void PrintObject::make_slices()
{
const Print *print = this->print();
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin";
{
// Compensation value, scaled. Only applying the negative scaling here, as the positive scaling has already been applied during slicing.
@ -802,6 +817,7 @@ void PrintObject::slice_volumes()
layer->m_regions[region_id]->trim_surfaces(trimming);
}
}
// Merge all regions' slices to get islands sorted topologically, chain them by a shortest path in separate index list
layer->make_slices();
}
@ -822,6 +838,106 @@ void PrintObject::slice_volumes()
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - end";
}
void
PrintObject::find_nonplanar_surfaces()
{
//skip if not active
if(!m_config.use_nonplanar_layers.value) {
BOOST_LOG_TRIVIAL(debug) << "Find nonplanar surfaces - disabled";
return;
}
BOOST_LOG_TRIVIAL(info) << "Find nonplanar surfaces - start";
//Itterate over all model volumes
const ModelVolumePtrs volumes = this->model_object()->volumes;
for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it) {
//only check non modifier volumes
if (! (*it)->is_modifier()) {
const TriangleMesh tmesh = (*it)->mesh();
AABBMesh mesh(tmesh, true);
std::map<int, NonplanarFacet> facets;
// store all meshes with slope <= nonplanar_layers_angle in map. Map is necessary to keep facet ID
for (int face_id = 0; face_id < mesh.indices().size(); ++ face_id) {
auto &face = mesh.indices(face_id);
Vec3d normal = mesh.normal_by_face_id(face_id);
//TODO check if normals exist
if (normal.z() >= std::cos(m_config.nonplanar_layers_angle.value * 3.14159265/180.0)) {
//copy facet
NonplanarFacet new_facet;
new_facet.normal.x = normal.x();
new_facet.normal.y = normal.y();
new_facet.normal.z = normal.z();
Vec3i neighbor = mesh.face_neighbor_index()[face_id];
its_triangle vertex = its_triangle_vertices(*mesh.get_triangle_mesh(), face_id);
for (int j=0; j<=2 ;j++) {
new_facet.vertex[j].x = vertex[j].x();
new_facet.vertex[j].y = vertex[j].y();
new_facet.vertex[j].z = vertex[j].z();
new_facet.neighbor[j] = neighbor[j];
}
new_facet.calculate_stats();
facets[face_id] = new_facet;
}
}
// create nonplanar surface from facets
NonplanarSurface nf = NonplanarSurface(facets);
BOOST_LOG_TRIVIAL(trace) << "Find nonplanar surfaces - moving surfaces by z=" << -tmesh.stats().min.z();
nf.translate(0, 0, -tmesh.stats().min.z());
// group surfaces and attach all nonplanar surfaces to the PrintObject
m_nonplanar_surfaces = nf.group_surfaces();
// check if surfaces maintain maximum printing height, if not, erase it
for (NonplanarSurfaces::iterator it = m_nonplanar_surfaces.begin(); it!=m_nonplanar_surfaces.end();) {
if((*it).check_max_printing_height(m_config.nonplanar_layers_height.value)) {
it = m_nonplanar_surfaces.erase(it);
}else {
it++;
}
}
// check if surfaces area is not too small
for (NonplanarSurfaces::iterator it = m_nonplanar_surfaces.begin(); it!=m_nonplanar_surfaces.end();) {
if((*it).check_surface_area()) {
it = m_nonplanar_surfaces.erase(it);
}else {
it++;
}
}
// check if surfaces areas collide
for (NonplanarSurfaces::iterator it = m_nonplanar_surfaces.begin(); it!=m_nonplanar_surfaces.end();) {
if(check_nonplanar_collisions((*it))) {
it = m_nonplanar_surfaces.erase(it);
} else {
it++;
}
}
//nf.debug_output();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t id = 0; id < m_nonplanar_surfaces.size(); ++ id) {
auto& surface = m_nonplanar_surfaces[id];
Surfaces surfaces;
surfaces_append(surfaces, surface.horizontal_projection(), SurfaceType::stTopNonplanar);
SurfaceCollection c(surfaces);
c.export_to_svg(debug_out_path("0_find_nonplanar_surface-%d.svg", id).c_str(), true);
}
#endif
BOOST_LOG_TRIVIAL(info) << "Find nonplanar surfaces - found " << m_nonplanar_surfaces.size() << " in " << (*it)->name;
}
}
BOOST_LOG_TRIVIAL(info) << "Find nonplanar surfaces - end";
}
std::vector<Polygons> PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const
{
auto it_volume = this->model_object()->volumes.begin();

View File

@ -243,9 +243,9 @@ float get_flow_width(const LayerRegion *region, ExtrusionRole role)
if (role == ExtrusionRole::ExternalPerimeter) return region->flow(FlowRole::frExternalPerimeter).width();
if (role == ExtrusionRole::GapFill) return region->flow(FlowRole::frInfill).width();
if (role == ExtrusionRole::Perimeter) return region->flow(FlowRole::frPerimeter).width();
if (role == ExtrusionRole::SolidInfill) return region->flow(FlowRole::frSolidInfill).width();
if (role == ExtrusionRole::SolidInfill || role == ExtrusionRole::SolidInfillNonplanar) return region->flow(FlowRole::frSolidInfill).width();
if (role == ExtrusionRole::InternalInfill) return region->flow(FlowRole::frInfill).width();
if (role == ExtrusionRole::TopSolidInfill) return region->flow(FlowRole::frTopSolidInfill).width();
if (role == ExtrusionRole::TopSolidInfill || role == ExtrusionRole::TopSolidInfillNonplanar) return region->flow(FlowRole::frTopSolidInfill).width();
// default
return region->flow(FlowRole::frPerimeter).width();
}

View File

@ -35,10 +35,12 @@ const char* surface_type_to_color_name(const SurfaceType surface_type)
{
switch (surface_type) {
case stTop: return "rgb(255,0,0)"; // "red";
case stTopNonplanar: return "rgb(166,2,255)"; // "purple";
case stBottom: return "rgb(0,255,0)"; // "green";
case stBottomBridge: return "rgb(0,0,255)"; // "blue";
case stInternal: return "rgb(255,255,128)"; // yellow
case stInternalSolid: return "rgb(255,0,255)"; // magenta
case stInternalSolidNonplanar: return "rgb(255,133,2)"; // orange
case stInternalBridge: return "rgb(0,255,255)";
case stInternalVoid: return "rgb(128,128,128)";
case stPerimeter: return "rgb(128,0,0)"; // maroon
@ -48,7 +50,7 @@ const char* surface_type_to_color_name(const SurfaceType surface_type)
Point export_surface_type_legend_to_svg_box_size()
{
return Point(scale_(1.+10.*8.), scale_(3.));
return Point(scale_(1.+16.*8.), scale_(3.));
}
void export_surface_type_legend_to_svg(SVG &svg, const Point &pos)
@ -57,11 +59,13 @@ void export_surface_type_legend_to_svg(SVG &svg, const Point &pos)
coord_t pos_x0 = pos(0) + scale_(1.);
coord_t pos_x = pos_x0;
coord_t pos_y = pos(1) + scale_(1.5);
coord_t step_x = scale_(10.);
coord_t step_x = scale_(16.);
svg.draw_legend(Point(pos_x, pos_y), "perimeter" , surface_type_to_color_name(stPerimeter));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "top" , surface_type_to_color_name(stTop));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "top nonplanar" , surface_type_to_color_name(stTopNonplanar));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom" , surface_type_to_color_name(stBottom));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom bridge" , surface_type_to_color_name(stBottomBridge));
@ -74,6 +78,8 @@ void export_surface_type_legend_to_svg(SVG &svg, const Point &pos)
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "internal solid" , surface_type_to_color_name(stInternalSolid));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "internal solid nonplanar" , surface_type_to_color_name(stInternalSolidNonplanar));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "internal bridge", surface_type_to_color_name(stInternalBridge));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "internal void" , surface_type_to_color_name(stInternalVoid));

View File

@ -24,6 +24,10 @@ enum SurfaceType {
stInternalVoid,
// Inner/outer perimeters.
stPerimeter,
//
stTopNonplanar,
//
stInternalSolidNonplanar,
// Number of SurfaceType enums.
stCount,
};
@ -37,6 +41,7 @@ public:
unsigned short thickness_layers { 1 }; // in layers
double bridge_angle { -1. }; // in radians, ccw, 0 = East, only 0+ (negative means undefined)
unsigned short extra_perimeters { 0 };
float distance_to_top { 0 };
Surface(const Slic3r::Surface &rhs) :
surface_type(rhs.surface_type), expolygon(rhs.expolygon),
@ -67,6 +72,7 @@ public:
thickness_layers = rhs.thickness_layers;
bridge_angle = rhs.bridge_angle;
extra_perimeters = rhs.extra_perimeters;
distance_to_top = rhs.distance_to_top;
return *this;
}
@ -78,6 +84,7 @@ public:
thickness_layers = rhs.thickness_layers;
bridge_angle = rhs.bridge_angle;
extra_perimeters = rhs.extra_perimeters;
distance_to_top = rhs.distance_to_top;
return *this;
}
@ -86,12 +93,14 @@ public:
void clear() { expolygon.clear(); }
// The following methods do not test for stPerimeter.
bool is_top() const { return this->surface_type == stTop; }
bool is_top() const { return this->surface_type == stTop || this->surface_type == stTopNonplanar; }
bool is_bottom() const { return this->surface_type == stBottom || this->surface_type == stBottomBridge; }
bool is_bridge() const { return this->surface_type == stBottomBridge || this->surface_type == stInternalBridge; }
bool is_external() const { return this->is_top() || this->is_bottom(); }
bool is_internal() const { return ! this->is_external(); }
bool is_solid() const { return this->is_external() || this->surface_type == stInternalSolid || this->surface_type == stInternalBridge; }
bool is_internal_solid() const { return this->surface_type == stInternalSolid || this->surface_type == stInternalSolidNonplanar; }
bool is_solid() const { return this->is_external() || this->surface_type == stInternalSolid || this->surface_type == stInternalSolidNonplanar || this->surface_type == stInternalBridge; }
bool is_nonplanar() const { return this->surface_type == stTopNonplanar || this->surface_type == stInternalSolidNonplanar; }
};
typedef std::vector<Surface> Surfaces;
@ -228,9 +237,20 @@ inline void polygons_append(Polygons &dst, SurfacesPtr &&src)
inline void surfaces_append(Surfaces &dst, const ExPolygons &src, SurfaceType surfaceType)
{
dst.reserve(dst.size() + src.size());
for (const ExPolygon &expoly : src)
for (const ExPolygon &expoly : src) {
dst.emplace_back(Surface(surfaceType, expoly));
}
}
inline void surfaces_append(Surfaces &dst, const ExPolygons &src, SurfaceType surfaceType, float distance_to_top)
{
dst.reserve(dst.size() + src.size());
for (const ExPolygon &expoly : src) {
Surface s = Surface(surfaceType, expoly);
s.distance_to_top = distance_to_top;
dst.emplace_back(s);
}
}
inline void surfaces_append(Surfaces &dst, const ExPolygons &src, const Surface &surfaceTempl)
{
dst.reserve(dst.size() + number_polygons(src));
@ -278,7 +298,8 @@ inline bool surfaces_could_merge(const Surface &s1, const Surface &s2)
s1.surface_type == s2.surface_type &&
s1.thickness == s2.thickness &&
s1.thickness_layers == s2.thickness_layers &&
s1.bridge_angle == s2.bridge_angle;
s1.bridge_angle == s2.bridge_angle &&
s1.distance_to_top == s2.distance_to_top;
}
class SVG;

View File

@ -68,9 +68,11 @@ public:
void append(const SurfaceCollection &coll) { this->append(coll.surfaces); }
void append(SurfaceCollection &&coll) { this->append(std::move(coll.surfaces)); }
void append(const ExPolygons &src, SurfaceType surfaceType) { surfaces_append(this->surfaces, src, surfaceType); }
void append(const ExPolygons &src, SurfaceType surfaceType, float distance_to_top) { surfaces_append(this->surfaces, src, surfaceType, distance_to_top); }
void append(const ExPolygons &src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, src, surfaceTempl); }
void append(const Surfaces &src) { surfaces_append(this->surfaces, src); }
void append(ExPolygons &&src, SurfaceType surfaceType) { surfaces_append(this->surfaces, std::move(src), surfaceType); }
void append(ExPolygons &&src, SurfaceType surfaceType, float distance_to_top) { surfaces_append(this->surfaces, std::move(src), surfaceType, distance_to_top); }
void append(ExPolygons &&src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, std::move(src), surfaceTempl); }
void append(Surfaces &&src) { surfaces_append(this->surfaces, std::move(src)); }

View File

@ -569,7 +569,9 @@ const std::array<ColorRGBA, static_cast<size_t>(GCodeExtrusionRole::Count)> GCod
{ 0.12f, 0.12f, 1.00f, 1.0f }, // GCodeExtrusionRole::OverhangPerimeter
{ 0.69f, 0.19f, 0.16f, 1.0f }, // GCodeExtrusionRole::InternalInfill
{ 0.59f, 0.33f, 0.80f, 1.0f }, // GCodeExtrusionRole::SolidInfill
{ 0.75f, 0.12f, 0.45f, 1.0f }, // GCodeExtrusionRole::SolidInfillNonplanar
{ 0.94f, 0.25f, 0.25f, 1.0f }, // GCodeExtrusionRole::TopSolidInfill
{ 0.32f, 0.12f, 0.45f, 1.0f }, // GCodeExtrusionRole::TopSolidInfillNonplanar
{ 1.00f, 0.55f, 0.41f, 1.0f }, // GCodeExtrusionRole::Ironing
{ 0.30f, 0.50f, 0.73f, 1.0f }, // GCodeExtrusionRole::BridgeInfill
{ 1.00f, 1.00f, 1.00f, 1.0f }, // GCodeExtrusionRole::GapFill

View File

@ -1459,6 +1459,12 @@ void TabPrint::build()
optgroup->append_single_option_line("fuzzy_skin_thickness", category_path + "fuzzy-skin-thickness");
optgroup->append_single_option_line("fuzzy_skin_point_dist", category_path + "fuzzy-skin-point-distance");
optgroup = page->new_optgroup(L("Nonplanar layers (experimental)"));
category_path = "nonplanar-layers_19282821/#";
optgroup->append_single_option_line("use_nonplanar_layers", category_path + "use-nonplanar-layers");
optgroup->append_single_option_line("nonplanar_layers_angle", category_path + "nonplanar-layers-height");
optgroup->append_single_option_line("nonplanar_layers_height", category_path + "nonplanar-layers-height");
page = add_options_page(L("Infill"), "infill");
category_path = "infill_42#";
optgroup = page->new_optgroup(L("Infill"));

Binary file not shown.