Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_extended_m73

This commit is contained in:
enricoturri1966 2021-03-24 12:05:36 +01:00
commit 645e5b6862
27 changed files with 402 additions and 275 deletions

View file

@ -1581,89 +1581,109 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
{
// extrusion roles
// in older versions the comments did not contain the key 'feature'
std::string_view cmt = comment;
size_t pos = cmt.find(" feature");
if (pos == 0)
cmt.remove_prefix(8);
// ; skirt
size_t pos = comment.find(" skirt");
pos = cmt.find(" skirt");
if (pos == 0) {
m_extrusion_role = erSkirt;
return true;
}
// ; outer perimeter
pos = comment.find(" outer perimeter");
pos = cmt.find(" outer perimeter");
if (pos == 0) {
m_extrusion_role = erExternalPerimeter;
return true;
}
// ; inner perimeter
pos = comment.find(" inner perimeter");
pos = cmt.find(" inner perimeter");
if (pos == 0) {
m_extrusion_role = erPerimeter;
return true;
}
// ; gap fill
pos = comment.find(" gap fill");
pos = cmt.find(" gap fill");
if (pos == 0) {
m_extrusion_role = erGapFill;
return true;
}
// ; infill
pos = comment.find(" infill");
pos = cmt.find(" infill");
if (pos == 0) {
m_extrusion_role = erInternalInfill;
return true;
}
// ; solid layer
pos = comment.find(" solid layer");
pos = cmt.find(" solid layer");
if (pos == 0) {
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
m_extrusion_role = erSolidInfill;
return true;
}
// ; bridge
pos = comment.find(" bridge");
pos = cmt.find(" bridge");
if (pos == 0) {
m_extrusion_role = erBridgeInfill;
return true;
}
// ; support
pos = comment.find(" support");
pos = cmt.find(" support");
if (pos == 0) {
m_extrusion_role = erSupportMaterial;
return true;
}
// ; dense support
pos = cmt.find(" dense support");
if (pos == 0) {
m_extrusion_role = erSupportMaterialInterface;
return true;
}
// ; prime pillar
pos = comment.find(" prime pillar");
pos = cmt.find(" prime pillar");
if (pos == 0) {
m_extrusion_role = erWipeTower;
return true;
}
// ; ooze shield
pos = comment.find(" ooze shield");
pos = cmt.find(" ooze shield");
if (pos == 0) {
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
m_extrusion_role = erNone; // Missing mapping
return true;
}
// ; raft
pos = comment.find(" raft");
pos = cmt.find(" raft");
if (pos == 0) {
m_extrusion_role = erSkirt;
m_extrusion_role = erSupportMaterial;
return true;
}
// ; internal single extrusion
pos = cmt.find(" internal single extrusion");
if (pos == 0) {
m_extrusion_role = erNone; // Missing mapping
return true;
}
// geometry
// ; tool
std::string tag = " tool";
pos = comment.find(tag);
pos = cmt.find(tag);
if (pos == 0) {
const std::string_view data = comment.substr(pos + tag.length());
const std::string_view data = cmt.substr(pos + tag.length());
std::string h_tag = "H";
size_t h_start = data.find(h_tag);
size_t h_end = data.find_first_of(' ', h_start);
@ -1684,10 +1704,10 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
// ; layer
tag = " layer";
pos = comment.find(tag);
pos = cmt.find(tag);
if (pos == 0) {
// skip lines "; layer end"
const std::string_view data = comment.substr(pos + tag.length());
const std::string_view data = cmt.substr(pos + tag.length());
size_t end_start = data.find("end");
if (end_start == data.npos)
++m_layer_id;
@ -2662,7 +2682,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
{
MoveVertex vertex = {
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
m_line_id,
(type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ? m_line_id + 1 : m_line_id,
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
type,
m_extrusion_role,

View file

@ -122,7 +122,7 @@ static bool clip_narrow_corner(
} else {
assert(backward == Free);
p02 = it0.prev()->cast<int64_t>();
if (cross2(p0 - p2, p02 - p2) > 0)
if (cross2(p02 - p2, p0 - p2) > 0)
backward = Blocked;
else {
// New clipping edge lenght.
@ -141,14 +141,37 @@ static bool clip_narrow_corner(
}
}
assert(dist2_current <= shortcut_length2);
assert(polygon.size() >= 2);
assert(polygon.size() == 2 || forward == Blocked || forward == Far);
assert(polygon.size() == 2 || backward == Blocked || backward == Far);
if (polygon.size() <= 3) {
// A hole degenerated to an empty polygon, or a tiny triangle remained.
assert(polygon.size() < 3 || (forward == Blocked && backward == Blocked) || (forward == Far && backward == Far));
if (polygon.size() < 3 || forward == Far) {
assert(polygon.size() < 3 || dist2_current <= shortcut_length2);
bool blocked = forward == Blocked || backward == Blocked;
assert(polygon.size() < 3 ||
// Remaining triangle is CCW oriented. Both sides must be "blocked", but the other side may have not been
// updated after the the p02 / p22 became united into a single point.
blocked ||
// Remaining triangle is concave, however both of its arms are long.
(forward == Far && backward == Far));
#ifndef _NDEBUG
if (polygon.size() == 3) {
// Verify that the remaining triangle is CCW or CW.
p02 = it0.prev()->cast<int64_t>();
p22 = it2.next()->cast<int64_t>();
assert(p02 == p22);
auto orient1 = cross2(p02 - p2, p0 - p2);
auto orient2 = cross2(p2 - p0, p22 - p0);
assert(orient1 > 0 == blocked);
assert(orient2 > 0 == blocked);
}
#endif // _NDEBUG
if (polygon.size() < 3 || (forward == Far && backward == Far)) {
polygon.clear();
} else {
// The remaining triangle is CCW oriented, keep it.
assert(forward == Blocked || backward == Blocked);
}
return true;
}
@ -168,7 +191,7 @@ static bool clip_narrow_corner(
} else if (forward == Blocked || backward == Blocked) {
// One side is far, the other blocked.
assert(forward == Far || backward == Far);
if (backward == Far) {
if (forward == Far) {
// Sort, so we will clip the 1st edge.
std::swap(p0, p2);
std::swap(p02, p22);
@ -177,6 +200,10 @@ static bool clip_narrow_corner(
// Circle intersects a line at two points, however because |p2 - p0| < shortcut_length,
// only the second intersection is valid. Because |p2 - p02| > shortcut_length, such
// intersection should always be found on (p0, p02).
#ifndef NDEBUG
auto dfar2 = (p02 - p2).squaredNorm();
assert(dfar2 >= shortcut_length2);
#endif // NDEBUG
const Vec2d v = (p02 - p0).cast<double>();
const Vec2d d = (p0 - p2).cast<double>();
const double a = v.squaredNorm();
@ -273,15 +300,17 @@ void smooth_outward(MutablePolygon &polygon, coord_t clip_dist_scaled)
Point pt_new = p1 + (t * v2d).cast<coord_t>();
#ifndef NDEBUG
double d2new = (pt_new - (swap ? p2 : p0)).cast<double>().squaredNorm();
assert(std::abs(d2new - clip_dist_scaled2) < sqr(10. * SCALED_EPSILON));
assert(std::abs(d2new - clip_dist_scaled2) < 1e-5 * clip_dist_scaled2);
#endif // NDEBUG
it2.insert(pt_new);
} else {
// Cut the corner with a line perpendicular to the bisector.
double t = sqrt(0.25 * clip_dist_scaled2 / d2);
assert(t > 0. && t < 1.);
Point p0 = p1 + (v1d * t).cast<coord_t>();
Point p2 = p1 + (v2d * (t * lv2 / lv1)).cast<coord_t>();
double t2 = t * lv1 / lv2;
assert(t > 0. && t < 1.);
assert(t2 > 0. && t2 < 1.);
Point p0 = p1 + (v1d * t ).cast<coord_t>();
Point p2 = p1 + (v2d * t2).cast<coord_t>();
if (swap)
std::swap(p0, p2);
it2.insert(p2).insert(p0);

View file

@ -85,7 +85,7 @@ public:
}
void advance_front() {
assert(!this->empty());
assert(! this->empty());
if (m_begin == m_end)
this->make_empty();
else
@ -93,7 +93,7 @@ public:
}
void retract_back() {
assert(!this->empty());
assert(! this->empty());
if (m_begin == m_end)
this->make_empty();
else
@ -101,13 +101,13 @@ public:
}
MutablePolygon::iterator remove_front(MutablePolygon::iterator it) {
if (m_begin == it)
if (! this->empty() && m_begin == it)
this->advance_front();
return it.remove();
}
MutablePolygon::iterator remove_back(MutablePolygon::iterator it) {
if (m_end == it)
if (! this->empty() && m_end == it)
this->retract_back();
return it.remove();
}

View file

@ -427,7 +427,7 @@ const std::vector<std::string>& Preset::print_options()
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
"min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_style",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_contact_distance", "support_material_bottom_contact_distance",

View file

@ -2419,6 +2419,20 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(60));
def = this->add("support_material_style", coEnum);
def->label = L("Style");
def->category = L("Support material");
def->tooltip = L("Style and shape of the support towers. Projecting the supports into a regular grid "
"will create more stable supports, while snug support towers will save material and reduce "
"object scarring.");
def->enum_keys_map = &ConfigOptionEnum<SupportMaterialStyle>::get_enum_values();
def->enum_values.push_back("grid");
def->enum_values.push_back("snug");
def->enum_labels.push_back(L("Grid"));
def->enum_labels.push_back(L("Snug"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsGrid));
def = this->add("support_material_synchronize_layers", coBool);
def->label = L("Synchronize with object layers");
def->category = L("Support material");

View file

@ -65,6 +65,10 @@ enum SupportMaterialPattern {
smpRectilinear, smpRectilinearGrid, smpHoneycomb,
};
enum SupportMaterialStyle {
smsGrid, smsSnug,
};
enum SupportMaterialInterfacePattern {
smipAuto, smipRectilinear, smipConcentric,
};
@ -211,6 +215,15 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialPa
return keys_map;
}
template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialStyle>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["grid"] = smsGrid;
keys_map["snug"] = smsSnug;
}
return keys_map;
}
template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialInterfacePattern>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
@ -519,6 +532,7 @@ public:
// Spacing between support material lines (the hatching distance).
ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed;
ConfigOptionEnum<SupportMaterialStyle> support_material_style;
ConfigOptionBool support_material_synchronize_layers;
// Overhang angle threshold.
ConfigOptionInt support_material_threshold;
@ -570,6 +584,7 @@ protected:
OPT_PTR(support_material_interface_pattern);
OPT_PTR(support_material_spacing);
OPT_PTR(support_material_speed);
OPT_PTR(support_material_style);
OPT_PTR(support_material_synchronize_layers);
OPT_PTR(support_material_xy_spacing);
OPT_PTR(support_material_threshold);

View file

@ -588,6 +588,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_material_interface_extruder"
|| opt_key == "support_material_interface_spacing"
|| opt_key == "support_material_pattern"
|| opt_key == "support_material_style"
|| opt_key == "support_material_xy_spacing"
|| opt_key == "support_material_spacing"
|| opt_key == "support_material_synchronize_layers"

View file

@ -6,6 +6,7 @@
#include "Fill/FillBase.hpp"
#include "Geometry.hpp"
#include "Point.hpp"
#include "MutablePolygon.hpp"
#include <cmath>
#include <memory>
@ -667,17 +668,19 @@ Polygons collect_slices_outer(const Layer &layer)
struct SupportGridParams {
SupportGridParams(const PrintObjectConfig &object_config, const Flow &support_material_flow) :
style(object_config.support_material_style.value),
grid_resolution(object_config.support_material_spacing.value + support_material_flow.spacing()),
support_angle(Geometry::deg2rad(object_config.support_material_angle.value)),
extrusion_width(support_material_flow.spacing()),
expansion_to_slice(coord_t(support_material_flow.scaled_spacing() / 2 + 5)),
expansion_to_propagate(-3) {}
double grid_resolution;
double support_angle;
double extrusion_width;
coord_t expansion_to_slice;
coord_t expansion_to_propagate;
SupportMaterialStyle style;
double grid_resolution;
double support_angle;
double extrusion_width;
coord_t expansion_to_slice;
coord_t expansion_to_propagate;
};
class SupportGridPattern
@ -689,75 +692,88 @@ public:
// Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
const Polygons *trimming_polygons,
const SupportGridParams &params) :
m_style(params.style),
m_support_polygons(support_polygons), m_trimming_polygons(trimming_polygons),
m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle)
{
if (m_support_angle != 0.) {
// Create a copy of the rotated contours.
m_support_polygons_rotated = *support_polygons;
m_trimming_polygons_rotated = *trimming_polygons;
m_support_polygons = &m_support_polygons_rotated;
m_trimming_polygons = &m_trimming_polygons_rotated;
polygons_rotate(m_support_polygons_rotated, - params.support_angle);
polygons_rotate(m_trimming_polygons_rotated, - params.support_angle);
}
// Resolution of the sparse support grid.
coord_t grid_resolution = coord_t(scale_(m_support_spacing));
BoundingBox bbox = get_extents(*m_support_polygons);
bbox.offset(20);
// Align the bounding box with the sparse support grid.
bbox.align_to_grid(grid_resolution);
#ifdef SUPPORT_USE_AGG_RASTERIZER
m_bbox = bbox;
// Oversample the grid to avoid leaking of supports through or around the object walls.
int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100)));
m_pixel_size = scale_(m_support_spacing / oversampling);
assert(scale_(params.extrusion_width) + 20 < m_pixel_size);
// Add one empty column / row boundaries.
m_bbox.offset(m_pixel_size);
// Grid size fitting the support polygons plus one pixel boundary around the polygons.
Vec2i grid_size_raw(int(ceil((m_bbox.max.x() - m_bbox.min.x()) / m_pixel_size)),
int(ceil((m_bbox.max.y() - m_bbox.min.y()) / m_pixel_size)));
// Overlay macro blocks of (oversampling x oversampling) over the grid.
Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling,
(grid_size_raw.y() + oversampling - 1 - 2) / oversampling);
// and resize the grid to fit the macro blocks + one pixel boundary.
m_grid_size = grid_blocks * oversampling + Vec2i(2, 2);
assert(m_grid_size.x() >= grid_size_raw.x());
assert(m_grid_size.y() >= grid_size_raw.y());
m_grid2 = rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_support_polygons);
seed_fill_block(m_grid2, m_grid_size,
dilate_trimming_region(rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_trimming_polygons), m_grid_size),
grid_blocks, oversampling);
#ifdef SLIC3R_DEBUG
switch (m_style) {
case smsGrid:
{
static int irun;
Slic3r::png::write_gray_to_file_scaled(debug_out_path("support-rasterizer-%d.png", irun++), m_grid_size.x(), m_grid_size.y(), m_grid2.data(), 4);
}
#endif // SLIC3R_DEBUG
// Prepare the grid data, it will be reused when extracting support structures.
if (m_support_angle != 0.) {
// Create a copy of the rotated contours.
m_support_polygons_rotated = *support_polygons;
m_trimming_polygons_rotated = *trimming_polygons;
m_support_polygons = &m_support_polygons_rotated;
m_trimming_polygons = &m_trimming_polygons_rotated;
polygons_rotate(m_support_polygons_rotated, - params.support_angle);
polygons_rotate(m_trimming_polygons_rotated, - params.support_angle);
}
#else // SUPPORT_USE_AGG_RASTERIZER
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
m_grid.set_bbox(bbox);
m_grid.create(*m_support_polygons, grid_resolution);
#if 0
if (m_grid.has_intersecting_edges()) {
// EdgeGrid fails to produce valid signed distance function for self-intersecting polygons.
m_support_polygons_rotated = simplify_polygons(*m_support_polygons);
m_support_polygons = &m_support_polygons_rotated;
// Resolution of the sparse support grid.
coord_t grid_resolution = coord_t(scale_(m_support_spacing));
BoundingBox bbox = get_extents(*m_support_polygons);
bbox.offset(20);
// Align the bounding box with the sparse support grid.
bbox.align_to_grid(grid_resolution);
#ifdef SUPPORT_USE_AGG_RASTERIZER
m_bbox = bbox;
// Oversample the grid to avoid leaking of supports through or around the object walls.
int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100)));
m_pixel_size = scale_(m_support_spacing / oversampling);
assert(scale_(params.extrusion_width) + 20 < m_pixel_size);
// Add one empty column / row boundaries.
m_bbox.offset(m_pixel_size);
// Grid size fitting the support polygons plus one pixel boundary around the polygons.
Vec2i grid_size_raw(int(ceil((m_bbox.max.x() - m_bbox.min.x()) / m_pixel_size)),
int(ceil((m_bbox.max.y() - m_bbox.min.y()) / m_pixel_size)));
// Overlay macro blocks of (oversampling x oversampling) over the grid.
Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling,
(grid_size_raw.y() + oversampling - 1 - 2) / oversampling);
// and resize the grid to fit the macro blocks + one pixel boundary.
m_grid_size = grid_blocks * oversampling + Vec2i(2, 2);
assert(m_grid_size.x() >= grid_size_raw.x());
assert(m_grid_size.y() >= grid_size_raw.y());
m_grid2 = rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_support_polygons);
seed_fill_block(m_grid2, m_grid_size,
dilate_trimming_region(rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_trimming_polygons), m_grid_size),
grid_blocks, oversampling);
#ifdef SLIC3R_DEBUG
{
static int irun;
Slic3r::png::write_gray_to_file_scaled(debug_out_path("support-rasterizer-%d.png", irun++), m_grid_size.x(), m_grid_size.y(), m_grid2.data(), 4);
}
#endif // SLIC3R_DEBUG
#else // SUPPORT_USE_AGG_RASTERIZER
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
m_grid.set_bbox(bbox);
m_grid.create(*m_support_polygons, grid_resolution);
// assert(! m_grid.has_intersecting_edges());
printf("SupportGridPattern: fixing polygons with intersection %s\n",
m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED");
#if 0
if (m_grid.has_intersecting_edges()) {
// EdgeGrid fails to produce valid signed distance function for self-intersecting polygons.
m_support_polygons_rotated = simplify_polygons(*m_support_polygons);
m_support_polygons = &m_support_polygons_rotated;
m_grid.set_bbox(bbox);
m_grid.create(*m_support_polygons, grid_resolution);
// assert(! m_grid.has_intersecting_edges());
printf("SupportGridPattern: fixing polygons with intersection %s\n",
m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED");
}
#endif
m_grid.calculate_sdf();
#endif // SUPPORT_USE_AGG_RASTERIZER
break;
}
case smsSnug:
default:
// nothing to prepare
break;
}
#endif
m_grid.calculate_sdf();
#endif // SUPPORT_USE_AGG_RASTERIZER
}
// Extract polygons from the grid, offsetted by offset_in_grid,
@ -770,97 +786,104 @@ public:
#endif
)
{
#ifdef SUPPORT_USE_AGG_RASTERIZER
Polygons support_polygons_simplified = contours_simplified(m_grid_size, m_pixel_size, m_bbox.min, m_grid2, offset_in_grid, fill_holes);
#else // SUPPORT_USE_AGG_RASTERIZER
// Generate islands, so each island may be tested for overlap with island_samples.
assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes);
#endif // SUPPORT_USE_AGG_RASTERIZER
switch (m_style) {
case smsGrid:
{
#ifdef SUPPORT_USE_AGG_RASTERIZER
Polygons support_polygons_simplified = contours_simplified(m_grid_size, m_pixel_size, m_bbox.min, m_grid2, offset_in_grid, fill_holes);
#else // SUPPORT_USE_AGG_RASTERIZER
// Generate islands, so each island may be tested for overlap with island_samples.
assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes);
#endif // SUPPORT_USE_AGG_RASTERIZER
ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false);
ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false);
// Extract polygons, which contain some of the island_samples.
Polygons out;
#if 0
out = to_polygons(std::move(islands));
#else
// Extract polygons, which contain some of the island_samples.
Polygons out;
// Sample a single point per input support polygon, keep it as a reference to maintain corresponding
// polygons if ever these polygons get split into parts by the trimming polygons.
// As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands.
// Trim ti with islands.
Points samples = island_samples(
offset_in_grid > 0 ?
// Expanding, thus m_support_polygons are all inside islands.
union_ex(*m_support_polygons) :
// Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands.
intersection_ex(*m_support_polygons, to_polygons(islands)));
// Sample a single point per input support polygon, keep it as a reference to maintain corresponding
// polygons if ever these polygons get split into parts by the trimming polygons.
// As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands.
// Trim ti with islands.
Points samples = island_samples(
offset_in_grid > 0 ?
// Expanding, thus m_support_polygons are all inside islands.
union_ex(*m_support_polygons) :
// Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands.
intersection_ex(*m_support_polygons, to_polygons(islands)));
std::vector<std::pair<Point,bool>> samples_inside;
for (ExPolygon &island : islands) {
BoundingBox bbox = get_extents(island.contour);
// Samples are sorted lexicographically.
auto it_lower = std::lower_bound(samples.begin(), samples.end(), Point(bbox.min - Point(1, 1)));
auto it_upper = std::upper_bound(samples.begin(), samples.end(), Point(bbox.max + Point(1, 1)));
samples_inside.clear();
for (auto it = it_lower; it != it_upper; ++ it)
if (bbox.contains(*it))
samples_inside.push_back(std::make_pair(*it, false));
if (! samples_inside.empty()) {
// For all samples_inside count the boundary crossing.
for (size_t i_contour = 0; i_contour <= island.holes.size(); ++ i_contour) {
Polygon &contour = (i_contour == 0) ? island.contour : island.holes[i_contour - 1];
Points::const_iterator i = contour.points.begin();
Points::const_iterator j = contour.points.end() - 1;
for (; i != contour.points.end(); j = i ++) {
//FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well.
// Does the ray with y == point(1) intersect this line segment?
for (auto &sample_inside : samples_inside) {
if (((*i)(1) > sample_inside.first(1)) != ((*j)(1) > sample_inside.first(1))) {
double x1 = (double)sample_inside.first(0);
double x2 = (double)(*i)(0) + (double)((*j)(0) - (*i)(0)) * (double)(sample_inside.first(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1));
if (x1 < x2)
sample_inside.second = !sample_inside.second;
std::vector<std::pair<Point,bool>> samples_inside;
for (ExPolygon &island : islands) {
BoundingBox bbox = get_extents(island.contour);
// Samples are sorted lexicographically.
auto it_lower = std::lower_bound(samples.begin(), samples.end(), Point(bbox.min - Point(1, 1)));
auto it_upper = std::upper_bound(samples.begin(), samples.end(), Point(bbox.max + Point(1, 1)));
samples_inside.clear();
for (auto it = it_lower; it != it_upper; ++ it)
if (bbox.contains(*it))
samples_inside.push_back(std::make_pair(*it, false));
if (! samples_inside.empty()) {
// For all samples_inside count the boundary crossing.
for (size_t i_contour = 0; i_contour <= island.holes.size(); ++ i_contour) {
Polygon &contour = (i_contour == 0) ? island.contour : island.holes[i_contour - 1];
Points::const_iterator i = contour.points.begin();
Points::const_iterator j = contour.points.end() - 1;
for (; i != contour.points.end(); j = i ++) {
//FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well.
// Does the ray with y == point(1) intersect this line segment?
for (auto &sample_inside : samples_inside) {
if (((*i)(1) > sample_inside.first(1)) != ((*j)(1) > sample_inside.first(1))) {
double x1 = (double)sample_inside.first(0);
double x2 = (double)(*i)(0) + (double)((*j)(0) - (*i)(0)) * (double)(sample_inside.first(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1));
if (x1 < x2)
sample_inside.second = !sample_inside.second;
}
}
}
}
// If any of the sample is inside this island, add this island to the output.
for (auto &sample_inside : samples_inside)
if (sample_inside.second) {
polygons_append(out, std::move(island));
island.clear();
break;
}
}
// If any of the sample is inside this island, add this island to the output.
for (auto &sample_inside : samples_inside)
if (sample_inside.second) {
polygons_append(out, std::move(island));
island.clear();
break;
}
}
#ifdef SLIC3R_DEBUG
BoundingBox bbox = get_extents(*m_trimming_polygons);
if (! islands.empty())
bbox.merge(get_extents(islands));
if (!out.empty())
bbox.merge(get_extents(out));
if (!support_polygons_simplified.empty())
bbox.merge(get_extents(support_polygons_simplified));
SVG svg(debug_out_path("extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg", step_name, iRun, layer_id, print_z).c_str(), bbox);
svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f);
svg.draw(islands, "red", 0.5f);
svg.draw(union_ex(out), "green", 0.5f);
svg.draw(union_ex(*m_support_polygons), "blue", 0.5f);
svg.draw_outline(islands, "red", "red", scale_(0.05));
svg.draw_outline(union_ex(out), "green", "green", scale_(0.05));
svg.draw_outline(union_ex(*m_support_polygons), "blue", "blue", scale_(0.05));
for (const Point &pt : samples)
svg.draw(pt, "black", coord_t(scale_(0.15)));
svg.Close();
#endif /* SLIC3R_DEBUG */
if (m_support_angle != 0.)
polygons_rotate(out, m_support_angle);
return out;
}
#endif
#ifdef SLIC3R_DEBUG
BoundingBox bbox = get_extents(*m_trimming_polygons);
if (! islands.empty())
bbox.merge(get_extents(islands));
if (!out.empty())
bbox.merge(get_extents(out));
if (!support_polygons_simplified.empty())
bbox.merge(get_extents(support_polygons_simplified));
SVG svg(debug_out_path("extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg", step_name, iRun, layer_id, print_z).c_str(), bbox);
svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f);
svg.draw(islands, "red", 0.5f);
svg.draw(union_ex(out), "green", 0.5f);
svg.draw(union_ex(*m_support_polygons), "blue", 0.5f);
svg.draw_outline(islands, "red", "red", scale_(0.05));
svg.draw_outline(union_ex(out), "green", "green", scale_(0.05));
svg.draw_outline(union_ex(*m_support_polygons), "blue", "blue", scale_(0.05));
for (const Point &pt : samples)
svg.draw(pt, "black", coord_t(scale_(0.15)));
svg.Close();
#endif /* SLIC3R_DEBUG */
if (m_support_angle != 0.)
polygons_rotate(out, m_support_angle);
return out;
case smsSnug:
// Just close the gaps.
float thr = scaled<float>(0.5);
return smooth_outward(offset(offset_ex(*m_support_polygons, thr), - thr), thr);
}
assert(false);
return Polygons();
}
#if defined(SLIC3R_DEBUG) && ! defined(SUPPORT_USE_AGG_RASTERIZER)
@ -1096,6 +1119,7 @@ private:
return pts;
}
SupportMaterialStyle m_style;
const Polygons *m_support_polygons;
const Polygons *m_trimming_polygons;
Polygons m_support_polygons_rotated;
@ -1525,7 +1549,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
ClipperLib::jtRound,
// round mitter limit
scale_(0.05)),
slices_margin_cached);
slices_margin.polygons);
}
#else
diff_polygons = diff(diff_polygons, slices_margin.polygons);