Merge branch 'master' into cppsvg

This commit is contained in:
Alessandro Ranellucci 2016-06-22 17:20:34 +02:00
commit b0378dddc6
36 changed files with 355 additions and 110 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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:" }
];
}

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

View 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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -41,6 +41,8 @@
%code{% RETVAL = Polyline(*THIS); %};
Clone<Point> normal();
Clone<Point> vector();
double ccw(Point* point)
%code{% RETVAL = THIS->ccw(*point); %};
%{
Line*

View File

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