mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-13 23:31:48 +08:00
Merge branch 'master' into cppsvg
This commit is contained in:
commit
b0378dddc6
@ -53,6 +53,8 @@ use constant FILE_WILDCARDS => {
|
||||
use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf)};
|
||||
|
||||
our $datadir;
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
our $no_controller;
|
||||
our $no_plater;
|
||||
our $mode;
|
||||
our $autosave;
|
||||
@ -64,6 +66,9 @@ our $Settings = {
|
||||
version_check => 1,
|
||||
autocenter => 1,
|
||||
background_processing => 1,
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
# By default, Prusa has the controller hidden.
|
||||
no_controller => 1,
|
||||
},
|
||||
};
|
||||
|
||||
@ -115,6 +120,8 @@ sub OnInit {
|
||||
$Settings->{_}{mode} ||= 'expert';
|
||||
$Settings->{_}{autocenter} //= 1;
|
||||
$Settings->{_}{background_processing} //= 1;
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
$Settings->{_}{no_controller} //= 1;
|
||||
}
|
||||
$Settings->{_}{version} = $Slic3r::VERSION;
|
||||
$self->save_settings;
|
||||
@ -122,8 +129,10 @@ sub OnInit {
|
||||
# application frame
|
||||
Wx::Image::AddHandler(Wx::PNGHandler->new);
|
||||
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
|
||||
mode => $mode // $Settings->{_}{mode},
|
||||
no_plater => $no_plater,
|
||||
mode => $mode // $Settings->{_}{mode},
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
no_controller => $no_controller // $Settings->{_}{no_controller},
|
||||
no_plater => $no_plater,
|
||||
);
|
||||
$self->SetTopWindow($frame);
|
||||
|
||||
|
@ -87,7 +87,8 @@ sub new {
|
||||
$bed_sizer->Add($sizer, 1, wxEXPAND, 0);
|
||||
}
|
||||
|
||||
$bed_sizer->AddSpacer(0);
|
||||
# XYZ home button
|
||||
$move_button->($bed_sizer, 'XYZ', 'house', 1, wxTOP, sub { $self->home(undef) });
|
||||
|
||||
# X buttons
|
||||
{
|
||||
@ -180,10 +181,11 @@ sub rel_move {
|
||||
sub home {
|
||||
my ($self, $axis) = @_;
|
||||
|
||||
$axis //= '';
|
||||
$self->sender->send(sprintf("G28 %s", $axis), 1);
|
||||
$self->{canvas}->set_pos(undef);
|
||||
$self->x_homed if $axis eq 'X';
|
||||
$self->y_homed if $axis eq 'Y';
|
||||
$self->x_homed(1) if $axis eq 'X';
|
||||
$self->y_homed(1) if $axis eq 'Y';
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -24,6 +24,8 @@ sub new {
|
||||
# store input params
|
||||
$self->{mode} = $params{mode};
|
||||
$self->{mode} = 'expert' if $self->{mode} !~ /^(?:simple|expert)$/;
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
$self->{no_controller} = $params{no_controller};
|
||||
$self->{no_plater} = $params{no_plater};
|
||||
$self->{loaded} = 0;
|
||||
|
||||
@ -96,7 +98,9 @@ sub _init_tabpanel {
|
||||
|
||||
if (!$self->{no_plater}) {
|
||||
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater");
|
||||
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
|
||||
if (!$self->{no_controller}) {
|
||||
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
|
||||
}
|
||||
}
|
||||
$self->{options_tabs} = {};
|
||||
|
||||
@ -109,7 +113,9 @@ sub _init_tabpanel {
|
||||
my $class_prefix = $self->{mode} eq 'simple' ? "Slic3r::GUI::SimpleTab::" : "Slic3r::GUI::Tab::";
|
||||
for my $tab_name (qw(print filament printer)) {
|
||||
my $tab;
|
||||
$tab = $self->{options_tabs}{$tab_name} = ($class_prefix . ucfirst $tab_name)->new($panel);
|
||||
$tab = $self->{options_tabs}{$tab_name} = ($class_prefix . ucfirst $tab_name)->new(
|
||||
$panel,
|
||||
no_controller => $self->{no_controller});
|
||||
$tab->on_value_change(sub {
|
||||
my ($opt_key, $value) = @_;
|
||||
|
||||
@ -136,7 +142,9 @@ sub _init_tabpanel {
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->update_presets($tab_name, @_);
|
||||
$self->{plater}->on_config_change($tab->config);
|
||||
$self->{controller}->update_presets($tab_name, @_);
|
||||
if ($self->{controller}) {
|
||||
$self->{controller}->update_presets($tab_name, @_);
|
||||
}
|
||||
}
|
||||
});
|
||||
$tab->load_presets;
|
||||
@ -246,9 +254,11 @@ sub _init_menubar {
|
||||
$self->_append_menu_item($windowMenu, "Select &Plater Tab\tCtrl+1", 'Show the plater', sub {
|
||||
$self->select_tab(0);
|
||||
}, undef, 'application_view_tile.png');
|
||||
$self->_append_menu_item($windowMenu, "Select &Controller Tab\tCtrl+T", 'Show the printer controller', sub {
|
||||
$self->select_tab(1);
|
||||
}, undef, 'printer_empty.png');
|
||||
if (!$self->{no_controller}) {
|
||||
$self->_append_menu_item($windowMenu, "Select &Controller Tab\tCtrl+T", 'Show the printer controller', sub {
|
||||
$self->select_tab(1);
|
||||
}, undef, 'printer_empty.png');
|
||||
}
|
||||
$windowMenu->AppendSeparator();
|
||||
$tab_offset += 2;
|
||||
}
|
||||
|
@ -58,6 +58,13 @@ sub new {
|
||||
default => $Slic3r::GUI::Settings->{_}{background_processing},
|
||||
readonly => !$Slic3r::have_threads,
|
||||
));
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'no_controller',
|
||||
type => 'bool',
|
||||
label => 'Disable USB/serial connection',
|
||||
tooltip => 'Disable communication with the printer over a serial / USB cable. This simplifies the user interface in case the printer is never attached to the computer.',
|
||||
default => $Slic3r::GUI::Settings->{_}{no_controller},
|
||||
));
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
@ -75,7 +82,7 @@ sub new {
|
||||
sub _accept {
|
||||
my $self = shift;
|
||||
|
||||
if ($self->{values}{mode}) {
|
||||
if ($self->{values}{mode} || defined($self->{values}{no_controller})) {
|
||||
Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective.");
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ sub new {
|
||||
});
|
||||
|
||||
$self->{config} = Slic3r::Config->new;
|
||||
$self->build;
|
||||
$self->build(%params);
|
||||
$self->update_tree;
|
||||
$self->_update;
|
||||
if ($self->hidden_options) {
|
||||
@ -985,6 +985,7 @@ sub title { 'Printer Settings' }
|
||||
|
||||
sub build {
|
||||
my $self = shift;
|
||||
my (%params) = @_;
|
||||
|
||||
$self->init_config_options(qw(
|
||||
bed_shape z_offset
|
||||
@ -1064,6 +1065,7 @@ sub build {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!$params{no_controller})
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('USB/Serial connection');
|
||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
||||
@ -1239,7 +1241,7 @@ sub build {
|
||||
|
||||
$self->{extruder_pages} = [];
|
||||
$self->_build_extruder_pages;
|
||||
$self->_update_serial_ports;
|
||||
$self->_update_serial_ports if (!$params{no_controller});
|
||||
}
|
||||
|
||||
sub _update_serial_ports {
|
||||
@ -1341,11 +1343,14 @@ sub _update {
|
||||
|
||||
my $config = $self->{config};
|
||||
|
||||
$self->get_field('serial_speed')->toggle($config->get('serial_port'));
|
||||
if ($config->get('serial_speed') && $config->get('serial_port')) {
|
||||
$self->{serial_test_btn}->Enable;
|
||||
} else {
|
||||
$self->{serial_test_btn}->Disable;
|
||||
my $serial_speed = $self->get_field('serial_speed');
|
||||
if ($serial_speed) {
|
||||
$self->get_field('serial_speed')->toggle($config->get('serial_port'));
|
||||
if ($config->get('serial_speed') && $config->get('serial_port')) {
|
||||
$self->{serial_test_btn}->Enable;
|
||||
} else {
|
||||
$self->{serial_test_btn}->Disable;
|
||||
}
|
||||
}
|
||||
if ($config->get('octoprint_host') && eval "use LWP::UserAgent; 1") {
|
||||
$self->{octoprint_host_test_btn}->Enable;
|
||||
|
@ -75,7 +75,8 @@ sub BUILD {
|
||||
}
|
||||
}
|
||||
}
|
||||
@mm3_per_mm = grep $_ != 0, @mm3_per_mm;
|
||||
# filter out 0-width segments
|
||||
@mm3_per_mm = grep $_ > 0.000001, @mm3_per_mm;
|
||||
if (@mm3_per_mm) {
|
||||
my $min_mm3_per_mm = min(@mm3_per_mm);
|
||||
# In order to honor max_print_speed we need to find a target volumetric
|
||||
|
10
slic3r.pl
10
slic3r.pl
@ -32,6 +32,7 @@ my %cli_options = ();
|
||||
'load=s@' => \$opt{load},
|
||||
'autosave=s' => \$opt{autosave},
|
||||
'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config},
|
||||
'no-controller' => \$opt{no_controller},
|
||||
'no-plater' => \$opt{no_plater},
|
||||
'gui-mode=s' => \$opt{gui_mode},
|
||||
'datadir=s' => \$opt{datadir},
|
||||
@ -99,10 +100,11 @@ my $gui;
|
||||
if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
||||
{
|
||||
no warnings 'once';
|
||||
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
|
||||
$Slic3r::GUI::no_plater = $opt{no_plater};
|
||||
$Slic3r::GUI::mode = $opt{gui_mode};
|
||||
$Slic3r::GUI::autosave = $opt{autosave};
|
||||
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
|
||||
$Slic3r::GUI::no_controller = $opt{no_controller};
|
||||
$Slic3r::GUI::no_plater = $opt{no_plater};
|
||||
$Slic3r::GUI::mode = $opt{gui_mode};
|
||||
$Slic3r::GUI::autosave = $opt{autosave};
|
||||
}
|
||||
$gui = Slic3r::GUI->new;
|
||||
setlocale(LC_NUMERIC, 'C');
|
||||
|
19
t/thin.t
19
t/thin.t
@ -1,4 +1,4 @@
|
||||
use Test::More tests => 21;
|
||||
use Test::More tests => 23;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
@ -8,7 +8,7 @@ BEGIN {
|
||||
}
|
||||
|
||||
use Slic3r;
|
||||
use List::Util qw(first sum);
|
||||
use List::Util qw(first sum none);
|
||||
use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon Y);
|
||||
use Slic3r::Test;
|
||||
|
||||
@ -91,7 +91,20 @@ if (0) {
|
||||
is scalar(@$res), 1, 'medial axis of a narrow rectangle with an extra vertex is still a single line';
|
||||
ok unscale($res->[0]->length) >= (200-100 - (120-100)) - epsilon, 'medial axis has still a reasonable length';
|
||||
ok !(grep { abs($_ - scale 150) < scaled_epsilon } map $_->[Y], map @$_, @$res2), "extra vertices don't influence medial axis";
|
||||
}
|
||||
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new(
|
||||
Slic3r::Polygon->new([1185881,829367],[1421988,1578184],[1722442,2303558],[2084981,2999998],[2506843,3662186],[2984809,4285086],[3515250,4863959],[4094122,5394400],[4717018,5872368],[5379210,6294226],[6075653,6656769],[6801033,6957229],[7549842,7193328],[8316383,7363266],[9094809,7465751],[9879211,7500000],[10663611,7465750],[11442038,7363265],[12208580,7193327],[12957389,6957228],[13682769,6656768],[14379209,6294227],[15041405,5872366],[15664297,5394401],[16243171,4863960],[16758641,4301424],[17251579,3662185],[17673439,3000000],[18035980,2303556],[18336441,1578177],[18572539,829368],[18750748,0],[19758422,0],[19727293,236479],[19538467,1088188],[19276136,1920196],[18942292,2726179],[18539460,3499999],[18070731,4235755],[17539650,4927877],[16950279,5571067],[16307090,6160437],[15614974,6691519],[14879209,7160248],[14105392,7563079],[13299407,7896927],[12467399,8159255],[11615691,8348082],[10750769,8461952],[9879211,8500000],[9007652,8461952],[8142729,8348082],[7291022,8159255],[6459015,7896927],[5653029,7563079],[4879210,7160247],[4143447,6691519],[3451331,6160437],[2808141,5571066],[2218773,4927878],[1687689,4235755],[1218962,3499999],[827499,2748020],[482284,1920196],[219954,1088186],[31126,236479],[0,0],[1005754,0]),
|
||||
);
|
||||
my $res = $expolygon->medial_axis(scale 1.324888, scale 0.25);
|
||||
is scalar(@$res), 1, 'medial axis of a semicircumference is a single line';
|
||||
|
||||
# check whether turns are all CCW or all CW
|
||||
my @lines = @{$res->[0]->lines};
|
||||
my @angles = map { $lines[$_-1]->ccw($lines[$_]->b) } 1..$#lines;
|
||||
ok !!(none { $_ < 0 } @angles) || (none { $_ > 0 } @angles),
|
||||
'all medial axis segments of a semicircumference have the same orientation';
|
||||
}
|
||||
|
||||
{
|
||||
@ -136,7 +149,7 @@ if (0) {
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_scale(
|
||||
[50, 100],
|
||||
[300, 102],
|
||||
[1000, 102],
|
||||
[50, 104],
|
||||
));
|
||||
my $res = $expolygon->medial_axis(scale 4, scale 0.5);
|
||||
|
19
utils/modifier_helpers/layer_generator.jscad
Normal file
19
utils/modifier_helpers/layer_generator.jscad
Normal file
@ -0,0 +1,19 @@
|
||||
// title: Layer_generator
|
||||
// written by: Joseph Lenox
|
||||
// Used for generating cubes oriented about the center
|
||||
// for making simple modifier meshes.
|
||||
|
||||
var width = 100;
|
||||
var layer_height = 0.3;
|
||||
var z = 30;
|
||||
function main() {
|
||||
|
||||
return cube(size=[width,width,layer_height], center=true).translate([0,0,z]);
|
||||
}
|
||||
function getParameterDefinitions() {
|
||||
return [
|
||||
{ name: 'width', type: 'float', initial: 100, caption: "Width of the cube:" },
|
||||
{ name: 'layer_height', type: 'float', initial: 0.3, caption: "Layer height used:" },
|
||||
{ name: 'z', type: 'float', initial: 0, caption: "Z:" }
|
||||
];
|
||||
}
|
24
utils/modifier_helpers/solid_layers.scad
Normal file
24
utils/modifier_helpers/solid_layers.scad
Normal file
@ -0,0 +1,24 @@
|
||||
// Used to generate a modifier mesh to do something every few layers.
|
||||
// Load into OpenSCAD, tweak the variables below, export as STL and load as
|
||||
// a modifier mesh. Then change settings for the modifier mesh.
|
||||
|
||||
// Written by Joseph Lenox; in public domain.
|
||||
|
||||
layer_height = 0.3; // set to layer height in slic3r for "best" results.
|
||||
number_of_solid_layers = 2;
|
||||
N = 4; // N > number_of_solid_layers or else the whole thing will be solid
|
||||
model_height = 300.0;
|
||||
model_width = 300.0; // these two should be at least as big as the model
|
||||
model_depth = 300.0; // but bigger isn't a problem
|
||||
initial_offset=0; // don't generate below this
|
||||
|
||||
position_on_bed=[0,0,0]; // in case you need to move it around
|
||||
|
||||
// don't touch below unless you know what you are doing.
|
||||
simple_layers = round(model_height/layer_height);
|
||||
translate(position_on_bed)
|
||||
for (i = [initial_offset:N:simple_layers]) {
|
||||
translate([0,0,i*layer_height])
|
||||
translate([0,0,(layer_height*number_of_solid_layers)/2])
|
||||
cube([model_width,model_depth,layer_height*number_of_solid_layers], center=true);
|
||||
}
|
13
utils/post-processing/fan_kickstart.py
Normal file
13
utils/post-processing/fan_kickstart.py
Normal file
@ -0,0 +1,13 @@
|
||||
#!/usr/bin/python
|
||||
import sys
|
||||
import re
|
||||
|
||||
sea = re.compile("M106 S[1-9]+[0-9]*")
|
||||
rep = re.compile("M106 S255\n\g<0>")
|
||||
out = open(sys.argv[1]+"_fixed", 'w')
|
||||
with open(sys.argv[1]) as f:
|
||||
for r in f:
|
||||
if re.search(sea, r) is not None:
|
||||
out.write(re.sub(sea,"M106 S255\n\g<0>",r))
|
||||
else:
|
||||
out.write(r)
|
@ -38,6 +38,8 @@ src/libslic3r/GCodeWriter.cpp
|
||||
src/libslic3r/GCodeWriter.hpp
|
||||
src/libslic3r/Geometry.cpp
|
||||
src/libslic3r/Geometry.hpp
|
||||
src/libslic3r/IO.cpp
|
||||
src/libslic3r/IO.hpp
|
||||
src/libslic3r/Layer.cpp
|
||||
src/libslic3r/Layer.hpp
|
||||
src/libslic3r/LayerRegion.cpp
|
||||
|
@ -234,7 +234,7 @@ stl_write_vrml(stl_file *stl, char *file) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void stl_write_obj (stl_file *stl, char *file) {
|
||||
void stl_write_obj (stl_file *stl, const char *file) {
|
||||
int i;
|
||||
FILE* fp;
|
||||
|
||||
|
@ -135,7 +135,7 @@ typedef struct {
|
||||
} stl_file;
|
||||
|
||||
|
||||
extern void stl_open(stl_file *stl, char *file);
|
||||
extern void stl_open(stl_file *stl, const char *file);
|
||||
extern void stl_close(stl_file *stl);
|
||||
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
|
||||
extern void stl_print_edges(stl_file *stl, FILE *file);
|
||||
@ -171,7 +171,7 @@ extern void stl_mirror_xz(stl_file *stl);
|
||||
extern void stl_open_merge(stl_file *stl, char *file);
|
||||
extern void stl_invalidate_shared_vertices(stl_file *stl);
|
||||
extern void stl_generate_shared_vertices(stl_file *stl);
|
||||
extern void stl_write_obj(stl_file *stl, char *file);
|
||||
extern void stl_write_obj(stl_file *stl, const char *file);
|
||||
extern void stl_write_off(stl_file *stl, char *file);
|
||||
extern void stl_write_dxf(stl_file *stl, char *file, char *label);
|
||||
extern void stl_write_vrml(stl_file *stl, char *file);
|
||||
@ -182,7 +182,7 @@ extern void stl_calculate_volume(stl_file *stl);
|
||||
extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag);
|
||||
|
||||
extern void stl_initialize(stl_file *stl);
|
||||
extern void stl_count_facets(stl_file *stl, char *file);
|
||||
extern void stl_count_facets(stl_file *stl, const char *file);
|
||||
extern void stl_allocate(stl_file *stl);
|
||||
extern void stl_read(stl_file *stl, int first_facet, int first);
|
||||
extern void stl_facet_stats(stl_file *stl, stl_facet facet, int first);
|
||||
|
@ -34,7 +34,7 @@
|
||||
#endif
|
||||
|
||||
void
|
||||
stl_open(stl_file *stl, char *file) {
|
||||
stl_open(stl_file *stl, const char *file) {
|
||||
stl_initialize(stl);
|
||||
stl_count_facets(stl, file);
|
||||
stl_allocate(stl);
|
||||
@ -65,7 +65,7 @@ stl_initialize(stl_file *stl) {
|
||||
}
|
||||
|
||||
void
|
||||
stl_count_facets(stl_file *stl, char *file) {
|
||||
stl_count_facets(stl_file *stl, const char *file) {
|
||||
long file_size;
|
||||
int header_num_facets;
|
||||
int num_facets;
|
||||
|
@ -174,17 +174,10 @@ void
|
||||
ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polylines) const
|
||||
{
|
||||
// init helper object
|
||||
Slic3r::Geometry::MedialAxis ma(max_width, min_width);
|
||||
ma.expolygon = this;
|
||||
Slic3r::Geometry::MedialAxis ma(max_width, min_width, this);
|
||||
ma.lines = this->lines();
|
||||
|
||||
// populate list of segments for the Voronoi diagram
|
||||
ma.lines = this->contour.lines();
|
||||
for (Polygons::const_iterator hole = this->holes.begin(); hole != this->holes.end(); ++hole) {
|
||||
Lines lines = hole->lines();
|
||||
ma.lines.insert(ma.lines.end(), lines.begin(), lines.end());
|
||||
}
|
||||
|
||||
// compute the Voronoi diagram
|
||||
// compute the Voronoi diagram and extract medial axis polylines
|
||||
ThickPolylines pp;
|
||||
ma.build(&pp);
|
||||
|
||||
@ -195,15 +188,14 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
|
||||
svg.Close();
|
||||
*/
|
||||
|
||||
// find the maximum width returned
|
||||
/* Find the maximum width returned; we're going to use this for validating and
|
||||
filtering the output segments. */
|
||||
double max_w = 0;
|
||||
if (!pp.empty()) {
|
||||
std::vector<double> widths;
|
||||
for (ThickPolylines::const_iterator it = pp.begin(); it != pp.end(); ++it)
|
||||
widths.insert(widths.end(), it->width.begin(), it->width.end());
|
||||
max_w = *std::max_element(widths.begin(), widths.end());
|
||||
}
|
||||
for (ThickPolylines::const_iterator it = pp.begin(); it != pp.end(); ++it)
|
||||
max_w = fmaxf(max_w, *std::max_element(it->width.begin(), it->width.end()));
|
||||
|
||||
/* Loop through all returned polylines in order to extend their endpoints to the
|
||||
expolygon boundaries */
|
||||
bool removed = false;
|
||||
for (size_t i = 0; i < pp.size(); ++i) {
|
||||
ThickPolyline& polyline = pp[i];
|
||||
@ -502,4 +494,15 @@ ExPolygon::lines() const
|
||||
return lines;
|
||||
}
|
||||
|
||||
std::string
|
||||
ExPolygon::dump_perl() const
|
||||
{
|
||||
std::ostringstream ret;
|
||||
ret << "[" << this->contour.dump_perl();
|
||||
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h)
|
||||
ret << "," << h->dump_perl();
|
||||
ret << "]";
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ class ExPolygon
|
||||
void triangulate_pp(Polygons* polygons) const;
|
||||
void triangulate_p2t(Polygons* polygons) const;
|
||||
Lines lines() const;
|
||||
std::string dump_perl() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <math.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -177,7 +178,7 @@ Wipe::wipe(GCode &gcodegen, bool toolchange)
|
||||
/* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
|
||||
due to rounding (TODO: test and/or better math for this) */
|
||||
double dE = length * (segment_length / wipe_dist) * 0.95;
|
||||
gcode += gcodegen.writer.set_speed(wipe_speed*60, gcodegen.enable_cooling_markers ? ";_WIPE" : "");
|
||||
gcode += gcodegen.writer.set_speed(wipe_speed*60, "", gcodegen.enable_cooling_markers ? ";_WIPE" : "");
|
||||
gcode += gcodegen.writer.extrude_to_xy(
|
||||
gcodegen.point_to_gcode(line->b),
|
||||
-dE,
|
||||
@ -390,7 +391,7 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed)
|
||||
Polygon polygon = loop.polygon();
|
||||
Point centroid = polygon.centroid();
|
||||
last_pos = Point(polygon.bounding_box().max.x, centroid.y);
|
||||
last_pos.rotate(rand() % 2*PI, centroid);
|
||||
last_pos.rotate(fmod((float)rand()/16.0, 2.0*PI), centroid);
|
||||
}
|
||||
loop.split_at(last_pos);
|
||||
}
|
||||
@ -562,7 +563,7 @@ GCode::_extrude(ExtrusionPath path, std::string description, double speed)
|
||||
// extrude arc or line
|
||||
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" : "");
|
||||
gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : "");
|
||||
double path_length = 0;
|
||||
{
|
||||
std::string comment = this->config.gcode_comments ? description : "";
|
||||
|
@ -273,11 +273,13 @@ GCodeWriter::toolchange(unsigned int extruder_id)
|
||||
}
|
||||
|
||||
std::string
|
||||
GCodeWriter::set_speed(double F, const std::string &comment) const
|
||||
GCodeWriter::set_speed(double F, const std::string &comment,
|
||||
const std::string &cooling_marker) const
|
||||
{
|
||||
std::ostringstream gcode;
|
||||
gcode << "G1 F" << F;
|
||||
COMMENT(comment);
|
||||
gcode << cooling_marker;
|
||||
gcode << "\n";
|
||||
return gcode.str();
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class GCodeWriter {
|
||||
bool need_toolchange(unsigned int extruder_id) const;
|
||||
std::string set_extruder(unsigned int extruder_id);
|
||||
std::string toolchange(unsigned int extruder_id);
|
||||
std::string set_speed(double F, const std::string &comment = std::string()) const;
|
||||
std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const;
|
||||
std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string());
|
||||
std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string());
|
||||
std::string travel_to_z(double z, const std::string &comment = std::string());
|
||||
|
@ -324,7 +324,7 @@ MedialAxis::build(ThickPolylines* polylines)
|
||||
if (edge->is_secondary() || edge->is_infinite()) continue;
|
||||
|
||||
// don't re-validate twins
|
||||
if (seen_edges.find(&*edge) != seen_edges.end()) continue;
|
||||
if (seen_edges.find(&*edge) != seen_edges.end()) continue; // TODO: is this needed?
|
||||
seen_edges.insert(&*edge);
|
||||
seen_edges.insert(edge->twin());
|
||||
|
||||
@ -445,49 +445,66 @@ MedialAxis::validate_edge(const VD::edge_type* edge)
|
||||
}
|
||||
|
||||
// retrieve the original line segments which generated the edge we're checking
|
||||
const VD::cell_type* cell1 = edge->cell();
|
||||
const VD::cell_type* cell2 = edge->twin()->cell();
|
||||
const Line &segment1 = this->retrieve_segment(cell1);
|
||||
const Line &segment2 = this->retrieve_segment(cell2);
|
||||
const VD::cell_type* cell_l = edge->cell();
|
||||
const VD::cell_type* cell_r = edge->twin()->cell();
|
||||
const Line &segment_l = this->retrieve_segment(cell_l);
|
||||
const Line &segment_r = this->retrieve_segment(cell_r);
|
||||
|
||||
/* Calculate thickness of the section at both the endpoints of this edge.
|
||||
Our Voronoi edge is part of a CCW sequence going around its Voronoi cell
|
||||
(segment1). This edge's twin goes around segment2. Thus, segment2 is
|
||||
oriented in the same direction as our main edge, and segment1 is oriented
|
||||
/*
|
||||
SVG svg("edge.svg");
|
||||
svg.draw(*this->expolygon);
|
||||
svg.draw(line);
|
||||
svg.draw(segment_l, "red");
|
||||
svg.draw(segment_r, "blue");
|
||||
svg.Close();
|
||||
*/
|
||||
|
||||
/* Calculate thickness of the cross-section at both the endpoints of this edge.
|
||||
Our Voronoi edge is part of a CCW sequence going around its Voronoi cell
|
||||
located on the left side. (segment_l).
|
||||
This edge's twin goes around segment_r. Thus, segment_r is
|
||||
oriented in the same direction as our main edge, and segment_l is oriented
|
||||
in the same direction as our twin edge.
|
||||
We used to only consider the (half-)distances to segment2, and that works
|
||||
whenever segment1 and segment2 are almost specular and facing. However,
|
||||
We used to only consider the (half-)distances to segment_r, and that works
|
||||
whenever segment_l and segment_r are almost specular and facing. However,
|
||||
at curves they are staggered and they only face for a very little length
|
||||
(such visibility actually coincides with our very short edge). This is why
|
||||
we calculate w0 and w1 this way.
|
||||
When cell1 or cell2 don't refer to the segment but only to an endpoint, we
|
||||
(our very short edge represents such visibility).
|
||||
Both w0 and w1 can be calculated either towards cell_l or cell_r with equal
|
||||
results by Voronoi definition.
|
||||
When cell_l or cell_r don't refer to the segment but only to an endpoint, we
|
||||
calculate the distance to that endpoint instead. */
|
||||
|
||||
coordf_t w0 = cell2->contains_segment()
|
||||
? line.a.perp_distance_to(segment2)*2
|
||||
: line.a.distance_to(this->retrieve_endpoint(cell2))*2;
|
||||
coordf_t w0 = cell_r->contains_segment()
|
||||
? line.a.distance_to(segment_r)*2
|
||||
: line.a.distance_to(this->retrieve_endpoint(cell_r))*2;
|
||||
|
||||
coordf_t w1 = cell1->contains_segment()
|
||||
? line.b.perp_distance_to(segment1)*2
|
||||
: line.b.distance_to(this->retrieve_endpoint(cell1))*2;
|
||||
coordf_t w1 = cell_l->contains_segment()
|
||||
? line.b.distance_to(segment_l)*2
|
||||
: line.b.distance_to(this->retrieve_endpoint(cell_l))*2;
|
||||
|
||||
// if this edge is the centerline for a very thin area, we might want to skip it
|
||||
// in case the area is too thin
|
||||
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON) {
|
||||
if (cell1->contains_segment() && cell2->contains_segment()) {
|
||||
// calculate the relative angle between the two boundary segments
|
||||
double angle = fabs(segment2.orientation() - segment1.orientation());
|
||||
if (cell_l->contains_segment() && cell_r->contains_segment()) {
|
||||
// calculate the relative angle between the two boundary segments
|
||||
double angle = fabs(segment_r.orientation() - segment_l.orientation());
|
||||
if (angle > PI) angle = 2*PI - angle;
|
||||
assert(angle >= 0 && angle <= PI);
|
||||
|
||||
// fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction)
|
||||
// we're interested only in segments close to the second case (facing segments)
|
||||
// so we allow some tolerance.
|
||||
// this filter ensures that we're dealing with a narrow/oriented area (longer than thick)
|
||||
// we don't run it on edges not generated by two segments (thus generated by one segment
|
||||
// and the endpoint of another segment), since their orientation would not be meaningful
|
||||
if (PI - angle > PI/8) {
|
||||
// angle is not narrow enough
|
||||
|
||||
// fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction)
|
||||
// we're interested only in segments close to the second case (facing segments)
|
||||
// so we allow some tolerance.
|
||||
// this filter ensures that we're dealing with a narrow/oriented area (longer than thick)
|
||||
// we don't run it on edges not generated by two segments (thus generated by one segment
|
||||
// and the endpoint of another segment), since their orientation would not be meaningful
|
||||
if (fabs(angle - PI) > PI/5) return false;
|
||||
} else {
|
||||
return false;
|
||||
// only apply this filter to segments that are not too short otherwise their
|
||||
// angle could possibly be not meaningful
|
||||
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= this->min_width)
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (w0 < this->min_width && w1 < this->min_width)
|
||||
|
@ -46,8 +46,8 @@ class MedialAxis {
|
||||
const ExPolygon* expolygon;
|
||||
double max_width;
|
||||
double min_width;
|
||||
MedialAxis(double _max_width, double _min_width)
|
||||
: max_width(_max_width), min_width(_min_width), expolygon(NULL) {};
|
||||
MedialAxis(double _max_width, double _min_width, const ExPolygon* _expolygon = NULL)
|
||||
: max_width(_max_width), min_width(_min_width), expolygon(_expolygon) {};
|
||||
void build(ThickPolylines* polylines);
|
||||
void build(Polylines* polylines);
|
||||
|
||||
|
47
xs/src/libslic3r/IO.cpp
Normal file
47
xs/src/libslic3r/IO.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "IO.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Slic3r { namespace IO {
|
||||
|
||||
bool
|
||||
STL::read_file(std::string input_file, Model* model)
|
||||
{
|
||||
// TODO: encode file name
|
||||
// TODO: check that file exists
|
||||
|
||||
TriangleMesh mesh;
|
||||
mesh.ReadSTLFile(input_file);
|
||||
mesh.repair();
|
||||
|
||||
if (mesh.facets_count() == 0)
|
||||
throw std::runtime_error("This STL file couldn't be read because it's empty.");
|
||||
|
||||
ModelObject* object = model->add_object();
|
||||
object->name = input_file; // TODO: use basename()
|
||||
object->input_file = input_file;
|
||||
|
||||
ModelVolume* volume = object->add_volume(mesh);
|
||||
volume->name = input_file; // TODO: use basename()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
STL::write(TriangleMesh& mesh, std::string output_file, bool binary)
|
||||
{
|
||||
if (binary) {
|
||||
mesh.write_binary(output_file);
|
||||
} else {
|
||||
mesh.write_ascii(output_file);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
OBJ::write(TriangleMesh& mesh, std::string output_file)
|
||||
{
|
||||
mesh.WriteOBJFile(output_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
} }
|
26
xs/src/libslic3r/IO.hpp
Normal file
26
xs/src/libslic3r/IO.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef slic3r_IO_hpp_
|
||||
#define slic3r_IO_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Model.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r { namespace IO {
|
||||
|
||||
class STL
|
||||
{
|
||||
public:
|
||||
bool read_file(std::string input_file, Model* model);
|
||||
bool write(TriangleMesh& mesh, std::string output_file, bool binary = true);
|
||||
};
|
||||
|
||||
class OBJ
|
||||
{
|
||||
public:
|
||||
bool write(TriangleMesh& mesh, std::string output_file);
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
#endif
|
@ -212,6 +212,12 @@ Line::intersection(const Line& line, Point* intersection) const
|
||||
return false; // not intersecting
|
||||
}
|
||||
|
||||
double
|
||||
Line::ccw(const Point& point) const
|
||||
{
|
||||
return point.ccw(*this);
|
||||
}
|
||||
|
||||
Pointf3
|
||||
Linef3::intersect_plane(double z) const
|
||||
{
|
||||
|
@ -44,6 +44,7 @@ class Line
|
||||
void extend_end(double distance);
|
||||
void extend_start(double distance);
|
||||
bool intersection(const Line& line, Point* intersection) const;
|
||||
double ccw(const Point& point) const;
|
||||
};
|
||||
|
||||
class ThickLine : public Line
|
||||
|
@ -128,6 +128,19 @@ MultiPoint::intersection(const Line& line, Point* intersection) const
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string
|
||||
MultiPoint::dump_perl() const
|
||||
{
|
||||
std::ostringstream ret;
|
||||
ret << "[";
|
||||
for (Points::const_iterator p = this->points.begin(); p != this->points.end(); ++p) {
|
||||
ret << p->dump_perl();
|
||||
if (p != this->points.end()-1) ret << ",";
|
||||
}
|
||||
ret << "]";
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
Points
|
||||
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
|
||||
{
|
||||
|
@ -37,6 +37,7 @@ class MultiPoint
|
||||
void append(const Points &points);
|
||||
void append(const Points::const_iterator &begin, const Points::const_iterator &end);
|
||||
bool intersection(const Line& line, Point* intersection) const;
|
||||
std::string dump_perl() const;
|
||||
|
||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||
};
|
||||
|
@ -92,7 +92,7 @@ PerimeterGenerator::process()
|
||||
|
||||
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
||||
// (actually, something larger than that still may exist due to mitering or other causes)
|
||||
coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 2);
|
||||
coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 3);
|
||||
ExPolygons expp = offset2_ex(diffpp, -min_width/2, +min_width/2);
|
||||
|
||||
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
|
||||
|
@ -26,6 +26,14 @@ Point::wkt() const
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
Point::dump_perl() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "[" << this->x << "," << this->y << "]";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void
|
||||
Point::scale(double factor)
|
||||
{
|
||||
@ -313,6 +321,14 @@ Pointf::wkt() const
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
Pointf::dump_perl() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "[" << this->x << "," << this->y << "]";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void
|
||||
Pointf::scale(double factor)
|
||||
{
|
||||
|
@ -38,6 +38,7 @@ class Point
|
||||
};
|
||||
bool operator==(const Point& rhs) const;
|
||||
std::string wkt() const;
|
||||
std::string dump_perl() const;
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
void translate(const Vector &vector);
|
||||
@ -87,6 +88,7 @@ class Pointf
|
||||
return Pointf(unscale(p.x), unscale(p.y));
|
||||
};
|
||||
std::string wkt() const;
|
||||
std::string dump_perl() const;
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
void translate(const Vectorf &vector);
|
||||
|
@ -1015,7 +1015,7 @@ PrintConfigDef::PrintConfigDef()
|
||||
|
||||
def = this->add("standby_temperature_delta", coInt);
|
||||
def->label = "Temperature variation";
|
||||
def->tooltip = "Temperature difference to be applied when an extruder is not active.";
|
||||
def->tooltip = "Temperature difference to be applied when an extruder is not active. Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped.";
|
||||
def->sidetext = "∆°C";
|
||||
def->cli = "standby-temperature-delta=i";
|
||||
def->min = -500;
|
||||
|
@ -70,20 +70,20 @@ TriangleMesh::~TriangleMesh() {
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::ReadSTLFile(char* input_file) {
|
||||
stl_open(&stl, input_file);
|
||||
TriangleMesh::ReadSTLFile(const std::string &input_file) {
|
||||
stl_open(&stl, input_file.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::write_ascii(char* output_file)
|
||||
TriangleMesh::write_ascii(const std::string &output_file)
|
||||
{
|
||||
stl_write_ascii(&this->stl, output_file, "");
|
||||
stl_write_ascii(&this->stl, output_file.c_str(), "");
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::write_binary(char* output_file)
|
||||
TriangleMesh::write_binary(const std::string &output_file)
|
||||
{
|
||||
stl_write_binary(&this->stl, output_file, "");
|
||||
stl_write_binary(&this->stl, output_file.c_str(), "");
|
||||
}
|
||||
|
||||
void
|
||||
@ -173,9 +173,9 @@ TriangleMesh::facets_count() const
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMesh::WriteOBJFile(char* output_file) {
|
||||
TriangleMesh::WriteOBJFile(const std::string &output_file) {
|
||||
stl_generate_shared_vertices(&stl);
|
||||
stl_write_obj(&stl, output_file);
|
||||
stl_write_obj(&stl, output_file.c_str());
|
||||
}
|
||||
|
||||
void TriangleMesh::scale(float factor)
|
||||
|
@ -24,11 +24,11 @@ class TriangleMesh
|
||||
TriangleMesh& operator= (TriangleMesh other);
|
||||
void swap(TriangleMesh &other);
|
||||
~TriangleMesh();
|
||||
void ReadSTLFile(char* input_file);
|
||||
void write_ascii(char* output_file);
|
||||
void write_binary(char* output_file);
|
||||
void ReadSTLFile(const std::string &input_file);
|
||||
void write_ascii(const std::string &output_file);
|
||||
void write_binary(const std::string &output_file);
|
||||
void repair();
|
||||
void WriteOBJFile(char* output_file);
|
||||
void WriteOBJFile(const std::string &output_file);
|
||||
void scale(float factor);
|
||||
void scale(const Pointf3 &versor);
|
||||
void translate(float x, float y, float z);
|
||||
|
@ -41,6 +41,8 @@
|
||||
%code{% RETVAL = Polyline(*THIS); %};
|
||||
Clone<Point> normal();
|
||||
Clone<Point> vector();
|
||||
double ccw(Point* point)
|
||||
%code{% RETVAL = THIS->ccw(*point); %};
|
||||
%{
|
||||
|
||||
Line*
|
||||
|
@ -10,11 +10,11 @@
|
||||
~TriangleMesh();
|
||||
Clone<TriangleMesh> clone()
|
||||
%code{% RETVAL = THIS; %};
|
||||
void ReadSTLFile(char* input_file);
|
||||
void write_ascii(char* output_file);
|
||||
void write_binary(char* output_file);
|
||||
void ReadSTLFile(std::string input_file);
|
||||
void write_ascii(std::string output_file);
|
||||
void write_binary(std::string output_file);
|
||||
void repair();
|
||||
void WriteOBJFile(char* output_file);
|
||||
void WriteOBJFile(std::string output_file);
|
||||
void scale(float factor);
|
||||
void scale_xyz(Pointf3* versor)
|
||||
%code{% THIS->scale(*versor); %};
|
||||
|
Loading…
x
Reference in New Issue
Block a user