mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 00:01:09 -06:00
Merge branch 'master' into sender
Conflicts: lib/Slic3r/GUI/Tab.pm
This commit is contained in:
commit
9ec7b43ca1
53 changed files with 1535 additions and 838 deletions
|
@ -105,6 +105,23 @@ ExPolygon::contains(const Point &point) const
|
|||
return true;
|
||||
}
|
||||
|
||||
// inclusive version of contains() that also checks whether point is on boundaries
|
||||
bool
|
||||
ExPolygon::contains_b(const Point &point) const
|
||||
{
|
||||
return this->contains(point) || this->has_boundary_point(point);
|
||||
}
|
||||
|
||||
bool
|
||||
ExPolygon::has_boundary_point(const Point &point) const
|
||||
{
|
||||
if (this->contour.has_boundary_point(point)) return true;
|
||||
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
|
||||
if (h->has_boundary_point(point)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Polygons
|
||||
ExPolygon::simplify_p(double tolerance) const
|
||||
{
|
||||
|
@ -364,6 +381,16 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
|
|||
}
|
||||
}
|
||||
|
||||
Lines
|
||||
ExPolygon::lines() const
|
||||
{
|
||||
Lines lines;
|
||||
this->contour.lines(&lines);
|
||||
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h)
|
||||
h->lines(&lines);
|
||||
return lines;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
|
||||
REGISTER_CLASS(ExPolygon, "ExPolygon");
|
||||
|
|
|
@ -25,6 +25,8 @@ class ExPolygon
|
|||
bool contains(const Line &line) const;
|
||||
bool contains(const Polyline &polyline) const;
|
||||
bool contains(const Point &point) const;
|
||||
bool contains_b(const Point &point) const;
|
||||
bool has_boundary_point(const Point &point) const;
|
||||
Polygons simplify_p(double tolerance) const;
|
||||
ExPolygons simplify(double tolerance) const;
|
||||
void simplify(double tolerance, ExPolygons &expolygons) const;
|
||||
|
@ -36,6 +38,7 @@ class ExPolygon
|
|||
void triangulate(Polygons* polygons) const;
|
||||
void triangulate_pp(Polygons* polygons) const;
|
||||
void triangulate_p2t(Polygons* polygons) const;
|
||||
Lines lines() const;
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
void from_SV(SV* poly_sv);
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
ExPolygonCollection::ExPolygonCollection(const ExPolygon &expolygon)
|
||||
{
|
||||
this->expolygons.push_back(expolygon);
|
||||
}
|
||||
|
||||
ExPolygonCollection::operator Points() const
|
||||
{
|
||||
Points points;
|
||||
|
@ -68,6 +73,15 @@ template bool ExPolygonCollection::contains<Point>(const Point &item) const;
|
|||
template bool ExPolygonCollection::contains<Line>(const Line &item) const;
|
||||
template bool ExPolygonCollection::contains<Polyline>(const Polyline &item) const;
|
||||
|
||||
bool
|
||||
ExPolygonCollection::contains_b(const Point &point) const
|
||||
{
|
||||
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
|
||||
if (it->contains_b(point)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygonCollection::simplify(double tolerance)
|
||||
{
|
||||
|
@ -87,6 +101,17 @@ ExPolygonCollection::convex_hull(Polygon* hull) const
|
|||
Slic3r::Geometry::convex_hull(pp, hull);
|
||||
}
|
||||
|
||||
Lines
|
||||
ExPolygonCollection::lines() const
|
||||
{
|
||||
Lines lines;
|
||||
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
|
||||
Lines ex_lines = it->lines();
|
||||
lines.insert(lines.end(), ex_lines.begin(), ex_lines.end());
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(ExPolygonCollection, "ExPolygon::Collection");
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@ class ExPolygonCollection
|
|||
ExPolygons expolygons;
|
||||
|
||||
ExPolygonCollection() {};
|
||||
ExPolygonCollection(const ExPolygon &expolygon);
|
||||
ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {};
|
||||
operator Points() const;
|
||||
operator Polygons() const;
|
||||
|
@ -25,8 +26,10 @@ class ExPolygonCollection
|
|||
void translate(double x, double y);
|
||||
void rotate(double angle, const Point ¢er);
|
||||
template <class T> bool contains(const T &item) const;
|
||||
bool contains_b(const Point &point) const;
|
||||
void simplify(double tolerance);
|
||||
void convex_hull(Polygon* hull) const;
|
||||
Lines lines() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -9,8 +9,12 @@ Extruder::Extruder(int id, GCodeConfig *config)
|
|||
reset();
|
||||
|
||||
// cache values that are going to be called often
|
||||
this->e_per_mm3 = this->extrusion_multiplier()
|
||||
* (4 / ((this->filament_diameter() * this->filament_diameter()) * PI));
|
||||
if (config->use_volumetric_e) {
|
||||
this->e_per_mm3 = this->extrusion_multiplier();
|
||||
} else {
|
||||
this->e_per_mm3 = this->extrusion_multiplier()
|
||||
* (4 / ((this->filament_diameter() * this->filament_diameter()) * PI));
|
||||
}
|
||||
this->retract_speed_mm_min = this->retract_speed() * 60;
|
||||
}
|
||||
|
||||
|
@ -80,12 +84,22 @@ Extruder::e_per_mm(double mm3_per_mm) const
|
|||
double
|
||||
Extruder::extruded_volume() const
|
||||
{
|
||||
if (this->config->use_volumetric_e) {
|
||||
// Any current amount of retraction should not affect used filament, since
|
||||
// it represents empty volume in the nozzle. We add it back to E.
|
||||
return this->absolute_E + this->retracted;
|
||||
}
|
||||
|
||||
return this->used_filament() * (this->filament_diameter() * this->filament_diameter()) * PI/4;
|
||||
}
|
||||
|
||||
double
|
||||
Extruder::used_filament() const
|
||||
{
|
||||
if (this->config->use_volumetric_e) {
|
||||
return this->extruded_volume() / (this->filament_diameter() * this->filament_diameter() * PI/4);
|
||||
}
|
||||
|
||||
// Any current amount of retraction should not affect used filament, since
|
||||
// it represents empty volume in the nozzle. We add it back to E.
|
||||
return this->absolute_E + this->retracted;
|
||||
|
|
|
@ -278,7 +278,7 @@ ExtrusionLoop::split_at(const Point &point)
|
|||
{
|
||||
if (this->paths.empty()) return;
|
||||
|
||||
// find the closest path and closest point
|
||||
// find the closest path and closest point belonging to that path
|
||||
size_t path_idx = 0;
|
||||
Point p = this->paths.front().first_point();
|
||||
double min = point.distance_to(p);
|
||||
|
|
|
@ -35,6 +35,9 @@ enum ExtrusionLoopRole {
|
|||
class ExtrusionEntity
|
||||
{
|
||||
public:
|
||||
virtual bool is_loop() const {
|
||||
return false;
|
||||
};
|
||||
virtual ExtrusionEntity* clone() const = 0;
|
||||
virtual ~ExtrusionEntity() {};
|
||||
virtual void reverse() = 0;
|
||||
|
@ -84,6 +87,9 @@ class ExtrusionLoop : public ExtrusionEntity
|
|||
ExtrusionLoopRole role;
|
||||
|
||||
ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : role(role) {};
|
||||
bool is_loop() const {
|
||||
return true;
|
||||
};
|
||||
operator Polygon() const;
|
||||
ExtrusionLoop* clone() const;
|
||||
bool make_clockwise();
|
||||
|
|
|
@ -37,7 +37,9 @@ void
|
|||
ExtrusionEntityCollection::reverse()
|
||||
{
|
||||
for (ExtrusionEntitiesPtr::iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
|
||||
(*it)->reverse();
|
||||
// Don't reverse it if it's a loop, as it doesn't change anything in terms of elements ordering
|
||||
// and caller might rely on winding order
|
||||
if (!(*it)->is_loop()) (*it)->reverse();
|
||||
}
|
||||
std::reverse(this->entities.begin(), this->entities.end());
|
||||
}
|
||||
|
@ -96,7 +98,8 @@ ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCo
|
|||
int start_index = start_near.nearest_point_index(endpoints);
|
||||
int path_index = start_index/2;
|
||||
ExtrusionEntity* entity = my_paths.at(path_index);
|
||||
if (start_index % 2 && !no_reverse) {
|
||||
// never reverse loops, since it's pointless for chained path and callers might depend on orientation
|
||||
if (start_index % 2 && !no_reverse && !entity->is_loop()) {
|
||||
entity->reverse();
|
||||
}
|
||||
retval->entities.push_back(my_paths.at(path_index));
|
||||
|
|
|
@ -48,22 +48,32 @@ GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
|
|||
std::string
|
||||
GCodeWriter::preamble()
|
||||
{
|
||||
std::string gcode;
|
||||
std::ostringstream gcode;
|
||||
|
||||
if (FLAVOR_IS_NOT(gcfMakerWare)) {
|
||||
gcode += "G21 ; set units to millimeters\n";
|
||||
gcode += "G90 ; use absolute coordinates\n";
|
||||
gcode << "G21 ; set units to millimeters\n";
|
||||
gcode << "G90 ; use absolute coordinates\n";
|
||||
}
|
||||
if (FLAVOR_IS(gcfRepRap) || FLAVOR_IS(gcfTeacup)) {
|
||||
if (this->config.use_relative_e_distances) {
|
||||
gcode += "M83 ; use relative distances for extrusion\n";
|
||||
gcode << "M83 ; use relative distances for extrusion\n";
|
||||
} else {
|
||||
gcode += "M82 ; use absolute distances for extrusion\n";
|
||||
gcode << "M82 ; use absolute distances for extrusion\n";
|
||||
}
|
||||
gcode += this->reset_e(true);
|
||||
if (this->config.use_volumetric_e && this->config.start_gcode.value.find("M200") == std::string::npos) {
|
||||
for (std::map<unsigned int,Extruder>::const_iterator it = this->extruders.begin(); it != this->extruders.end(); ++it) {
|
||||
unsigned int extruder_id = it->first;
|
||||
gcode << "M200 D" << E_NUM(this->config.filament_diameter.get_at(extruder_id));
|
||||
if (this->multiple_extruders || extruder_id != 0) {
|
||||
gcode << " T" << extruder_id;
|
||||
}
|
||||
gcode << " ; set filament diameter\n";
|
||||
}
|
||||
}
|
||||
gcode << this->reset_e(true);
|
||||
}
|
||||
|
||||
return gcode;
|
||||
return gcode.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
|
@ -423,6 +433,14 @@ GCodeWriter::_retract(double length, double restart_extra, const std::string &co
|
|||
might be 0, in which case the retraction logic gets skipped. */
|
||||
if (this->config.use_firmware_retraction) length = 1;
|
||||
|
||||
// If we use volumetric E values we turn lengths into volumes */
|
||||
if (this->config.use_volumetric_e) {
|
||||
double d = this->_extruder->filament_diameter();
|
||||
double area = d * d * PI/4;
|
||||
length = length * area;
|
||||
restart_extra = restart_extra * area;
|
||||
}
|
||||
|
||||
double dE = this->_extruder->retract(length, restart_extra);
|
||||
if (dE != 0) {
|
||||
if (this->config.use_firmware_retraction) {
|
||||
|
|
|
@ -129,20 +129,18 @@ Layer::any_internal_region_slice_contains(const T &item) const
|
|||
}
|
||||
return false;
|
||||
}
|
||||
template bool Layer::any_internal_region_slice_contains<Line>(const Line &item) const;
|
||||
template bool Layer::any_internal_region_slice_contains<Polyline>(const Polyline &item) const;
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
Layer::any_internal_region_fill_surface_contains(const T &item) const
|
||||
Layer::any_bottom_region_slice_contains(const T &item) const
|
||||
{
|
||||
FOREACH_LAYERREGION(this, layerm) {
|
||||
if ((*layerm)->fill_surfaces.any_internal_contains(item)) return true;
|
||||
if ((*layerm)->slices.any_bottom_contains(item)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template bool Layer::any_internal_region_fill_surface_contains<Line>(const Line &item) const;
|
||||
template bool Layer::any_internal_region_fill_surface_contains<Polyline>(const Polyline &item) const;
|
||||
|
||||
template bool Layer::any_bottom_region_slice_contains<Polyline>(const Polyline &item) const;
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
REGISTER_CLASS(Layer, "Layer");
|
||||
|
|
|
@ -94,8 +94,8 @@ class Layer {
|
|||
|
||||
void make_slices();
|
||||
template <class T> bool any_internal_region_slice_contains(const T &item) const;
|
||||
template <class T> bool any_internal_region_fill_surface_contains(const T &item) const;
|
||||
|
||||
template <class T> bool any_bottom_region_slice_contains(const T &item) const;
|
||||
|
||||
protected:
|
||||
int _id; // sequential number of layer, 0-based
|
||||
PrintObject *_object;
|
||||
|
|
|
@ -44,7 +44,8 @@ void
|
|||
LayerRegion::merge_slices()
|
||||
{
|
||||
ExPolygons expp;
|
||||
union_(this->slices, &expp);
|
||||
// without safety offset, artifacts are generated (GH #2494)
|
||||
union_(this->slices, &expp, true);
|
||||
this->slices.surfaces.clear();
|
||||
this->slices.surfaces.reserve(expp.size());
|
||||
|
||||
|
|
|
@ -16,6 +16,13 @@ Line::wkt() const
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
Line::operator Lines() const
|
||||
{
|
||||
Lines lines;
|
||||
lines.push_back(*this);
|
||||
return lines;
|
||||
}
|
||||
|
||||
Line::operator Polyline() const
|
||||
{
|
||||
Polyline pl;
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Slic3r {
|
|||
class Line;
|
||||
class Linef3;
|
||||
class Polyline;
|
||||
typedef std::vector<Line> Lines;
|
||||
|
||||
class Line
|
||||
{
|
||||
|
@ -18,6 +19,7 @@ class Line
|
|||
Line() {};
|
||||
explicit Line(Point _a, Point _b): a(_a), b(_b) {};
|
||||
std::string wkt() const;
|
||||
operator Lines() const;
|
||||
operator Polyline() const;
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
|
@ -45,8 +47,6 @@ class Line
|
|||
#endif
|
||||
};
|
||||
|
||||
typedef std::vector<Line> Lines;
|
||||
|
||||
class Linef3
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -72,11 +72,23 @@ MotionPlanner::initialize()
|
|||
this->initialized = true;
|
||||
}
|
||||
|
||||
ExPolygonCollection
|
||||
MotionPlanner::get_env(size_t island_idx) const
|
||||
{
|
||||
if (island_idx == -1) {
|
||||
return ExPolygonCollection(this->outer);
|
||||
} else {
|
||||
return this->inner[island_idx];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyline)
|
||||
{
|
||||
// lazy generation of configuration space
|
||||
if (!this->initialized) this->initialize();
|
||||
|
||||
// if we have an empty configuration space, return a straight move
|
||||
if (this->islands.empty()) {
|
||||
polyline->points.push_back(from);
|
||||
polyline->points.push_back(to);
|
||||
|
@ -99,28 +111,28 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl
|
|||
}
|
||||
}
|
||||
|
||||
// get environment
|
||||
ExPolygonCollection env = this->get_env(island_idx);
|
||||
if (env.expolygons.empty()) {
|
||||
// if this environment is empty (probably because it's too small), perform straight move
|
||||
// and avoid running the algorithms on empty dataset
|
||||
polyline->points.push_back(from);
|
||||
polyline->points.push_back(to);
|
||||
return; // bye bye
|
||||
}
|
||||
|
||||
// Now check whether points are inside the environment.
|
||||
Point inner_from = from;
|
||||
Point inner_to = to;
|
||||
bool from_is_inside, to_is_inside;
|
||||
if (island_idx == -1) {
|
||||
if (!(from_is_inside = this->outer.contains(from))) {
|
||||
// Find the closest inner point to start from.
|
||||
from.nearest_point(this->outer, &inner_from);
|
||||
}
|
||||
if (!(to_is_inside = this->outer.contains(to))) {
|
||||
// Find the closest inner point to start from.
|
||||
to.nearest_point(this->outer, &inner_to);
|
||||
}
|
||||
} else {
|
||||
if (!(from_is_inside = this->inner[island_idx].contains(from))) {
|
||||
// Find the closest inner point to start from.
|
||||
from.nearest_point(this->inner[island_idx], &inner_from);
|
||||
}
|
||||
if (!(to_is_inside = this->inner[island_idx].contains(to))) {
|
||||
// Find the closest inner point to start from.
|
||||
to.nearest_point(this->inner[island_idx], &inner_to);
|
||||
}
|
||||
|
||||
if (!(from_is_inside = env.contains(from))) {
|
||||
// Find the closest inner point to start from.
|
||||
inner_from = this->nearest_env_point(env, from, to);
|
||||
}
|
||||
if (!(to_is_inside = env.contains(to))) {
|
||||
// Find the closest inner point to start from.
|
||||
inner_to = this->nearest_env_point(env, to, inner_from);
|
||||
}
|
||||
|
||||
// perform actual path search
|
||||
|
@ -129,85 +141,147 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl
|
|||
|
||||
polyline->points.insert(polyline->points.begin(), from);
|
||||
polyline->points.push_back(to);
|
||||
|
||||
{
|
||||
// grow our environment slightly in order for simplify_by_visibility()
|
||||
// to work best by considering moves on boundaries valid as well
|
||||
ExPolygonCollection grown_env;
|
||||
offset(env, &grown_env.expolygons, +SCALED_EPSILON);
|
||||
|
||||
// remove unnecessary vertices
|
||||
polyline->simplify_by_visibility(grown_env);
|
||||
}
|
||||
|
||||
/*
|
||||
SVG svg("shortest_path.svg");
|
||||
svg.draw(this->outer);
|
||||
svg.arrows = false;
|
||||
for (MotionPlannerGraph::adjacency_list_t::const_iterator it = graph->adjacency_list.begin(); it != graph->adjacency_list.end(); ++it) {
|
||||
Point a = graph->nodes[it - graph->adjacency_list.begin()];
|
||||
for (std::vector<MotionPlannerGraph::neighbor>::const_iterator n = it->begin(); n != it->end(); ++n) {
|
||||
Point b = graph->nodes[n->target];
|
||||
svg.draw(Line(a, b));
|
||||
}
|
||||
}
|
||||
svg.arrows = true;
|
||||
svg.draw(from);
|
||||
svg.draw(inner_from, "red");
|
||||
svg.draw(to);
|
||||
svg.draw(inner_to, "red");
|
||||
svg.draw(*polyline, "red");
|
||||
svg.Close();
|
||||
*/
|
||||
}
|
||||
|
||||
Point
|
||||
MotionPlanner::nearest_env_point(const ExPolygonCollection &env, const Point &from, const Point &to) const
|
||||
{
|
||||
/* In order to ensure that the move between 'from' and the initial env point does
|
||||
not violate any of the configuration space boundaries, we limit our search to
|
||||
the points that satisfy this condition. */
|
||||
|
||||
/* Assume that this method is never called when 'env' contains 'from';
|
||||
so 'from' is either inside a hole or outside all contours */
|
||||
|
||||
// get the points of the hole containing 'from', if any
|
||||
Points pp;
|
||||
for (ExPolygons::const_iterator ex = env.expolygons.begin(); ex != env.expolygons.end(); ++ex) {
|
||||
for (Polygons::const_iterator h = ex->holes.begin(); h != ex->holes.end(); ++h) {
|
||||
if (h->contains(from)) {
|
||||
pp = *h;
|
||||
}
|
||||
}
|
||||
if (!pp.empty()) break;
|
||||
}
|
||||
|
||||
/* If 'from' is not inside a hole, it's outside of all contours, so take all
|
||||
contours' points */
|
||||
if (pp.empty()) {
|
||||
for (ExPolygons::const_iterator ex = env.expolygons.begin(); ex != env.expolygons.end(); ++ex) {
|
||||
Points contour_pp = ex->contour;
|
||||
pp.insert(pp.end(), contour_pp.begin(), contour_pp.end());
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the candidate result and check that it doesn't cross any boundary.
|
||||
(We could skip all of the above polygon finding logic and directly test all points
|
||||
in env, but this way we probably reduce complexity). */
|
||||
Polygons env_pp = env;
|
||||
while (pp.size() >= 2) {
|
||||
// find the point in pp that is closest to both 'from' and 'to'
|
||||
size_t result = from.nearest_waypoint_index(pp, to);
|
||||
|
||||
if (intersects((Lines)Line(from, pp[result]), env_pp)) {
|
||||
// discard result
|
||||
pp.erase(pp.begin() + result);
|
||||
} else {
|
||||
return pp[result];
|
||||
}
|
||||
}
|
||||
|
||||
// if we're here, return last point if any (better than nothing)
|
||||
if (!pp.empty()) return pp.front();
|
||||
|
||||
// if we have no points at all, then we have an empty environment and we
|
||||
// make this method behave as a no-op (we shouldn't get here by the way)
|
||||
return from;
|
||||
}
|
||||
|
||||
MotionPlannerGraph*
|
||||
MotionPlanner::init_graph(int island_idx)
|
||||
{
|
||||
if (this->graphs[island_idx + 1] == NULL) {
|
||||
Polygons pp;
|
||||
if (island_idx == -1) {
|
||||
pp = this->outer;
|
||||
} else {
|
||||
pp = this->inner[island_idx];
|
||||
}
|
||||
|
||||
// if this graph doesn't exist, initialize it
|
||||
MotionPlannerGraph* graph = this->graphs[island_idx + 1] = new MotionPlannerGraph();
|
||||
|
||||
// add polygon boundaries as edges
|
||||
size_t node_idx = 0;
|
||||
Lines lines;
|
||||
for (Polygons::const_iterator polygon = pp.begin(); polygon != pp.end(); ++polygon) {
|
||||
graph->nodes.push_back(polygon->points.back());
|
||||
node_idx++;
|
||||
for (Points::const_iterator p = polygon->points.begin(); p != polygon->points.end(); ++p) {
|
||||
graph->nodes.push_back(*p);
|
||||
double dist = graph->nodes[node_idx-1].distance_to(*p);
|
||||
graph->add_edge(node_idx-1, node_idx, dist);
|
||||
graph->add_edge(node_idx, node_idx-1, dist);
|
||||
node_idx++;
|
||||
}
|
||||
polygon->lines(&lines);
|
||||
}
|
||||
/* We don't add polygon boundaries as graph edges, because we'd need to connect
|
||||
them to the Voronoi-generated edges by recognizing coinciding nodes. */
|
||||
|
||||
// add Voronoi edges as internal edges
|
||||
{
|
||||
typedef voronoi_diagram<double> VD;
|
||||
typedef std::map<const VD::vertex_type*,size_t> t_vd_vertices;
|
||||
VD vd;
|
||||
t_vd_vertices vd_vertices;
|
||||
typedef voronoi_diagram<double> VD;
|
||||
VD vd;
|
||||
|
||||
// mapping between Voronoi vertices and graph nodes
|
||||
typedef std::map<const VD::vertex_type*,size_t> t_vd_vertices;
|
||||
t_vd_vertices vd_vertices;
|
||||
|
||||
// get boundaries as lines
|
||||
ExPolygonCollection env = this->get_env(island_idx);
|
||||
Lines lines = env.lines();
|
||||
boost::polygon::construct_voronoi(lines.begin(), lines.end(), &vd);
|
||||
|
||||
// traverse the Voronoi diagram and generate graph nodes and edges
|
||||
for (VD::const_edge_iterator edge = vd.edges().begin(); edge != vd.edges().end(); ++edge) {
|
||||
if (edge->is_infinite()) continue;
|
||||
|
||||
boost::polygon::construct_voronoi(lines.begin(), lines.end(), &vd);
|
||||
for (VD::const_edge_iterator edge = vd.edges().begin(); edge != vd.edges().end(); ++edge) {
|
||||
if (edge->is_infinite()) continue;
|
||||
|
||||
const VD::vertex_type* v0 = edge->vertex0();
|
||||
const VD::vertex_type* v1 = edge->vertex1();
|
||||
Point p0 = Point(v0->x(), v0->y());
|
||||
Point p1 = Point(v1->x(), v1->y());
|
||||
// contains() should probably be faster than contains(),
|
||||
// and should it fail on any boundary points it's not a big problem
|
||||
if (island_idx == -1) {
|
||||
if (!this->outer.contains(p0) || !this->outer.contains(p1)) continue;
|
||||
} else {
|
||||
if (!this->inner[island_idx].contains(p0) || !this->inner[island_idx].contains(p1)) continue;
|
||||
}
|
||||
|
||||
t_vd_vertices::const_iterator i_v0 = vd_vertices.find(v0);
|
||||
size_t v0_idx;
|
||||
if (i_v0 == vd_vertices.end()) {
|
||||
graph->nodes.push_back(p0);
|
||||
v0_idx = node_idx;
|
||||
vd_vertices[v0] = node_idx;
|
||||
node_idx++;
|
||||
} else {
|
||||
v0_idx = i_v0->second;
|
||||
}
|
||||
|
||||
t_vd_vertices::const_iterator i_v1 = vd_vertices.find(v1);
|
||||
size_t v1_idx;
|
||||
if (i_v1 == vd_vertices.end()) {
|
||||
graph->nodes.push_back(p1);
|
||||
v1_idx = node_idx;
|
||||
vd_vertices[v1] = node_idx;
|
||||
node_idx++;
|
||||
} else {
|
||||
v1_idx = i_v1->second;
|
||||
}
|
||||
|
||||
double dist = graph->nodes[v0_idx].distance_to(graph->nodes[v1_idx]);
|
||||
graph->add_edge(v0_idx, v1_idx, dist);
|
||||
const VD::vertex_type* v0 = edge->vertex0();
|
||||
const VD::vertex_type* v1 = edge->vertex1();
|
||||
Point p0 = Point(v0->x(), v0->y());
|
||||
Point p1 = Point(v1->x(), v1->y());
|
||||
|
||||
// skip edge if any of its endpoints is outside our configuration space
|
||||
if (!env.contains_b(p0) || !env.contains_b(p1)) continue;
|
||||
|
||||
t_vd_vertices::const_iterator i_v0 = vd_vertices.find(v0);
|
||||
size_t v0_idx;
|
||||
if (i_v0 == vd_vertices.end()) {
|
||||
graph->nodes.push_back(p0);
|
||||
vd_vertices[v0] = v0_idx = graph->nodes.size()-1;
|
||||
} else {
|
||||
v0_idx = i_v0->second;
|
||||
}
|
||||
|
||||
t_vd_vertices::const_iterator i_v1 = vd_vertices.find(v1);
|
||||
size_t v1_idx;
|
||||
if (i_v1 == vd_vertices.end()) {
|
||||
graph->nodes.push_back(p1);
|
||||
vd_vertices[v1] = v1_idx = graph->nodes.size()-1;
|
||||
} else {
|
||||
v1_idx = i_v1->second;
|
||||
}
|
||||
|
||||
// Euclidean distance is used as weight for the graph edge
|
||||
double dist = graph->nodes[v0_idx].distance_to(graph->nodes[v1_idx]);
|
||||
graph->add_edge(v0_idx, v1_idx, dist);
|
||||
}
|
||||
|
||||
return graph;
|
||||
|
@ -244,38 +318,61 @@ MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline)
|
|||
|
||||
const weight_t max_weight = std::numeric_limits<weight_t>::infinity();
|
||||
|
||||
std::vector<weight_t> min_distance;
|
||||
std::vector<weight_t> dist;
|
||||
std::vector<node_t> previous;
|
||||
{
|
||||
int n = this->adjacency_list.size();
|
||||
min_distance.clear();
|
||||
min_distance.resize(n, max_weight);
|
||||
min_distance[from] = 0;
|
||||
// number of nodes
|
||||
size_t n = this->adjacency_list.size();
|
||||
|
||||
// initialize dist and previous
|
||||
dist.clear();
|
||||
dist.resize(n, max_weight);
|
||||
dist[from] = 0; // distance from 'from' to itself
|
||||
previous.clear();
|
||||
previous.resize(n, -1);
|
||||
std::set<std::pair<weight_t, node_t> > vertex_queue;
|
||||
vertex_queue.insert(std::make_pair(min_distance[from], from));
|
||||
|
||||
while (!vertex_queue.empty())
|
||||
|
||||
// initialize the Q with all nodes
|
||||
std::set<node_t> Q;
|
||||
for (node_t i = 0; i < n; ++i) Q.insert(i);
|
||||
|
||||
while (!Q.empty())
|
||||
{
|
||||
weight_t dist = vertex_queue.begin()->first;
|
||||
node_t u = vertex_queue.begin()->second;
|
||||
vertex_queue.erase(vertex_queue.begin());
|
||||
|
||||
// Visit each edge exiting u
|
||||
// get node in Q having the minimum dist ('from' in the first loop)
|
||||
node_t u;
|
||||
{
|
||||
double min_dist = -1;
|
||||
for (std::set<node_t>::const_iterator n = Q.begin(); n != Q.end(); ++n) {
|
||||
if (dist[*n] < min_dist || min_dist == -1) {
|
||||
u = *n;
|
||||
min_dist = dist[*n];
|
||||
}
|
||||
}
|
||||
}
|
||||
Q.erase(u);
|
||||
|
||||
// stop searching if we reached our destination
|
||||
if (u == to) break;
|
||||
|
||||
// Visit each edge starting from node u
|
||||
const std::vector<neighbor> &neighbors = this->adjacency_list[u];
|
||||
for (std::vector<neighbor>::const_iterator neighbor_iter = neighbors.begin();
|
||||
neighbor_iter != neighbors.end();
|
||||
neighbor_iter++)
|
||||
{
|
||||
// neighbor node is v
|
||||
node_t v = neighbor_iter->target;
|
||||
weight_t weight = neighbor_iter->weight;
|
||||
weight_t distance_through_u = dist + weight;
|
||||
if (distance_through_u < min_distance[v]) {
|
||||
vertex_queue.erase(std::make_pair(min_distance[v], v));
|
||||
min_distance[v] = distance_through_u;
|
||||
|
||||
// skip if we already visited this
|
||||
if (Q.find(v) == Q.end()) continue;
|
||||
|
||||
// calculate total distance
|
||||
weight_t alt = dist[u] + neighbor_iter->weight;
|
||||
|
||||
// if total distance through u is shorter than the previous
|
||||
// distance (if any) between 'from' and 'v', replace it
|
||||
if (alt < dist[v]) {
|
||||
dist[v] = alt;
|
||||
previous[v] = u;
|
||||
vertex_queue.insert(std::make_pair(min_distance[v], v));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -284,6 +381,7 @@ MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline)
|
|||
|
||||
for (node_t vertex = to; vertex != -1; vertex = previous[vertex])
|
||||
polyline->points.push_back(this->nodes[vertex]);
|
||||
polyline->points.push_back(this->nodes[from]);
|
||||
polyline->reverse();
|
||||
}
|
||||
|
||||
|
|
|
@ -33,10 +33,14 @@ class MotionPlanner
|
|||
|
||||
void initialize();
|
||||
MotionPlannerGraph* init_graph(int island_idx);
|
||||
ExPolygonCollection get_env(size_t island_idx) const;
|
||||
Point nearest_env_point(const ExPolygonCollection &env, const Point &from, const Point &to) const;
|
||||
};
|
||||
|
||||
class MotionPlannerGraph
|
||||
{
|
||||
friend class MotionPlanner;
|
||||
|
||||
private:
|
||||
typedef size_t node_t;
|
||||
typedef double weight_t;
|
||||
|
|
|
@ -76,6 +76,13 @@ MultiPoint::find_point(const Point &point) const
|
|||
return -1; // not found
|
||||
}
|
||||
|
||||
bool
|
||||
MultiPoint::has_boundary_point(const Point &point) const
|
||||
{
|
||||
double dist = point.distance_to(point.projection_onto(*this));
|
||||
return dist < SCALED_EPSILON;
|
||||
}
|
||||
|
||||
void
|
||||
MultiPoint::bounding_box(BoundingBox* bb) const
|
||||
{
|
||||
|
|
|
@ -30,6 +30,7 @@ class MultiPoint
|
|||
double length() const;
|
||||
bool is_valid() const;
|
||||
int find_point(const Point &point) const;
|
||||
bool has_boundary_point(const Point &point) const;
|
||||
void bounding_box(BoundingBox* bb) const;
|
||||
|
||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||
|
|
|
@ -104,6 +104,32 @@ Point::nearest_point_index(const PointConstPtrs &points) const
|
|||
return idx;
|
||||
}
|
||||
|
||||
/* This method finds the point that is closest to both this point and the supplied one */
|
||||
size_t
|
||||
Point::nearest_waypoint_index(const Points &points, const Point &dest) const
|
||||
{
|
||||
size_t idx = -1;
|
||||
double distance = -1; // double because long is limited to 2147483647 on some platforms and it's not enough
|
||||
|
||||
for (Points::const_iterator p = points.begin(); p != points.end(); ++p) {
|
||||
// distance from this to candidate
|
||||
double d = pow(this->x - p->x, 2) + pow(this->y - p->y, 2);
|
||||
|
||||
// distance from candidate to dest
|
||||
d += pow(p->x - dest.x, 2) + pow(p->y - dest.y, 2);
|
||||
|
||||
// if the total distance is greater than current min distance, ignore it
|
||||
if (distance != -1 && d > distance) continue;
|
||||
|
||||
idx = p - points.begin();
|
||||
distance = d;
|
||||
|
||||
if (distance < EPSILON) break;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
int
|
||||
Point::nearest_point_index(const PointPtrs &points) const
|
||||
{
|
||||
|
@ -123,6 +149,15 @@ Point::nearest_point(const Points &points, Point* point) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Point::nearest_waypoint(const Points &points, const Point &dest, Point* point) const
|
||||
{
|
||||
int idx = this->nearest_waypoint_index(points, dest);
|
||||
if (idx == -1) return false;
|
||||
*point = points.at(idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
double
|
||||
Point::distance_to(const Point &point) const
|
||||
{
|
||||
|
|
|
@ -45,7 +45,9 @@ class Point
|
|||
int nearest_point_index(const Points &points) const;
|
||||
int nearest_point_index(const PointConstPtrs &points) const;
|
||||
int nearest_point_index(const PointPtrs &points) const;
|
||||
size_t nearest_waypoint_index(const Points &points, const Point &point) const;
|
||||
bool nearest_point(const Points &points, Point* point) const;
|
||||
bool nearest_waypoint(const Points &points, const Point &dest, Point* point) const;
|
||||
double distance_to(const Point &point) const;
|
||||
double distance_to(const Line &line) const;
|
||||
double perp_distance_to(const Line &line) const;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include "Polyline.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "ExPolygonCollection.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include <iostream>
|
||||
|
@ -127,6 +129,37 @@ Polyline::simplify(double tolerance)
|
|||
this->points = MultiPoint::_douglas_peucker(this->points, tolerance);
|
||||
}
|
||||
|
||||
/* This method simplifies all *lines* contained in the supplied area */
|
||||
template <class T>
|
||||
void
|
||||
Polyline::simplify_by_visibility(const T &area)
|
||||
{
|
||||
Points &pp = this->points;
|
||||
|
||||
// find first point in area
|
||||
size_t start = 0;
|
||||
while (start < pp.size() && !area.contains(pp[start])) {
|
||||
start++;
|
||||
}
|
||||
|
||||
for (size_t s = start; s < pp.size() && !pp.empty(); ++s) {
|
||||
// find the farthest point to which we can build
|
||||
// a line that is contained in the supplied area
|
||||
// a binary search would be more efficient for this
|
||||
for (size_t e = pp.size()-1; e > (s + 1); --e) {
|
||||
if (area.contains(Line(pp[s], pp[e]))) {
|
||||
// we can suppress points between s and e
|
||||
pp.erase(pp.begin() + s + 1, pp.begin() + e);
|
||||
|
||||
// repeat recursively until no further simplification is possible
|
||||
return this->simplify_by_visibility(area);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
template void Polyline::simplify_by_visibility<ExPolygon>(const ExPolygon &area);
|
||||
template void Polyline::simplify_by_visibility<ExPolygonCollection>(const ExPolygonCollection &area);
|
||||
|
||||
void
|
||||
Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
|
||||
{
|
||||
|
@ -159,7 +192,7 @@ Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
|
|||
p2->points.clear();
|
||||
p2->points.push_back(point);
|
||||
for (Lines::const_iterator line = lines.begin() + line_idx; line != lines.end(); ++line) {
|
||||
if (!line->b.coincides_with(p)) p2->points.push_back(line->b);
|
||||
p2->points.push_back(line->b);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
class ExPolygon;
|
||||
class Polyline;
|
||||
typedef std::vector<Polyline> Polylines;
|
||||
|
||||
|
@ -23,6 +24,7 @@ class Polyline : public MultiPoint {
|
|||
void extend_start(double distance);
|
||||
void equally_spaced_points(double distance, Points* points) const;
|
||||
void simplify(double tolerance);
|
||||
template <class T> void simplify_by_visibility(const T &area);
|
||||
void split_at(const Point &point, Polyline* p1, Polyline* p2) const;
|
||||
bool is_straight() const;
|
||||
std::string wkt() const;
|
||||
|
|
|
@ -561,25 +561,26 @@ Print::validate() const
|
|||
FOREACH_OBJECT(this, i_object) {
|
||||
PrintObject* object = *i_object;
|
||||
|
||||
// get convex hulls of all meshes assigned to this print object
|
||||
Polygons mesh_convex_hulls;
|
||||
for (size_t i = 0; i < this->regions.size(); ++i) {
|
||||
for (std::vector<int>::const_iterator it = object->region_volumes[i].begin(); it != object->region_volumes[i].end(); ++it) {
|
||||
Polygon hull;
|
||||
object->model_object()->volumes[*it]->mesh.convex_hull(&hull);
|
||||
mesh_convex_hulls.push_back(hull);
|
||||
}
|
||||
}
|
||||
|
||||
// make a single convex hull for all of them
|
||||
/* get convex hull of all meshes assigned to this print object
|
||||
(this is the same as model_object()->raw_mesh.convex_hull()
|
||||
but probably more efficient */
|
||||
Polygon convex_hull;
|
||||
Slic3r::Geometry::convex_hull(mesh_convex_hulls, &convex_hull);
|
||||
{
|
||||
Polygons mesh_convex_hulls;
|
||||
for (size_t i = 0; i < this->regions.size(); ++i) {
|
||||
for (std::vector<int>::const_iterator it = object->region_volumes[i].begin(); it != object->region_volumes[i].end(); ++it) {
|
||||
Polygon hull;
|
||||
object->model_object()->volumes[*it]->mesh.convex_hull(&hull);
|
||||
mesh_convex_hulls.push_back(hull);
|
||||
}
|
||||
}
|
||||
|
||||
// make a single convex hull for all of them
|
||||
Slic3r::Geometry::convex_hull(mesh_convex_hulls, &convex_hull);
|
||||
}
|
||||
|
||||
// apply the same transformations we apply to the actual meshes when slicing them
|
||||
object->model_object()->instances.front()->transform_polygon(&convex_hull);
|
||||
|
||||
// align object to Z = 0 and apply XY shift
|
||||
convex_hull.translate(object->_copies_shift);
|
||||
|
||||
// grow convex hull with the clearance margin
|
||||
{
|
||||
|
|
|
@ -964,6 +964,11 @@ PrintConfigDef::build_def() {
|
|||
Options["use_relative_e_distances"].tooltip = "If your firmware requires relative E values, check this, otherwise leave it unchecked. Most firmwares use absolute values.";
|
||||
Options["use_relative_e_distances"].cli = "use-relative-e-distances!";
|
||||
|
||||
Options["use_volumetric_e"].type = coBool;
|
||||
Options["use_volumetric_e"].label = "Use volumetric E";
|
||||
Options["use_volumetric_e"].tooltip = "This experimental setting uses outputs the E values in cubic millimeters instead of linear millimeters. The M200 command is prepended to the generated G-code, unless it is found in the configured start G-code. This is only supported in recent Marlin.";
|
||||
Options["use_volumetric_e"].cli = "use-volumetric-e!";
|
||||
|
||||
Options["vibration_limit"].type = coFloat;
|
||||
Options["vibration_limit"].label = "Vibration limit (deprecated)";
|
||||
Options["vibration_limit"].tooltip = "This experimental option will slow down those moves hitting the configured frequency limit. The purpose of limiting vibrations is to avoid mechanical resonance. Set zero to disable.";
|
||||
|
|
|
@ -321,11 +321,13 @@ class PrintRegionConfig : public virtual StaticPrintConfig
|
|||
class GCodeConfig : public virtual StaticPrintConfig
|
||||
{
|
||||
public:
|
||||
ConfigOptionString end_gcode;
|
||||
ConfigOptionString extrusion_axis;
|
||||
ConfigOptionFloats extrusion_multiplier;
|
||||
ConfigOptionFloats filament_diameter;
|
||||
ConfigOptionBool gcode_comments;
|
||||
ConfigOptionEnum<GCodeFlavor> gcode_flavor;
|
||||
ConfigOptionString layer_gcode;
|
||||
ConfigOptionFloat pressure_advance;
|
||||
ConfigOptionFloats retract_length;
|
||||
ConfigOptionFloats retract_length_toolchange;
|
||||
|
@ -333,11 +335,15 @@ class GCodeConfig : public virtual StaticPrintConfig
|
|||
ConfigOptionFloats retract_restart_extra;
|
||||
ConfigOptionFloats retract_restart_extra_toolchange;
|
||||
ConfigOptionInts retract_speed;
|
||||
ConfigOptionString start_gcode;
|
||||
ConfigOptionString toolchange_gcode;
|
||||
ConfigOptionFloat travel_speed;
|
||||
ConfigOptionBool use_firmware_retraction;
|
||||
ConfigOptionBool use_relative_e_distances;
|
||||
ConfigOptionBool use_volumetric_e;
|
||||
|
||||
GCodeConfig() : StaticPrintConfig() {
|
||||
this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n";
|
||||
this->extrusion_axis.value = "E";
|
||||
this->extrusion_multiplier.values.resize(1);
|
||||
this->extrusion_multiplier.values[0] = 1;
|
||||
|
@ -345,6 +351,7 @@ class GCodeConfig : public virtual StaticPrintConfig
|
|||
this->filament_diameter.values[0] = 3;
|
||||
this->gcode_comments.value = false;
|
||||
this->gcode_flavor.value = gcfRepRap;
|
||||
this->layer_gcode.value = "";
|
||||
this->pressure_advance.value = 0;
|
||||
this->retract_length.values.resize(1);
|
||||
this->retract_length.values[0] = 1;
|
||||
|
@ -358,17 +365,22 @@ class GCodeConfig : public virtual StaticPrintConfig
|
|||
this->retract_restart_extra_toolchange.values[0] = 0;
|
||||
this->retract_speed.values.resize(1);
|
||||
this->retract_speed.values[0] = 30;
|
||||
this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n";
|
||||
this->toolchange_gcode.value = "";
|
||||
this->travel_speed.value = 130;
|
||||
this->use_firmware_retraction.value = false;
|
||||
this->use_relative_e_distances.value = false;
|
||||
this->use_volumetric_e.value = false;
|
||||
};
|
||||
|
||||
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
|
||||
if (opt_key == "end_gcode") return &this->end_gcode;
|
||||
if (opt_key == "extrusion_axis") return &this->extrusion_axis;
|
||||
if (opt_key == "extrusion_multiplier") return &this->extrusion_multiplier;
|
||||
if (opt_key == "filament_diameter") return &this->filament_diameter;
|
||||
if (opt_key == "gcode_comments") return &this->gcode_comments;
|
||||
if (opt_key == "gcode_flavor") return &this->gcode_flavor;
|
||||
if (opt_key == "layer_gcode") return &this->layer_gcode;
|
||||
if (opt_key == "pressure_advance") return &this->pressure_advance;
|
||||
if (opt_key == "retract_length") return &this->retract_length;
|
||||
if (opt_key == "retract_length_toolchange") return &this->retract_length_toolchange;
|
||||
|
@ -376,9 +388,12 @@ class GCodeConfig : public virtual StaticPrintConfig
|
|||
if (opt_key == "retract_restart_extra") return &this->retract_restart_extra;
|
||||
if (opt_key == "retract_restart_extra_toolchange") return &this->retract_restart_extra_toolchange;
|
||||
if (opt_key == "retract_speed") return &this->retract_speed;
|
||||
if (opt_key == "start_gcode") return &this->start_gcode;
|
||||
if (opt_key == "toolchange_gcode") return &this->toolchange_gcode;
|
||||
if (opt_key == "travel_speed") return &this->travel_speed;
|
||||
if (opt_key == "use_firmware_retraction") return &this->use_firmware_retraction;
|
||||
if (opt_key == "use_relative_e_distances") return &this->use_relative_e_distances;
|
||||
if (opt_key == "use_volumetric_e") return &this->use_volumetric_e;
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
@ -409,7 +424,6 @@ class PrintConfig : public GCodeConfig
|
|||
ConfigOptionFloat default_acceleration;
|
||||
ConfigOptionInt disable_fan_first_layers;
|
||||
ConfigOptionFloat duplicate_distance;
|
||||
ConfigOptionString end_gcode;
|
||||
ConfigOptionFloat extruder_clearance_height;
|
||||
ConfigOptionFloat extruder_clearance_radius;
|
||||
ConfigOptionPoints extruder_offset;
|
||||
|
@ -423,7 +437,6 @@ class PrintConfig : public GCodeConfig
|
|||
ConfigOptionBool gcode_arcs;
|
||||
ConfigOptionFloat infill_acceleration;
|
||||
ConfigOptionBool infill_first;
|
||||
ConfigOptionString layer_gcode;
|
||||
ConfigOptionInt max_fan_speed;
|
||||
ConfigOptionInt min_fan_speed;
|
||||
ConfigOptionInt min_print_speed;
|
||||
|
@ -444,10 +457,8 @@ class PrintConfig : public GCodeConfig
|
|||
ConfigOptionInt slowdown_below_layer_time;
|
||||
ConfigOptionBool spiral_vase;
|
||||
ConfigOptionInt standby_temperature_delta;
|
||||
ConfigOptionString start_gcode;
|
||||
ConfigOptionInts temperature;
|
||||
ConfigOptionInt threads;
|
||||
ConfigOptionString toolchange_gcode;
|
||||
ConfigOptionFloat vibration_limit;
|
||||
ConfigOptionBools wipe;
|
||||
ConfigOptionFloat z_offset;
|
||||
|
@ -467,7 +478,6 @@ class PrintConfig : public GCodeConfig
|
|||
this->default_acceleration.value = 0;
|
||||
this->disable_fan_first_layers.value = 1;
|
||||
this->duplicate_distance.value = 6;
|
||||
this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n";
|
||||
this->extruder_clearance_height.value = 20;
|
||||
this->extruder_clearance_radius.value = 20;
|
||||
this->extruder_offset.values.resize(1);
|
||||
|
@ -485,7 +495,6 @@ class PrintConfig : public GCodeConfig
|
|||
this->gcode_arcs.value = false;
|
||||
this->infill_acceleration.value = 0;
|
||||
this->infill_first.value = false;
|
||||
this->layer_gcode.value = "";
|
||||
this->max_fan_speed.value = 100;
|
||||
this->min_fan_speed.value = 35;
|
||||
this->min_print_speed.value = 10;
|
||||
|
@ -508,11 +517,9 @@ class PrintConfig : public GCodeConfig
|
|||
this->slowdown_below_layer_time.value = 30;
|
||||
this->spiral_vase.value = false;
|
||||
this->standby_temperature_delta.value = -5;
|
||||
this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n";
|
||||
this->temperature.values.resize(1);
|
||||
this->temperature.values[0] = 200;
|
||||
this->threads.value = 2;
|
||||
this->toolchange_gcode.value = "";
|
||||
this->vibration_limit.value = 0;
|
||||
this->wipe.values.resize(1);
|
||||
this->wipe.values[0] = false;
|
||||
|
@ -531,7 +538,6 @@ class PrintConfig : public GCodeConfig
|
|||
if (opt_key == "default_acceleration") return &this->default_acceleration;
|
||||
if (opt_key == "disable_fan_first_layers") return &this->disable_fan_first_layers;
|
||||
if (opt_key == "duplicate_distance") return &this->duplicate_distance;
|
||||
if (opt_key == "end_gcode") return &this->end_gcode;
|
||||
if (opt_key == "extruder_clearance_height") return &this->extruder_clearance_height;
|
||||
if (opt_key == "extruder_clearance_radius") return &this->extruder_clearance_radius;
|
||||
if (opt_key == "extruder_offset") return &this->extruder_offset;
|
||||
|
@ -545,7 +551,6 @@ class PrintConfig : public GCodeConfig
|
|||
if (opt_key == "gcode_arcs") return &this->gcode_arcs;
|
||||
if (opt_key == "infill_acceleration") return &this->infill_acceleration;
|
||||
if (opt_key == "infill_first") return &this->infill_first;
|
||||
if (opt_key == "layer_gcode") return &this->layer_gcode;
|
||||
if (opt_key == "max_fan_speed") return &this->max_fan_speed;
|
||||
if (opt_key == "min_fan_speed") return &this->min_fan_speed;
|
||||
if (opt_key == "min_print_speed") return &this->min_print_speed;
|
||||
|
@ -566,10 +571,8 @@ class PrintConfig : public GCodeConfig
|
|||
if (opt_key == "slowdown_below_layer_time") return &this->slowdown_below_layer_time;
|
||||
if (opt_key == "spiral_vase") return &this->spiral_vase;
|
||||
if (opt_key == "standby_temperature_delta") return &this->standby_temperature_delta;
|
||||
if (opt_key == "start_gcode") return &this->start_gcode;
|
||||
if (opt_key == "temperature") return &this->temperature;
|
||||
if (opt_key == "threads") return &this->threads;
|
||||
if (opt_key == "toolchange_gcode") return &this->toolchange_gcode;
|
||||
if (opt_key == "vibration_limit") return &this->vibration_limit;
|
||||
if (opt_key == "wipe") return &this->wipe;
|
||||
if (opt_key == "z_offset") return &this->z_offset;
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
#include "SVG.hpp"
|
||||
#include <iostream>
|
||||
|
||||
#define COORD(x) ((float)unscale(x)*10)
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
SVG::SVG(const char* filename)
|
||||
: arrows(true), filename(filename), fill("grey"), stroke("black")
|
||||
{
|
||||
this->f = fopen(filename, "w");
|
||||
fprintf(this->f,
|
||||
|
@ -13,21 +17,14 @@ SVG::SVG(const char* filename)
|
|||
" <polyline fill=\"darkblue\" points=\"0,0 10,5 0,10 1,5\" />\n"
|
||||
" </marker>\n"
|
||||
);
|
||||
this->arrows = true;
|
||||
}
|
||||
|
||||
float
|
||||
SVG::coordinate(long c)
|
||||
{
|
||||
return (float)unscale(c)*10;
|
||||
}
|
||||
|
||||
void
|
||||
SVG::AddLine(const Line &line)
|
||||
SVG::draw(const Line &line, std::string stroke)
|
||||
{
|
||||
fprintf(this->f,
|
||||
" <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: black; stroke-width: 2\"",
|
||||
this->coordinate(line.a.x), this->coordinate(line.a.y), this->coordinate(line.b.x), this->coordinate(line.b.y)
|
||||
" <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: %s; stroke-width: 1\"",
|
||||
COORD(line.a.x), COORD(line.a.y), COORD(line.b.x), COORD(line.b.y), stroke.c_str()
|
||||
);
|
||||
if (this->arrows)
|
||||
fprintf(this->f, " marker-end=\"url(#endArrow)\"");
|
||||
|
@ -37,7 +34,72 @@ SVG::AddLine(const Line &line)
|
|||
void
|
||||
SVG::AddLine(const IntersectionLine &line)
|
||||
{
|
||||
this->AddLine(Line(line.a, line.b));
|
||||
this->draw(Line(line.a, line.b));
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const ExPolygon &expolygon, std::string fill)
|
||||
{
|
||||
this->fill = fill;
|
||||
|
||||
std::string d;
|
||||
Polygons pp = expolygon;
|
||||
for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) {
|
||||
d += this->get_path_d(*p, true) + " ";
|
||||
}
|
||||
this->path(d, true);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polygon &polygon, std::string fill)
|
||||
{
|
||||
this->fill = fill;
|
||||
this->path(this->get_path_d(polygon, true), true);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Polyline &polyline, std::string stroke)
|
||||
{
|
||||
this->stroke = stroke;
|
||||
this->path(this->get_path_d(polyline, false), false);
|
||||
}
|
||||
|
||||
void
|
||||
SVG::draw(const Point &point, std::string fill, unsigned int radius)
|
||||
{
|
||||
std::ostringstream svg;
|
||||
svg << " <circle cx=\"" << COORD(point.x) << "\" cy=\"" << COORD(point.y)
|
||||
<< "\" r=\"" << radius << "\" "
|
||||
<< "style=\"stroke: none; fill: " << fill << "\" />";
|
||||
|
||||
fprintf(this->f, "%s\n", svg.str().c_str());
|
||||
}
|
||||
|
||||
void
|
||||
SVG::path(const std::string &d, bool fill)
|
||||
{
|
||||
fprintf(
|
||||
this->f,
|
||||
" <path d=\"%s\" style=\"fill: %s; stroke: %s; stroke-width: %s; fill-type: evenodd\" %s />\n",
|
||||
d.c_str(),
|
||||
fill ? this->fill.c_str() : "none",
|
||||
this->stroke.c_str(),
|
||||
fill ? "0" : "2",
|
||||
(this->arrows && !fill) ? " marker-end=\"url(#endArrow)\"" : ""
|
||||
);
|
||||
}
|
||||
|
||||
std::string
|
||||
SVG::get_path_d(const MultiPoint &mp, bool closed) const
|
||||
{
|
||||
std::ostringstream d;
|
||||
d << "M ";
|
||||
for (Points::const_iterator p = mp.points.begin(); p != mp.points.end(); ++p) {
|
||||
d << COORD(p->x) << " ";
|
||||
d << COORD(p->y) << " ";
|
||||
}
|
||||
if (closed) d << "z";
|
||||
return d.str();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -45,7 +107,7 @@ SVG::Close()
|
|||
{
|
||||
fprintf(this->f, "</svg>\n");
|
||||
fclose(this->f);
|
||||
printf("SVG file written.\n");
|
||||
printf("SVG written to %s\n", this->filename.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define slic3r_SVG_hpp_
|
||||
|
||||
#include <myinit.h>
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
|
||||
|
@ -9,15 +10,25 @@ namespace Slic3r {
|
|||
|
||||
class SVG
|
||||
{
|
||||
private:
|
||||
FILE* f;
|
||||
float coordinate(long c);
|
||||
public:
|
||||
bool arrows;
|
||||
std::string fill, stroke;
|
||||
|
||||
SVG(const char* filename);
|
||||
void AddLine(const Line &line);
|
||||
void AddLine(const IntersectionLine &line);
|
||||
void draw(const Line &line, std::string stroke = "black");
|
||||
void draw(const ExPolygon &expolygon, std::string fill = "grey");
|
||||
void draw(const Polygon &polygon, std::string fill = "grey");
|
||||
void draw(const Polyline &polyline, std::string stroke = "black");
|
||||
void draw(const Point &point, std::string fill = "black", unsigned int radius = 3);
|
||||
void Close();
|
||||
|
||||
private:
|
||||
std::string filename;
|
||||
FILE* f;
|
||||
|
||||
void path(const std::string &d, bool fill);
|
||||
std::string get_path_d(const MultiPoint &mp, bool closed = false) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -77,9 +77,19 @@ SurfaceCollection::any_internal_contains(const T &item) const
|
|||
}
|
||||
return false;
|
||||
}
|
||||
template bool SurfaceCollection::any_internal_contains<Line>(const Line &item) const;
|
||||
template bool SurfaceCollection::any_internal_contains<Polyline>(const Polyline &item) const;
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
SurfaceCollection::any_bottom_contains(const T &item) const
|
||||
{
|
||||
for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
|
||||
if (surface->is_bottom() && surface->expolygon.contains(item)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template bool SurfaceCollection::any_bottom_contains<Polyline>(const Polyline &item) const;
|
||||
|
||||
SurfacesPtr
|
||||
SurfaceCollection::filter_by_type(SurfaceType type)
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ class SurfaceCollection
|
|||
void simplify(double tolerance);
|
||||
void group(std::vector<SurfacesPtr> *retval);
|
||||
template <class T> bool any_internal_contains(const T &item) const;
|
||||
template <class T> bool any_bottom_contains(const T &item) const;
|
||||
SurfacesPtr filter_by_type(SurfaceType type);
|
||||
void filter_by_type(SurfaceType type, Polygons* polygons);
|
||||
};
|
||||
|
|
|
@ -614,10 +614,8 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
|
|||
if (!points.empty()) {
|
||||
assert(points.size() == 2); // facets must intersect each plane 0 or 2 times
|
||||
IntersectionLine line;
|
||||
line.a.x = points[1].x;
|
||||
line.a.y = points[1].y;
|
||||
line.b.x = points[0].x;
|
||||
line.b.y = points[0].y;
|
||||
line.a = (Point)points[1];
|
||||
line.b = (Point)points[0];
|
||||
line.a_id = points[1].point_id;
|
||||
line.b_id = points[0].point_id;
|
||||
line.edge_a_id = points[1].edge_id;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <admesh/stl.h>
|
||||
#include <vector>
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
|
@ -71,11 +72,9 @@ class IntersectionPoint : public Point
|
|||
IntersectionPoint() : point_id(-1), edge_id(-1) {};
|
||||
};
|
||||
|
||||
class IntersectionLine
|
||||
class IntersectionLine : public Line
|
||||
{
|
||||
public:
|
||||
Point a;
|
||||
Point b;
|
||||
int a_id;
|
||||
int b_id;
|
||||
int edge_a_id;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#define SLIC3R_VERSION "1.2.4"
|
||||
#define SLIC3R_VERSION "1.2.5-dev"
|
||||
|
||||
#define EPSILON 1e-4
|
||||
#define SCALING_FACTOR 0.000001
|
||||
|
|
|
@ -5,7 +5,7 @@ use warnings;
|
|||
|
||||
use List::Util qw(sum);
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 46;
|
||||
use Test::More tests => 48;
|
||||
|
||||
{
|
||||
my $square = [
|
||||
|
@ -133,8 +133,10 @@ use Test::More tests => 46;
|
|||
Slic3r::ExtrusionPath->new(polyline => $polylines[2], role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, mm3_per_mm => 1),
|
||||
Slic3r::ExtrusionPath->new(polyline => $polylines[3], role => Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, mm3_per_mm => 1),
|
||||
);
|
||||
my $len = $loop->length;
|
||||
my $point = Slic3r::Point->new(4821067,9321068);
|
||||
$loop->split_at_vertex($point) or $loop->split_at($point);
|
||||
is $loop->length, $len, 'total length is preserved after splitting';
|
||||
is_deeply [ map $_->role, @$loop ], [
|
||||
Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
|
||||
Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER,
|
||||
|
@ -144,4 +146,15 @@ use Test::More tests => 46;
|
|||
], 'order is correctly preserved after splitting';
|
||||
}
|
||||
|
||||
{
|
||||
my $loop = Slic3r::ExtrusionLoop->new;
|
||||
$loop->append(Slic3r::ExtrusionPath->new(
|
||||
polyline => Slic3r::Polyline->new([15896783,15868739],[24842049,12117558],[33853238,15801279],[37591780,24780128],[37591780,24844970],[33853231,33825297],[24842049,37509013],[15896798,33757841],[12211841,24812544],[15896783,15868739]),
|
||||
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, mm3_per_mm => 1
|
||||
));
|
||||
my $len = $loop->length;
|
||||
$loop->split_at(Slic3r::Point->new(15896783,15868739));
|
||||
is $loop->length, $len, 'split_at() preserves total length';
|
||||
}
|
||||
|
||||
__END__
|
||||
|
|
|
@ -4,7 +4,7 @@ use strict;
|
|||
use warnings;
|
||||
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 16;
|
||||
use Test::More tests => 21;
|
||||
|
||||
my $points = [
|
||||
[100, 100],
|
||||
|
@ -79,4 +79,49 @@ is_deeply $polyline->pp, [ @$points, @$points ], 'append_polyline';
|
|||
ok $p2->first_point->coincides_with($point), 'split_at';
|
||||
}
|
||||
|
||||
{
|
||||
my $polyline = Slic3r::Polyline->new(@$points[0,1,2,0]);
|
||||
my $p1 = Slic3r::Polyline->new;
|
||||
my $p2 = Slic3r::Polyline->new;
|
||||
$polyline->split_at($polyline->first_point, $p1, $p2);
|
||||
is scalar(@$p1), 1, 'split_at';
|
||||
is scalar(@$p2), 4, 'split_at';
|
||||
}
|
||||
|
||||
{
|
||||
my $polyline = Slic3r::Polyline->new(
|
||||
map [$_,10], (0,10,20,30,40,50,60)
|
||||
);
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new(
|
||||
[25,0], [55,0], [55,30], [25,30],
|
||||
));
|
||||
my $p = $polyline->clone;
|
||||
$p->simplify_by_visibility($expolygon);
|
||||
is_deeply $p->pp, [
|
||||
map [$_,10], (0,10,20,30,50,60)
|
||||
], 'simplify_by_visibility()';
|
||||
}
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new(
|
||||
[-15,0], [75,0], [75,30], [-15,30],
|
||||
));
|
||||
my $p = $polyline->clone;
|
||||
$p->simplify_by_visibility($expolygon);
|
||||
is_deeply $p->pp, [
|
||||
map [$_,10], (0,60)
|
||||
], 'simplify_by_visibility()';
|
||||
}
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new(
|
||||
[-15,0], [25,0], [25,30], [-15,30],
|
||||
));
|
||||
my $p = $polyline->clone;
|
||||
$p->simplify_by_visibility($expolygon);
|
||||
is_deeply $p->pp, [
|
||||
map [$_,10], (0,20,30,40,50,60)
|
||||
], 'simplify_by_visibility()';
|
||||
}
|
||||
}
|
||||
|
||||
__END__
|
||||
|
|
|
@ -69,12 +69,10 @@
|
|||
%code%{ RETVAL = (int)(intptr_t)THIS; %};
|
||||
|
||||
void make_slices();
|
||||
bool any_internal_region_slice_contains_line(Line* line)
|
||||
%code%{ RETVAL = THIS->any_internal_region_slice_contains(*line); %};
|
||||
bool any_internal_region_fill_surface_contains_line(Line* line)
|
||||
%code%{ RETVAL = THIS->any_internal_region_fill_surface_contains(*line); %};
|
||||
bool any_internal_region_fill_surface_contains_polyline(Polyline* polyline)
|
||||
%code%{ RETVAL = THIS->any_internal_region_fill_surface_contains(*polyline); %};
|
||||
bool any_internal_region_slice_contains_polyline(Polyline* polyline)
|
||||
%code%{ RETVAL = THIS->any_internal_region_slice_contains(*polyline); %};
|
||||
bool any_bottom_region_slice_contains_polyline(Polyline* polyline)
|
||||
%code%{ RETVAL = THIS->any_bottom_region_slice_contains(*polyline); %};
|
||||
};
|
||||
|
||||
%name{Slic3r::Layer::Support} class SupportLayer {
|
||||
|
@ -121,10 +119,8 @@
|
|||
Ref<ExPolygonCollection> slices()
|
||||
%code%{ RETVAL = &THIS->slices; %};
|
||||
|
||||
bool any_internal_region_slice_contains_line(Line* line)
|
||||
%code%{ RETVAL = THIS->any_internal_region_slice_contains(*line); %};
|
||||
bool any_internal_region_fill_surface_contains_line(Line* line)
|
||||
%code%{ RETVAL = THIS->any_internal_region_fill_surface_contains(*line); %};
|
||||
bool any_internal_region_fill_surface_contains_polyline(Polyline* polyline)
|
||||
%code%{ RETVAL = THIS->any_internal_region_fill_surface_contains(*polyline); %};
|
||||
bool any_internal_region_slice_contains_polyline(Polyline* polyline)
|
||||
%code%{ RETVAL = THIS->any_internal_region_slice_contains(*polyline); %};
|
||||
bool any_bottom_region_slice_contains_polyline(Polyline* polyline)
|
||||
%code%{ RETVAL = THIS->any_bottom_region_slice_contains(*polyline); %};
|
||||
};
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
void extend_end(double distance);
|
||||
void extend_start(double distance);
|
||||
void simplify(double tolerance);
|
||||
void simplify_by_visibility(ExPolygon* expolygon)
|
||||
%code{% THIS->simplify_by_visibility(*expolygon); %};
|
||||
void split_at(Point* point, Polyline* p1, Polyline* p2)
|
||||
%code{% THIS->split_at(*point, p1, p2); %};
|
||||
bool is_straight();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue