Merge branch 'master' into sender

Conflicts:
	lib/Slic3r/GUI/Tab.pm
This commit is contained in:
Alessandro Ranellucci 2015-01-08 22:47:43 +01:00
commit 9ec7b43ca1
53 changed files with 1535 additions and 838 deletions

View file

@ -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");

View file

@ -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);

View file

@ -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

View file

@ -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 &center);
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;
};
}

View file

@ -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;

View file

@ -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);

View file

@ -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();

View file

@ -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));

View file

@ -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) {

View file

@ -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");

View file

@ -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;

View file

@ -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());

View file

@ -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;

View file

@ -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:

View file

@ -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();
}

View file

@ -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;

View file

@ -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
{

View file

@ -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);

View file

@ -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
{

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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
{

View file

@ -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.";

View file

@ -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;

View file

@ -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());
}
}

View file

@ -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;
};
}

View file

@ -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)
{

View file

@ -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);
};

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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__

View file

@ -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__

View file

@ -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); %};
};

View file

@ -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();