Merge remote-tracking branch 'origin/master' into ys_extruders_color
This commit is contained in:
commit
a0b46a4019
50 changed files with 538 additions and 637 deletions
|
@ -90,7 +90,7 @@ struct stl_neighbors {
|
|||
|
||||
struct stl_stats {
|
||||
stl_stats() { memset(&header, 0, 81); }
|
||||
char header[81] = "";
|
||||
char header[81];
|
||||
stl_type type = (stl_type)0;
|
||||
uint32_t number_of_facets = 0;
|
||||
stl_vertex max = stl_vertex::Zero();
|
||||
|
|
|
@ -100,7 +100,7 @@ add_library(libslic3r STATIC
|
|||
Geometry.cpp
|
||||
Geometry.hpp
|
||||
Int128.hpp
|
||||
KdTreeIndirect.hpp
|
||||
KDTreeIndirect.hpp
|
||||
Layer.cpp
|
||||
Layer.hpp
|
||||
LayerRegion.cpp
|
||||
|
@ -131,8 +131,6 @@ add_library(libslic3r STATIC
|
|||
PolygonTrimmer.hpp
|
||||
Polyline.cpp
|
||||
Polyline.hpp
|
||||
PolylineCollection.cpp
|
||||
PolylineCollection.hpp
|
||||
Print.cpp
|
||||
Print.hpp
|
||||
PrintBase.cpp
|
||||
|
|
|
@ -13,12 +13,12 @@ typedef std::vector<ExPolygonCollection> ExPolygonCollections;
|
|||
|
||||
class ExPolygonCollection
|
||||
{
|
||||
public:
|
||||
public:
|
||||
ExPolygons expolygons;
|
||||
|
||||
ExPolygonCollection() {};
|
||||
ExPolygonCollection() {}
|
||||
ExPolygonCollection(const ExPolygon &expolygon);
|
||||
ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {};
|
||||
ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {}
|
||||
operator Points() const;
|
||||
operator Polygons() const;
|
||||
operator ExPolygons&();
|
||||
|
|
|
@ -14,7 +14,7 @@ class ExtrusionEntityCollection;
|
|||
class Extruder;
|
||||
|
||||
// Each ExtrusionRole value identifies a distinct set of { extruder, speed }
|
||||
enum ExtrusionRole {
|
||||
enum ExtrusionRole : uint8_t {
|
||||
erNone,
|
||||
erPerimeter,
|
||||
erExternalPerimeter,
|
||||
|
@ -81,8 +81,8 @@ public:
|
|||
virtual ExtrusionEntity* clone_move() = 0;
|
||||
virtual ~ExtrusionEntity() {}
|
||||
virtual void reverse() = 0;
|
||||
virtual Point first_point() const = 0;
|
||||
virtual Point last_point() const = 0;
|
||||
virtual const Point& first_point() const = 0;
|
||||
virtual const Point& last_point() const = 0;
|
||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
||||
virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0;
|
||||
|
@ -117,30 +117,23 @@ public:
|
|||
float width;
|
||||
// Height of the extrusion, used for visualization purposes.
|
||||
float height;
|
||||
// Feedrate of the extrusion, used for visualization purposes.
|
||||
float feedrate;
|
||||
// Id of the extruder, used for visualization purposes.
|
||||
unsigned int extruder_id;
|
||||
// Id of the color, used for visualization purposes in the color printing case.
|
||||
unsigned int cp_color_id;
|
||||
|
||||
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {}
|
||||
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {}
|
||||
ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {}
|
||||
// ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(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) {};
|
||||
ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
|
||||
ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = rhs.polyline; return *this; }
|
||||
ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = std::move(rhs.polyline); return *this; }
|
||||
ExtrusionPath& operator=(const ExtrusionPath& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = rhs.polyline; return *this; }
|
||||
ExtrusionPath& operator=(ExtrusionPath&& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = std::move(rhs.polyline); return *this; }
|
||||
|
||||
ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); }
|
||||
// Create a new object, initialize it with this object using the move semantics.
|
||||
ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); }
|
||||
void reverse() override { this->polyline.reverse(); }
|
||||
Point first_point() const override { return this->polyline.points.front(); }
|
||||
Point last_point() const override { return this->polyline.points.back(); }
|
||||
const Point& first_point() const override { return this->polyline.points.front(); }
|
||||
const Point& last_point() const override { return this->polyline.points.back(); }
|
||||
size_t size() const { return this->polyline.size(); }
|
||||
bool empty() const { return this->polyline.empty(); }
|
||||
bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
|
||||
|
@ -200,8 +193,8 @@ public:
|
|||
// Create a new object, initialize it with this object using the move semantics.
|
||||
ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); }
|
||||
void reverse() override;
|
||||
Point first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||
Point last_point() const override { return this->paths.back().polyline.points.back(); }
|
||||
const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||
const Point& last_point() const override { return this->paths.back().polyline.points.back(); }
|
||||
double length() const override;
|
||||
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||
|
@ -243,8 +236,8 @@ public:
|
|||
bool make_clockwise();
|
||||
bool make_counter_clockwise();
|
||||
void reverse() override;
|
||||
Point first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||
Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
|
||||
const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||
const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); }
|
||||
Polygon polygon() const;
|
||||
double length() const override;
|
||||
bool split_at_vertex(const Point &point);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "ExtrusionEntityCollection.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
|
@ -73,78 +74,31 @@ void ExtrusionEntityCollection::remove(size_t i)
|
|||
this->entities.erase(this->entities.begin() + i);
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const
|
||||
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const
|
||||
{
|
||||
ExtrusionEntityCollection coll;
|
||||
this->chained_path(&coll, no_reverse, role);
|
||||
return coll;
|
||||
}
|
||||
|
||||
void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
|
||||
{
|
||||
if (this->entities.empty()) return;
|
||||
this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role);
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const
|
||||
{
|
||||
ExtrusionEntityCollection coll;
|
||||
this->chained_path_from(start_near, &coll, no_reverse, role);
|
||||
return coll;
|
||||
}
|
||||
|
||||
void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
|
||||
{
|
||||
if (this->no_sort) {
|
||||
*retval = *this;
|
||||
return;
|
||||
}
|
||||
|
||||
retval->entities.reserve(this->entities.size());
|
||||
|
||||
// if we're asked to return the original indices, build a map
|
||||
std::map<ExtrusionEntity*,size_t> indices_map;
|
||||
|
||||
ExtrusionEntitiesPtr my_paths;
|
||||
for (ExtrusionEntity * const &entity_src : this->entities) {
|
||||
if (role != erMixed) {
|
||||
// The caller wants only paths with a specific extrusion role.
|
||||
auto role2 = entity_src->role();
|
||||
if (role != role2) {
|
||||
// This extrusion entity does not match the role asked.
|
||||
assert(role2 != erMixed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ExtrusionEntity *entity = entity_src->clone();
|
||||
my_paths.push_back(entity);
|
||||
// if (orig_indices != nullptr)
|
||||
// indices_map[entity] = &entity_src - &this->entities.front();
|
||||
}
|
||||
|
||||
Points endpoints;
|
||||
for (const ExtrusionEntity *entity : my_paths) {
|
||||
endpoints.push_back(entity->first_point());
|
||||
endpoints.push_back((no_reverse || ! entity->can_reverse()) ?
|
||||
entity->first_point() : entity->last_point());
|
||||
}
|
||||
|
||||
while (! my_paths.empty()) {
|
||||
// find nearest point
|
||||
int start_index = start_near.nearest_point_index(endpoints);
|
||||
int path_index = start_index/2;
|
||||
ExtrusionEntity* entity = my_paths.at(path_index);
|
||||
// never reverse loops, since it's pointless for chained path and callers might depend on orientation
|
||||
if (start_index % 2 && !no_reverse && entity->can_reverse())
|
||||
entity->reverse();
|
||||
retval->entities.push_back(my_paths.at(path_index));
|
||||
// if (orig_indices != nullptr)
|
||||
// orig_indices->push_back(indices_map[entity]);
|
||||
my_paths.erase(my_paths.begin() + path_index);
|
||||
endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2);
|
||||
start_near = retval->entities.back()->last_point();
|
||||
}
|
||||
ExtrusionEntityCollection out;
|
||||
if (this->no_sort) {
|
||||
out = *this;
|
||||
} else {
|
||||
if (role == erMixed)
|
||||
out = *this;
|
||||
else {
|
||||
for (const ExtrusionEntity *ee : this->entities) {
|
||||
if (role != erMixed) {
|
||||
// The caller wants only paths with a specific extrusion role.
|
||||
auto role2 = ee->role();
|
||||
if (role != role2) {
|
||||
// This extrusion entity does not match the role asked.
|
||||
assert(role2 != erMixed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out.entities.emplace_back(ee->clone());
|
||||
}
|
||||
}
|
||||
chain_and_reorder_extrusion_entities(out.entities, &start_near);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
|
||||
|
|
|
@ -65,13 +65,10 @@ public:
|
|||
}
|
||||
void replace(size_t i, const ExtrusionEntity &entity);
|
||||
void remove(size_t i);
|
||||
ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const;
|
||||
void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
|
||||
ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const;
|
||||
void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
|
||||
ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const;
|
||||
void reverse();
|
||||
Point first_point() const { return this->entities.front()->first_point(); }
|
||||
Point last_point() const { return this->entities.back()->last_point(); }
|
||||
const Point& first_point() const { return this->entities.front()->first_point(); }
|
||||
const Point& last_point() const { return this->entities.back()->last_point(); }
|
||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
||||
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
|
||||
|
|
|
@ -176,7 +176,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
|||
}
|
||||
}
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
|
||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
|
|
|
@ -167,7 +167,7 @@ void FillGyroid::_fill_surface_single(
|
|||
}
|
||||
}
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
|
||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
|
|
|
@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
|
||||
// connect paths
|
||||
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
Polylines chained = chain_infill_polylines(std::move(paths));
|
||||
Polylines chained = chain_polylines(std::move(paths));
|
||||
assert(paths.empty());
|
||||
paths.clear();
|
||||
for (Polyline &path : chained) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillPlanePath.hpp"
|
||||
|
|
|
@ -93,7 +93,7 @@ void FillRectilinear::_fill_surface_single(
|
|||
}
|
||||
}
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
|
||||
for (Polyline &polyline : chain_polylines(std::move(polylines))) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
|
|
|
@ -117,11 +117,11 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
|
|||
const Layer* layer1 = object->layers()[i * 2];
|
||||
const Layer* layer2 = object->layers()[i * 2 + 1];
|
||||
Polygons polys;
|
||||
polys.reserve(layer1->slices.expolygons.size() + layer2->slices.expolygons.size());
|
||||
for (const ExPolygon &expoly : layer1->slices.expolygons)
|
||||
polys.reserve(layer1->slices.size() + layer2->slices.size());
|
||||
for (const ExPolygon &expoly : layer1->slices)
|
||||
//FIXME no holes?
|
||||
polys.emplace_back(expoly.contour);
|
||||
for (const ExPolygon &expoly : layer2->slices.expolygons)
|
||||
for (const ExPolygon &expoly : layer2->slices)
|
||||
//FIXME no holes?
|
||||
polys.emplace_back(expoly.contour);
|
||||
polygons_per_layer[i] = union_(polys);
|
||||
|
@ -130,8 +130,8 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
|
|||
if (object->layers().size() & 1) {
|
||||
const Layer *layer = object->layers().back();
|
||||
Polygons polys;
|
||||
polys.reserve(layer->slices.expolygons.size());
|
||||
for (const ExPolygon &expoly : layer->slices.expolygons)
|
||||
polys.reserve(layer->slices.size());
|
||||
for (const ExPolygon &expoly : layer->slices)
|
||||
//FIXME no holes?
|
||||
polys.emplace_back(expoly.contour);
|
||||
polygons_per_layer.back() = union_(polys);
|
||||
|
@ -1802,11 +1802,8 @@ void GCode::process_layer(
|
|||
// - for each island, we extrude perimeters first, unless user set the infill_first
|
||||
// option
|
||||
// (Still, we have to keep track of regions because we need to apply their config)
|
||||
size_t n_slices = layer.slices.expolygons.size();
|
||||
std::vector<BoundingBox> layer_surface_bboxes;
|
||||
layer_surface_bboxes.reserve(n_slices);
|
||||
for (const ExPolygon &expoly : layer.slices.expolygons)
|
||||
layer_surface_bboxes.push_back(get_extents(expoly.contour));
|
||||
size_t n_slices = layer.slices.size();
|
||||
const std::vector<BoundingBox> &layer_surface_bboxes = layer.slices_bboxes;
|
||||
// Traverse the slices in an increasing order of bounding box size, so that the islands inside another islands are tested first,
|
||||
// so we can just test a point inside ExPolygon::contour and we may skip testing the holes.
|
||||
std::vector<size_t> slices_test_order;
|
||||
|
@ -1822,7 +1819,7 @@ void GCode::process_layer(
|
|||
const BoundingBox &bbox = layer_surface_bboxes[i];
|
||||
return point(0) >= bbox.min(0) && point(0) < bbox.max(0) &&
|
||||
point(1) >= bbox.min(1) && point(1) < bbox.max(1) &&
|
||||
layer.slices.expolygons[i].contour.contains(point);
|
||||
layer.slices[i].contour.contains(point);
|
||||
};
|
||||
|
||||
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
|
||||
|
@ -1969,7 +1966,7 @@ void GCode::process_layer(
|
|||
m_layer = layers[instance_to_print.layer_id].support_layer;
|
||||
gcode += this->extrude_support(
|
||||
// support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
|
||||
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, false, instance_to_print.object_by_extruder.support_extrusion_role));
|
||||
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role));
|
||||
m_layer = layers[instance_to_print.layer_id].layer();
|
||||
}
|
||||
for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
|
||||
|
@ -2418,7 +2415,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
static int iRun = 0;
|
||||
SVG svg(debug_out_path("GCode_extrude_loop-%d.svg", iRun ++));
|
||||
if (m_layer->lower_layer != NULL)
|
||||
svg.draw(m_layer->lower_layer->slices.expolygons);
|
||||
svg.draw(m_layer->lower_layer->slices);
|
||||
for (size_t i = 0; i < loop.paths.size(); ++ i)
|
||||
svg.draw(loop.paths[i].as_polyline(), "red");
|
||||
Polylines polylines;
|
||||
|
@ -2588,10 +2585,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
|
|||
std::string gcode;
|
||||
for (const ObjectByExtruder::Island::Region ®ion : by_region) {
|
||||
m_config.apply(print.regions()[®ion - &by_region.front()]->config());
|
||||
for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) {
|
||||
for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos).entities) {
|
||||
auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill);
|
||||
if (eec) {
|
||||
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities)
|
||||
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities)
|
||||
gcode += this->extrude_entity(*ee, "infill");
|
||||
} else
|
||||
gcode += this->extrude_entity(*fill, "infill");
|
||||
|
|
|
@ -20,6 +20,7 @@ static const unsigned int DEFAULT_EXTRUDER_ID = 0;
|
|||
static const unsigned int DEFAULT_COLOR_PRINT_ID = 0;
|
||||
static const Slic3r::Vec3d DEFAULT_START_POSITION = Slic3r::Vec3d(0.0f, 0.0f, 0.0f);
|
||||
static const float DEFAULT_START_EXTRUSION = 0.0f;
|
||||
static const float DEFAULT_FAN_SPEED = 0.0f;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -36,21 +37,23 @@ const float GCodeAnalyzer::Default_Height = 0.0f;
|
|||
GCodeAnalyzer::Metadata::Metadata()
|
||||
: extrusion_role(erNone)
|
||||
, extruder_id(DEFAULT_EXTRUDER_ID)
|
||||
, cp_color_id(DEFAULT_COLOR_PRINT_ID)
|
||||
, mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm)
|
||||
, width(GCodeAnalyzer::Default_Width)
|
||||
, height(GCodeAnalyzer::Default_Height)
|
||||
, feedrate(DEFAULT_FEEDRATE)
|
||||
, fan_speed(DEFAULT_FAN_SPEED)
|
||||
, cp_color_id(DEFAULT_COLOR_PRINT_ID)
|
||||
{
|
||||
}
|
||||
|
||||
GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id/* = 0*/)
|
||||
GCodeAnalyzer::Metadata::Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id/* = 0*/)
|
||||
: extrusion_role(extrusion_role)
|
||||
, extruder_id(extruder_id)
|
||||
, mm3_per_mm(mm3_per_mm)
|
||||
, width(width)
|
||||
, height(height)
|
||||
, feedrate(feedrate)
|
||||
, fan_speed(fan_speed)
|
||||
, cp_color_id(cp_color_id)
|
||||
{
|
||||
}
|
||||
|
@ -75,15 +78,18 @@ bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other)
|
|||
if (feedrate != other.feedrate)
|
||||
return true;
|
||||
|
||||
if (fan_speed != other.fan_speed)
|
||||
return true;
|
||||
|
||||
if (cp_color_id != other.cp_color_id)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id/* = 0*/)
|
||||
GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id/* = 0*/)
|
||||
: type(type)
|
||||
, data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, cp_color_id)
|
||||
, data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate, fan_speed, cp_color_id)
|
||||
, start_position(start_position)
|
||||
, end_position(end_position)
|
||||
, delta_extruder(delta_extruder)
|
||||
|
@ -133,6 +139,7 @@ void GCodeAnalyzer::reset()
|
|||
_set_feedrate(DEFAULT_FEEDRATE);
|
||||
_set_start_position(DEFAULT_START_POSITION);
|
||||
_set_start_extrusion(DEFAULT_START_EXTRUSION);
|
||||
_set_fan_speed(DEFAULT_FAN_SPEED);
|
||||
_reset_axes_position();
|
||||
_reset_cached_position();
|
||||
|
||||
|
@ -259,6 +266,16 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
|
|||
_processM83(line);
|
||||
break;
|
||||
}
|
||||
case 106: // Set fan speed
|
||||
{
|
||||
_processM106(line);
|
||||
break;
|
||||
}
|
||||
case 107: // Disable fan
|
||||
{
|
||||
_processM107(line);
|
||||
break;
|
||||
}
|
||||
case 108:
|
||||
case 135:
|
||||
{
|
||||
|
@ -448,6 +465,24 @@ void GCodeAnalyzer::_processM83(const GCodeReader::GCodeLine& line)
|
|||
_set_e_local_positioning_type(Relative);
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_processM106(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (!line.has('P'))
|
||||
{
|
||||
// The absence of P means the print cooling fan, so ignore anything else.
|
||||
float new_fan_speed;
|
||||
if (line.has_value('S', new_fan_speed))
|
||||
_set_fan_speed((100.0f / 256.0f) * new_fan_speed);
|
||||
else
|
||||
_set_fan_speed(100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_processM107(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
_set_fan_speed(0.0f);
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
// These M-codes are used by MakerWare and Sailfish to change active tool.
|
||||
|
@ -726,6 +761,16 @@ float GCodeAnalyzer::_get_feedrate() const
|
|||
return m_state.data.feedrate;
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_set_fan_speed(float fan_speed_percentage)
|
||||
{
|
||||
m_state.data.fan_speed = fan_speed_percentage;
|
||||
}
|
||||
|
||||
float GCodeAnalyzer::_get_fan_speed() const
|
||||
{
|
||||
return m_state.data.fan_speed;
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_set_axis_position(EAxis axis, float position)
|
||||
{
|
||||
m_state.position[axis] = position;
|
||||
|
@ -798,7 +843,7 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type)
|
|||
|
||||
Vec3d start_position = _get_start_position() + extruder_offset;
|
||||
Vec3d end_position = _get_end_position() + extruder_offset;
|
||||
it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_cp_color_id());
|
||||
it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_fan_speed(), _get_cp_color_id());
|
||||
}
|
||||
|
||||
bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const
|
||||
|
@ -821,7 +866,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
|
|||
}
|
||||
|
||||
// if layer not found, create and return it
|
||||
layers.emplace_back(z, ExtrusionPaths());
|
||||
layers.emplace_back(z, GCodePreviewData::Extrusion::Paths());
|
||||
return layers.back();
|
||||
}
|
||||
|
||||
|
@ -830,13 +875,18 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
|
|||
// if the polyline is valid, create the extrusion path from it and store it
|
||||
if (polyline.is_valid())
|
||||
{
|
||||
ExtrusionPath path(data.extrusion_role, data.mm3_per_mm, data.width, data.height);
|
||||
auto& paths = get_layer_at_z(preview_data.extrusion.layers, z).paths;
|
||||
paths.emplace_back(GCodePreviewData::Extrusion::Path());
|
||||
GCodePreviewData::Extrusion::Path &path = paths.back();
|
||||
path.polyline = polyline;
|
||||
path.extrusion_role = data.extrusion_role;
|
||||
path.mm3_per_mm = data.mm3_per_mm;
|
||||
path.width = data.width;
|
||||
path.height = data.height;
|
||||
path.feedrate = data.feedrate;
|
||||
path.extruder_id = data.extruder_id;
|
||||
path.cp_color_id = data.cp_color_id;
|
||||
|
||||
get_layer_at_z(preview_data.extrusion.layers, z).paths.push_back(path);
|
||||
path.fan_speed = data.fan_speed;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -854,6 +904,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
|
|||
GCodePreviewData::Range width_range;
|
||||
GCodePreviewData::Range feedrate_range;
|
||||
GCodePreviewData::Range volumetric_rate_range;
|
||||
GCodePreviewData::Range fan_speed_range;
|
||||
|
||||
// to avoid to call the callback too often
|
||||
unsigned int cancel_callback_threshold = (unsigned int)std::max((int)extrude_moves->second.size() / 25, 1);
|
||||
|
@ -888,6 +939,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
|
|||
width_range.update_from(move.data.width);
|
||||
feedrate_range.update_from(move.data.feedrate);
|
||||
volumetric_rate_range.update_from(volumetric_rate);
|
||||
fan_speed_range.update_from(move.data.fan_speed);
|
||||
}
|
||||
else
|
||||
// append end vertex of the move to current polyline
|
||||
|
@ -906,6 +958,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
|
|||
preview_data.ranges.width.update_from(width_range);
|
||||
preview_data.ranges.feedrate.update_from(feedrate_range);
|
||||
preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range);
|
||||
preview_data.ranges.fan_speed.update_from(fan_speed_range);
|
||||
|
||||
// we need to sort the layers by their z as they can be shuffled in case of sequential prints
|
||||
std::sort(preview_data.extrusion.layers.begin(), preview_data.extrusion.layers.end(), [](const GCodePreviewData::Extrusion::Layer& l1, const GCodePreviewData::Extrusion::Layer& l2)->bool { return l1.z < l2.z; });
|
||||
|
|
|
@ -54,10 +54,11 @@ public:
|
|||
float width; // mm
|
||||
float height; // mm
|
||||
float feedrate; // mm/s
|
||||
float fan_speed; // percentage
|
||||
unsigned int cp_color_id;
|
||||
|
||||
Metadata();
|
||||
Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, unsigned int cp_color_id = 0);
|
||||
Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, float fan_speed, unsigned int cp_color_id = 0);
|
||||
|
||||
bool operator != (const Metadata& other) const;
|
||||
};
|
||||
|
@ -81,7 +82,7 @@ public:
|
|||
Vec3d end_position;
|
||||
float delta_extruder;
|
||||
|
||||
GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, unsigned int cp_color_id = 0);
|
||||
GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder, float fan_speed, unsigned int cp_color_id = 0);
|
||||
GCodeMove(EType type, const Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder);
|
||||
};
|
||||
|
||||
|
@ -171,6 +172,12 @@ private:
|
|||
// Set extruder to relative mode
|
||||
void _processM83(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set fan speed
|
||||
void _processM106(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Disable fan
|
||||
void _processM107(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set tool (MakerWare and Sailfish flavor)
|
||||
void _processM108orM135(const GCodeReader::GCodeLine& line);
|
||||
|
||||
|
@ -233,6 +240,9 @@ private:
|
|||
void _set_feedrate(float feedrate_mm_sec);
|
||||
float _get_feedrate() const;
|
||||
|
||||
void _set_fan_speed(float fan_speed_percentage);
|
||||
float _get_fan_speed() const;
|
||||
|
||||
void _set_axis_position(EAxis axis, float position);
|
||||
float _get_axis_position(EAxis axis) const;
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ std::vector<unsigned char> GCodePreviewData::Color::as_bytes() const
|
|||
return ret;
|
||||
}
|
||||
|
||||
GCodePreviewData::Extrusion::Layer::Layer(float z, const ExtrusionPaths& paths)
|
||||
GCodePreviewData::Extrusion::Layer::Layer(float z, const Paths& paths)
|
||||
: z(z)
|
||||
, paths(paths)
|
||||
{
|
||||
|
@ -171,8 +171,8 @@ size_t GCodePreviewData::Extrusion::memory_used() const
|
|||
size_t out = sizeof(*this);
|
||||
out += SLIC3R_STDVEC_MEMSIZE(this->layers, Layer);
|
||||
for (const Layer &layer : this->layers) {
|
||||
out += SLIC3R_STDVEC_MEMSIZE(layer.paths, ExtrusionPath);
|
||||
for (const ExtrusionPath &path : layer.paths)
|
||||
out += SLIC3R_STDVEC_MEMSIZE(layer.paths, Path);
|
||||
for (const Path &path : layer.paths)
|
||||
out += SLIC3R_STDVEC_MEMSIZE(path.polyline.points, Point);
|
||||
}
|
||||
return out;
|
||||
|
@ -241,6 +241,7 @@ void GCodePreviewData::set_default()
|
|||
::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
|
||||
::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
|
||||
::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
|
||||
::memcpy((void*)ranges.fan_speed.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
|
||||
::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
|
||||
|
||||
extrusion.set_default();
|
||||
|
@ -287,6 +288,11 @@ GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) con
|
|||
return ranges.feedrate.get_color_at(feedrate);
|
||||
}
|
||||
|
||||
GCodePreviewData::Color GCodePreviewData::get_fan_speed_color(float fan_speed) const
|
||||
{
|
||||
return ranges.fan_speed.get_color_at(fan_speed);
|
||||
}
|
||||
|
||||
GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const
|
||||
{
|
||||
return ranges.volumetric_rate.get_color_at(rate);
|
||||
|
@ -358,6 +364,8 @@ std::string GCodePreviewData::get_legend_title() const
|
|||
return L("Width (mm)");
|
||||
case Extrusion::Feedrate:
|
||||
return L("Speed (mm/s)");
|
||||
case Extrusion::FanSpeed:
|
||||
return L("Fan Speed (%)");
|
||||
case Extrusion::VolumetricRate:
|
||||
return L("Volumetric flow rate (mm³/s)");
|
||||
case Extrusion::Tool:
|
||||
|
@ -421,6 +429,11 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
|
|||
Helper::FillListFromRange(items, ranges.feedrate, 1, 1.0f);
|
||||
break;
|
||||
}
|
||||
case Extrusion::FanSpeed:
|
||||
{
|
||||
Helper::FillListFromRange(items, ranges.fan_speed, 0, 1.0f);
|
||||
break;
|
||||
}
|
||||
case Extrusion::VolumetricRate:
|
||||
{
|
||||
Helper::FillListFromRange(items, ranges.volumetric_rate, 3, 1.0f);
|
||||
|
|
|
@ -52,6 +52,8 @@ public:
|
|||
Range width;
|
||||
// Color mapping by feedrate.
|
||||
Range feedrate;
|
||||
// Color mapping by fan speed.
|
||||
Range fan_speed;
|
||||
// Color mapping by volumetric extrusion rate.
|
||||
Range volumetric_rate;
|
||||
};
|
||||
|
@ -74,6 +76,7 @@ public:
|
|||
Height,
|
||||
Width,
|
||||
Feedrate,
|
||||
FanSpeed,
|
||||
VolumetricRate,
|
||||
Tool,
|
||||
ColorPrint,
|
||||
|
@ -84,12 +87,34 @@ public:
|
|||
static const std::string Default_Extrusion_Role_Names[erCount];
|
||||
static const EViewType Default_View_Type;
|
||||
|
||||
class Path
|
||||
{
|
||||
public:
|
||||
Polyline polyline;
|
||||
ExtrusionRole extrusion_role;
|
||||
// Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator.
|
||||
float mm3_per_mm;
|
||||
// Width of the extrusion, used for visualization purposes.
|
||||
float width;
|
||||
// Height of the extrusion, used for visualization purposes.
|
||||
float height;
|
||||
// Feedrate of the extrusion, used for visualization purposes.
|
||||
float feedrate;
|
||||
// Id of the extruder, used for visualization purposes.
|
||||
uint32_t extruder_id;
|
||||
// Id of the color, used for visualization purposes in the color printing case.
|
||||
uint32_t cp_color_id;
|
||||
// Fan speed for the extrusion, used for visualization purposes.
|
||||
float fan_speed;
|
||||
};
|
||||
using Paths = std::vector<Path>;
|
||||
|
||||
struct Layer
|
||||
{
|
||||
float z;
|
||||
ExtrusionPaths paths;
|
||||
Paths paths;
|
||||
|
||||
Layer(float z, const ExtrusionPaths& paths);
|
||||
Layer(float z, const Paths& paths);
|
||||
};
|
||||
|
||||
typedef std::vector<Layer> LayersList;
|
||||
|
@ -205,6 +230,7 @@ public:
|
|||
Color get_height_color(float height) const;
|
||||
Color get_width_color(float width) const;
|
||||
Color get_feedrate_color(float feedrate) const;
|
||||
Color get_fan_speed_color(float fan_speed) const;
|
||||
Color get_volumetric_rate_color(float rate) const;
|
||||
|
||||
void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "ClipperUtils.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "PolylineCollection.hpp"
|
||||
#include "clipper.hpp"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
|
|
@ -19,7 +19,10 @@ public:
|
|||
static constexpr size_t NumDimensions = ANumDimensions;
|
||||
using CoordinateFn = ACoordinateFn;
|
||||
using CoordType = ACoordType;
|
||||
static constexpr size_t npos = size_t(-1);
|
||||
// Following could be static constexpr size_t, but that would not link in C++11
|
||||
enum : size_t {
|
||||
npos = size_t(-1)
|
||||
};
|
||||
|
||||
KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {}
|
||||
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> indices) : coordinate(coordinate) { this->build(std::move(indices)); }
|
||||
|
@ -61,15 +64,17 @@ public:
|
|||
{
|
||||
CoordType dist = point_coord - this->coordinate(idx, dimension);
|
||||
return (dist * dist < search_radius + CoordType(EPSILON)) ?
|
||||
// The plane intersects a hypersphere centered at point_coord of search_radius.
|
||||
((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
|
||||
(dist < CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
|
||||
// The plane does not intersect the hypersphere.
|
||||
(dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
|
||||
}
|
||||
|
||||
// Visitor is supposed to return a bit mask of VisitorReturnMask.
|
||||
template<typename Visitor>
|
||||
void visit(Visitor &visitor) const
|
||||
{
|
||||
return m_nodes.empty() ? npos : visit_recursive(0, 0, visitor);
|
||||
visit_recursive(0, 0, visitor);
|
||||
}
|
||||
|
||||
CoordinateFn coordinate;
|
||||
|
|
|
@ -47,8 +47,8 @@ void Layer::make_slices()
|
|||
slices = union_ex(slices_p);
|
||||
}
|
||||
|
||||
this->slices.expolygons.clear();
|
||||
this->slices.expolygons.reserve(slices.size());
|
||||
this->slices.clear();
|
||||
this->slices.reserve(slices.size());
|
||||
|
||||
// prepare ordering points
|
||||
Points ordering_points;
|
||||
|
@ -61,7 +61,7 @@ void Layer::make_slices()
|
|||
|
||||
// populate slices vector
|
||||
for (size_t i : order)
|
||||
this->slices.expolygons.push_back(std::move(slices[i]));
|
||||
this->slices.push_back(std::move(slices[i]));
|
||||
}
|
||||
|
||||
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
|
||||
|
@ -70,7 +70,7 @@ void Layer::merge_slices()
|
|||
if (m_regions.size() == 1) {
|
||||
// Optimization, also more robust. Don't merge classified pieces of layerm->slices,
|
||||
// but use the non-split islands of a layer. For a single region print, these shall be equal.
|
||||
m_regions.front()->slices.set(this->slices.expolygons, stInternal);
|
||||
m_regions.front()->slices.set(this->slices, stInternal);
|
||||
} else {
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
// without safety offset, artifacts are generated (GH #2494)
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
#include "SurfaceCollection.hpp"
|
||||
#include "ExtrusionEntityCollection.hpp"
|
||||
#include "ExPolygonCollection.hpp"
|
||||
#include "PolylineCollection.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -48,7 +46,7 @@ public:
|
|||
Polygons bridged;
|
||||
|
||||
// collection of polylines representing the unsupported bridge edges
|
||||
PolylineCollection unsupported_bridge_edges;
|
||||
Polylines unsupported_bridge_edges;
|
||||
|
||||
// ordered collection of extrusion paths/loops to build all perimeters
|
||||
// (this collection contains only ExtrusionEntityCollection objects)
|
||||
|
@ -112,7 +110,8 @@ public:
|
|||
// also known as 'islands' (all regions and surface types are merged here)
|
||||
// The slices are chained by the shortest traverse distance and this traversal
|
||||
// order will be recovered by the G-code generator.
|
||||
ExPolygonCollection slices;
|
||||
ExPolygons slices;
|
||||
std::vector<BoundingBox> slices_bboxes;
|
||||
|
||||
size_t region_count() const { return m_regions.size(); }
|
||||
const LayerRegion* get_region(int idx) const { return m_regions.at(idx); }
|
||||
|
|
|
@ -140,7 +140,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
// Remove voids from fill_boundaries, that are not supported by the layer below.
|
||||
if (lower_layer_covered == nullptr) {
|
||||
lower_layer_covered = &lower_layer_covered_tmp;
|
||||
lower_layer_covered_tmp = to_polygons(lower_layer->slices.expolygons);
|
||||
lower_layer_covered_tmp = to_polygons(lower_layer->slices);
|
||||
}
|
||||
if (! lower_layer_covered->empty())
|
||||
voids = diff(voids, *lower_layer_covered);
|
||||
|
@ -272,7 +272,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
bridges[idx_last].bridge_angle = bd.angle;
|
||||
if (this->layer()->object()->config().support_material) {
|
||||
polygons_append(this->bridged, bd.coverage());
|
||||
this->unsupported_bridge_edges.append(bd.unsupported_edges());
|
||||
append(this->unsupported_bridge_edges, bd.unsupported_edges());
|
||||
}
|
||||
} else if (custom_angle > 0) {
|
||||
// Bridge was not detected (likely it is only supported at one side). Still it is a surface filled in
|
||||
|
|
|
@ -3,11 +3,6 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
MultiPoint::operator Points() const
|
||||
{
|
||||
return this->points;
|
||||
}
|
||||
|
||||
void MultiPoint::scale(double factor)
|
||||
{
|
||||
for (Point &pt : points)
|
||||
|
@ -57,18 +52,7 @@ void MultiPoint::rotate(double angle, const Point ¢er)
|
|||
}
|
||||
}
|
||||
|
||||
void MultiPoint::reverse()
|
||||
{
|
||||
std::reverse(this->points.begin(), this->points.end());
|
||||
}
|
||||
|
||||
Point MultiPoint::first_point() const
|
||||
{
|
||||
return this->points.front();
|
||||
}
|
||||
|
||||
double
|
||||
MultiPoint::length() const
|
||||
double MultiPoint::length() const
|
||||
{
|
||||
Lines lines = this->lines();
|
||||
double len = 0;
|
||||
|
@ -78,8 +62,7 @@ MultiPoint::length() const
|
|||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
MultiPoint::find_point(const Point &point) const
|
||||
int MultiPoint::find_point(const Point &point) const
|
||||
{
|
||||
for (const Point &pt : this->points)
|
||||
if (pt == point)
|
||||
|
@ -87,21 +70,18 @@ MultiPoint::find_point(const Point &point) const
|
|||
return -1; // not found
|
||||
}
|
||||
|
||||
bool
|
||||
MultiPoint::has_boundary_point(const Point &point) const
|
||||
bool MultiPoint::has_boundary_point(const Point &point) const
|
||||
{
|
||||
double dist = (point.projection_onto(*this) - point).cast<double>().norm();
|
||||
return dist < SCALED_EPSILON;
|
||||
}
|
||||
|
||||
BoundingBox
|
||||
MultiPoint::bounding_box() const
|
||||
BoundingBox MultiPoint::bounding_box() const
|
||||
{
|
||||
return BoundingBox(this->points);
|
||||
}
|
||||
|
||||
bool
|
||||
MultiPoint::has_duplicate_points() const
|
||||
bool MultiPoint::has_duplicate_points() const
|
||||
{
|
||||
for (size_t i = 1; i < points.size(); ++i)
|
||||
if (points[i-1] == points[i])
|
||||
|
@ -109,8 +89,7 @@ MultiPoint::has_duplicate_points() const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
MultiPoint::remove_duplicate_points()
|
||||
bool MultiPoint::remove_duplicate_points()
|
||||
{
|
||||
size_t j = 0;
|
||||
for (size_t i = 1; i < points.size(); ++i) {
|
||||
|
@ -129,8 +108,7 @@ MultiPoint::remove_duplicate_points()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
MultiPoint::intersection(const Line& line, Point* intersection) const
|
||||
bool MultiPoint::intersection(const Line& line, Point* intersection) const
|
||||
{
|
||||
Lines lines = this->lines();
|
||||
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) {
|
||||
|
|
|
@ -17,7 +17,8 @@ class MultiPoint
|
|||
public:
|
||||
Points points;
|
||||
|
||||
operator Points() const;
|
||||
operator Points() const { return this->points; }
|
||||
|
||||
MultiPoint() {}
|
||||
MultiPoint(const MultiPoint &other) : points(other.points) {}
|
||||
MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {}
|
||||
|
@ -32,9 +33,10 @@ public:
|
|||
void rotate(double angle) { this->rotate(cos(angle), sin(angle)); }
|
||||
void rotate(double cos_angle, double sin_angle);
|
||||
void rotate(double angle, const Point ¢er);
|
||||
void reverse();
|
||||
Point first_point() const;
|
||||
virtual Point last_point() const = 0;
|
||||
void reverse() { std::reverse(this->points.begin(), this->points.end()); }
|
||||
|
||||
const Point& first_point() const { return this->points.front(); }
|
||||
virtual const Point& last_point() const = 0;
|
||||
virtual Lines lines() const = 0;
|
||||
size_t size() const { return points.size(); }
|
||||
bool empty() const { return points.empty(); }
|
||||
|
|
|
@ -175,10 +175,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
|||
perimeter_generator.overhang_flow.width,
|
||||
perimeter_generator.overhang_flow.height);
|
||||
|
||||
// reapply the nearest point search for starting point
|
||||
// We allow polyline reversal because Clipper may have randomly
|
||||
// reversed polylines during clipping.
|
||||
paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path();
|
||||
// Reapply the nearest point search for starting point.
|
||||
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
|
||||
chain_and_reorder_extrusion_paths(paths, &paths.front().first_point());
|
||||
} else {
|
||||
ExtrusionPath path(role);
|
||||
path.polyline = loop.polygon.split_at_first_point();
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "libslic3r.h"
|
||||
#include <vector>
|
||||
#include "ExPolygonCollection.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
|
@ -15,7 +14,7 @@ class PerimeterGenerator {
|
|||
public:
|
||||
// Inputs:
|
||||
const SurfaceCollection *slices;
|
||||
const ExPolygonCollection *lower_slices;
|
||||
const ExPolygons *lower_slices;
|
||||
double layer_height;
|
||||
int layer_id;
|
||||
Flow perimeter_flow;
|
||||
|
@ -45,7 +44,7 @@ public:
|
|||
ExtrusionEntityCollection* gap_fill,
|
||||
// Infills without the gap fills
|
||||
SurfaceCollection* fill_surfaces)
|
||||
: slices(slices), lower_slices(NULL), layer_height(layer_height),
|
||||
: slices(slices), lower_slices(nullptr), layer_height(layer_height),
|
||||
layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow),
|
||||
overhang_flow(flow), solid_infill_flow(flow),
|
||||
config(config), object_config(object_config), print_config(print_config),
|
||||
|
|
|
@ -5,43 +5,12 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
Polygon::operator Polygons() const
|
||||
{
|
||||
Polygons pp;
|
||||
pp.push_back(*this);
|
||||
return pp;
|
||||
}
|
||||
|
||||
Polygon::operator Polyline() const
|
||||
{
|
||||
return this->split_at_first_point();
|
||||
}
|
||||
|
||||
Point&
|
||||
Polygon::operator[](Points::size_type idx)
|
||||
{
|
||||
return this->points[idx];
|
||||
}
|
||||
|
||||
const Point&
|
||||
Polygon::operator[](Points::size_type idx) const
|
||||
{
|
||||
return this->points[idx];
|
||||
}
|
||||
|
||||
Point
|
||||
Polygon::last_point() const
|
||||
{
|
||||
return this->points.front(); // last point == first point for polygons
|
||||
}
|
||||
|
||||
Lines Polygon::lines() const
|
||||
{
|
||||
return to_lines(*this);
|
||||
}
|
||||
|
||||
Polyline
|
||||
Polygon::split_at_vertex(const Point &point) const
|
||||
Polyline Polygon::split_at_vertex(const Point &point) const
|
||||
{
|
||||
// find index of point
|
||||
for (const Point &pt : this->points)
|
||||
|
@ -52,8 +21,7 @@ Polygon::split_at_vertex(const Point &point) const
|
|||
}
|
||||
|
||||
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
|
||||
Polyline
|
||||
Polygon::split_at_index(int index) const
|
||||
Polyline Polygon::split_at_index(int index) const
|
||||
{
|
||||
Polyline polyline;
|
||||
polyline.points.reserve(this->points.size() + 1);
|
||||
|
@ -64,19 +32,6 @@ Polygon::split_at_index(int index) const
|
|||
return polyline;
|
||||
}
|
||||
|
||||
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
|
||||
Polyline
|
||||
Polygon::split_at_first_point() const
|
||||
{
|
||||
return this->split_at_index(0);
|
||||
}
|
||||
|
||||
Points
|
||||
Polygon::equally_spaced_points(double distance) const
|
||||
{
|
||||
return this->split_at_first_point().equally_spaced_points(distance);
|
||||
}
|
||||
|
||||
/*
|
||||
int64_t Polygon::area2x() const
|
||||
{
|
||||
|
@ -107,20 +62,17 @@ double Polygon::area() const
|
|||
return 0.5 * a;
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::is_counter_clockwise() const
|
||||
bool Polygon::is_counter_clockwise() const
|
||||
{
|
||||
return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::is_clockwise() const
|
||||
bool Polygon::is_clockwise() const
|
||||
{
|
||||
return !this->is_counter_clockwise();
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::make_counter_clockwise()
|
||||
bool Polygon::make_counter_clockwise()
|
||||
{
|
||||
if (!this->is_counter_clockwise()) {
|
||||
this->reverse();
|
||||
|
@ -129,8 +81,7 @@ Polygon::make_counter_clockwise()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::make_clockwise()
|
||||
bool Polygon::make_clockwise()
|
||||
{
|
||||
if (this->is_counter_clockwise()) {
|
||||
this->reverse();
|
||||
|
@ -139,16 +90,9 @@ Polygon::make_clockwise()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Polygon::is_valid() const
|
||||
{
|
||||
return this->points.size() >= 3;
|
||||
}
|
||||
|
||||
// Does an unoriented polygon contain a point?
|
||||
// Tested by counting intersections along a horizontal line.
|
||||
bool
|
||||
Polygon::contains(const Point &point) const
|
||||
bool Polygon::contains(const Point &point) const
|
||||
{
|
||||
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
|
||||
bool result = false;
|
||||
|
@ -174,8 +118,7 @@ Polygon::contains(const Point &point) const
|
|||
}
|
||||
|
||||
// this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons()
|
||||
Polygons
|
||||
Polygon::simplify(double tolerance) const
|
||||
Polygons Polygon::simplify(double tolerance) const
|
||||
{
|
||||
// repeat first point at the end in order to apply Douglas-Peucker
|
||||
// on the whole polygon
|
||||
|
@ -189,8 +132,7 @@ Polygon::simplify(double tolerance) const
|
|||
return simplify_polygons(pp);
|
||||
}
|
||||
|
||||
void
|
||||
Polygon::simplify(double tolerance, Polygons &polygons) const
|
||||
void Polygon::simplify(double tolerance, Polygons &polygons) const
|
||||
{
|
||||
Polygons pp = this->simplify(tolerance);
|
||||
polygons.reserve(polygons.size() + pp.size());
|
||||
|
@ -198,8 +140,7 @@ Polygon::simplify(double tolerance, Polygons &polygons) const
|
|||
}
|
||||
|
||||
// Only call this on convex polygons or it will return invalid results
|
||||
void
|
||||
Polygon::triangulate_convex(Polygons* polygons) const
|
||||
void Polygon::triangulate_convex(Polygons* polygons) const
|
||||
{
|
||||
for (Points::const_iterator it = this->points.begin() + 2; it != this->points.end(); ++it) {
|
||||
Polygon p;
|
||||
|
@ -214,8 +155,7 @@ Polygon::triangulate_convex(Polygons* polygons) const
|
|||
}
|
||||
|
||||
// center of mass
|
||||
Point
|
||||
Polygon::centroid() const
|
||||
Point Polygon::centroid() const
|
||||
{
|
||||
double area_temp = this->area();
|
||||
double x_temp = 0;
|
||||
|
@ -232,8 +172,7 @@ Polygon::centroid() const
|
|||
|
||||
// find all concave vertices (i.e. having an internal angle greater than the supplied angle)
|
||||
// (external = right side, thus we consider ccw orientation)
|
||||
Points
|
||||
Polygon::concave_points(double angle) const
|
||||
Points Polygon::concave_points(double angle) const
|
||||
{
|
||||
Points points;
|
||||
angle = 2*PI - angle;
|
||||
|
@ -256,8 +195,7 @@ Polygon::concave_points(double angle) const
|
|||
|
||||
// find all convex vertices (i.e. having an internal angle smaller than the supplied angle)
|
||||
// (external = right side, thus we consider ccw orientation)
|
||||
Points
|
||||
Polygon::convex_points(double angle) const
|
||||
Points Polygon::convex_points(double angle) const
|
||||
{
|
||||
Points points;
|
||||
angle = 2*PI - angle;
|
||||
|
|
|
@ -13,13 +13,14 @@ namespace Slic3r {
|
|||
class Polygon;
|
||||
typedef std::vector<Polygon> Polygons;
|
||||
|
||||
class Polygon : public MultiPoint {
|
||||
class Polygon : public MultiPoint
|
||||
{
|
||||
public:
|
||||
operator Polygons() const;
|
||||
operator Polyline() const;
|
||||
Point& operator[](Points::size_type idx);
|
||||
const Point& operator[](Points::size_type idx) const;
|
||||
|
||||
operator Polygons() const { Polygons pp; pp.push_back(*this); return pp; }
|
||||
operator Polyline() const { return this->split_at_first_point(); }
|
||||
Point& operator[](Points::size_type idx) { return this->points[idx]; }
|
||||
const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
|
||||
|
||||
Polygon() {}
|
||||
explicit Polygon(const Points &points): MultiPoint(points) {}
|
||||
Polygon(const Polygon &other) : MultiPoint(other.points) {}
|
||||
|
@ -34,20 +35,24 @@ public:
|
|||
Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
|
||||
Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }
|
||||
|
||||
Point last_point() const;
|
||||
// last point == first point for polygons
|
||||
const Point& last_point() const override { return this->points.front(); }
|
||||
|
||||
virtual Lines lines() const;
|
||||
Polyline split_at_vertex(const Point &point) const;
|
||||
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
|
||||
Polyline split_at_index(int index) const;
|
||||
// Split a closed polygon into an open polyline, with the split point duplicated at both ends.
|
||||
Polyline split_at_first_point() const;
|
||||
Points equally_spaced_points(double distance) const;
|
||||
Polyline split_at_first_point() const { return this->split_at_index(0); }
|
||||
Points equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); }
|
||||
|
||||
double area() const;
|
||||
bool is_counter_clockwise() const;
|
||||
bool is_clockwise() const;
|
||||
bool make_counter_clockwise();
|
||||
bool make_clockwise();
|
||||
bool is_valid() const;
|
||||
bool is_valid() const { return this->points.size() >= 3; }
|
||||
|
||||
// Does an unoriented polygon contain a point?
|
||||
// Tested by counting intersections along a horizontal line.
|
||||
bool contains(const Point &point) const;
|
||||
|
|
|
@ -23,18 +23,17 @@ Polyline::operator Line() const
|
|||
return Line(this->points.front(), this->points.back());
|
||||
}
|
||||
|
||||
Point
|
||||
Polyline::leftmost_point() const
|
||||
const Point& Polyline::leftmost_point() const
|
||||
{
|
||||
Point p = this->points.front();
|
||||
for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) {
|
||||
if ((*it)(0) < p(0)) p = *it;
|
||||
const Point *p = &this->points.front();
|
||||
for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++ it) {
|
||||
if (it->x() < p->x())
|
||||
p = &(*it);
|
||||
}
|
||||
return p;
|
||||
return *p;
|
||||
}
|
||||
|
||||
Lines
|
||||
Polyline::lines() const
|
||||
Lines Polyline::lines() const
|
||||
{
|
||||
Lines lines;
|
||||
if (this->points.size() >= 2) {
|
||||
|
@ -205,6 +204,20 @@ BoundingBox get_extents(const Polylines &polylines)
|
|||
return bb;
|
||||
}
|
||||
|
||||
const Point& leftmost_point(const Polylines &polylines)
|
||||
{
|
||||
if (polylines.empty())
|
||||
throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
|
||||
Polylines::const_iterator it = polylines.begin();
|
||||
const Point *p = &it->leftmost_point();
|
||||
for (++ it; it != polylines.end(); ++it) {
|
||||
const Point *p2 = &it->leftmost_point();
|
||||
if (p2->x() < p->x())
|
||||
p = p2;
|
||||
}
|
||||
return *p;
|
||||
}
|
||||
|
||||
bool remove_degenerate(Polylines &polylines)
|
||||
{
|
||||
bool modified = false;
|
||||
|
|
|
@ -62,9 +62,9 @@ public:
|
|||
|
||||
operator Polylines() const;
|
||||
operator Line() const;
|
||||
Point last_point() const override { return this->points.back(); }
|
||||
const Point& last_point() const override { return this->points.back(); }
|
||||
|
||||
Point leftmost_point() const;
|
||||
const Point& leftmost_point() const;
|
||||
virtual Lines lines() const;
|
||||
void clip_end(double distance);
|
||||
void clip_start(double distance);
|
||||
|
@ -77,6 +77,15 @@ public:
|
|||
bool is_straight() const;
|
||||
};
|
||||
|
||||
// Don't use this class in production code, it is used exclusively by the Perl binding for unit tests!
|
||||
#ifdef PERL_UCHAR_MIN
|
||||
class PolylineCollection
|
||||
{
|
||||
public:
|
||||
Polylines polylines;
|
||||
};
|
||||
#endif /* PERL_UCHAR_MIN */
|
||||
|
||||
extern BoundingBox get_extents(const Polyline &polyline);
|
||||
extern BoundingBox get_extents(const Polylines &polylines);
|
||||
|
||||
|
@ -129,6 +138,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src)
|
|||
}
|
||||
}
|
||||
|
||||
const Point& leftmost_point(const Polylines &polylines);
|
||||
|
||||
bool remove_degenerate(Polylines &polylines);
|
||||
|
||||
class ThickPolyline : public Polyline {
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
#include "PolylineCollection.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct Chaining
|
||||
{
|
||||
Point first;
|
||||
Point last;
|
||||
size_t idx;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline int nearest_point_index(const std::vector<Chaining> &pairs, const Point &start_near, bool no_reverse)
|
||||
{
|
||||
T dmin = std::numeric_limits<T>::max();
|
||||
int idx = 0;
|
||||
for (std::vector<Chaining>::const_iterator it = pairs.begin(); it != pairs.end(); ++it) {
|
||||
T d = sqr(T(start_near(0) - it->first(0)));
|
||||
if (d <= dmin) {
|
||||
d += sqr(T(start_near(1) - it->first(1)));
|
||||
if (d < dmin) {
|
||||
idx = (it - pairs.begin()) * 2;
|
||||
dmin = d;
|
||||
if (dmin < EPSILON)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! no_reverse) {
|
||||
d = sqr(T(start_near(0) - it->last(0)));
|
||||
if (d <= dmin) {
|
||||
d += sqr(T(start_near(1) - it->last(1)));
|
||||
if (d < dmin) {
|
||||
idx = (it - pairs.begin()) * 2 + 1;
|
||||
dmin = d;
|
||||
if (dmin < EPSILON)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
Polylines PolylineCollection::_chained_path_from(
|
||||
const Polylines &src,
|
||||
Point start_near,
|
||||
bool no_reverse,
|
||||
bool move_from_src)
|
||||
{
|
||||
std::vector<Chaining> endpoints;
|
||||
endpoints.reserve(src.size());
|
||||
for (size_t i = 0; i < src.size(); ++ i) {
|
||||
Chaining c;
|
||||
c.first = src[i].first_point();
|
||||
if (! no_reverse)
|
||||
c.last = src[i].last_point();
|
||||
c.idx = i;
|
||||
endpoints.push_back(c);
|
||||
}
|
||||
Polylines retval;
|
||||
while (! endpoints.empty()) {
|
||||
// find nearest point
|
||||
int endpoint_index = nearest_point_index<double>(endpoints, start_near, no_reverse);
|
||||
assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2);
|
||||
if (move_from_src) {
|
||||
retval.push_back(std::move(src[endpoints[endpoint_index/2].idx]));
|
||||
} else {
|
||||
retval.push_back(src[endpoints[endpoint_index/2].idx]);
|
||||
}
|
||||
if (endpoint_index & 1)
|
||||
retval.back().reverse();
|
||||
endpoints.erase(endpoints.begin() + endpoint_index/2);
|
||||
start_near = retval.back().last_point();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
Point PolylineCollection::leftmost_point(const Polylines &polylines)
|
||||
{
|
||||
if (polylines.empty())
|
||||
throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
|
||||
Polylines::const_iterator it = polylines.begin();
|
||||
Point p = it->leftmost_point();
|
||||
for (++ it; it != polylines.end(); ++it) {
|
||||
Point p2 = it->leftmost_point();
|
||||
if (p2(0) < p(0))
|
||||
p = p2;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef slic3r_PolylineCollection_hpp_
|
||||
#define slic3r_PolylineCollection_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PolylineCollection
|
||||
{
|
||||
static Polylines _chained_path_from(
|
||||
const Polylines &src,
|
||||
Point start_near,
|
||||
bool no_reverse,
|
||||
bool move_from_src);
|
||||
|
||||
public:
|
||||
Polylines polylines;
|
||||
void chained_path(PolylineCollection* retval, bool no_reverse = false) const
|
||||
{ retval->polylines = chained_path(this->polylines, no_reverse); }
|
||||
void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const
|
||||
{ retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); }
|
||||
Point leftmost_point() const
|
||||
{ return leftmost_point(polylines); }
|
||||
void append(const Polylines &polylines)
|
||||
{ this->polylines.insert(this->polylines.end(), polylines.begin(), polylines.end()); }
|
||||
|
||||
static Point leftmost_point(const Polylines &polylines);
|
||||
static Polylines chained_path(Polylines &&src, bool no_reverse = false) {
|
||||
return (src.empty() || src.front().points.empty()) ?
|
||||
Polylines() :
|
||||
_chained_path_from(src, src.front().first_point(), no_reverse, true);
|
||||
}
|
||||
static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false)
|
||||
{ return _chained_path_from(src, start_near, no_reverse, true); }
|
||||
static Polylines chained_path(const Polylines &src, bool no_reverse = false) {
|
||||
return (src.empty() || src.front().points.empty()) ?
|
||||
Polylines() :
|
||||
_chained_path_from(src, src.front().first_point(), no_reverse, false);
|
||||
}
|
||||
static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false)
|
||||
{ return _chained_path_from(src, start_near, no_reverse, false); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -328,17 +328,6 @@ unsigned int Print::num_object_instances() const
|
|||
return instances;
|
||||
}
|
||||
|
||||
void Print::_simplify_slices(double distance)
|
||||
{
|
||||
for (PrintObject *object : m_objects) {
|
||||
for (Layer *layer : object->m_layers) {
|
||||
layer->slices.simplify(distance);
|
||||
for (LayerRegion *layerm : layer->regions())
|
||||
layerm->slices.simplify(distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double Print::max_allowed_layer_height() const
|
||||
{
|
||||
double nozzle_diameter_max = 0.;
|
||||
|
@ -1114,6 +1103,9 @@ std::string Print::validate() const
|
|||
if (m_objects.empty())
|
||||
return L("All objects are outside of the print volume.");
|
||||
|
||||
if (extruders().empty())
|
||||
return L("The supplied settings will cause an empty print.");
|
||||
|
||||
if (m_config.complete_objects) {
|
||||
// Check horizontal clearance.
|
||||
{
|
||||
|
@ -1271,10 +1263,7 @@ std::string Print::validate() const
|
|||
}
|
||||
|
||||
{
|
||||
// find the smallest nozzle diameter
|
||||
std::vector<unsigned int> extruders = this->extruders();
|
||||
if (extruders.empty())
|
||||
return L("The supplied settings will cause an empty print.");
|
||||
|
||||
// Find the smallest used nozzle diameter and the number of unique nozzle diameters.
|
||||
double min_nozzle_diameter = std::numeric_limits<double>::max();
|
||||
|
@ -1593,7 +1582,7 @@ void Print::_make_skirt()
|
|||
for (const Layer *layer : object->m_layers) {
|
||||
if (layer->print_z > skirt_height_z)
|
||||
break;
|
||||
for (const ExPolygon &expoly : layer->slices.expolygons)
|
||||
for (const ExPolygon &expoly : layer->slices)
|
||||
// Collect the outer contour points only, ignore holes for the calculation of the convex hull.
|
||||
append(object_points, expoly.contour.points);
|
||||
}
|
||||
|
@ -1704,7 +1693,7 @@ void Print::_make_brim()
|
|||
Polygons islands;
|
||||
for (PrintObject *object : m_objects) {
|
||||
Polygons object_islands;
|
||||
for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons)
|
||||
for (ExPolygon &expoly : object->m_layers.front()->slices)
|
||||
object_islands.push_back(expoly.contour);
|
||||
if (! object->support_layers().empty())
|
||||
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
|
||||
|
|
|
@ -180,7 +180,6 @@ private:
|
|||
void _slice(const std::vector<coordf_t> &layer_height_profile);
|
||||
std::string _fix_slicing_errors();
|
||||
void _simplify_slices(double distance);
|
||||
void _make_perimeters();
|
||||
bool has_support_material() const;
|
||||
void detect_surfaces_type();
|
||||
void process_external_surfaces();
|
||||
|
@ -383,7 +382,6 @@ private:
|
|||
void _make_skirt();
|
||||
void _make_brim();
|
||||
void _make_wipe_tower();
|
||||
void _simplify_slices(double distance);
|
||||
|
||||
// Declared here to have access to Model / ModelObject / ModelInstance
|
||||
static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src);
|
||||
|
|
|
@ -2392,6 +2392,7 @@ void PrintConfigDef::init_sla_params()
|
|||
"the threshold in the middle. This behaviour eliminates "
|
||||
"antialiasing without losing holes in polygons.");
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(1.0));
|
||||
|
||||
|
|
|
@ -117,6 +117,19 @@ void PrintObject::slice()
|
|||
// Simplify slices if required.
|
||||
if (m_print->config().resolution)
|
||||
this->_simplify_slices(scale_(this->print()->config().resolution));
|
||||
// Update bounding boxes
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[this](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
Layer &layer = *m_layers[layer_idx];
|
||||
layer.slices_bboxes.clear();
|
||||
layer.slices_bboxes.reserve(layer.slices.size());
|
||||
for (const ExPolygon &expoly : layer.slices)
|
||||
layer.slices_bboxes.emplace_back(get_extents(expoly));
|
||||
}
|
||||
});
|
||||
if (m_layers.empty())
|
||||
throw std::runtime_error("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);
|
||||
|
@ -865,7 +878,7 @@ void PrintObject::process_external_surfaces()
|
|||
// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
|
||||
polygons_append(voids, offset(surface.expolygon, unsupported_width));
|
||||
}
|
||||
surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices.expolygons), voids);
|
||||
surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices), voids);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -975,8 +988,8 @@ void PrintObject::discover_vertical_shells()
|
|||
polygons_append(cache.holes, offset(offset_ex(layer.slices, 0.3f * perimeter_min_spacing), - perimeter_offset - 0.3f * perimeter_min_spacing));
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices.expolygons));
|
||||
svg.draw(layer.slices.expolygons, "blue");
|
||||
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices));
|
||||
svg.draw(layer.slices, "blue");
|
||||
svg.draw(union_ex(cache.holes), "red");
|
||||
svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05));
|
||||
svg.Close();
|
||||
|
@ -1659,25 +1672,26 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
|||
// Trim volumes in a single layer, one by the other, possibly apply upscaling.
|
||||
{
|
||||
Polygons processed;
|
||||
for (SlicedVolume &sliced_volume : sliced_volumes) {
|
||||
ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]);
|
||||
if (upscale)
|
||||
slices = offset_ex(std::move(slices), delta);
|
||||
if (! processed.empty())
|
||||
// Trim by the slices of already processed regions.
|
||||
slices = diff_ex(to_polygons(std::move(slices)), processed);
|
||||
if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size())
|
||||
// Collect the already processed regions to trim the to be processed regions.
|
||||
polygons_append(processed, slices);
|
||||
sliced_volume.expolygons_by_layer[layer_id] = std::move(slices);
|
||||
}
|
||||
for (SlicedVolume &sliced_volume : sliced_volumes)
|
||||
if (! sliced_volume.expolygons_by_layer.empty()) {
|
||||
ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]);
|
||||
if (upscale)
|
||||
slices = offset_ex(std::move(slices), delta);
|
||||
if (! processed.empty())
|
||||
// Trim by the slices of already processed regions.
|
||||
slices = diff_ex(to_polygons(std::move(slices)), processed);
|
||||
if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size())
|
||||
// Collect the already processed regions to trim the to be processed regions.
|
||||
polygons_append(processed, slices);
|
||||
sliced_volume.expolygons_by_layer[layer_id] = std::move(slices);
|
||||
}
|
||||
}
|
||||
// Collect and union volumes of a single region.
|
||||
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
|
||||
ExPolygons expolygons;
|
||||
size_t num_volumes = 0;
|
||||
for (SlicedVolume &sliced_volume : sliced_volumes)
|
||||
if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer[layer_id].empty()) {
|
||||
if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer.empty() && ! sliced_volume.expolygons_by_layer[layer_id].empty()) {
|
||||
++ num_volumes;
|
||||
append(expolygons, std::move(sliced_volume.expolygons_by_layer[layer_id]));
|
||||
}
|
||||
|
@ -2140,7 +2154,7 @@ std::string PrintObject::_fix_slicing_errors()
|
|||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end";
|
||||
|
||||
// remove empty layers from bottom
|
||||
while (! m_layers.empty() && m_layers.front()->slices.expolygons.empty()) {
|
||||
while (! m_layers.empty() && m_layers.front()->slices.empty()) {
|
||||
delete m_layers.front();
|
||||
m_layers.erase(m_layers.begin());
|
||||
m_layers.front()->lower_layer = nullptr;
|
||||
|
@ -2167,115 +2181,17 @@ void PrintObject::_simplify_slices(double distance)
|
|||
Layer *layer = m_layers[layer_idx];
|
||||
for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx)
|
||||
layer->m_regions[region_idx]->slices.simplify(distance);
|
||||
layer->slices.simplify(distance);
|
||||
{
|
||||
ExPolygons simplified;
|
||||
for (const ExPolygon& expoly : layer->slices)
|
||||
expoly.simplify(distance, &simplified);
|
||||
layer->slices = std::move(simplified);
|
||||
}
|
||||
}
|
||||
});
|
||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end";
|
||||
}
|
||||
|
||||
void PrintObject::_make_perimeters()
|
||||
{
|
||||
if (! this->set_started(posPerimeters))
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
|
||||
|
||||
// merge slices if they were split into types
|
||||
if (this->typed_slices) {
|
||||
for (Layer *layer : m_layers)
|
||||
layer->merge_slices();
|
||||
this->typed_slices = false;
|
||||
this->invalidate_step(posPrepareInfill);
|
||||
}
|
||||
|
||||
// compare each layer to the one below, and mark those slices needing
|
||||
// one additional inner perimeter, like the top of domed objects-
|
||||
|
||||
// this algorithm makes sure that at least one perimeter is overlapping
|
||||
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
|
||||
// inside the object - infill_only_where_needed should be the method of choice for printing
|
||||
// hollow objects
|
||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||
const PrintRegion ®ion = *m_print->regions()[region_id];
|
||||
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
|
||||
continue;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size() - 1),
|
||||
[this, ®ion, region_id](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
LayerRegion &layerm = *m_layers[layer_idx]->regions()[region_id];
|
||||
const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->regions()[region_id];
|
||||
const Polygons upper_layerm_polygons = upper_layerm.slices;
|
||||
// Filter upper layer polygons in intersection_ppl by their bounding boxes?
|
||||
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
|
||||
const double total_loop_length = total_length(upper_layerm_polygons);
|
||||
const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing();
|
||||
const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter);
|
||||
const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width();
|
||||
const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
|
||||
|
||||
for (Surface &slice : layerm.slices.surfaces) {
|
||||
for (;;) {
|
||||
// compute the total thickness of perimeters
|
||||
const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
|
||||
+ (region.config().perimeters-1 + slice.extra_perimeters) * perimeter_spacing;
|
||||
// define a critical area where we don't want the upper slice to fall into
|
||||
// (it should either lay over our perimeters or outside this area)
|
||||
const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5);
|
||||
const Polygons critical_area = diff(
|
||||
offset(slice.expolygon, float(- perimeters_thickness)),
|
||||
offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth))
|
||||
);
|
||||
// check whether a portion of the upper slices falls inside the critical area
|
||||
const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area);
|
||||
// only add an additional loop if at least 30% of the slice loop would benefit from it
|
||||
if (total_length(intersection) <= total_loop_length*0.3)
|
||||
break;
|
||||
/*
|
||||
if (0) {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"extra.svg",
|
||||
no_arrows => 1,
|
||||
expolygons => union_ex($critical_area),
|
||||
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
|
||||
);
|
||||
}
|
||||
*/
|
||||
++ slice.extra_perimeters;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (slice.extra_perimeters > 0)
|
||||
printf(" adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
});
|
||||
BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end";
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[this](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
|
||||
m_layers[layer_idx]->make_perimeters();
|
||||
}
|
||||
);
|
||||
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
|
||||
|
||||
/*
|
||||
simplify slices (both layer and region slices),
|
||||
we only need the max resolution for perimeters
|
||||
### This makes this method not-idempotent, so we keep it disabled for now.
|
||||
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
|
||||
*/
|
||||
|
||||
this->set_done(posPerimeters);
|
||||
}
|
||||
|
||||
// Only active if config->infill_only_where_needed. This step trims the sparse infill,
|
||||
// so it acts as an internal support. It maintains all other infill types intact.
|
||||
// Here the internal surfaces and perimeters have to be supported by the sparse infill.
|
||||
|
@ -2301,7 +2217,7 @@ void PrintObject::clip_fill_surfaces()
|
|||
// Detect things that we need to support.
|
||||
// Cummulative slices.
|
||||
Polygons slices;
|
||||
polygons_append(slices, layer->slices.expolygons);
|
||||
polygons_append(slices, layer->slices);
|
||||
// Cummulative fill surfaces.
|
||||
Polygons fill_surfaces;
|
||||
// Solid surfaces to be supported.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#undef assert
|
||||
#endif
|
||||
|
||||
#include "clipper.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "KDTreeIndirect.hpp"
|
||||
#include "MutablePriorityQueue.hpp"
|
||||
|
@ -14,6 +15,44 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
// Naive implementation of the Traveling Salesman Problem, it works by always taking the next closest neighbor.
|
||||
// This implementation will always produce valid result even if some segments cannot reverse.
|
||||
template<typename EndPointType, typename KDTreeType, typename CouldReverseFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<EndPointType> &end_points, KDTreeType &kdtree, CouldReverseFunc &could_reverse_func, EndPointType &first_point)
|
||||
{
|
||||
assert((end_points.size() & 1) == 0);
|
||||
size_t num_segments = end_points.size() / 2;
|
||||
assert(num_segments >= 2);
|
||||
for (EndPointType &ep : end_points)
|
||||
ep.chain_id = 0;
|
||||
std::vector<std::pair<size_t, bool>> out;
|
||||
out.reserve(num_segments);
|
||||
size_t first_point_idx = &first_point - end_points.data();
|
||||
out.emplace_back(first_point_idx / 2, (first_point_idx & 1) != 0);
|
||||
first_point.chain_id = 1;
|
||||
size_t this_idx = first_point_idx ^ 1;
|
||||
for (int iter = (int)num_segments - 2; iter >= 0; -- iter) {
|
||||
EndPointType &this_point = end_points[this_idx];
|
||||
this_point.chain_id = 1;
|
||||
// Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda).
|
||||
// Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it.
|
||||
size_t next_idx = find_closest_point(kdtree, this_point.pos,
|
||||
[this_idx, &end_points, &could_reverse_func](size_t idx) {
|
||||
return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1));
|
||||
});
|
||||
assert(next_idx < end_points.size());
|
||||
EndPointType &end_point = end_points[next_idx];
|
||||
end_point.chain_id = 1;
|
||||
this_idx = next_idx ^ 1;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
assert(end_points[this_idx].chain_id == 0);
|
||||
for (EndPointType &ep : end_points)
|
||||
assert(&ep == &end_points[this_idx] || ep.chain_id == 1);
|
||||
#endif /* NDEBUG */
|
||||
return out;
|
||||
}
|
||||
|
||||
// Chain perimeters (always closed) and thin fills (closed or open) using a greedy algorithm.
|
||||
// Solving a Traveling Salesman Problem (TSP) with the modification, that the sites are not always points, but points and segments.
|
||||
// Solving using a greedy algorithm, where a shortest edge is added to the solution if it does not produce a bifurcation or a cycle.
|
||||
|
@ -22,8 +61,8 @@ namespace Slic3r {
|
|||
// The algorithm builds a tour for the traveling salesman one edge at a time and thus maintains multiple tour fragments, each of which
|
||||
// is a simple path in the complete graph of cities. At each stage, the algorithm selects the edge of minimal cost that either creates
|
||||
// a new fragment, extends one of the existing paths or creates a cycle of length equal to the number of cities.
|
||||
template<typename PointType, typename SegmentEndPointFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near)
|
||||
template<typename PointType, typename SegmentEndPointFunc, bool REVERSE_COULD_FAIL, typename CouldReverseFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals_(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near)
|
||||
{
|
||||
std::vector<std::pair<size_t, bool>> out;
|
||||
|
||||
|
@ -34,7 +73,7 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
|
|||
{
|
||||
// Just sort the end points so that the first point visited is closest to start_near.
|
||||
out.emplace_back(0, start_near != nullptr &&
|
||||
(end_point_func(0, true) - *start_near).cast<double>().squaredNorm() < (end_point_func(0, false) - *start_near).cast<double>().squaredNorm());
|
||||
(end_point_func(0, true) - *start_near).template cast<double>().squaredNorm() < (end_point_func(0, false) - *start_near).template cast<double>().squaredNorm());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -55,8 +94,8 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
|
|||
std::vector<EndPoint> end_points;
|
||||
end_points.reserve(num_segments * 2);
|
||||
for (size_t i = 0; i < num_segments; ++ i) {
|
||||
end_points.emplace_back(end_point_func(i, true ).cast<double>());
|
||||
end_points.emplace_back(end_point_func(i, false).cast<double>());
|
||||
end_points.emplace_back(end_point_func(i, true ).template cast<double>());
|
||||
end_points.emplace_back(end_point_func(i, false).template cast<double>());
|
||||
}
|
||||
|
||||
// Construct the closest point KD tree over end points of segments.
|
||||
|
@ -125,13 +164,15 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
|
|||
EndPoint *first_point = nullptr;
|
||||
size_t first_point_idx = std::numeric_limits<size_t>::max();
|
||||
if (start_near != nullptr) {
|
||||
size_t idx = find_closest_point(kdtree, start_near->cast<double>());
|
||||
size_t idx = find_closest_point(kdtree, start_near->template cast<double>());
|
||||
assert(idx < end_points.size());
|
||||
first_point = &end_points[idx];
|
||||
first_point->distance_out = 0.;
|
||||
first_point->chain_id = equivalent_chain.next();
|
||||
first_point_idx = idx;
|
||||
}
|
||||
EndPoint *initial_point = first_point;
|
||||
EndPoint *last_point = nullptr;
|
||||
|
||||
// Assign the closest point and distance to the end points.
|
||||
for (EndPoint &end_point : end_points) {
|
||||
|
@ -240,12 +281,15 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
|
|||
if (iter == 0) {
|
||||
// Last iteration. There shall be exactly one or two end points waiting to be connected.
|
||||
assert(queue.size() == ((first_point == nullptr) ? 2 : 1));
|
||||
if (first_point == nullptr)
|
||||
if (first_point == nullptr) {
|
||||
first_point = queue.top();
|
||||
while (! queue.empty()) {
|
||||
queue.top()->edge_out = nullptr;
|
||||
queue.pop();
|
||||
first_point->edge_out = nullptr;
|
||||
}
|
||||
last_point = queue.top();
|
||||
last_point->edge_out = nullptr;
|
||||
queue.pop();
|
||||
assert(queue.empty());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -280,24 +324,77 @@ std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_poin
|
|||
|
||||
// Now interconnect pairs of segments into a chain.
|
||||
assert(first_point != nullptr);
|
||||
out.reserve(num_segments);
|
||||
bool failed = false;
|
||||
do {
|
||||
assert(out.size() < num_segments);
|
||||
size_t first_point_id = first_point - &end_points.front();
|
||||
size_t segment_id = first_point_id >> 1;
|
||||
bool reverse = (first_point_id & 1) != 0;
|
||||
EndPoint *second_point = &end_points[first_point_id ^ 1];
|
||||
out.emplace_back(segment_id, (first_point_id & 1) != 0);
|
||||
if (REVERSE_COULD_FAIL) {
|
||||
if (reverse && ! could_reverse_func(segment_id)) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
assert(! reverse || could_reverse_func(segment_id));
|
||||
}
|
||||
out.emplace_back(segment_id, reverse);
|
||||
first_point = second_point->edge_out;
|
||||
} while (first_point != nullptr);
|
||||
if (REVERSE_COULD_FAIL) {
|
||||
if (failed) {
|
||||
if (start_near == nullptr) {
|
||||
// We may try the reverse order.
|
||||
out.clear();
|
||||
first_point = last_point;
|
||||
failed = false;
|
||||
do {
|
||||
assert(out.size() < num_segments);
|
||||
size_t first_point_id = first_point - &end_points.front();
|
||||
size_t segment_id = first_point_id >> 1;
|
||||
bool reverse = (first_point_id & 1) != 0;
|
||||
EndPoint *second_point = &end_points[first_point_id ^ 1];
|
||||
if (reverse && ! could_reverse_func(segment_id)) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
out.emplace_back(segment_id, reverse);
|
||||
first_point = second_point->edge_out;
|
||||
} while (first_point != nullptr);
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
// As a last resort, try a dumb algorithm, which is not sensitive to edge reversal constraints.
|
||||
out = chain_segments_closest_point<EndPoint, decltype(kdtree), CouldReverseFunc>(end_points, kdtree, could_reverse_func, (initial_point != nullptr) ? *initial_point : end_points.front());
|
||||
} else {
|
||||
assert(! failed);
|
||||
}
|
||||
}
|
||||
|
||||
assert(out.size() == num_segments);
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename PointType, typename SegmentEndPointFunc, typename CouldReverseFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals(SegmentEndPointFunc end_point_func, CouldReverseFunc could_reverse_func, size_t num_segments, const PointType *start_near)
|
||||
{
|
||||
return chain_segments_greedy_constrained_reversals_<PointType, SegmentEndPointFunc, true, CouldReverseFunc>(end_point_func, could_reverse_func, num_segments, start_near);
|
||||
}
|
||||
|
||||
template<typename PointType, typename SegmentEndPointFunc>
|
||||
std::vector<std::pair<size_t, bool>> chain_segments_greedy(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near)
|
||||
{
|
||||
auto could_reverse_func = [](size_t /* idx */) -> bool { return true; };
|
||||
return chain_segments_greedy_constrained_reversals_<PointType, SegmentEndPointFunc, false, decltype(could_reverse_func)>(end_point_func, could_reverse_func, num_segments, start_near);
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
|
||||
{
|
||||
auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
|
||||
std::vector<std::pair<size_t, bool>> out = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, entities.size(), start_near);
|
||||
auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); };
|
||||
std::vector<std::pair<size_t, bool>> out = chain_segments_greedy_constrained_reversals<Point, decltype(segment_end_point), decltype(could_reverse)>(segment_end_point, could_reverse, entities.size(), start_near);
|
||||
for (size_t i = 0; i < entities.size(); ++ i) {
|
||||
ExtrusionEntity *ee = entities[i];
|
||||
if (ee->is_loop())
|
||||
|
@ -309,7 +406,7 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
|
|||
return out;
|
||||
}
|
||||
|
||||
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain)
|
||||
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain)
|
||||
{
|
||||
assert(entities.size() == chain.size());
|
||||
std::vector<ExtrusionEntity*> out;
|
||||
|
@ -328,10 +425,34 @@ void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entitie
|
|||
reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near));
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near)
|
||||
{
|
||||
auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); };
|
||||
return chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, extrusion_paths.size(), start_near);
|
||||
}
|
||||
|
||||
void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const std::vector<std::pair<size_t, bool>> &chain)
|
||||
{
|
||||
assert(extrusion_paths.size() == chain.size());
|
||||
std::vector<ExtrusionPath> out;
|
||||
out.reserve(extrusion_paths.size());
|
||||
for (const std::pair<size_t, bool> &idx : chain) {
|
||||
out.emplace_back(std::move(extrusion_paths[idx.first]));
|
||||
if (idx.second)
|
||||
out.back().reverse();
|
||||
}
|
||||
extrusion_paths.swap(out);
|
||||
}
|
||||
|
||||
void chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near)
|
||||
{
|
||||
reorder_extrusion_paths(extrusion_paths, chain_extrusion_paths(extrusion_paths, start_near));
|
||||
}
|
||||
|
||||
std::vector<size_t> chain_points(const Points &points, Point *start_near)
|
||||
{
|
||||
auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), start_near);
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, points.size(), start_near);
|
||||
std::vector<size_t> out;
|
||||
out.reserve(ordered.size());
|
||||
for (auto &segment_and_reversal : ordered)
|
||||
|
@ -339,12 +460,12 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near)
|
|||
return out;
|
||||
}
|
||||
|
||||
Polylines chain_infill_polylines(Polylines &polylines)
|
||||
Polylines chain_polylines(Polylines &&polylines, const Point *start_near)
|
||||
{
|
||||
auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); };
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), nullptr);
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), start_near);
|
||||
Polylines out;
|
||||
out.reserve(polylines.size());
|
||||
out.reserve(polylines.size());
|
||||
for (auto &segment_and_reversal : ordered) {
|
||||
out.emplace_back(std::move(polylines[segment_and_reversal.first]));
|
||||
if (segment_and_reversal.second)
|
||||
|
@ -356,7 +477,7 @@ Polylines chain_infill_polylines(Polylines &polylines)
|
|||
template<class T> static inline T chain_path_items(const Points &points, const T &items)
|
||||
{
|
||||
auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr);
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr);
|
||||
T out;
|
||||
out.reserve(items.size());
|
||||
for (auto &segment_and_reversal : ordered)
|
||||
|
@ -382,7 +503,7 @@ std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print
|
|||
}
|
||||
}
|
||||
auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; };
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
|
||||
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
|
||||
std::vector<std::pair<size_t, size_t>> out;
|
||||
out.reserve(instances.size());
|
||||
for (auto &segment_and_reversal : ordered)
|
||||
|
|
|
@ -15,10 +15,15 @@ namespace Slic3r {
|
|||
std::vector<size_t> chain_points(const Points &points, Point *start_near = nullptr);
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
|
||||
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, std::vector<std::pair<size_t, bool>> &chain);
|
||||
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain);
|
||||
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
|
||||
|
||||
Polylines chain_infill_polylines(Polylines &src);
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
|
||||
void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, std::vector<std::pair<size_t, bool>> &chain);
|
||||
void chain_and_reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
|
||||
|
||||
Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr);
|
||||
inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); }
|
||||
|
||||
std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
|
||||
|
||||
|
|
|
@ -445,8 +445,8 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
|
|||
Polygons collect_slices_outer(const Layer &layer)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(out.size() + layer.slices.expolygons.size());
|
||||
for (const ExPolygon &expoly : layer.slices.expolygons)
|
||||
out.reserve(out.size() + layer.slices.size());
|
||||
for (const ExPolygon &expoly : layer.slices)
|
||||
out.emplace_back(expoly.contour);
|
||||
return out;
|
||||
}
|
||||
|
@ -907,9 +907,13 @@ namespace SupportMaterialInternal {
|
|||
polyline.extend_start(fw);
|
||||
polyline.extend_end(fw);
|
||||
// Is the straight perimeter segment supported at both sides?
|
||||
if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point()))
|
||||
// Offset a polyline into a thick line.
|
||||
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
|
||||
for (size_t i = 0; i < lower_layer.slices.size(); ++ i)
|
||||
if (lower_layer.slices_bboxes[i].contains(polyline.first_point()) && lower_layer.slices_bboxes[i].contains(polyline.last_point()) &&
|
||||
lower_layer.slices[i].contains(polyline.first_point()) && lower_layer.slices[i].contains(polyline.last_point())) {
|
||||
// Offset a polyline into a thick line.
|
||||
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
|
||||
break;
|
||||
}
|
||||
}
|
||||
bridges = union_(bridges);
|
||||
}
|
||||
|
@ -923,7 +927,7 @@ namespace SupportMaterialInternal {
|
|||
//FIXME add supports at regular intervals to support long bridges!
|
||||
bridges = diff(bridges,
|
||||
// Offset unsupported edges into polygons.
|
||||
offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
// Remove bridged areas from the supported areas.
|
||||
contact_polygons = diff(contact_polygons, bridges, true);
|
||||
}
|
||||
|
@ -994,7 +998,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
// inflate the polygons over and over.
|
||||
Polygons &covered = buildplate_covered[layer_id];
|
||||
covered = buildplate_covered[layer_id - 1];
|
||||
polygons_append(covered, offset(lower_layer.slices.expolygons, scale_(0.01)));
|
||||
polygons_append(covered, offset(lower_layer.slices, scale_(0.01)));
|
||||
covered = union_(covered, false); // don't apply the safety offset.
|
||||
}
|
||||
}
|
||||
|
@ -1023,7 +1027,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
Polygons contact_polygons;
|
||||
Polygons slices_margin_cached;
|
||||
float slices_margin_cached_offset = -1.;
|
||||
Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices.expolygons);
|
||||
Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices);
|
||||
// Offset of the lower layer, to trim the support polygons with to calculate dense supports.
|
||||
float no_interface_offset = 0.f;
|
||||
if (layer_id == 0) {
|
||||
|
@ -1162,7 +1166,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
slices_margin_cached_offset = slices_margin_offset;
|
||||
slices_margin_cached = (slices_margin_offset == 0.f) ?
|
||||
lower_layer_polygons :
|
||||
offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
offset2(to_polygons(lower_layer.slices), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
if (! buildplate_covered.empty()) {
|
||||
// Trim the inflated contact surfaces by the top surfaces as well.
|
||||
polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
|
||||
|
@ -1468,7 +1472,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
|
|||
svg.draw(union_ex(top, false), "blue", 0.5f);
|
||||
svg.draw(union_ex(projection_raw, true), "red", 0.5f);
|
||||
svg.draw_outline(union_ex(projection_raw, true), "red", "blue", scale_(0.1f));
|
||||
svg.draw(layer.slices.expolygons, "green", 0.5f);
|
||||
svg.draw(layer.slices, "green", 0.5f);
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
|
@ -1568,8 +1572,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
|
|||
Polygons &layer_support_area = layer_support_areas[layer_id];
|
||||
task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area, layer_id] {
|
||||
// Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
|
||||
// Polygons trimming = union_(to_polygons(layer.slices.expolygons), touching, true);
|
||||
Polygons trimming = offset(layer.slices.expolygons, float(SCALED_EPSILON));
|
||||
// Polygons trimming = union_(to_polygons(layer.slices), touching, true);
|
||||
Polygons trimming = offset(layer.slices, float(SCALED_EPSILON));
|
||||
projection = diff(projection_raw, trimming, false);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
{
|
||||
|
@ -2101,7 +2105,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
|||
const Layer &object_layer = *object.layers()[i];
|
||||
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
|
||||
break;
|
||||
polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
polygons_append(polygons_trimming, offset(object_layer.slices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
}
|
||||
if (! m_slicing_params.soluble_interface) {
|
||||
// Collect all bottom surfaces, which will be extruded with a bridging flow.
|
||||
|
@ -2214,7 +2218,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
|
|||
// Expand the bases of the support columns in the 1st layer.
|
||||
columns_base->polygons = diff(
|
||||
offset(columns_base->polygons, inflate_factor_1st_layer),
|
||||
offset(m_object->layers().front()->slices.expolygons, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
offset(m_object->layers().front()->slices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
if (contacts != nullptr)
|
||||
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
|
||||
}
|
||||
|
|
|
@ -1717,13 +1717,18 @@ static void thick_point_to_verts(const Vec3crd& point,
|
|||
point_to_indexed_vertex_array(point, width, height, volume.indexed_vertex_array);
|
||||
}
|
||||
|
||||
void _3DScene::extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume)
|
||||
{
|
||||
if (polyline.size() >= 2) {
|
||||
size_t num_segments = polyline.size() - 1;
|
||||
thick_lines_to_verts(polyline.lines(), std::vector<double>(num_segments, width), std::vector<double>(num_segments, height), false, print_z, volume);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the qverts and tverts with quads and triangles for the extrusion_path.
|
||||
void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume)
|
||||
{
|
||||
Lines lines = extrusion_path.polyline.lines();
|
||||
std::vector<double> widths(lines.size(), extrusion_path.width);
|
||||
std::vector<double> heights(lines.size(), extrusion_path.height);
|
||||
thick_lines_to_verts(lines, widths, heights, false, print_z, volume);
|
||||
extrusionentity_to_verts(extrusion_path.polyline, extrusion_path.width, extrusion_path.height, print_z, volume);
|
||||
}
|
||||
|
||||
// Fill in the qverts and tverts with quads and triangles for the extrusion_path.
|
||||
|
|
|
@ -656,6 +656,7 @@ public:
|
|||
|
||||
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
|
||||
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume);
|
||||
static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume);
|
||||
|
|
|
@ -354,8 +354,7 @@ void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config)
|
|||
toggle_field("pad_wall_slope", pad_en);
|
||||
toggle_field("pad_around_object", pad_en);
|
||||
|
||||
bool has_suppad = pad_en && supports_en;
|
||||
bool zero_elev = config->opt_bool("pad_around_object") && has_suppad;
|
||||
bool zero_elev = config->opt_bool("pad_around_object") && pad_en;
|
||||
|
||||
toggle_field("support_object_elevation", supports_en && !zero_elev);
|
||||
toggle_field("pad_object_gap", zero_elev);
|
||||
|
|
|
@ -4987,19 +4987,21 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
// helper functions to select data in dependence of the extrusion view type
|
||||
struct Helper
|
||||
{
|
||||
static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path)
|
||||
static float path_filter(GCodePreviewData::Extrusion::EViewType type, const GCodePreviewData::Extrusion::Path& path)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GCodePreviewData::Extrusion::FeatureType:
|
||||
// The role here is used for coloring.
|
||||
return (float)path.role();
|
||||
return (float)path.extrusion_role;
|
||||
case GCodePreviewData::Extrusion::Height:
|
||||
return path.height;
|
||||
case GCodePreviewData::Extrusion::Width:
|
||||
return path.width;
|
||||
case GCodePreviewData::Extrusion::Feedrate:
|
||||
return path.feedrate;
|
||||
case GCodePreviewData::Extrusion::FanSpeed:
|
||||
return path.fan_speed;
|
||||
case GCodePreviewData::Extrusion::VolumetricRate:
|
||||
return path.feedrate * (float)path.mm3_per_mm;
|
||||
case GCodePreviewData::Extrusion::Tool:
|
||||
|
@ -5025,6 +5027,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
return data.get_width_color(value);
|
||||
case GCodePreviewData::Extrusion::Feedrate:
|
||||
return data.get_feedrate_color(value);
|
||||
case GCodePreviewData::Extrusion::FanSpeed:
|
||||
return data.get_fan_speed_color(value);
|
||||
case GCodePreviewData::Extrusion::VolumetricRate:
|
||||
return data.get_volumetric_rate_color(value);
|
||||
case GCodePreviewData::Extrusion::Tool:
|
||||
|
@ -5067,15 +5071,15 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
{
|
||||
std::vector<size_t> num_paths_per_role(size_t(erCount), 0);
|
||||
for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers)
|
||||
for (const ExtrusionPath &path : layer.paths)
|
||||
++ num_paths_per_role[size_t(path.role())];
|
||||
for (const GCodePreviewData::Extrusion::Path &path : layer.paths)
|
||||
++ num_paths_per_role[size_t(path.extrusion_role)];
|
||||
std::vector<std::vector<float>> roles_values;
|
||||
roles_values.assign(size_t(erCount), std::vector<float>());
|
||||
for (size_t i = 0; i < roles_values.size(); ++ i)
|
||||
roles_values[i].reserve(num_paths_per_role[i]);
|
||||
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
|
||||
for (const ExtrusionPath& path : layer.paths)
|
||||
roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path));
|
||||
for (const GCodePreviewData::Extrusion::Path &path : layer.paths)
|
||||
roles_values[size_t(path.extrusion_role)].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path));
|
||||
roles_filters.reserve(size_t(erCount));
|
||||
size_t num_buffers = 0;
|
||||
for (std::vector<float> &values : roles_values) {
|
||||
|
@ -5103,9 +5107,9 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
// populates volumes
|
||||
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
|
||||
{
|
||||
for (const ExtrusionPath& path : layer.paths)
|
||||
for (const GCodePreviewData::Extrusion::Path& path : layer.paths)
|
||||
{
|
||||
std::vector<std::pair<float, GLVolume*>> &filters = roles_filters[size_t(path.role())];
|
||||
std::vector<std::pair<float, GLVolume*>> &filters = roles_filters[size_t(path.extrusion_role)];
|
||||
auto key = std::make_pair<float, GLVolume*>(Helper::path_filter(preview_data.extrusion.view_type, path), nullptr);
|
||||
auto it_filter = std::lower_bound(filters.begin(), filters.end(), key);
|
||||
assert(it_filter != filters.end() && key.first == it_filter->first);
|
||||
|
@ -5115,7 +5119,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
|
|||
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
|
||||
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
|
||||
|
||||
_3DScene::extrusionentity_to_verts(path, layer.z, vol);
|
||||
_3DScene::extrusionentity_to_verts(path.polyline, path.width, path.height, layer.z, vol);
|
||||
}
|
||||
// Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
|
||||
for (std::vector<std::pair<float, GLVolume*>> &filters : roles_filters) {
|
||||
|
|
|
@ -131,7 +131,7 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
{
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
if (sels.front() == m_last_selected_item)
|
||||
if (! sels.empty() && sels.front() == m_last_selected_item)
|
||||
m_last_selected_item = sels.back();
|
||||
else
|
||||
m_last_selected_item = event.GetItem();
|
||||
|
|
|
@ -221,6 +221,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
|
|||
m_choice_view_type->Append(_(L("Height")));
|
||||
m_choice_view_type->Append(_(L("Width")));
|
||||
m_choice_view_type->Append(_(L("Speed")));
|
||||
m_choice_view_type->Append(_(L("Fan speed")));
|
||||
m_choice_view_type->Append(_(L("Volumetric flow rate")));
|
||||
m_choice_view_type->Append(_(L("Tool")));
|
||||
m_choice_view_type->Append(_(L("Color Print")));
|
||||
|
|
|
@ -1808,7 +1808,10 @@ void TabPrinter::build_fff()
|
|||
optgroup->append_single_option_line("single_extruder_multi_material");
|
||||
|
||||
optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) {
|
||||
size_t extruders_count = boost::any_cast<size_t>(optgroup->get_value("extruders_count"));
|
||||
// optgroup->get_value() return int for def.type == coInt,
|
||||
// Thus, there should be boost::any_cast<int> !
|
||||
// Otherwise, boost::any_cast<size_t> causes an "unhandled unknown exception"
|
||||
size_t extruders_count = size_t(boost::any_cast<int>(optgroup->get_value("extruders_count")));
|
||||
wxTheApp->CallAfter([this, opt_key, value, extruders_count]() {
|
||||
if (opt_key == "extruders_count" || opt_key == "single_extruder_multi_material") {
|
||||
extruders_count_changed(extruders_count);
|
||||
|
|
|
@ -14,13 +14,17 @@
|
|||
void clear();
|
||||
ExtrusionEntityCollection* chained_path(bool no_reverse, ExtrusionRole role = erMixed)
|
||||
%code{%
|
||||
if (no_reverse)
|
||||
croak("no_reverse must be false");
|
||||
RETVAL = new ExtrusionEntityCollection();
|
||||
THIS->chained_path(RETVAL, no_reverse, role);
|
||||
*RETVAL = THIS->chained_path_from(THIS->entities.front()->first_point());
|
||||
%};
|
||||
ExtrusionEntityCollection* chained_path_from(Point* start_near, bool no_reverse, ExtrusionRole role = erMixed)
|
||||
%code{%
|
||||
if (no_reverse)
|
||||
croak("no_reverse must be false");
|
||||
RETVAL = new ExtrusionEntityCollection();
|
||||
THIS->chained_path_from(*start_near, RETVAL, no_reverse, role);
|
||||
*RETVAL = THIS->chained_path_from(*start_near, role);
|
||||
%};
|
||||
Clone<Point> first_point();
|
||||
Clone<Point> last_point();
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/Fill/Fill.hpp"
|
||||
#include "libslic3r/PolylineCollection.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
||||
%}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/Layer.hpp"
|
||||
#include "libslic3r/ExPolygonCollection.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::Layer::Region} class LayerRegion {
|
||||
|
@ -19,8 +20,6 @@
|
|||
%code%{ RETVAL = &THIS->fill_surfaces; %};
|
||||
Polygons bridged()
|
||||
%code%{ RETVAL = THIS->bridged; %};
|
||||
Ref<PolylineCollection> unsupported_bridge_edges()
|
||||
%code%{ RETVAL = &THIS->unsupported_bridge_edges; %};
|
||||
Ref<ExtrusionEntityCollection> perimeters()
|
||||
%code%{ RETVAL = &THIS->perimeters; %};
|
||||
Ref<ExtrusionEntityCollection> fills()
|
||||
|
@ -61,8 +60,8 @@
|
|||
Ref<LayerRegion> get_region(int idx);
|
||||
Ref<LayerRegion> add_region(PrintRegion* print_region);
|
||||
|
||||
Ref<ExPolygonCollection> slices()
|
||||
%code%{ RETVAL = &THIS->slices; %};
|
||||
ExPolygonCollection* slices()
|
||||
%code%{ RETVAL = new ExPolygonCollection(THIS->slices); %};
|
||||
|
||||
int ptr()
|
||||
%code%{ RETVAL = (int)(intptr_t)THIS; %};
|
||||
|
@ -110,8 +109,8 @@
|
|||
Ref<LayerRegion> get_region(int idx);
|
||||
Ref<LayerRegion> add_region(PrintRegion* print_region);
|
||||
|
||||
Ref<ExPolygonCollection> slices()
|
||||
%code%{ RETVAL = &THIS->slices; %};
|
||||
ExPolygonCollection* slices()
|
||||
%code%{ RETVAL = new ExPolygonCollection(THIS->slices); %};
|
||||
|
||||
void export_region_slices_to_svg(const char *path);
|
||||
void export_region_fill_surfaces_to_svg(const char *path);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
~PerimeterGenerator();
|
||||
|
||||
void set_lower_slices(ExPolygonCollection* lower_slices)
|
||||
%code{% THIS->lower_slices = lower_slices; %};
|
||||
%code{% THIS->lower_slices = &lower_slices->expolygons; %};
|
||||
void set_layer_id(int layer_id)
|
||||
%code{% THIS->layer_id = layer_id; %};
|
||||
void set_perimeter_flow(Flow* flow)
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/PolylineCollection.hpp"
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Polyline.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
|
||||
%}
|
||||
|
||||
%name{Slic3r::Polyline::Collection} class PolylineCollection {
|
||||
|
@ -14,16 +18,15 @@
|
|||
PolylineCollection* chained_path(bool no_reverse)
|
||||
%code{%
|
||||
RETVAL = new PolylineCollection();
|
||||
THIS->chained_path(RETVAL, no_reverse);
|
||||
RETVAL->polylines = chain_polylines(THIS->polylines, &THIS->polylines.front().first_point());
|
||||
%};
|
||||
PolylineCollection* chained_path_from(Point* start_near, bool no_reverse)
|
||||
%code{%
|
||||
RETVAL = new PolylineCollection();
|
||||
THIS->chained_path_from(*start_near, RETVAL, no_reverse);
|
||||
RETVAL->polylines = chain_polylines(THIS->polylines, start_near);
|
||||
%};
|
||||
int count()
|
||||
%code{% RETVAL = THIS->polylines.size(); %};
|
||||
Clone<Point> leftmost_point();
|
||||
%{
|
||||
|
||||
PolylineCollection*
|
||||
|
|
Loading…
Reference in a new issue