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
|
@ -10,6 +10,7 @@ has '_spiral_vase' => (is => 'rw');
|
||||||
has '_vibration_limit' => (is => 'rw');
|
has '_vibration_limit' => (is => 'rw');
|
||||||
has '_arc_fitting' => (is => 'rw');
|
has '_arc_fitting' => (is => 'rw');
|
||||||
has '_pressure_regulator' => (is => 'rw');
|
has '_pressure_regulator' => (is => 'rw');
|
||||||
|
has '_pressure_equalizer' => (is => 'rw');
|
||||||
has '_skirt_done' => (is => 'rw', default => sub { {} }); # print_z => 1
|
has '_skirt_done' => (is => 'rw', default => sub { {} }); # print_z => 1
|
||||||
has '_brim_done' => (is => 'rw');
|
has '_brim_done' => (is => 'rw');
|
||||||
has '_second_layer_things_done' => (is => 'rw');
|
has '_second_layer_things_done' => (is => 'rw');
|
||||||
|
@ -114,6 +115,11 @@ sub BUILD {
|
||||||
|
|
||||||
$self->_pressure_regulator(Slic3r::GCode::PressureRegulator->new(config => $self->config))
|
$self->_pressure_regulator(Slic3r::GCode::PressureRegulator->new(config => $self->config))
|
||||||
if $self->config->pressure_advance > 0;
|
if $self->config->pressure_advance > 0;
|
||||||
|
|
||||||
|
$self->_pressure_equalizer(Slic3r::GCode::PressureEqualizer->new($self->config))
|
||||||
|
if defined($ENV{"SLIC3R_PRESSURE_EQUALIZER"}) && $ENV{'SLIC3R_PRESSURE_EQUALIZER'} == 1;
|
||||||
|
|
||||||
|
$self->_gcodegen->set_enable_extrusion_role_markers(defined $self->_pressure_equalizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Export a G-code for the complete print.
|
# Export a G-code for the complete print.
|
||||||
|
@ -560,7 +566,7 @@ sub process_layer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} # for regions
|
||||||
|
|
||||||
# tweak extruder ordering to save toolchanges
|
# tweak extruder ordering to save toolchanges
|
||||||
my @extruders = sort keys %by_extruder;
|
my @extruders = sort keys %by_extruder;
|
||||||
|
@ -586,7 +592,7 @@ sub process_layer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} # for object copies
|
||||||
|
|
||||||
# apply spiral vase post-processing if this layer contains suitable geometry
|
# apply spiral vase post-processing if this layer contains suitable geometry
|
||||||
# (we must feed all the G-code into the post-processor, including the first
|
# (we must feed all the G-code into the post-processor, including the first
|
||||||
|
@ -658,7 +664,13 @@ sub filter {
|
||||||
# this depends on actual speeds
|
# this depends on actual speeds
|
||||||
$gcode = $self->_pressure_regulator->process($gcode, $flush)
|
$gcode = $self->_pressure_regulator->process($gcode, $flush)
|
||||||
if defined $self->_pressure_regulator;
|
if defined $self->_pressure_regulator;
|
||||||
|
|
||||||
|
# apply pressure equalization if enabled;
|
||||||
|
# print "G-code before filter:\n", $gcode;
|
||||||
|
$gcode = $self->_pressure_equalizer->process($gcode, $flush)
|
||||||
|
if defined $self->_pressure_equalizer;
|
||||||
|
# print "G-code after filter:\n", $gcode;
|
||||||
|
|
||||||
# apply arc fitting if enabled;
|
# apply arc fitting if enabled;
|
||||||
# this does not depend on speeds but changes G1 XY commands into G2/G2 IJ
|
# this does not depend on speeds but changes G1 XY commands into G2/G2 IJ
|
||||||
$gcode = $self->_arc_fitting->process($gcode)
|
$gcode = $self->_arc_fitting->process($gcode)
|
||||||
|
|
|
@ -11,30 +11,44 @@
|
||||||
"name": "Run",
|
"name": "Run",
|
||||||
"working_dir": "$project_path",
|
"working_dir": "$project_path",
|
||||||
"file_regex": " at (.*?) line ([0-9]*)",
|
"file_regex": " at (.*?) line ([0-9]*)",
|
||||||
"shell_cmd": "perl slic3r.pl --gui \"..\\Slic3r-tests\\gap fill torture 20 -rt.stl\""
|
"shell_cmd": "chdir & perl slic3r.pl --DataDir \"C:\\Users\\Public\\Documents\\Prusa3D\\Slic3r settings MK2\" --gui \"..\\Slic3r-tests\\gap fill torture 20 -rt.stl\""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "full",
|
"name": "full",
|
||||||
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
|
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
|
||||||
"shell_cmd": "perl Build.pl"
|
"shell_cmd": "chdir & perl Build.pl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "xs",
|
"name": "xs",
|
||||||
"working_dir": "$project_path/xs",
|
"working_dir": "$project_path/xs",
|
||||||
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
|
// for Visual Studio:
|
||||||
"shell_cmd": "perl Build install"
|
"file_regex": "^(..[^:]*)\\(([0-9]+)\\)(.*)$",
|
||||||
|
// For GCC:
|
||||||
|
// "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
|
||||||
|
"shell_cmd": "chdir & perl Build install",
|
||||||
|
"env": {
|
||||||
|
// "PATH": "C:\\Program Files (x86)\\MSBuild\\12.0\\bin\\amd64;C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\BIN\\amd64;C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\Common7\\IDE;C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\Common7\\Tools;%PATH%;c:\\wperl64d\\site\\bin;c:\\wperl64d\\bin",
|
||||||
|
// "PERL_CPANM_HOME": "c:\\wperl64d\\cpanm",
|
||||||
|
// "WXDIR": "D:\\src-perl\\wxWidgets-3.0.3-beta1",
|
||||||
|
// "BOOST_DIR": "D:\\src-perl\\boost_1_61_0",
|
||||||
|
// "BOOST_INCLUDEDIR": "D:\\src-perl\\boost_1_61_0",
|
||||||
|
// "BOOST_LIBRARYDIR": "D:\\src-perl\\boost_1_61_0\\stage\\x64\\lib",
|
||||||
|
// "SLIC3R_STATIC": "1"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "xs & run",
|
"name": "xs & run",
|
||||||
"working_dir": "$project_path/xs",
|
"working_dir": "$project_path/xs",
|
||||||
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
|
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
|
||||||
"shell_cmd": "perl Build install & cd .. & perl slic3r.pl --gui \"..\\Slic3r-tests\\star3-big2.stl\""
|
"shell_cmd": "chdir & perl Build install & cd .. & perl slic3r.pl --gui \"..\\Slic3r-tests\\star3-big2.stl\""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"folders":
|
"folders":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"path": "."
|
"path": ".",
|
||||||
|
// "folder_exclude_patterns": [".svn", "._d", ".metadata", ".settings"],
|
||||||
|
"file_exclude_patterns": ["XS.c"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
16
xs/Build.PL
16
xs/Build.PL
|
@ -25,8 +25,22 @@ if ($mswin) {
|
||||||
push @cflags, qw(-DNOMINMAX -D_USE_MATH_DEFINES);
|
push @cflags, qw(-DNOMINMAX -D_USE_MATH_DEFINES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my @early_includes = ();
|
||||||
my @INC = qw(-Isrc/libslic3r);
|
my @INC = qw(-Isrc/libslic3r);
|
||||||
my @LIBS = $cpp_guess->is_msvc ? qw(-LIBPATH:src/libslic3r) : qw(-Lsrc/libslic3r);
|
my @LIBS = $cpp_guess->is_msvc ? qw(-LIBPATH:src/libslic3r) : qw(-Lsrc/libslic3r);
|
||||||
|
|
||||||
|
#if ($ENV{SLIC3R_GUI})
|
||||||
|
{
|
||||||
|
print "Slic3r will be built with GUI support\n";
|
||||||
|
require Alien::wxWidgets;
|
||||||
|
Alien::wxWidgets->load;
|
||||||
|
push @INC, Alien::wxWidgets->include_path;
|
||||||
|
push @cflags, qw(-DSLIC3R_GUI -DUNICODE), Alien::wxWidgets->defines, Alien::wxWidgets->c_flags;
|
||||||
|
my $alienwx_libraries = Alien::wxWidgets->libraries(qw(gl html));
|
||||||
|
$alienwx_libraries =~ s/-L/-LIBPATH:/g if ($cpp_guess->is_msvc);
|
||||||
|
push @ldflags, Alien::wxWidgets->link_flags, $alienwx_libraries;
|
||||||
|
# push @early_includes, qw(slic3r/GUI/wxinit.h);
|
||||||
|
}
|
||||||
|
|
||||||
# search for Boost in a number of places
|
# search for Boost in a number of places
|
||||||
my @boost_include = ();
|
my @boost_include = ();
|
||||||
|
@ -195,7 +209,7 @@ my $build = Module::Build::WithXSpp->new(
|
||||||
ostream
|
ostream
|
||||||
sstream
|
sstream
|
||||||
libslic3r/GCodeSender.hpp
|
libslic3r/GCodeSender.hpp
|
||||||
)]
|
), @early_includes]
|
||||||
);
|
);
|
||||||
|
|
||||||
$build->create_build_script;
|
$build->create_build_script;
|
||||||
|
|
|
@ -52,6 +52,8 @@ src/libslic3r/GCodeSender.cpp
|
||||||
src/libslic3r/GCodeSender.hpp
|
src/libslic3r/GCodeSender.hpp
|
||||||
src/libslic3r/GCodeWriter.cpp
|
src/libslic3r/GCodeWriter.cpp
|
||||||
src/libslic3r/GCodeWriter.hpp
|
src/libslic3r/GCodeWriter.hpp
|
||||||
|
src/libslic3r/GCode/PressureEqualizer.cpp
|
||||||
|
src/libslic3r/GCode/PressureEqualizer.hpp
|
||||||
src/libslic3r/Geometry.cpp
|
src/libslic3r/Geometry.cpp
|
||||||
src/libslic3r/Geometry.hpp
|
src/libslic3r/Geometry.hpp
|
||||||
src/libslic3r/Layer.cpp
|
src/libslic3r/Layer.cpp
|
||||||
|
@ -151,6 +153,7 @@ xsp/Flow.xsp
|
||||||
xsp/GCode.xsp
|
xsp/GCode.xsp
|
||||||
xsp/GCodeSender.xsp
|
xsp/GCodeSender.xsp
|
||||||
xsp/GCodeWriter.xsp
|
xsp/GCodeWriter.xsp
|
||||||
|
xsp/GCodePressureEqualizer.xsp
|
||||||
xsp/Geometry.xsp
|
xsp/Geometry.xsp
|
||||||
xsp/GUI.xsp
|
xsp/GUI.xsp
|
||||||
xsp/GUI_3DScene.xsp
|
xsp/GUI_3DScene.xsp
|
||||||
|
|
|
@ -4,6 +4,19 @@ use strict;
|
||||||
|
|
||||||
our $VERSION = '0.01';
|
our $VERSION = '0.01';
|
||||||
|
|
||||||
|
# We have to load these modules in order to have Wx.pm find the correct paths
|
||||||
|
# for wxWidgets dlls on MSW.
|
||||||
|
# We avoid loading these on OS X because Wx::Load() initializes a Wx App
|
||||||
|
# automatically and it steals focus even when we're not running Slic3r in GUI mode.
|
||||||
|
# TODO: only load these when compiling with GUI support
|
||||||
|
BEGIN {
|
||||||
|
if ($^O eq 'MSWin32') {
|
||||||
|
eval "use Wx";
|
||||||
|
# eval "use Wx::Html";
|
||||||
|
eval "use Wx::Print"; # because of some Wx bug, thread creation fails if we don't have this (looks like Wx::Printout is hard-coded in some thread cleanup code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
use Carp qw();
|
use Carp qw();
|
||||||
use XSLoader;
|
use XSLoader;
|
||||||
XSLoader::load(__PACKAGE__, $VERSION);
|
XSLoader::load(__PACKAGE__, $VERSION);
|
||||||
|
|
1025
xs/src/libslic3r/EdgeGrid.cpp
Normal file
1025
xs/src/libslic3r/EdgeGrid.cpp
Normal file
File diff suppressed because it is too large
Load diff
81
xs/src/libslic3r/EdgeGrid.hpp
Normal file
81
xs/src/libslic3r/EdgeGrid.hpp
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
#ifndef slic3r_EdgeGrid_hpp_
|
||||||
|
#define slic3r_EdgeGrid_hpp_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "Point.hpp"
|
||||||
|
#include "BoundingBox.hpp"
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
#include "ExPolygonCollection.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace EdgeGrid {
|
||||||
|
|
||||||
|
struct Grid
|
||||||
|
{
|
||||||
|
Grid();
|
||||||
|
~Grid();
|
||||||
|
|
||||||
|
void create(const Polygons &polygons, coord_t resolution);
|
||||||
|
void create(const ExPolygon &expoly, coord_t resolution);
|
||||||
|
void create(const ExPolygons &expolygons, coord_t resolution);
|
||||||
|
void create(const ExPolygonCollection &expolygons, coord_t resolution);
|
||||||
|
|
||||||
|
// Fill in a rough m_signed_distance_field from the edge grid.
|
||||||
|
// The rough SDF is used by signed_distance() for distances outside of the search_radius.
|
||||||
|
void calculate_sdf();
|
||||||
|
|
||||||
|
// Return an estimate of the signed distance based on m_signed_distance_field grid.
|
||||||
|
float signed_distance_bilinear(const Point &pt) const;
|
||||||
|
|
||||||
|
// Calculate a signed distance to the contours in search_radius from the point.
|
||||||
|
bool signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment = NULL) const;
|
||||||
|
|
||||||
|
// Calculate a signed distance to the contours in search_radius from the point. If no edge is found in search_radius,
|
||||||
|
// return an interpolated value from m_signed_distance_field, if it exists.
|
||||||
|
bool signed_distance(const Point &pt, coord_t search_radius, coordf_t &result_min_dist) const;
|
||||||
|
|
||||||
|
const BoundingBox& bbox() const { return m_bbox; }
|
||||||
|
const coord_t resolution() const { return m_resolution; }
|
||||||
|
const size_t rows() const { return m_rows; }
|
||||||
|
const size_t cols() const { return m_cols; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void create_from_m_contours(coord_t resolution);
|
||||||
|
|
||||||
|
// Bounding box around the contours.
|
||||||
|
BoundingBox m_bbox;
|
||||||
|
// Grid dimensions.
|
||||||
|
coord_t m_resolution;
|
||||||
|
size_t m_rows;
|
||||||
|
size_t m_cols;
|
||||||
|
|
||||||
|
// Referencing the source contours.
|
||||||
|
// This format allows one to work with any Slic3r fixed point contour format
|
||||||
|
// (Polygon, ExPolygon, ExPolygonCollection etc).
|
||||||
|
std::vector<const Slic3r::Points*> m_contours;
|
||||||
|
|
||||||
|
// Referencing a contour and a line segment of m_contours.
|
||||||
|
std::vector<std::pair<size_t, size_t>> m_cell_data;
|
||||||
|
|
||||||
|
struct Cell {
|
||||||
|
Cell() : begin(0), end(0) {}
|
||||||
|
size_t begin;
|
||||||
|
size_t end;
|
||||||
|
};
|
||||||
|
// Full grid of cells.
|
||||||
|
std::vector<Cell> m_cells;
|
||||||
|
|
||||||
|
// Distance field derived from the edge grid, seed filled by the Danielsson chamfer metric.
|
||||||
|
// May be empty.
|
||||||
|
std::vector<float> m_signed_distance_field;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Debugging utility. Save the signed distance field.
|
||||||
|
extern void save_png(const Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path);
|
||||||
|
|
||||||
|
} // namespace EdgeGrid
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif /* slic3r_EdgeGrid_hpp_ */
|
|
@ -1,9 +1,21 @@
|
||||||
#include "GCode.hpp"
|
#include "GCode.hpp"
|
||||||
#include "ExtrusionEntity.hpp"
|
#include "ExtrusionEntity.hpp"
|
||||||
|
#include "EdgeGrid.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <math.h>
|
#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 {
|
namespace Slic3r {
|
||||||
|
|
||||||
AvoidCrossingPerimeters::AvoidCrossingPerimeters()
|
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)
|
#define EXTRUDER_CONFIG(OPT) this->config.OPT.get_at(this->writer.extruder()->id)
|
||||||
|
|
||||||
GCode::GCode()
|
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),
|
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&
|
const Point&
|
||||||
GCode::last_pos() const
|
GCode::last_pos() const
|
||||||
{
|
{
|
||||||
|
@ -279,6 +301,8 @@ GCode::change_layer(const Layer &layer)
|
||||||
this->layer = &layer;
|
this->layer = &layer;
|
||||||
this->layer_index++;
|
this->layer_index++;
|
||||||
this->first_layer = (layer.id() == 0);
|
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
|
// avoid computing islands and overhangs if they're not needed
|
||||||
if (this->config.avoid_crossing_perimeters) {
|
if (this->config.avoid_crossing_perimeters) {
|
||||||
|
@ -308,12 +332,230 @@ GCode::change_layer(const Layer &layer)
|
||||||
return gcode;
|
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
|
std::string
|
||||||
GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
|
GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
|
||||||
{
|
{
|
||||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||||
// next copies (if any) would not detect the correct orientation
|
// 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
|
// extrude all loops ccw
|
||||||
bool was_clockwise = loop.make_counter_clockwise();
|
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) {
|
if (this->config.spiral_vase) {
|
||||||
loop.split_at(last_pos);
|
loop.split_at(last_pos);
|
||||||
} else if (seam_position == spNearest || seam_position == spAligned) {
|
} else if (seam_position == spNearest || seam_position == spAligned) {
|
||||||
const Polygon polygon = loop.polygon();
|
Polygon polygon = loop.polygon();
|
||||||
|
const coordf_t nozzle_dmr = EXTRUDER_CONFIG(nozzle_diameter);
|
||||||
// simplify polygon in order to skip false positives in concave/convex detection
|
const coord_t nozzle_r = scale_(0.5*nozzle_dmr);
|
||||||
// (loop is always ccw as polygon.simplify() only works on ccw polygons)
|
|
||||||
Polygons simplified = polygon.simplify(scale_(EXTRUDER_CONFIG(nozzle_diameter))/2);
|
// Retrieve the last start position for this object.
|
||||||
|
float last_pos_weight = 1.f;
|
||||||
// restore original winding order so that concave and convex detection always happens
|
if (seam_position == spAligned && this->layer != NULL && this->_seam_position.count(this->layer->object()) > 0) {
|
||||||
// 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) {
|
|
||||||
last_pos = this->_seam_position[this->layer->object()];
|
last_pos = this->_seam_position[this->layer->object()];
|
||||||
|
last_pos_weight = 5.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point point;
|
// Insert a projection of last_pos into the polygon.
|
||||||
if (seam_position == spNearest) {
|
size_t last_pos_proj_idx;
|
||||||
if (candidates.empty()) candidates = polygon.points;
|
{
|
||||||
last_pos.nearest_point(candidates, &point);
|
auto it = project_point_to_polygon_and_insert(polygon, last_pos, 0.1 * nozzle_r);
|
||||||
|
last_pos_proj_idx = it - polygon.points.begin();
|
||||||
// On 32-bit Linux, Clipper will change some point coordinates by 1 unit
|
}
|
||||||
// while performing simplify_polygons(), thus split_at_vertex() won't
|
Point last_pos_proj = polygon.points[last_pos_proj_idx];
|
||||||
// find them anymore.
|
// Parametrize the polygon by its length.
|
||||||
if (!loop.split_at_vertex(point)) loop.split_at(point);
|
std::vector<float> lengths = polygon_parameter_by_length(polygon);
|
||||||
} else if (!candidates.empty()) {
|
|
||||||
Points non_overhang;
|
// For each polygon point, store a penalty.
|
||||||
for (Points::const_iterator p = candidates.begin(); p != candidates.end(); ++p) {
|
// First calculate the angles, store them as penalties. The angles are caluculated over a minimum arm length of nozzle_r.
|
||||||
if (!loop.has_overhang_point(*p))
|
std::vector<float> penalties = polygon_angles_at_vertices(polygon, lengths, nozzle_r);
|
||||||
non_overhang.push_back(*p);
|
// 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.));
|
||||||
}
|
}
|
||||||
|
// Give a negative penalty for points close to the last point or the prefered seam location.
|
||||||
if (!non_overhang.empty())
|
//float dist_to_last_pos_proj = last_pos_proj.distance_to(polygon.points[i]);
|
||||||
candidates = non_overhang;
|
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]) :
|
||||||
last_pos.nearest_point(candidates, &point);
|
std::min(lengths[i] - lengths[last_pos_proj_idx], lengths.back() - lengths[i] + lengths[last_pos_proj_idx]);
|
||||||
if (!loop.split_at_vertex(point)) loop.split_at(point); // see note above
|
float dist_max = 0.1f * lengths.back(); // 5.f * nozzle_dmr
|
||||||
} else {
|
penalty -= last_pos_weight * bspline_kernel(dist_to_last_pos_proj / dist_max);
|
||||||
point = last_pos.projection_onto(polygon);
|
penalties[i] = std::max(0.f, penalty);
|
||||||
loop.split_at(point);
|
|
||||||
}
|
}
|
||||||
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) {
|
} else if (seam_position == spRandom) {
|
||||||
if (loop.role == elrContourInternalPerimeter) {
|
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();
|
Polygon polygon = loop.polygon();
|
||||||
Point centroid = polygon.centroid();
|
Point centroid = polygon.centroid();
|
||||||
last_pos = Point(polygon.bounding_box().max.x, centroid.y);
|
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
|
// extrude along the path
|
||||||
std::string gcode;
|
std::string gcode;
|
||||||
for (ExtrusionPaths::const_iterator path = paths.begin(); path != paths.end(); ++path)
|
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);
|
gcode += this->_extrude(*path, description, speed);
|
||||||
|
|
||||||
// reset acceleration
|
// reset acceleration
|
||||||
|
@ -477,6 +779,7 @@ GCode::extrude(const ExtrusionEntity &entity, std::string description, double sp
|
||||||
std::string
|
std::string
|
||||||
GCode::extrude(const ExtrusionPath &path, std::string description, double speed)
|
GCode::extrude(const ExtrusionPath &path, std::string description, double speed)
|
||||||
{
|
{
|
||||||
|
// description += ExtrusionRole2String(path.role);
|
||||||
std::string gcode = this->_extrude(path, description, speed);
|
std::string gcode = this->_extrude(path, description, speed);
|
||||||
|
|
||||||
// reset acceleration
|
// 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
|
double F = speed * 60; // convert mm/sec to mm/min
|
||||||
|
|
||||||
// extrude arc or line
|
// 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)
|
if (path.is_bridge() && this->enable_cooling_markers)
|
||||||
gcode += ";_BRIDGE_FAN_START\n";
|
gcode += ";_BRIDGE_FAN_START\n";
|
||||||
gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : "");
|
gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : "");
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// Forward declarations.
|
||||||
class GCode;
|
class GCode;
|
||||||
|
namespace EdgeGrid { class Grid; }
|
||||||
|
|
||||||
class AvoidCrossingPerimeters {
|
class AvoidCrossingPerimeters {
|
||||||
public:
|
public:
|
||||||
|
@ -77,15 +79,23 @@ class GCode {
|
||||||
AvoidCrossingPerimeters avoid_crossing_perimeters;
|
AvoidCrossingPerimeters avoid_crossing_perimeters;
|
||||||
bool enable_loop_clipping;
|
bool enable_loop_clipping;
|
||||||
bool enable_cooling_markers;
|
bool enable_cooling_markers;
|
||||||
|
// Markers for the Pressure Equalizer to recognize the extrusion type.
|
||||||
|
// The Pressure Equalizer removes the markers from the final G-code.
|
||||||
|
bool enable_extrusion_role_markers;
|
||||||
size_t layer_count;
|
size_t layer_count;
|
||||||
int layer_index; // just a counter
|
int layer_index; // just a counter
|
||||||
const Layer* layer;
|
const Layer* layer;
|
||||||
std::map<const PrintObject*,Point> _seam_position;
|
std::map<const PrintObject*,Point> _seam_position;
|
||||||
|
// Distance Field structure to
|
||||||
|
EdgeGrid::Grid *_lower_layer_edge_grid;
|
||||||
bool first_layer; // this flag triggers first layer speeds
|
bool first_layer; // this flag triggers first layer speeds
|
||||||
float elapsed_time; // seconds
|
float elapsed_time; // seconds
|
||||||
double volumetric_speed;
|
double volumetric_speed;
|
||||||
|
// Support for the extrusion role markers. Which marker is active?
|
||||||
|
ExtrusionRole _last_extrusion_role;
|
||||||
|
|
||||||
GCode();
|
GCode();
|
||||||
|
~GCode();
|
||||||
const Point& last_pos() const;
|
const Point& last_pos() const;
|
||||||
void set_last_pos(const Point &pos);
|
void set_last_pos(const Point &pos);
|
||||||
bool last_pos_defined() const;
|
bool last_pos_defined() const;
|
||||||
|
|
608
xs/src/libslic3r/GCode/PressureEqualizer.cpp
Normal file
608
xs/src/libslic3r/GCode/PressureEqualizer.cpp
Normal file
|
@ -0,0 +1,608 @@
|
||||||
|
#include <memory.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../libslic3r.h"
|
||||||
|
#include "../PrintConfig.hpp"
|
||||||
|
|
||||||
|
#include "PressureEqualizer.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
GCodePressureEqualizer::GCodePressureEqualizer(const Slic3r::GCodeConfig *config) :
|
||||||
|
m_config(config)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
GCodePressureEqualizer::~GCodePressureEqualizer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodePressureEqualizer::reset()
|
||||||
|
{
|
||||||
|
circular_buffer_pos = 0;
|
||||||
|
circular_buffer_size = 100;
|
||||||
|
circular_buffer_items = 0;
|
||||||
|
circular_buffer.assign(circular_buffer_size, GCodeLine());
|
||||||
|
|
||||||
|
output_buffer.clear();
|
||||||
|
output_buffer_length = 0;
|
||||||
|
|
||||||
|
m_current_extruder = 0;
|
||||||
|
// Zero the position of the XYZE axes + the current feed
|
||||||
|
memset(m_current_pos, 0, sizeof(float) * 5);
|
||||||
|
m_current_extrusion_role = erNone;
|
||||||
|
// Expect the first command to fill the nozzle (deretract).
|
||||||
|
m_retracted = true;
|
||||||
|
|
||||||
|
// Calculate filamet crossections for the multiple extruders.
|
||||||
|
m_filament_crossections.clear();
|
||||||
|
for (size_t i = 0; i < m_config->filament_diameter.values.size(); ++ i) {
|
||||||
|
double r = m_config->filament_diameter.values[i];
|
||||||
|
double a = 0.25f*M_PI*r*r;
|
||||||
|
m_filament_crossections.push_back(float(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_max_segment_length = 20.f;
|
||||||
|
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/min XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min
|
||||||
|
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/min XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min
|
||||||
|
// Slope of the volumetric rate, changing from 20mm^3/min to 60mm^3/min over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2
|
||||||
|
m_max_volumetric_extrusion_rate_slope_positive = 6480.f;
|
||||||
|
m_max_volumetric_extrusion_rate_slope_negative = 6480.f;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numExtrusionRoles; ++ i) {
|
||||||
|
m_max_volumetric_extrusion_rate_slopes[i].negative = m_max_volumetric_extrusion_rate_slope_negative;
|
||||||
|
m_max_volumetric_extrusion_rate_slopes[i].positive = m_max_volumetric_extrusion_rate_slope_positive;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't regulate the pressure in infill.
|
||||||
|
m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].negative = 0;
|
||||||
|
m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].positive = 0;
|
||||||
|
// Don't regulate the pressure in gap fill.
|
||||||
|
m_max_volumetric_extrusion_rate_slopes[erGapFill].negative = 0;
|
||||||
|
m_max_volumetric_extrusion_rate_slopes[erGapFill].positive = 0;
|
||||||
|
|
||||||
|
m_stat.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GCodePressureEqualizer::process(const char *szGCode, bool flush)
|
||||||
|
{
|
||||||
|
// Reset length of the output_buffer.
|
||||||
|
output_buffer_length = 0;
|
||||||
|
|
||||||
|
if (szGCode != 0) {
|
||||||
|
const char *p = szGCode;
|
||||||
|
while (*p != 0) {
|
||||||
|
// Find end of the line.
|
||||||
|
const char *endl = p;
|
||||||
|
// Slic3r always generates end of lines in a Unix style.
|
||||||
|
for (; *endl != 0 && *endl != '\n'; ++ endl) ;
|
||||||
|
if (circular_buffer_items == circular_buffer_size)
|
||||||
|
// Buffer is full. Push out the oldest line.
|
||||||
|
output_gcode_line(circular_buffer[circular_buffer_pos]);
|
||||||
|
else
|
||||||
|
++ circular_buffer_items;
|
||||||
|
// Process a G-code line, store it into the provided GCodeLine object.
|
||||||
|
size_t idx_tail = circular_buffer_pos;
|
||||||
|
circular_buffer_pos = circular_buffer_idx_next(circular_buffer_pos);
|
||||||
|
if (! process_line(p, endl - p, circular_buffer[idx_tail])) {
|
||||||
|
// The line has to be forgotten. It contains comment marks, which shall be
|
||||||
|
// filtered out of the target g-code.
|
||||||
|
circular_buffer_pos = idx_tail;
|
||||||
|
-- circular_buffer_items;
|
||||||
|
}
|
||||||
|
p = endl;
|
||||||
|
if (*p == '\n')
|
||||||
|
++ p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flush) {
|
||||||
|
// Flush the remaining valid lines of the circular buffer.
|
||||||
|
for (size_t idx = circular_buffer_idx_head(); circular_buffer_items > 0; -- circular_buffer_items) {
|
||||||
|
output_gcode_line(circular_buffer[idx]);
|
||||||
|
if (++ idx == circular_buffer_size)
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
// Reset the index pointer.
|
||||||
|
assert(circular_buffer_items == 0);
|
||||||
|
circular_buffer_pos = 0;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
printf("Statistics: \n");
|
||||||
|
printf("Minimum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_min);
|
||||||
|
printf("Maximum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_max);
|
||||||
|
if (m_stat.extrusion_length > 0)
|
||||||
|
m_stat.volumetric_extrusion_rate_avg /= m_stat.extrusion_length;
|
||||||
|
printf("Average volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_avg);
|
||||||
|
m_stat.reset();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is a white space?
|
||||||
|
static inline bool is_ws(const char c) { return c == ' ' || c == '\t'; }
|
||||||
|
// Is it an end of line? Consider a comment to be an end of line as well.
|
||||||
|
static inline bool is_eol(const char c) { return c == 0 || c == '\r' || c == '\n' || c == ';'; };
|
||||||
|
// Is it a white space or end of line?
|
||||||
|
static inline bool is_ws_or_eol(const char c) { return is_ws(c) || is_eol(c); };
|
||||||
|
|
||||||
|
// Eat whitespaces.
|
||||||
|
static void eatws(const char *&line)
|
||||||
|
{
|
||||||
|
while (is_ws(*line))
|
||||||
|
++ line;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse an int starting at the current position of a line.
|
||||||
|
// If succeeded, the line pointer is advanced.
|
||||||
|
static inline int parse_int(const char *&line)
|
||||||
|
{
|
||||||
|
char *endptr = NULL;
|
||||||
|
long result = strtol(line, &endptr, 10);
|
||||||
|
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
||||||
|
throw std::runtime_error("GCodePressureEqualizer: Error parsing an int");
|
||||||
|
line = endptr;
|
||||||
|
return int(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse an int starting at the current position of a line.
|
||||||
|
// If succeeded, the line pointer is advanced.
|
||||||
|
static inline float parse_float(const char *&line)
|
||||||
|
{
|
||||||
|
char *endptr = NULL;
|
||||||
|
float result = strtof(line, &endptr);
|
||||||
|
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
||||||
|
throw std::runtime_error("GCodePressureEqualizer: Error parsing a float");
|
||||||
|
line = endptr;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EXTRUSION_ROLE_TAG ";_EXTRUSION_ROLE:"
|
||||||
|
bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GCodeLine &buf)
|
||||||
|
{
|
||||||
|
if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) {
|
||||||
|
line += strlen(EXTRUSION_ROLE_TAG);
|
||||||
|
int role = atoi(line);
|
||||||
|
this->m_current_extrusion_role = ExtrusionRole(role);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the type, copy the line to the buffer.
|
||||||
|
buf.type = GCODELINETYPE_OTHER;
|
||||||
|
buf.modified = false;
|
||||||
|
if (buf.raw.size() < len + 1)
|
||||||
|
buf.raw.assign(line, line + len + 1);
|
||||||
|
else
|
||||||
|
memcpy(buf.raw.data(), line, len);
|
||||||
|
buf.raw[len] = 0;
|
||||||
|
buf.raw_length = len;
|
||||||
|
|
||||||
|
memcpy(buf.pos_start, m_current_pos, sizeof(float)*5);
|
||||||
|
memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
|
||||||
|
memset(buf.pos_provided, 0, 5);
|
||||||
|
|
||||||
|
buf.volumetric_extrusion_rate = 0.f;
|
||||||
|
buf.volumetric_extrusion_rate_start = 0.f;
|
||||||
|
buf.volumetric_extrusion_rate_end = 0.f;
|
||||||
|
buf.max_volumetric_extrusion_rate_slope_positive = 0.f;
|
||||||
|
buf.max_volumetric_extrusion_rate_slope_negative = 0.f;
|
||||||
|
buf.extrusion_role = m_current_extrusion_role;
|
||||||
|
|
||||||
|
// Parse the G-code line, store the result into the buf.
|
||||||
|
switch (toupper(*line ++)) {
|
||||||
|
case 'G': {
|
||||||
|
int gcode = parse_int(line);
|
||||||
|
eatws(line);
|
||||||
|
switch (gcode) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
// G0, G1: A FFF 3D printer does not make a difference between the two.
|
||||||
|
float new_pos[5];
|
||||||
|
memcpy(new_pos, m_current_pos, sizeof(float)*5);
|
||||||
|
bool changed[5] = { false, false, false, false, false };
|
||||||
|
while (!is_eol(*line)) {
|
||||||
|
char axis = toupper(*line++);
|
||||||
|
int i = -1;
|
||||||
|
switch (axis) {
|
||||||
|
case 'X':
|
||||||
|
case 'Y':
|
||||||
|
case 'Z':
|
||||||
|
i = axis - 'X';
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
i = 3;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
i = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
if (i == -1)
|
||||||
|
throw std::runtime_error(std::string("GCodePressureEqualizer: Invalid axis for G0/G1: ") + axis);
|
||||||
|
buf.pos_provided[i] = true;
|
||||||
|
new_pos[i] = parse_float(line);
|
||||||
|
if (i == 3 && m_config->use_relative_e_distances.value)
|
||||||
|
new_pos[i] += m_current_pos[i];
|
||||||
|
changed[i] = new_pos[i] != m_current_pos[i];
|
||||||
|
eatws(line);
|
||||||
|
}
|
||||||
|
if (changed[3]) {
|
||||||
|
// Extrusion, retract or unretract.
|
||||||
|
float diff = new_pos[3] - m_current_pos[3];
|
||||||
|
if (diff < 0) {
|
||||||
|
buf.type = GCODELINETYPE_RETRACT;
|
||||||
|
m_retracted = true;
|
||||||
|
} else if (! changed[0] && ! changed[1] && ! changed[2]) {
|
||||||
|
// assert(m_retracted);
|
||||||
|
buf.type = GCODELINETYPE_UNRETRACT;
|
||||||
|
m_retracted = false;
|
||||||
|
} else {
|
||||||
|
assert(changed[0] || changed[1]);
|
||||||
|
// Moving in XY plane.
|
||||||
|
buf.type = GCODELINETYPE_EXTRUDE;
|
||||||
|
// Calculate the volumetric extrusion rate.
|
||||||
|
float diff[4];
|
||||||
|
for (size_t i = 0; i < 4; ++ i)
|
||||||
|
diff[i] = new_pos[i] - m_current_pos[i];
|
||||||
|
// volumetric extrusion rate = A_filament * F_xyz * L_e / L_xyz [mm^3/min]
|
||||||
|
float len2 = diff[0]*diff[0]+diff[1]*diff[1]+diff[2]*diff[2];
|
||||||
|
float rate = m_filament_crossections[m_current_extruder] * new_pos[4] * sqrt((diff[3]*diff[3])/len2);
|
||||||
|
buf.volumetric_extrusion_rate = rate;
|
||||||
|
buf.volumetric_extrusion_rate_start = rate;
|
||||||
|
buf.volumetric_extrusion_rate_end = rate;
|
||||||
|
m_stat.update(rate, sqrt(len2));
|
||||||
|
if (rate < 10.f) {
|
||||||
|
printf("Extremely low flow rate: %f\n", rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (changed[0] || changed[1] || changed[2]) {
|
||||||
|
// Moving without extrusion.
|
||||||
|
buf.type = GCODELINETYPE_MOVE;
|
||||||
|
}
|
||||||
|
memcpy(m_current_pos, new_pos, sizeof(float) * 5);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 92:
|
||||||
|
{
|
||||||
|
// G92 : Set Position
|
||||||
|
// Set a logical coordinate position to a new value without actually moving the machine motors.
|
||||||
|
// Which axes to set?
|
||||||
|
bool set = false;
|
||||||
|
while (!is_eol(*line)) {
|
||||||
|
char axis = toupper(*line++);
|
||||||
|
switch (axis) {
|
||||||
|
case 'X':
|
||||||
|
case 'Y':
|
||||||
|
case 'Z':
|
||||||
|
m_current_pos[axis - 'X'] = (!is_ws_or_eol(*line)) ? parse_float(line) : 0.f;
|
||||||
|
set = true;
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
m_current_pos[3] = (!is_ws_or_eol(*line)) ? parse_float(line) : 0.f;
|
||||||
|
set = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(std::string("GCodePressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
|
||||||
|
}
|
||||||
|
eatws(line);
|
||||||
|
}
|
||||||
|
assert(set);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 10:
|
||||||
|
case 22:
|
||||||
|
// Firmware retract.
|
||||||
|
buf.type = GCODELINETYPE_RETRACT;
|
||||||
|
m_retracted = true;
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
case 23:
|
||||||
|
// Firmware unretract.
|
||||||
|
buf.type = GCODELINETYPE_UNRETRACT;
|
||||||
|
m_retracted = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Ignore the rest.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'M': {
|
||||||
|
int mcode = parse_int(line);
|
||||||
|
eatws(line);
|
||||||
|
switch (mcode) {
|
||||||
|
default:
|
||||||
|
// Ignore the rest of the M-codes.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'T':
|
||||||
|
{
|
||||||
|
// Activate an extruder head.
|
||||||
|
int new_extruder = parse_int(line);
|
||||||
|
if (new_extruder != m_current_extruder) {
|
||||||
|
m_current_extruder = new_extruder;
|
||||||
|
m_retracted = true;
|
||||||
|
buf.type = GCODELINETYPE_TOOL_CHANGE;
|
||||||
|
} else {
|
||||||
|
buf.type = GCODELINETYPE_NOOP;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.extruder_id = m_current_extruder;
|
||||||
|
memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
|
||||||
|
|
||||||
|
adjust_volumetric_rate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodePressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||||
|
{
|
||||||
|
if (! line.modified) {
|
||||||
|
push_to_output(line.raw.data(), line.raw_length, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The line was modified.
|
||||||
|
// Find the comment.
|
||||||
|
const char *comment = line.raw.data();
|
||||||
|
while (*comment != ';' && *comment != 0) ++comment;
|
||||||
|
if (*comment != ';')
|
||||||
|
comment = NULL;
|
||||||
|
|
||||||
|
// Emit the line with lowered extrusion rates.
|
||||||
|
float l2 = line.dist_xyz2();
|
||||||
|
float l = sqrt(l2);
|
||||||
|
size_t nSegments = size_t(ceil(l / m_max_segment_length));
|
||||||
|
char text[2048];
|
||||||
|
if (nSegments == 1) {
|
||||||
|
// Just update this segment.
|
||||||
|
push_line_to_output(line, line.feedrate() * line.volumetric_correction_avg(), comment);
|
||||||
|
} else {
|
||||||
|
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
|
||||||
|
// Update the initial and final feed rate values.
|
||||||
|
line.pos_start[4] = line.volumetric_extrusion_rate_start * line.pos_end[4] / line.volumetric_extrusion_rate;
|
||||||
|
line.pos_end [4] = line.volumetric_extrusion_rate_end * line.pos_end[4] / line.volumetric_extrusion_rate;
|
||||||
|
float feed_avg = 0.5f * (line.pos_start[4] + line.pos_end[4]);
|
||||||
|
// Limiting volumetric extrusion rate slope for this segment.
|
||||||
|
float max_volumetric_extrusion_rate_slope = accelerating ?
|
||||||
|
line.max_volumetric_extrusion_rate_slope_positive : line.max_volumetric_extrusion_rate_slope_negative;
|
||||||
|
// Total time for the segment, corrected for the possibly lowered volumetric feed rate,
|
||||||
|
// if accelerating / decelerating over the complete segment.
|
||||||
|
float t_total = line.dist_xyz() / feed_avg;
|
||||||
|
// Time of the acceleration / deceleration part of the segment, if accelerating / decelerating
|
||||||
|
// with the maximum volumetric extrusion rate slope.
|
||||||
|
float t_acc = 0.5f * (line.volumetric_extrusion_rate_start + line.volumetric_extrusion_rate_end) / max_volumetric_extrusion_rate_slope;
|
||||||
|
float l_acc = l;
|
||||||
|
float l_steady = 0.f;
|
||||||
|
if (t_acc < t_total) {
|
||||||
|
// One may achieve higher print speeds if part of the segment is not speed limited.
|
||||||
|
float l_acc = t_acc * feed_avg;
|
||||||
|
float l_steady = l - l_acc;
|
||||||
|
if (l_steady < 0.5f * m_max_segment_length) {
|
||||||
|
l_acc = l;
|
||||||
|
l_steady = 0.f;
|
||||||
|
} else
|
||||||
|
nSegments = size_t(ceil(l_acc / m_max_segment_length));
|
||||||
|
}
|
||||||
|
float pos_start[5];
|
||||||
|
float pos_end [5];
|
||||||
|
float pos_end2 [4];
|
||||||
|
memcpy(pos_start, line.pos_start, sizeof(float)*5);
|
||||||
|
memcpy(pos_end , line.pos_end , sizeof(float)*5);
|
||||||
|
if (l_steady > 0.f) {
|
||||||
|
// There will be a steady feed segment emitted.
|
||||||
|
if (accelerating) {
|
||||||
|
// Prepare the final steady feed rate segment.
|
||||||
|
memcpy(pos_end2, pos_end, sizeof(float)*4);
|
||||||
|
float t = l_acc / l;
|
||||||
|
for (int i = 0; i < 4; ++ i) {
|
||||||
|
pos_end[i] = pos_start[i] + (pos_end[i] - pos_start[i]) * t;
|
||||||
|
line.pos_provided[i] = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Emit the steady feed rate segment.
|
||||||
|
float t = l_steady / l;
|
||||||
|
for (int i = 0; i < 4; ++ i) {
|
||||||
|
line.pos_end[i] = pos_start[i] + (pos_end[i] - pos_start[i]) * t;
|
||||||
|
line.pos_provided[i] = true;
|
||||||
|
}
|
||||||
|
push_line_to_output(line, pos_start[4], comment);
|
||||||
|
comment = NULL;
|
||||||
|
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
|
||||||
|
memcpy(pos_start, line.pos_end, sizeof(float)*5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Split the segment into pieces.
|
||||||
|
for (size_t i = 1; i < nSegments; ++ i) {
|
||||||
|
float t = float(i) / float(nSegments);
|
||||||
|
for (size_t j = 0; j < 4; ++ j) {
|
||||||
|
line.pos_end[j] = pos_start[j] + (pos_end[j] - pos_start[j]) * t;
|
||||||
|
line.pos_provided[j] = true;
|
||||||
|
}
|
||||||
|
// Interpolate the feed rate at the center of the segment.
|
||||||
|
push_line_to_output(line, pos_start[4] + (pos_end[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment);
|
||||||
|
comment = NULL;
|
||||||
|
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
|
||||||
|
}
|
||||||
|
if (l_steady > 0.f && accelerating) {
|
||||||
|
for (int i = 0; i < 4; ++ i) {
|
||||||
|
line.pos_end[i] = pos_end2[i];
|
||||||
|
line.pos_provided[i] = true;
|
||||||
|
}
|
||||||
|
push_line_to_output(line, pos_end[4], comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodePressureEqualizer::adjust_volumetric_rate()
|
||||||
|
{
|
||||||
|
if (circular_buffer_items < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
|
||||||
|
const size_t idx_head = circular_buffer_idx_head();
|
||||||
|
const size_t idx_tail = circular_buffer_idx_prev(circular_buffer_idx_tail());
|
||||||
|
size_t idx = idx_tail;
|
||||||
|
if (idx == idx_head || ! circular_buffer[idx].extruding())
|
||||||
|
// Nothing to do, the last move is not extruding.
|
||||||
|
return;
|
||||||
|
|
||||||
|
float feedrate_per_extrusion_role[numExtrusionRoles];
|
||||||
|
for (size_t i = 0; i < numExtrusionRoles; ++ i)
|
||||||
|
feedrate_per_extrusion_role[i] = FLT_MAX;
|
||||||
|
feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_start;
|
||||||
|
|
||||||
|
bool modified = true;
|
||||||
|
while (modified && idx != idx_head) {
|
||||||
|
size_t idx_prev = circular_buffer_idx_prev(idx);
|
||||||
|
for (; ! circular_buffer[idx_prev].extruding() && idx_prev != idx_head; idx_prev = circular_buffer_idx_prev(idx_prev)) ;
|
||||||
|
if (! circular_buffer[idx_prev].extruding())
|
||||||
|
break;
|
||||||
|
float rate_succ = circular_buffer[idx].volumetric_extrusion_rate_start;
|
||||||
|
// What is the gradient of the extrusion rate between idx_prev and idx?
|
||||||
|
idx = idx_prev;
|
||||||
|
GCodeLine &line = circular_buffer[idx];
|
||||||
|
for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) {
|
||||||
|
float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative;
|
||||||
|
if (rate_slope == 0)
|
||||||
|
// The negative rate is unlimited.
|
||||||
|
continue;
|
||||||
|
float rate_end = feedrate_per_extrusion_role[iRole];
|
||||||
|
if (iRole == line.extrusion_role && rate_succ < rate_end)
|
||||||
|
rate_end = rate_succ;
|
||||||
|
if (line.volumetric_extrusion_rate_end > rate_end) {
|
||||||
|
line.volumetric_extrusion_rate_end = rate_end;
|
||||||
|
line.modified = true;
|
||||||
|
} else if (iRole == line.extrusion_role) {
|
||||||
|
rate_end = line.volumetric_extrusion_rate_end;
|
||||||
|
} else if (rate_end == FLT_MAX) {
|
||||||
|
// The rate for ExtrusionRole iRole is unlimited.
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
||||||
|
}
|
||||||
|
// modified = false;
|
||||||
|
float rate_start = rate_end + rate_slope * line.time_corrected();
|
||||||
|
if (rate_start < line.volumetric_extrusion_rate_start) {
|
||||||
|
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||||
|
// of ExtrusionType iRole, which will be extruded in the future.
|
||||||
|
line.volumetric_extrusion_rate_start = rate_start;
|
||||||
|
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
|
||||||
|
line.modified = true;
|
||||||
|
// modified = true;
|
||||||
|
}
|
||||||
|
feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
|
||||||
|
for (size_t i = 0; i < numExtrusionRoles; ++ i)
|
||||||
|
feedrate_per_extrusion_role[i] = FLT_MAX;
|
||||||
|
feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_end;
|
||||||
|
|
||||||
|
assert(circular_buffer[idx].extruding());
|
||||||
|
while (idx != idx_tail) {
|
||||||
|
size_t idx_next = circular_buffer_idx_next(idx);
|
||||||
|
for (; ! circular_buffer[idx_next].extruding() && idx_next != idx_tail; idx_next = circular_buffer_idx_next(idx_next)) ;
|
||||||
|
if (! circular_buffer[idx_next].extruding())
|
||||||
|
break;
|
||||||
|
float rate_prec = circular_buffer[idx].volumetric_extrusion_rate_end;
|
||||||
|
// What is the gradient of the extrusion rate between idx_prev and idx?
|
||||||
|
idx = idx_next;
|
||||||
|
GCodeLine &line = circular_buffer[idx];
|
||||||
|
for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) {
|
||||||
|
float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive;
|
||||||
|
if (rate_slope == 0)
|
||||||
|
// The positive rate is unlimited.
|
||||||
|
continue;
|
||||||
|
float rate_start = feedrate_per_extrusion_role[iRole];
|
||||||
|
if (iRole == line.extrusion_role && rate_prec < rate_start)
|
||||||
|
rate_start = rate_prec;
|
||||||
|
if (line.volumetric_extrusion_rate_start > rate_start) {
|
||||||
|
line.volumetric_extrusion_rate_start = rate_start;
|
||||||
|
line.modified = true;
|
||||||
|
} else if (iRole == line.extrusion_role) {
|
||||||
|
rate_start = line.volumetric_extrusion_rate_start;
|
||||||
|
} else if (rate_start == FLT_MAX) {
|
||||||
|
// The rate for ExtrusionRole iRole is unlimited.
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
||||||
|
}
|
||||||
|
float rate_end = (rate_slope == 0) ? FLT_MAX : rate_start + rate_slope * line.time_corrected();
|
||||||
|
if (rate_end < line.volumetric_extrusion_rate_end) {
|
||||||
|
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||||
|
// of ExtrusionType iRole, which was extruded before.
|
||||||
|
line.volumetric_extrusion_rate_end = rate_end;
|
||||||
|
line.max_volumetric_extrusion_rate_slope_positive = rate_slope;
|
||||||
|
line.modified = true;
|
||||||
|
}
|
||||||
|
feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodePressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol)
|
||||||
|
{
|
||||||
|
char buf[2048];
|
||||||
|
int len = sprintf(buf,
|
||||||
|
(axis == 'E') ? " %c%.3f" : " %c%.5f",
|
||||||
|
axis, value);
|
||||||
|
push_to_output(buf, len, add_eol);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodePressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol)
|
||||||
|
{
|
||||||
|
// New length of the output buffer content.
|
||||||
|
size_t len_new = output_buffer_length + len + 1;
|
||||||
|
if (add_eol)
|
||||||
|
++ len_new;
|
||||||
|
|
||||||
|
// Resize the output buffer to a power of 2 higher than the required memory.
|
||||||
|
if (output_buffer.size() < len_new) {
|
||||||
|
size_t v = len_new;
|
||||||
|
// Compute the next highest power of 2 of 32-bit v
|
||||||
|
// http://graphics.stanford.edu/~seander/bithacks.html
|
||||||
|
v--;
|
||||||
|
v |= v >> 1;
|
||||||
|
v |= v >> 2;
|
||||||
|
v |= v >> 4;
|
||||||
|
v |= v >> 8;
|
||||||
|
v |= v >> 16;
|
||||||
|
v++;
|
||||||
|
output_buffer.resize(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the text to the output.
|
||||||
|
if (len != 0) {
|
||||||
|
memcpy(output_buffer.data() + output_buffer_length, text, len);
|
||||||
|
output_buffer_length += len;
|
||||||
|
}
|
||||||
|
if (add_eol)
|
||||||
|
output_buffer[output_buffer_length ++] = '\n';
|
||||||
|
output_buffer[output_buffer_length] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GCodePressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment)
|
||||||
|
{
|
||||||
|
push_to_output("G1", 2, false);
|
||||||
|
for (size_t i = 0; i < 3; ++ i)
|
||||||
|
if (line.pos_provided[i])
|
||||||
|
push_axis_to_output('X'+i, line.pos_end[i]);
|
||||||
|
push_axis_to_output('E', m_config->use_relative_e_distances.value ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3]);
|
||||||
|
// if (line.pos_provided[4] || fabs(line.feedrate() - new_feedrate) > 1e-5)
|
||||||
|
push_axis_to_output('F', new_feedrate);
|
||||||
|
// output comment and EOL
|
||||||
|
push_to_output(comment, (comment == NULL) ? 0 : strlen(comment), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
209
xs/src/libslic3r/GCode/PressureEqualizer.hpp
Normal file
209
xs/src/libslic3r/GCode/PressureEqualizer.hpp
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
#ifndef slic3r_GCode_PressureEqualizer_hpp_
|
||||||
|
#define slic3r_GCode_PressureEqualizer_hpp_
|
||||||
|
|
||||||
|
#include "../libslic3r.h"
|
||||||
|
#include "../PrintConfig.hpp"
|
||||||
|
#include "../ExtrusionEntity.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// Processes a G-code. Finds changes in the volumetric extrusion speed and adjusts the transitions
|
||||||
|
// between these paths to limit fast changes in the volumetric extrusion speed.
|
||||||
|
class GCodePressureEqualizer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GCodePressureEqualizer(const Slic3r::GCodeConfig *config);
|
||||||
|
~GCodePressureEqualizer();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// Process a next batch of G-code lines. Flush the internal buffers if asked for.
|
||||||
|
const char* process(const char *szGCode, bool flush);
|
||||||
|
|
||||||
|
size_t get_output_buffer_length() const { return output_buffer_length; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Statistics
|
||||||
|
{
|
||||||
|
void reset() {
|
||||||
|
volumetric_extrusion_rate_min = std::numeric_limits<float>::max();
|
||||||
|
volumetric_extrusion_rate_max = 0.f;
|
||||||
|
volumetric_extrusion_rate_avg = 0.f;
|
||||||
|
extrusion_length = 0.f;
|
||||||
|
}
|
||||||
|
void update(float volumetric_extrusion_rate, float length) {
|
||||||
|
volumetric_extrusion_rate_min = std::min(volumetric_extrusion_rate_min, volumetric_extrusion_rate);
|
||||||
|
volumetric_extrusion_rate_max = std::max(volumetric_extrusion_rate_max, volumetric_extrusion_rate);
|
||||||
|
volumetric_extrusion_rate_avg += volumetric_extrusion_rate * length;
|
||||||
|
extrusion_length += length;
|
||||||
|
}
|
||||||
|
float volumetric_extrusion_rate_min;
|
||||||
|
float volumetric_extrusion_rate_max;
|
||||||
|
float volumetric_extrusion_rate_avg;
|
||||||
|
float extrusion_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Statistics m_stat;
|
||||||
|
|
||||||
|
// Keeps the reference, does not own the config.
|
||||||
|
const Slic3r::GCodeConfig *m_config;
|
||||||
|
|
||||||
|
// Private configuration values
|
||||||
|
// How fast could the volumetric extrusion rate increase / decrase? mm^3/sec^2
|
||||||
|
struct ExtrusionRateSlope {
|
||||||
|
float positive;
|
||||||
|
float negative;
|
||||||
|
};
|
||||||
|
enum { numExtrusionRoles = erSupportMaterialInterface + 1 };
|
||||||
|
ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[numExtrusionRoles];
|
||||||
|
float m_max_volumetric_extrusion_rate_slope_positive;
|
||||||
|
float m_max_volumetric_extrusion_rate_slope_negative;
|
||||||
|
// Maximum segment length to split a long segment, if the initial and the final flow rate differ.
|
||||||
|
float m_max_segment_length;
|
||||||
|
|
||||||
|
// Configuration extracted from config.
|
||||||
|
// Area of the crossestion of each filament. Necessary to calculate the volumetric flow rate.
|
||||||
|
std::vector<float> m_filament_crossections;
|
||||||
|
|
||||||
|
// Internal data.
|
||||||
|
// X,Y,Z,E,F
|
||||||
|
float m_current_pos[5];
|
||||||
|
size_t m_current_extruder;
|
||||||
|
ExtrusionRole m_current_extrusion_role;
|
||||||
|
bool m_retracted;
|
||||||
|
|
||||||
|
enum GCodeLineType
|
||||||
|
{
|
||||||
|
GCODELINETYPE_INVALID,
|
||||||
|
GCODELINETYPE_NOOP,
|
||||||
|
GCODELINETYPE_OTHER,
|
||||||
|
GCODELINETYPE_RETRACT,
|
||||||
|
GCODELINETYPE_UNRETRACT,
|
||||||
|
GCODELINETYPE_TOOL_CHANGE,
|
||||||
|
GCODELINETYPE_MOVE,
|
||||||
|
GCODELINETYPE_EXTRUDE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GCodeLine
|
||||||
|
{
|
||||||
|
GCodeLine() :
|
||||||
|
type(GCODELINETYPE_INVALID),
|
||||||
|
raw_length(0),
|
||||||
|
modified(false),
|
||||||
|
extruder_id(0),
|
||||||
|
volumetric_extrusion_rate(0.f),
|
||||||
|
volumetric_extrusion_rate_start(0.f),
|
||||||
|
volumetric_extrusion_rate_end(0.f)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool moving_xy() const { return fabs(pos_end[0] - pos_start[0]) > 0.f || fabs(pos_end[1] - pos_start[1]) > 0.f; }
|
||||||
|
bool moving_z () const { return fabs(pos_end[2] - pos_start[2]) > 0.f; }
|
||||||
|
bool extruding() const { return moving_xy() && pos_end[3] > pos_start[3]; }
|
||||||
|
bool retracting() const { return pos_end[3] < pos_start[3]; }
|
||||||
|
bool deretracting() const { return ! moving_xy() && pos_end[3] > pos_start[3]; }
|
||||||
|
|
||||||
|
float dist_xy2() const { return (pos_end[0] - pos_start[0]) * (pos_end[0] - pos_start[0]) + (pos_end[1] - pos_start[1]) * (pos_end[1] - pos_start[1]); }
|
||||||
|
float dist_xyz2() const { return (pos_end[0] - pos_start[0]) * (pos_end[0] - pos_start[0]) + (pos_end[1] - pos_start[1]) * (pos_end[1] - pos_start[1]) + (pos_end[2] - pos_start[2]) * (pos_end[2] - pos_start[2]); }
|
||||||
|
float dist_xy() const { return sqrt(dist_xy2()); }
|
||||||
|
float dist_xyz() const { return sqrt(dist_xyz2()); }
|
||||||
|
float dist_e() const { return fabs(pos_end[3] - pos_start[3]); }
|
||||||
|
|
||||||
|
float feedrate() const { return pos_end[4]; }
|
||||||
|
float time() const { return dist_xyz() / feedrate(); }
|
||||||
|
float time_inv() const { return feedrate() / dist_xyz(); }
|
||||||
|
float volumetric_correction_avg() const {
|
||||||
|
float avg_correction = 0.5f * (volumetric_extrusion_rate_start + volumetric_extrusion_rate_end) / volumetric_extrusion_rate;
|
||||||
|
assert(avg_correction > 0.f);
|
||||||
|
assert(avg_correction <= 1.00000001f);
|
||||||
|
return avg_correction;
|
||||||
|
}
|
||||||
|
float time_corrected() const { return time() * volumetric_correction_avg(); }
|
||||||
|
|
||||||
|
GCodeLineType type;
|
||||||
|
|
||||||
|
// We try to keep the string buffer once it has been allocated, so it will not be reallocated over and over.
|
||||||
|
std::vector<char> raw;
|
||||||
|
size_t raw_length;
|
||||||
|
// If modified, the raw text has to be adapted by the new extrusion rate,
|
||||||
|
// or maybe the line needs to be split into multiple lines.
|
||||||
|
bool modified;
|
||||||
|
|
||||||
|
// float timeStart;
|
||||||
|
// float timeEnd;
|
||||||
|
// X,Y,Z,E,F. Storing the state of the currently active extruder only.
|
||||||
|
float pos_start[5];
|
||||||
|
float pos_end[5];
|
||||||
|
// Was the axis found on the G-code line? X,Y,Z,F
|
||||||
|
bool pos_provided[5];
|
||||||
|
|
||||||
|
// Index of the active extruder.
|
||||||
|
size_t extruder_id;
|
||||||
|
// Extrusion role of this segment.
|
||||||
|
ExtrusionRole extrusion_role;
|
||||||
|
|
||||||
|
// Current volumetric extrusion rate.
|
||||||
|
float volumetric_extrusion_rate;
|
||||||
|
// Volumetric extrusion rate at the start of this segment.
|
||||||
|
float volumetric_extrusion_rate_start;
|
||||||
|
// Volumetric extrusion rate at the end of this segment.
|
||||||
|
float volumetric_extrusion_rate_end;
|
||||||
|
|
||||||
|
// Volumetric extrusion rate slope limiting this segment.
|
||||||
|
// If set to zero, the slope is unlimited.
|
||||||
|
float max_volumetric_extrusion_rate_slope_positive;
|
||||||
|
float max_volumetric_extrusion_rate_slope_negative;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Circular buffer of GCode lines. The circular buffer size will be limited to circular_buffer_size.
|
||||||
|
std::vector<GCodeLine> circular_buffer;
|
||||||
|
// Current position of the circular buffer (index, where to write the next line to, the line has to be pushed out before it is overwritten).
|
||||||
|
size_t circular_buffer_pos;
|
||||||
|
// Circular buffer size, configuration value.
|
||||||
|
size_t circular_buffer_size;
|
||||||
|
// Number of valid lines in the circular buffer. Lower or equal to circular_buffer_size.
|
||||||
|
size_t circular_buffer_items;
|
||||||
|
|
||||||
|
// Output buffer will only grow. It will not be reallocated over and over.
|
||||||
|
std::vector<char> output_buffer;
|
||||||
|
size_t output_buffer_length;
|
||||||
|
|
||||||
|
bool process_line(const char *line, const size_t len, GCodeLine &buf);
|
||||||
|
void output_gcode_line(GCodeLine &buf);
|
||||||
|
|
||||||
|
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
|
||||||
|
// Then go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
|
||||||
|
void adjust_volumetric_rate();
|
||||||
|
|
||||||
|
// Push the text to the end of the output_buffer.
|
||||||
|
void push_to_output(const char *text, const size_t len, bool add_eol = true);
|
||||||
|
// Push an axis assignment to the end of the output buffer.
|
||||||
|
void push_axis_to_output(const char axis, const float value, bool add_eol = false);
|
||||||
|
// Push a G-code line to the output,
|
||||||
|
void push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment);
|
||||||
|
|
||||||
|
size_t circular_buffer_idx_head() const {
|
||||||
|
size_t idx = circular_buffer_pos + circular_buffer_size - circular_buffer_items;
|
||||||
|
if (idx >= circular_buffer_size)
|
||||||
|
idx -= circular_buffer_size;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t circular_buffer_idx_tail() const { return circular_buffer_pos; }
|
||||||
|
|
||||||
|
size_t circular_buffer_idx_prev(size_t idx) const {
|
||||||
|
idx += circular_buffer_size - 1;
|
||||||
|
if (idx >= circular_buffer_size)
|
||||||
|
idx -= circular_buffer_size;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t circular_buffer_idx_next(size_t idx) const {
|
||||||
|
if (++ idx >= circular_buffer_size)
|
||||||
|
idx -= circular_buffer_size;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif /* slic3r_GCode_PressureEqualizer_hpp_ */
|
|
@ -235,7 +235,7 @@ PerimeterGenerator::process()
|
||||||
// append perimeters for this slice as a collection
|
// append perimeters for this slice as a collection
|
||||||
if (!entities.empty())
|
if (!entities.empty())
|
||||||
this->loops->append(entities);
|
this->loops->append(entities);
|
||||||
}
|
} // for each loop of an island
|
||||||
|
|
||||||
// fill gaps
|
// fill gaps
|
||||||
if (!gaps.empty()) {
|
if (!gaps.empty()) {
|
||||||
|
@ -311,7 +311,7 @@ PerimeterGenerator::process()
|
||||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
|
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
|
||||||
this->fill_surfaces->surfaces.push_back(Surface(stInternal, *ex)); // use a bogus surface type
|
this->fill_surfaces->surfaces.push_back(Surface(stInternal, *ex)); // use a bogus surface type
|
||||||
}
|
}
|
||||||
}
|
} // for each island
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtrusionEntityCollection
|
ExtrusionEntityCollection
|
||||||
|
|
|
@ -19,6 +19,7 @@ REGISTER_CLASS(Wipe, "GCode::Wipe");
|
||||||
REGISTER_CLASS(GCode, "GCode");
|
REGISTER_CLASS(GCode, "GCode");
|
||||||
REGISTER_CLASS(GCodeSender, "GCode::Sender");
|
REGISTER_CLASS(GCodeSender, "GCode::Sender");
|
||||||
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
||||||
|
REGISTER_CLASS(GCodePressureEqualizer, "GCode::PressureEqualizer");
|
||||||
REGISTER_CLASS(Layer, "Layer");
|
REGISTER_CLASS(Layer, "Layer");
|
||||||
REGISTER_CLASS(SupportLayer, "Layer::Support");
|
REGISTER_CLASS(SupportLayer, "Layer::Support");
|
||||||
REGISTER_CLASS(LayerRegion, "Layer::Region");
|
REGISTER_CLASS(LayerRegion, "Layer::Region");
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
%{
|
%{
|
||||||
#include <xsinit.h>
|
#include <xsinit.h>
|
||||||
#include "libslic3r/Fill/FillBase.hpp"
|
#include "libslic3r/Fill/FillBase.hpp"
|
||||||
|
#include "libslic3r/PolylineCollection.hpp"
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%name{Slic3r::Filler} class Filler {
|
%name{Slic3r::Filler} class Filler {
|
||||||
|
|
|
@ -107,6 +107,11 @@
|
||||||
void set_enable_cooling_markers(bool value)
|
void set_enable_cooling_markers(bool value)
|
||||||
%code{% THIS->enable_cooling_markers = value; %};
|
%code{% THIS->enable_cooling_markers = value; %};
|
||||||
|
|
||||||
|
bool enable_extrusion_role_markers()
|
||||||
|
%code{% RETVAL = THIS->enable_extrusion_role_markers; %};
|
||||||
|
void set_enable_extrusion_role_markers(bool value)
|
||||||
|
%code{% THIS->enable_extrusion_role_markers = value; %};
|
||||||
|
|
||||||
int layer_count()
|
int layer_count()
|
||||||
%code{% RETVAL = THIS->layer_count; %};
|
%code{% RETVAL = THIS->layer_count; %};
|
||||||
void set_layer_count(int value)
|
void set_layer_count(int value)
|
||||||
|
|
32
xs/xsp/GCodePressureEqualizer.xsp
Normal file
32
xs/xsp/GCodePressureEqualizer.xsp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
%module{Slic3r::XS};
|
||||||
|
|
||||||
|
%{
|
||||||
|
#include <xsinit.h>
|
||||||
|
#include "libslic3r/GCode/PressureEqualizer.hpp"
|
||||||
|
%}
|
||||||
|
|
||||||
|
%name{Slic3r::GCode::PressureEqualizer} class GCodePressureEqualizer {
|
||||||
|
GCodePressureEqualizer(StaticPrintConfig* config)
|
||||||
|
%code%{ RETVAL = new GCodePressureEqualizer(dynamic_cast<GCodeConfig*>(config)); %};
|
||||||
|
~GCodePressureEqualizer();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// Process a next batch of G-code lines. Flush the internal buffers if asked for.
|
||||||
|
// const char* process(const char *szGCode, bool flush);
|
||||||
|
// std::string process(const char *szGCode, bool flush)
|
||||||
|
// %code{% const char *out = THIS->process(szGCode, flush); RETVAL = (out == NULL) ? "" : std::string(out); %};
|
||||||
|
|
||||||
|
%{
|
||||||
|
|
||||||
|
SV*
|
||||||
|
GCodePressureEqualizer::process(const char *szGCode, bool flush)
|
||||||
|
CODE:
|
||||||
|
const char *out = THIS->process(szGCode, flush);
|
||||||
|
RETVAL = newSVpv(out, THIS->get_output_buffer_length());
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
};
|
|
@ -209,6 +209,10 @@ GCodeWriter* O_OBJECT_SLIC3R
|
||||||
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||||
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||||
|
|
||||||
|
GCodePressureEqualizer* O_OBJECT_SLIC3R
|
||||||
|
Ref<GCodePressureEqualizer> O_OBJECT_SLIC3R_T
|
||||||
|
Clone<GCodePressureEqualizer> O_OBJECT_SLIC3R_T
|
||||||
|
|
||||||
BridgeDetector* O_OBJECT_SLIC3R
|
BridgeDetector* O_OBJECT_SLIC3R
|
||||||
Ref<BridgeDetector> O_OBJECT_SLIC3R_T
|
Ref<BridgeDetector> O_OBJECT_SLIC3R_T
|
||||||
Clone<BridgeDetector> O_OBJECT_SLIC3R_T
|
Clone<BridgeDetector> O_OBJECT_SLIC3R_T
|
||||||
|
|
|
@ -102,6 +102,9 @@
|
||||||
%typemap{GCodeSender*};
|
%typemap{GCodeSender*};
|
||||||
%typemap{Ref<GCodeSender>}{simple};
|
%typemap{Ref<GCodeSender>}{simple};
|
||||||
%typemap{Clone<GCodeSender>}{simple};
|
%typemap{Clone<GCodeSender>}{simple};
|
||||||
|
%typemap{GCodePressureEqualizer*};
|
||||||
|
%typemap{Ref<GCodePressureEqualizer>}{simple};
|
||||||
|
%typemap{Clone<GCodePressureEqualizer>}{simple};
|
||||||
%typemap{BridgeDetector*};
|
%typemap{BridgeDetector*};
|
||||||
%typemap{Ref<BridgeDetector>}{simple};
|
%typemap{Ref<BridgeDetector>}{simple};
|
||||||
%typemap{Clone<BridgeDetector>}{simple};
|
%typemap{Clone<BridgeDetector>}{simple};
|
||||||
|
|
Loading…
Reference in a new issue