mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-12 09:17:52 -06:00
Initial commit of the new Pressure Equalizer, the EdgeGrid
signed distance field structure. The EdgeGrid is used to avoid placing the seams on overhangs.
This commit is contained in:
parent
73cbb4b5dc
commit
f518e0675c
18 changed files with 2417 additions and 71 deletions
|
@ -1,9 +1,21 @@
|
|||
#include "GCode.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <math.h>
|
||||
|
||||
#include "SVG.hpp"
|
||||
|
||||
#if 0
|
||||
// Enable debugging and asserts, even in the release build.
|
||||
#define DEBUG
|
||||
#define _DEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
AvoidCrossingPerimeters::AvoidCrossingPerimeters()
|
||||
|
@ -198,12 +210,22 @@ Wipe::wipe(GCode &gcodegen, bool toolchange)
|
|||
#define EXTRUDER_CONFIG(OPT) this->config.OPT.get_at(this->writer.extruder()->id)
|
||||
|
||||
GCode::GCode()
|
||||
: placeholder_parser(NULL), enable_loop_clipping(true), enable_cooling_markers(false), layer_count(0),
|
||||
: placeholder_parser(NULL), enable_loop_clipping(true),
|
||||
enable_cooling_markers(false), enable_extrusion_role_markers(false),
|
||||
layer_count(0),
|
||||
layer_index(-1), layer(NULL), first_layer(false), elapsed_time(0.0), volumetric_speed(0),
|
||||
_last_pos_defined(false)
|
||||
_last_pos_defined(false),
|
||||
_lower_layer_edge_grid(NULL),
|
||||
_last_extrusion_role(erNone)
|
||||
{
|
||||
}
|
||||
|
||||
GCode::~GCode()
|
||||
{
|
||||
delete _lower_layer_edge_grid;
|
||||
_lower_layer_edge_grid = NULL;
|
||||
}
|
||||
|
||||
const Point&
|
||||
GCode::last_pos() const
|
||||
{
|
||||
|
@ -279,6 +301,8 @@ GCode::change_layer(const Layer &layer)
|
|||
this->layer = &layer;
|
||||
this->layer_index++;
|
||||
this->first_layer = (layer.id() == 0);
|
||||
delete this->_lower_layer_edge_grid;
|
||||
this->_lower_layer_edge_grid = NULL;
|
||||
|
||||
// avoid computing islands and overhangs if they're not needed
|
||||
if (this->config.avoid_crossing_perimeters) {
|
||||
|
@ -308,12 +332,230 @@ GCode::change_layer(const Layer &layer)
|
|||
return gcode;
|
||||
}
|
||||
|
||||
static inline const char* ExtrusionRole2String(const ExtrusionRole role)
|
||||
{
|
||||
switch (role) {
|
||||
case erNone: return "erNone";
|
||||
case erPerimeter: return "erPerimeter";
|
||||
case erExternalPerimeter: return "erExternalPerimeter";
|
||||
case erOverhangPerimeter: return "erOverhangPerimeter";
|
||||
case erInternalInfill: return "erInternalInfill";
|
||||
case erSolidInfill: return "erSolidInfill";
|
||||
case erTopSolidInfill: return "erTopSolidInfill";
|
||||
case erBridgeInfill: return "erBridgeInfill";
|
||||
case erGapFill: return "erGapFill";
|
||||
case erSkirt: return "erSkirt";
|
||||
case erSupportMaterial: return "erSupportMaterial";
|
||||
case erSupportMaterialInterface: return "erSupportMaterialInterface";
|
||||
default: return "erInvalid";
|
||||
};
|
||||
}
|
||||
|
||||
static inline const char* ExtrusionLoopRole2String(const ExtrusionLoopRole role)
|
||||
{
|
||||
switch (role) {
|
||||
case elrDefault: return "elrDefault";
|
||||
case elrContourInternalPerimeter: return "elrContourInternalPerimeter";
|
||||
case elrSkirt: return "elrSkirt";
|
||||
default: return "elrInvalid";
|
||||
}
|
||||
};
|
||||
|
||||
// Return a value in <0, 1> of a cubic B-spline kernel centered around zero.
|
||||
// The B-spline is re-scaled so it has value 1 at zero.
|
||||
static inline float bspline_kernel(float x)
|
||||
{
|
||||
x = std::abs(x);
|
||||
if (x < 1.f) {
|
||||
return 1.f - (3. / 2.) * x * x + (3.f / 4.f) * x * x * x;
|
||||
}
|
||||
else if (x < 2.f) {
|
||||
x -= 1.f;
|
||||
float x2 = x * x;
|
||||
float x3 = x2 * x;
|
||||
return (1.f / 4.f) - (3.f / 4.f) * x + (3.f / 4.f) * x2 - (1.f / 4.f) * x3;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static float extrudate_overlap_penalty(float nozzle_r, float weight_zero, float overlap_distance)
|
||||
{
|
||||
// The extrudate is not fully supported by the lower layer. Fit a polynomial penalty curve.
|
||||
// Solved by sympy package:
|
||||
/*
|
||||
from sympy import *
|
||||
(x,a,b,c,d,r,z)=symbols('x a b c d r z')
|
||||
p = a + b*x + c*x*x + d*x*x*x
|
||||
p2 = p.subs(solve([p.subs(x, -r), p.diff(x).subs(x, -r), p.diff(x,x).subs(x, -r), p.subs(x, 0)-z], [a, b, c, d]))
|
||||
from sympy.plotting import plot
|
||||
plot(p2.subs(r,0.2).subs(z,1.), (x, -1, 3), adaptive=False, nb_of_points=400)
|
||||
*/
|
||||
if (overlap_distance < - nozzle_r) {
|
||||
// The extrudate is fully supported by the lower layer. This is the ideal case, therefore zero penalty.
|
||||
return 0.f;
|
||||
} else {
|
||||
float x = overlap_distance / nozzle_r;
|
||||
float x2 = x * x;
|
||||
float x3 = x2 * x;
|
||||
return weight_zero * (1.f + 3.f * x + 3.f * x2 + x3);
|
||||
}
|
||||
}
|
||||
|
||||
static Points::iterator project_point_to_polygon_and_insert(Polygon &polygon, const Point &pt, double eps)
|
||||
{
|
||||
assert(polygon.points.size() >= 2);
|
||||
if (polygon.points.size() <= 1)
|
||||
if (polygon.points.size() == 1)
|
||||
return polygon.points.begin();
|
||||
|
||||
Point pt_min;
|
||||
double d_min = std::numeric_limits<double>::max();
|
||||
size_t i_min = size_t(-1);
|
||||
|
||||
for (size_t i = 0; i < polygon.points.size(); ++ i) {
|
||||
size_t j = i + 1;
|
||||
if (j == polygon.points.size())
|
||||
j = 0;
|
||||
const Point &p1 = polygon.points[i];
|
||||
const Point &p2 = polygon.points[j];
|
||||
const Slic3r::Point v_seg = p1.vector_to(p2);
|
||||
const Slic3r::Point v_pt = p1.vector_to(pt);
|
||||
const int64_t l2_seg = int64_t(v_seg.x) * int64_t(v_seg.x) + int64_t(v_seg.y) * int64_t(v_seg.y);
|
||||
int64_t t_pt = int64_t(v_seg.x) * int64_t(v_pt.x) + int64_t(v_seg.y) * int64_t(v_pt.y);
|
||||
if (t_pt < 0) {
|
||||
// Closest to p1.
|
||||
double dabs = sqrt(int64_t(v_pt.x) * int64_t(v_pt.x) + int64_t(v_pt.y) * int64_t(v_pt.y));
|
||||
if (dabs < d_min) {
|
||||
d_min = dabs;
|
||||
i_min = i;
|
||||
pt_min = p1;
|
||||
}
|
||||
}
|
||||
else if (t_pt > l2_seg) {
|
||||
// Closest to p2. Then p2 is the starting point of another segment, which shall be discovered in the next step.
|
||||
continue;
|
||||
} else {
|
||||
// Closest to the segment.
|
||||
assert(t_pt >= 0 && t_pt <= l2_seg);
|
||||
int64_t d_seg = int64_t(v_seg.y) * int64_t(v_pt.x) - int64_t(v_seg.x) * int64_t(v_pt.y);
|
||||
double d = double(d_seg) / sqrt(double(l2_seg));
|
||||
double dabs = std::abs(d);
|
||||
if (dabs < d_min) {
|
||||
d_min = dabs;
|
||||
i_min = i;
|
||||
// Evaluate the foot point.
|
||||
pt_min = p1;
|
||||
double linv = double(d_seg) / double(l2_seg);
|
||||
pt_min.x = pt.x - coord_t(floor(double(v_seg.y) * linv + 0.5));
|
||||
pt_min.y = pt.y + coord_t(floor(double(v_seg.x) * linv + 0.5));
|
||||
assert(Line(p1, p2).distance_to(pt_min) < scale_(1e-5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(i_min != size_t(-1));
|
||||
if (pt_min.distance_to(polygon.points[i_min]) > eps) {
|
||||
// Insert a new point on the segment i_min, i_min+1.
|
||||
return polygon.points.insert(polygon.points.begin() + (i_min + 1), pt_min);
|
||||
}
|
||||
return polygon.points.begin() + i_min;
|
||||
}
|
||||
|
||||
std::vector<float> polygon_parameter_by_length(const Polygon &polygon)
|
||||
{
|
||||
// Parametrize the polygon by its length.
|
||||
std::vector<float> lengths(polygon.points.size()+1, 0.);
|
||||
for (size_t i = 1; i < polygon.points.size(); ++ i)
|
||||
lengths[i] = lengths[i-1] + polygon.points[i].distance_to(polygon.points[i-1]);
|
||||
lengths.back() = lengths[lengths.size()-2] + polygon.points.front().distance_to(polygon.points.back());
|
||||
return lengths;
|
||||
}
|
||||
|
||||
std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std::vector<float> &lengths, float min_arm_length)
|
||||
{
|
||||
assert(polygon.points.size() + 1 == lengths.size());
|
||||
if (min_arm_length > 0.25f * lengths.back())
|
||||
min_arm_length = 0.25f * lengths.back();
|
||||
|
||||
// Find the initial prev / next point span.
|
||||
size_t idx_prev = polygon.points.size();
|
||||
size_t idx_curr = 0;
|
||||
size_t idx_next = 1;
|
||||
while (idx_prev > idx_curr && lengths.back() - lengths[idx_prev] < min_arm_length)
|
||||
-- idx_prev;
|
||||
while (idx_next < idx_prev && lengths[idx_next] < min_arm_length)
|
||||
++ idx_next;
|
||||
|
||||
std::vector<float> angles(polygon.points.size(), 0.f);
|
||||
for (; idx_curr < polygon.points.size(); ++ idx_curr) {
|
||||
// Move idx_prev up until the distance between idx_prev and idx_curr is lower than min_arm_length.
|
||||
if (idx_prev >= idx_curr) {
|
||||
while (idx_prev < polygon.points.size() && lengths.back() - lengths[idx_prev] + lengths[idx_curr] > min_arm_length)
|
||||
++ idx_prev;
|
||||
if (idx_prev == polygon.points.size())
|
||||
idx_prev = 0;
|
||||
}
|
||||
while (idx_prev < idx_curr && lengths[idx_curr] - lengths[idx_prev] > min_arm_length)
|
||||
++ idx_prev;
|
||||
// Move idx_prev one step back.
|
||||
if (idx_prev == 0)
|
||||
idx_prev = polygon.points.size() - 1;
|
||||
else
|
||||
-- idx_prev;
|
||||
// Move idx_next up until the distance between idx_curr and idx_next is greater than min_arm_length.
|
||||
if (idx_curr <= idx_next) {
|
||||
while (idx_next < polygon.points.size() && lengths[idx_next] - lengths[idx_curr] < min_arm_length)
|
||||
++ idx_next;
|
||||
if (idx_next == polygon.points.size())
|
||||
idx_next = 0;
|
||||
}
|
||||
while (idx_next < idx_curr && lengths.back() - lengths[idx_curr] + lengths[idx_next] < min_arm_length)
|
||||
++ idx_next;
|
||||
// Calculate angle between idx_prev, idx_curr, idx_next.
|
||||
const Point &p0 = polygon.points[idx_prev];
|
||||
const Point &p1 = polygon.points[idx_curr];
|
||||
const Point &p2 = polygon.points[idx_next];
|
||||
const Point v1 = p0.vector_to(p1);
|
||||
const Point v2 = p1.vector_to(p2);
|
||||
int64_t dot = int64_t(v1.x)*int64_t(v2.x) + int64_t(v1.y)*int64_t(v2.y);
|
||||
int64_t cross = int64_t(v1.x)*int64_t(v2.y) - int64_t(v1.y)*int64_t(v2.x);
|
||||
float angle = float(atan2(double(cross), double(dot)));
|
||||
angles[idx_curr] = angle;
|
||||
}
|
||||
|
||||
return angles;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
|
||||
{
|
||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||
// next copies (if any) would not detect the correct orientation
|
||||
|
||||
|
||||
if (this->layer->lower_layer != NULL) {
|
||||
if (this->_lower_layer_edge_grid == NULL) {
|
||||
// Create the distance field for a layer below.
|
||||
const coord_t distance_field_resolution = scale_(1.f);
|
||||
this->_lower_layer_edge_grid = new EdgeGrid::Grid();
|
||||
this->_lower_layer_edge_grid->create(this->layer->lower_layer->slices, distance_field_resolution);
|
||||
this->_lower_layer_edge_grid->calculate_sdf();
|
||||
#if 0
|
||||
{
|
||||
static int iRun = 0;
|
||||
char path[2048];
|
||||
sprintf(path, "out\\GCode_extrude_loop_edge_grid-%d.png", iRun++);
|
||||
BoundingBox bbox = this->_lower_layer_edge_grid->bbox();
|
||||
bbox.min.x -= scale_(5.f);
|
||||
bbox.min.y -= scale_(5.f);
|
||||
bbox.max.x += scale_(5.f);
|
||||
bbox.max.y += scale_(5.f);
|
||||
EdgeGrid::save_png(*this->_lower_layer_edge_grid, bbox, scale_(0.1f), path);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// extrude all loops ccw
|
||||
bool was_clockwise = loop.make_counter_clockwise();
|
||||
|
||||
|
@ -326,68 +568,126 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
|
|||
if (this->config.spiral_vase) {
|
||||
loop.split_at(last_pos);
|
||||
} else if (seam_position == spNearest || seam_position == spAligned) {
|
||||
const Polygon polygon = loop.polygon();
|
||||
|
||||
// simplify polygon in order to skip false positives in concave/convex detection
|
||||
// (loop is always ccw as polygon.simplify() only works on ccw polygons)
|
||||
Polygons simplified = polygon.simplify(scale_(EXTRUDER_CONFIG(nozzle_diameter))/2);
|
||||
|
||||
// restore original winding order so that concave and convex detection always happens
|
||||
// on the right/outer side of the polygon
|
||||
if (was_clockwise) {
|
||||
for (Polygons::iterator p = simplified.begin(); p != simplified.end(); ++p)
|
||||
p->reverse();
|
||||
}
|
||||
|
||||
// concave vertices have priority
|
||||
Points candidates;
|
||||
for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) {
|
||||
Points concave = p->concave_points(PI*4/3);
|
||||
candidates.insert(candidates.end(), concave.begin(), concave.end());
|
||||
}
|
||||
|
||||
// if no concave points were found, look for convex vertices
|
||||
if (candidates.empty()) {
|
||||
for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) {
|
||||
Points convex = p->convex_points(PI*2/3);
|
||||
candidates.insert(candidates.end(), convex.begin(), convex.end());
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve the last start position for this object
|
||||
if (this->layer != NULL && this->_seam_position.count(this->layer->object()) > 0) {
|
||||
Polygon polygon = loop.polygon();
|
||||
const coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
||||
const coord_t nozzle_r = scale_(0.5*nozzle_dmr);
|
||||
|
||||
// Retrieve the last start position for this object.
|
||||
float last_pos_weight = 1.f;
|
||||
if (seam_position == spAligned && this->layer != NULL && this->_seam_position.count(this->layer->object()) > 0) {
|
||||
last_pos = this->_seam_position[this->layer->object()];
|
||||
last_pos_weight = 5.f;
|
||||
}
|
||||
|
||||
Point point;
|
||||
if (seam_position == spNearest) {
|
||||
if (candidates.empty()) candidates = polygon.points;
|
||||
last_pos.nearest_point(candidates, &point);
|
||||
|
||||
// On 32-bit Linux, Clipper will change some point coordinates by 1 unit
|
||||
// while performing simplify_polygons(), thus split_at_vertex() won't
|
||||
// find them anymore.
|
||||
if (!loop.split_at_vertex(point)) loop.split_at(point);
|
||||
} else if (!candidates.empty()) {
|
||||
Points non_overhang;
|
||||
for (Points::const_iterator p = candidates.begin(); p != candidates.end(); ++p) {
|
||||
if (!loop.has_overhang_point(*p))
|
||||
non_overhang.push_back(*p);
|
||||
|
||||
// Insert a projection of last_pos into the polygon.
|
||||
size_t last_pos_proj_idx;
|
||||
{
|
||||
auto it = project_point_to_polygon_and_insert(polygon, last_pos, 0.1 * nozzle_r);
|
||||
last_pos_proj_idx = it - polygon.points.begin();
|
||||
}
|
||||
Point last_pos_proj = polygon.points[last_pos_proj_idx];
|
||||
// Parametrize the polygon by its length.
|
||||
std::vector<float> lengths = polygon_parameter_by_length(polygon);
|
||||
|
||||
// For each polygon point, store a penalty.
|
||||
// First calculate the angles, store them as penalties. The angles are caluculated over a minimum arm length of nozzle_r.
|
||||
std::vector<float> penalties = polygon_angles_at_vertices(polygon, lengths, nozzle_r);
|
||||
// No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces.
|
||||
const float penaltyConvexVertex = 1.f;
|
||||
const float penaltyFlatSurface = 5.f;
|
||||
const float penaltySeam = 1.3f;
|
||||
const float penaltyOverhangHalf = 10.f;
|
||||
// Penalty for visible seams.
|
||||
for (size_t i = 0; i < polygon.points.size(); ++ i) {
|
||||
float ccwAngle = penalties[i];
|
||||
if (was_clockwise)
|
||||
ccwAngle = - ccwAngle;
|
||||
float penalty = 0;
|
||||
// if (ccwAngle <- float(PI/3.))
|
||||
if (ccwAngle <- float(0.6 * PI))
|
||||
// Sharp reflex vertex. We love that, it hides the seam perfectly.
|
||||
penalty = 0.f;
|
||||
// else if (ccwAngle > float(PI/3.))
|
||||
else if (ccwAngle > float(0.6 * PI))
|
||||
// Seams on sharp convex vertices are more visible than on reflex vertices.
|
||||
penalty = penaltyConvexVertex;
|
||||
else if (ccwAngle < 0.f) {
|
||||
// Interpolate penalty between maximum and zero.
|
||||
penalty = penaltyFlatSurface * bspline_kernel(ccwAngle * (PI * 2. / 3.));
|
||||
} else {
|
||||
assert(ccwAngle >= 0.f);
|
||||
// Interpolate penalty between maximum and the penalty for a convex vertex.
|
||||
penalty = penaltyConvexVertex + (penaltyFlatSurface - penaltyConvexVertex) * bspline_kernel(ccwAngle * (PI * 2. / 3.));
|
||||
}
|
||||
|
||||
if (!non_overhang.empty())
|
||||
candidates = non_overhang;
|
||||
|
||||
last_pos.nearest_point(candidates, &point);
|
||||
if (!loop.split_at_vertex(point)) loop.split_at(point); // see note above
|
||||
} else {
|
||||
point = last_pos.projection_onto(polygon);
|
||||
loop.split_at(point);
|
||||
// Give a negative penalty for points close to the last point or the prefered seam location.
|
||||
//float dist_to_last_pos_proj = last_pos_proj.distance_to(polygon.points[i]);
|
||||
float dist_to_last_pos_proj = (i < last_pos_proj_idx) ?
|
||||
std::min(lengths[last_pos_proj_idx] - lengths[i], lengths.back() - lengths[last_pos_proj_idx] + lengths[i]) :
|
||||
std::min(lengths[i] - lengths[last_pos_proj_idx], lengths.back() - lengths[i] + lengths[last_pos_proj_idx]);
|
||||
float dist_max = 0.1f * lengths.back(); // 5.f * nozzle_dmr
|
||||
penalty -= last_pos_weight * bspline_kernel(dist_to_last_pos_proj / dist_max);
|
||||
penalties[i] = std::max(0.f, penalty);
|
||||
}
|
||||
if (this->layer != NULL)
|
||||
this->_seam_position[this->layer->object()] = point;
|
||||
|
||||
// Penalty for overhangs.
|
||||
if (this->_lower_layer_edge_grid) {
|
||||
// Use the edge grid distance field structure over the lower layer to calculate overhangs.
|
||||
coord_t nozzle_r = scale_(0.5*nozzle_dmr);
|
||||
coord_t search_r = scale_(0.8*nozzle_dmr);
|
||||
for (size_t i = 0; i < polygon.points.size(); ++ i) {
|
||||
const Point &p = polygon.points[i];
|
||||
coordf_t dist;
|
||||
// Signed distance is positive outside the object, negative inside the object.
|
||||
// The point is considered at an overhang, if it is more than nozzle radius
|
||||
// outside of the lower layer contour.
|
||||
bool found = this->_lower_layer_edge_grid->signed_distance(p, search_r, dist);
|
||||
// If the approximate Signed Distance Field was initialized over this->_lower_layer_edge_grid,
|
||||
// then the signed distnace shall always be known.
|
||||
assert(found);
|
||||
penalties[i] += extrudate_overlap_penalty(nozzle_r, penaltyOverhangHalf, dist);
|
||||
}
|
||||
}
|
||||
|
||||
// Find a point with a minimum penalty.
|
||||
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
||||
|
||||
// Export the contour into a SVG file.
|
||||
#if 0
|
||||
{
|
||||
static int iRun = 0;
|
||||
char path[2048];
|
||||
sprintf(path, "out\\GCode_extrude_loop-%d.svg", iRun ++);
|
||||
SVG svg(path);
|
||||
if (this->layer->lower_layer != NULL)
|
||||
svg.draw(this->layer->lower_layer->slices.expolygons);
|
||||
for (size_t i = 0; i < loop.paths.size(); ++ i)
|
||||
svg.draw(loop.paths[i].as_polyline(), "red");
|
||||
Polylines polylines;
|
||||
for (size_t i = 0; i < loop.paths.size(); ++ i)
|
||||
polylines.push_back(loop.paths[i].as_polyline());
|
||||
Slic3r::Polygons polygons;
|
||||
coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
||||
coord_t delta = scale_(0.5*nozzle_dmr);
|
||||
Slic3r::offset(polylines, &polygons, delta);
|
||||
// for (size_t i = 0; i < polygons.size(); ++ i) svg.draw((Polyline)polygons[i], "blue");
|
||||
svg.draw(last_pos, "green", 3);
|
||||
svg.draw(polygon.points[idx_min], "yellow", 3);
|
||||
svg.Close();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Split the loop at the point with a minium penalty.
|
||||
if (!loop.split_at_vertex(polygon.points[idx_min]))
|
||||
// The point is not in the original loop. Insert it.
|
||||
loop.split_at(polygon.points[idx_min]);
|
||||
|
||||
} else if (seam_position == spRandom) {
|
||||
if (loop.role == elrContourInternalPerimeter) {
|
||||
// This loop does not contain any other loop. Set a random position.
|
||||
// The other loops will get a seam close to the random point chosen
|
||||
// on the inner most contour.
|
||||
//FIXME This works correctly for inner contours first only.
|
||||
//FIXME Better parametrize the loop by its length.
|
||||
Polygon polygon = loop.polygon();
|
||||
Point centroid = polygon.centroid();
|
||||
last_pos = Point(polygon.bounding_box().max.x, centroid.y);
|
||||
|
@ -416,6 +716,8 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
|
|||
// extrude along the path
|
||||
std::string gcode;
|
||||
for (ExtrusionPaths::const_iterator path = paths.begin(); path != paths.end(); ++path)
|
||||
// description += ExtrusionLoopRole2String(loop.role);
|
||||
// description += ExtrusionRole2String(path->role);
|
||||
gcode += this->_extrude(*path, description, speed);
|
||||
|
||||
// reset acceleration
|
||||
|
@ -477,6 +779,7 @@ GCode::extrude(const ExtrusionEntity &entity, std::string description, double sp
|
|||
std::string
|
||||
GCode::extrude(const ExtrusionPath &path, std::string description, double speed)
|
||||
{
|
||||
// description += ExtrusionRole2String(path.role);
|
||||
std::string gcode = this->_extrude(path, description, speed);
|
||||
|
||||
// reset acceleration
|
||||
|
@ -561,6 +864,14 @@ GCode::_extrude(ExtrusionPath path, std::string description, double speed)
|
|||
double F = speed * 60; // convert mm/sec to mm/min
|
||||
|
||||
// extrude arc or line
|
||||
if (this->enable_extrusion_role_markers) {
|
||||
if (path.role != this->_last_extrusion_role) {
|
||||
this->_last_extrusion_role = path.role;
|
||||
char buf[32];
|
||||
sprintf(buf, ";_EXTRUSION_ROLE:%d\n", int(path.role));
|
||||
gcode += buf;
|
||||
}
|
||||
}
|
||||
if (path.is_bridge() && this->enable_cooling_markers)
|
||||
gcode += ";_BRIDGE_FAN_START\n";
|
||||
gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : "");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue