Merge branch 'master' into sender

This commit is contained in:
Alessandro Ranellucci 2015-01-04 19:36:28 +01:00
commit 3ab4d4b094
16 changed files with 192 additions and 61 deletions

View File

@ -201,10 +201,10 @@ sub _init_menubar {
$self->_append_menu_item($self->{plater_menu}, "Export G-code...", 'Export current plate as G-code', sub {
$plater->export_gcode;
});
$self->_append_menu_item($self->{plater_menu}, "Export STL...", 'Export current plate as STL', sub {
$self->_append_menu_item($self->{plater_menu}, "Export plate as STL...", 'Export current plate as STL', sub {
$plater->export_stl;
});
$self->_append_menu_item($self->{plater_menu}, "Export AMF...", 'Export current plate as AMF', sub {
$self->_append_menu_item($self->{plater_menu}, "Export plate as AMF...", 'Export current plate as AMF', sub {
$plater->export_amf;
});

View File

@ -14,6 +14,8 @@ sub BUILD {
$self->growler(Growl::GNTP->new(AppName => 'Slic3r', AppIcon => $icon));
$self->growler->register([{Name => 'SKEIN_DONE', DisplayName => 'Slicing Done'}]);
};
# if register() fails (for example because of a timeout), disable growler at all
$self->growler(undef) if $@;
}
}

View File

@ -1009,6 +1009,9 @@ sub export_gcode {
$self->on_export_completed($result);
}
# this updates buttons status
$self->object_list_changed;
return $self->{export_gcode_output_file};
}
@ -1094,6 +1097,9 @@ sub on_export_completed {
$self->send_gcode if $send_gcode;
$self->{print_file} = undef;
$self->{send_gcode_file} = undef;
# this updates buttons status
$self->object_list_changed;
}
sub do_print {
@ -1122,7 +1128,7 @@ sub send_gcode {
$self->statusbar->StartBusy;
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->timeout(180);
my $res = $ua->post(
"http://" . $self->{config}->octoprint_host . "/api/files/local",
@ -1158,6 +1164,19 @@ sub export_stl {
Slic3r::thread_cleanup() if $Slic3r::have_threads;
}
sub export_object_stl {
my $self = shift;
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx];
my $output_file = $self->_get_export_file('STL') or return;
Slic3r::Format::STL->write_file($output_file, $model_object->mesh, binary => 1);
$self->statusbar->SetStatusText("STL file exported to $output_file");
}
sub export_amf {
my $self = shift;
@ -1405,6 +1424,11 @@ sub object_list_changed {
$self->{"btn_$_"}->$method
for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl print send_gcode);
if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) {
$self->{btn_export_gcode}->Disable;
$self->{btn_send_gcode}->Disable;
}
if ($self->{htoolbar}) {
$self->{htoolbar}->EnableTool($_, $have_objects)
for (TB_RESET, TB_ARRANGE);
@ -1584,6 +1608,10 @@ sub object_menu {
$frame->_append_menu_item($menu, "Settings…", 'Open the object editor dialog', sub {
$self->object_settings_dialog;
});
$menu->AppendSeparator();
$frame->_append_menu_item($menu, "Export object as STL…", 'Export this single object as STL file', sub {
$self->export_object_stl;
});
return $menu;
}

View File

@ -8,7 +8,7 @@ use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
use base qw(Wx::GLCanvas Class::Accessor);
use Math::Trig qw(asin);
use List::Util qw(reduce min max first);
use Slic3r::Geometry qw(X Y Z MIN MAX triangle_normal normalize deg2rad tan scale unscale);
use Slic3r::Geometry qw(X Y Z MIN MAX triangle_normal normalize deg2rad tan scale unscale scaled_epsilon);
use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl);
use Wx::GLCanvas qw(:all);
@ -28,6 +28,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
bed_shape
bed_triangles
bed_grid_lines
background
origin
_mouse_pos
_hover_volume_idx
@ -41,7 +42,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
use constant TRACKBALLSIZE => 0.8;
use constant TURNTABLE_MODE => 1;
use constant GROUND_Z => 0.02;
use constant GROUND_Z => -0.02;
use constant SELECTED_COLOR => [0,1,0,1];
use constant HOVER_COLOR => [0.8,0.8,0,1];
use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ];
@ -60,6 +61,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "",
[WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0]);
$self->background(1);
$self->_quat((0, 0, 0, 1));
$self->_stheta(45);
$self->_sphi(45);
@ -327,7 +329,15 @@ sub set_bed_shape {
for (my $y = $bed_bb->y_min; $y <= $bed_bb->y_max; $y += scale 10) {
push @lines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]);
}
@lines = @{intersection_pl(\@lines, [ @$expolygon ])};
# clip with a slightly grown expolygon because our lines lay on the contours and
# may get erroneously clipped
@lines = @{intersection_pl(\@lines, [ @{$expolygon->offset(+scaled_epsilon)} ])};
# append bed contours
foreach my $line (map @{$_->lines}, @$expolygon) {
push @lines, $line->as_polyline;
}
my @points = ();
foreach my $polyline (@lines) {
push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$polyline; #))
@ -580,7 +590,7 @@ sub Resize {
return unless $self->GetContext;
$self->_dirty(0);
$self->SetCurrent($self->GetContext);
glViewport(0, 0, $x, $y);
@ -693,8 +703,29 @@ sub Render {
glFinish();
glEnable(GL_LIGHTING);
}
# draw objects
$self->draw_volumes;
# draw fixed background
if ($self->background) {
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glBegin(GL_QUADS);
glColor3f(0.0,0.0,0.0);
glVertex2f(-1.0,-1.0);
glVertex2f(1,-1.0);
glColor3f(10/255,98/255,144/255);
glVertex2f(1, 1);
glVertex2f(-1.0, 1);
glEnd();
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
# draw ground and axes
glDisable(GL_LIGHTING);
@ -704,26 +735,29 @@ sub Render {
# draw ground
my $ground_z = GROUND_Z;
if ($self->bed_triangles) {
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(0.6, 0.7, 0.5, 0.3);
glColor4f(0.8, 0.6, 0.5, 0.4);
glNormal3d(0,0,1);
glVertexPointer_p(3, $self->bed_triangles);
glDrawArrays(GL_TRIANGLES, 0, $self->bed_triangles->elements / 3);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
# draw grid
glTranslatef(0, 0, 0.02);
glLineWidth(3);
glColor3f(0.95, 0.95, 0.95);
glColor4f(0.2, 0.2, 0.2, 0.4);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer_p(3, $self->bed_grid_lines);
glDrawArrays(GL_LINES, 0, $self->bed_grid_lines->elements / 3);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_BLEND);
}
my $volumes_bb = $self->volumes_bounding_box;
@ -774,6 +808,9 @@ sub Render {
glEnable(GL_LIGHTING);
# draw objects
$self->draw_volumes;
glFlush();
$self->SwapBuffers();

View File

@ -6,7 +6,7 @@ use utf8;
use File::Basename qw(basename);
use List::Util qw(first);
use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :treectrl :window
wxTheApp);
:button wxTheApp);
use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_TREE_SEL_CHANGED);
use base qw(Wx::Panel Class::Accessor);
@ -36,8 +36,10 @@ sub new {
$self->{presets_choice}->SetFont($Slic3r::GUI::small_font);
# buttons
$self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/disk.png", wxBITMAP_TYPE_PNG));
$self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG));
$self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/disk.png", wxBITMAP_TYPE_PNG),
wxDefaultPosition, [16,16], wxBORDER_NONE);
$self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG),
wxDefaultPosition, [16,16], wxBORDER_NONE);
$self->{btn_save_preset}->SetToolTipString("Save current " . lc($self->title));
$self->{btn_delete_preset}->SetToolTipString("Delete this preset");
$self->{btn_delete_preset}->Disable;
@ -972,11 +974,6 @@ sub build {
$optgroup->append_single_option_line('z_offset');
}
{
my $optgroup = $page->new_optgroup('Firmware');
$optgroup->append_single_option_line('gcode_flavor');
$optgroup->append_single_option_line('use_relative_e_distances');
}
{
my $optgroup = $page->new_optgroup('Capabilities');
{
@ -1058,6 +1055,11 @@ sub build {
$optgroup->append_line($host_line);
$optgroup->append_single_option_line('octoprint_apikey');
}
{
my $optgroup = $page->new_optgroup('Firmware');
$optgroup->append_single_option_line('gcode_flavor');
$optgroup->append_single_option_line('use_relative_e_distances');
}
{
my $optgroup = $page->new_optgroup('Advanced');
$optgroup->append_single_option_line('use_firmware_retraction');

View File

@ -412,6 +412,12 @@ sub _fill_gaps {
offset2([ map @$_, @$gaps ], -$max/2, +$max/2),
1,
);
my @polylines = map @{$_->medial_axis($max, $min/2)}, @$this;
return if !@polylines;
Slic3r::debugf " %d gaps filled with extrusion width = %s\n", scalar @$this, $w
if @$this;
my $flow = $self->flow(FLOW_ROLE_SOLID_INFILL, 0, $w);
my %path_args = (
@ -420,26 +426,23 @@ sub _fill_gaps {
width => $flow->width,
height => $self->height,
);
my @polylines = map @{$_->medial_axis($max, $min/2)}, @$this;
Slic3r::debugf " %d gaps filled with extrusion width = %s\n", scalar @$this, $w
if @$this;
for my $i (0..$#polylines) {
if ($polylines[$i]->isa('Slic3r::Polygon')) {
my $loop = Slic3r::ExtrusionLoop->new;
$loop->append(Slic3r::ExtrusionPath->new(polyline => $polylines[$i]->split_at_first_point, %path_args));
$polylines[$i] = $loop;
} elsif ($polylines[$i]->is_valid && $polylines[$i]->first_point->coincides_with($polylines[$i]->last_point)) {
my @entities = ();
foreach my $polyline (@polylines) {
#if ($polylines[$i]->isa('Slic3r::Polygon')) {
# my $loop = Slic3r::ExtrusionLoop->new;
# $loop->append(Slic3r::ExtrusionPath->new(polyline => $polylines[$i]->split_at_first_point, %path_args));
# $polylines[$i] = $loop;
if ($polyline->is_valid && $polyline->first_point->coincides_with($polyline->last_point)) {
# since medial_axis() now returns only Polyline objects, detect loops here
my $loop = Slic3r::ExtrusionLoop->new;
$loop->append(Slic3r::ExtrusionPath->new(polyline => $polylines[$i], %path_args));
$polylines[$i] = $loop;
push @entities, my $loop = Slic3r::ExtrusionLoop->new;
$loop->append(Slic3r::ExtrusionPath->new(polyline => $polyline, %path_args));
} else {
$polylines[$i] = Slic3r::ExtrusionPath->new(polyline => $polylines[$i], %path_args);
push @entities, Slic3r::ExtrusionPath->new(polyline => $polyline, %path_args);
}
}
return @polylines;
return @entities;
}
sub prepare_fill_surfaces {

View File

@ -182,7 +182,7 @@ my $polygons = [
{
my $line = Slic3r::Line->new([0, 0], [20, 0]);
is +Slic3r::Point->new(10, 10)->distance_to_line($line), 10, 'distance_to';
is +Slic3r::Point->new(50, 10)->distance_to_line($line), 10, 'distance_to';
is +Slic3r::Point->new(50, 0)->distance_to_line($line), 30, 'distance_to';
is +Slic3r::Point->new(0, 0)->distance_to_line($line), 0, 'distance_to';
is +Slic3r::Point->new(20, 0)->distance_to_line($line), 0, 'distance_to';
is +Slic3r::Point->new(10, 0)->distance_to_line($line), 0, 'distance_to';

View File

@ -77,8 +77,8 @@ stl_count_facets(stl_file *stl, char *file) {
if (stl->error) return;
/* Open the file */
stl->fp = fopen(file, "r");
/* Open the file in binary mode first */
stl->fp = fopen(file, "rb");
if(stl->fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
@ -104,9 +104,6 @@ stl_count_facets(stl_file *stl, char *file) {
for(s = 0; s < sizeof(chtest); s++) {
if(chtest[s] > 127) {
stl->stats.type = binary;
/* close and reopen with binary flag (needed on Windows) */
fclose(stl->fp);
stl->fp = fopen(file, "rb");
break;
}
}
@ -137,6 +134,9 @@ stl_count_facets(stl_file *stl, char *file) {
}
/* Otherwise, if the .STL file is ASCII, then do the following */
else {
/* Reopen the file in text mode (for getting correct newlines on Windows) */
freopen(file, "r", stl->fp);
/* Find the number of facets */
j = 0;
for(i = 0; i < file_size ; i++) {

View File

@ -328,8 +328,8 @@ MedialAxis::is_valid_edge(const VD::edge_type& edge) const
// our skeleton
Point v0( edge.vertex0()->x(), edge.vertex0()->y() );
Point v1( edge.vertex1()->x(), edge.vertex1()->y() );
double dist0 = v0.distance_to(segment1);
double dist1 = v1.distance_to(segment1);
double dist0 = v0.perp_distance_to(segment1);
double dist1 = v1.perp_distance_to(segment1);
/*
double diff = fabs(dist1 - dist0);

View File

@ -90,6 +90,7 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
size_t index = 0;
Line full(points.front(), points.back());
for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) {
// we use shortest distance, not perpendicular distance
double d = it->distance_to(full);
if (d > dmax) {
index = it - points.begin();

View File

@ -1,6 +1,7 @@
#include "Point.hpp"
#include "Line.hpp"
#include "MultiPoint.hpp"
#include <algorithm>
#include <cmath>
#include <sstream>
@ -130,8 +131,31 @@ Point::distance_to(const Point &point) const
return sqrt(dx*dx + dy*dy);
}
/* distance to the closest point of line */
double
Point::distance_to(const Line &line) const
{
const double dx = line.b.x - line.a.x;
const double dy = line.b.y - line.a.y;
const double l2 = dx*dx + dy*dy; // avoid a sqrt
if (l2 == 0.0) return this->distance_to(line.a); // line.a == line.b case
// Consider the line extending the segment, parameterized as line.a + t (line.b - line.a).
// We find projection of this point onto the line.
// It falls where t = [(this-line.a) . (line.b-line.a)] / |line.b-line.a|^2
const double t = ((this->x - line.a.x) * dx + (this->y - line.a.y) * dy) / l2;
if (t < 0.0) return this->distance_to(line.a); // beyond the 'a' end of the segment
else if (t > 1.0) return this->distance_to(line.b); // beyond the 'b' end of the segment
Point projection(
line.a.x + t * dx,
line.a.y + t * dy
);
return this->distance_to(projection);
}
double
Point::perp_distance_to(const Line &line) const
{
if (line.a.coincides_with(line.b)) return this->distance_to(line.a);

View File

@ -48,6 +48,7 @@ class Point
bool nearest_point(const Points &points, Point* point) const;
double distance_to(const Point &point) const;
double distance_to(const Line &line) const;
double perp_distance_to(const Line &line) const;
double ccw(const Point &p1, const Point &p2) const;
double ccw(const Line &line) const;
double ccw_angle(const Point &p1, const Point &p2) const;

View File

@ -126,11 +126,11 @@ PrintConfigDef::build_def() {
Options["external_fill_pattern"].enum_values.push_back("hilbertcurve");
Options["external_fill_pattern"].enum_values.push_back("archimedeanchords");
Options["external_fill_pattern"].enum_values.push_back("octagramspiral");
Options["external_fill_pattern"].enum_labels.push_back("rectilinear");
Options["external_fill_pattern"].enum_labels.push_back("concentric");
Options["external_fill_pattern"].enum_labels.push_back("hilbertcurve (slow)");
Options["external_fill_pattern"].enum_labels.push_back("archimedeanchords (slow)");
Options["external_fill_pattern"].enum_labels.push_back("octagramspiral (slow)");
Options["external_fill_pattern"].enum_labels.push_back("Rectilinear");
Options["external_fill_pattern"].enum_labels.push_back("Concentric");
Options["external_fill_pattern"].enum_labels.push_back("Hilbert Curve");
Options["external_fill_pattern"].enum_labels.push_back("Archimedean Chords");
Options["external_fill_pattern"].enum_labels.push_back("Octagram Spiral");
Options["external_fill_pattern"].aliases.push_back("solid_fill_pattern");
Options["external_perimeter_extrusion_width"].type = coFloatOrPercent;
@ -293,14 +293,14 @@ PrintConfigDef::build_def() {
Options["fill_pattern"].enum_values.push_back("hilbertcurve");
Options["fill_pattern"].enum_values.push_back("archimedeanchords");
Options["fill_pattern"].enum_values.push_back("octagramspiral");
Options["fill_pattern"].enum_labels.push_back("rectilinear");
Options["fill_pattern"].enum_labels.push_back("line");
Options["fill_pattern"].enum_labels.push_back("concentric");
Options["fill_pattern"].enum_labels.push_back("honeycomb");
Options["fill_pattern"].enum_labels.push_back("3D honeycomb");
Options["fill_pattern"].enum_labels.push_back("hilbertcurve");
Options["fill_pattern"].enum_labels.push_back("archimedeanchords");
Options["fill_pattern"].enum_labels.push_back("octagramspiral");
Options["fill_pattern"].enum_labels.push_back("Rectilinear");
Options["fill_pattern"].enum_labels.push_back("Line");
Options["fill_pattern"].enum_labels.push_back("Concentric");
Options["fill_pattern"].enum_labels.push_back("Honeycomb");
Options["fill_pattern"].enum_labels.push_back("3D Honeycomb");
Options["fill_pattern"].enum_labels.push_back("Hilbert Curve");
Options["fill_pattern"].enum_labels.push_back("Archimedean Chords");
Options["fill_pattern"].enum_labels.push_back("Octagram Spiral");
Options["first_layer_acceleration"].type = coFloat;
Options["first_layer_acceleration"].label = "First layer";
@ -965,7 +965,7 @@ PrintConfigDef::build_def() {
Options["use_relative_e_distances"].cli = "use-relative-e-distances!";
Options["vibration_limit"].type = coFloat;
Options["vibration_limit"].label = "Vibration limit";
Options["vibration_limit"].label = "Vibration limit (deprecated)";
Options["vibration_limit"].tooltip = "This experimental option will slow down those moves hitting the configured frequency limit. The purpose of limiting vibrations is to avoid mechanical resonance. Set zero to disable.";
Options["vibration_limit"].sidetext = "Hz";
Options["vibration_limit"].cli = "vibration-limit=f";

View File

@ -4,7 +4,7 @@ use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 15;
use Test::More tests => 22;
my $point = Slic3r::Point->new(10, 15);
is_deeply [ @$point ], [10, 15], 'point roundtrip';
@ -30,13 +30,28 @@ ok !$point->coincides_with($point2), 'coincides_with';
ok $nearest->coincides_with($point2), 'nearest_point';
}
{
my $line = Slic3r::Line->new([0,0], [100,0]);
is +Slic3r::Point->new(0,0)->distance_to_line($line), 0, 'distance_to_line()';
is +Slic3r::Point->new(100,0)->distance_to_line($line), 0, 'distance_to_line()';
is +Slic3r::Point->new(50,0)->distance_to_line($line), 0, 'distance_to_line()';
is +Slic3r::Point->new(150,0)->distance_to_line($line), 50, 'distance_to_line()';
is +Slic3r::Point->new(0,50)->distance_to_line($line), 50, 'distance_to_line()';
is +Slic3r::Point->new(50,50)->distance_to_line($line), 50, 'distance_to_line()';
}
{
my $line = Slic3r::Line->new([50,50], [125,-25]);
is +Slic3r::Point->new(100,0)->distance_to_line($line), 0, 'distance_to_line()';
}
{
my $line = Slic3r::Line->new(
[18335846,18335845],
[18335846,1664160],
);
$point = Slic3r::Point->new(1664161,18335848);
is $point->distance_to_line($line), 16671685, 'distance_to_line() does not overflow';
is $point->perp_distance_to_line($line), 16671685, 'perp_distance_to_line() does not overflow';
}
{

View File

@ -4,7 +4,7 @@ use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 14;
use Test::More tests => 16;
my $points = [
[100, 100],
@ -34,6 +34,14 @@ is_deeply $polyline->pp, [ @$points, @$points ], 'append_polyline';
ok abs($polyline->length - ($len-($len/3))) < 1, 'clip_end';
}
{
my $polyline = Slic3r::Polyline->new(
[0,0], [20,0], [50,0], [80,0], [100,0],
);
$polyline->simplify(2);
is_deeply $polyline->pp, [ [0,0], [100,0] ], 'Douglas-Peucker';
}
{
my $polyline = Slic3r::Polyline->new(
[0,0], [50,50], [100,0], [125,-25], [150,50],
@ -42,6 +50,14 @@ is_deeply $polyline->pp, [ @$points, @$points ], 'append_polyline';
is_deeply $polyline->pp, [ [0, 0], [50, 50], [125, -25], [150, 50] ], 'Douglas-Peucker';
}
{
my $polyline = Slic3r::Polyline->new(
[0,0], [100,0], [50,10],
);
$polyline->simplify(25);
is_deeply $polyline->pp, [ [0,0], [100,0], [50,10] ], 'Douglas-Peucker uses shortest distance instead of perpendicular distance';
}
{
my $polyline = Slic3r::Polyline->new(@$points);
is $polyline->length, 100*2, 'length';

View File

@ -29,6 +29,8 @@
%code{% RETVAL = THIS->distance_to(*point); %};
double distance_to_line(Line* line)
%code{% RETVAL = THIS->distance_to(*line); %};
double perp_distance_to_line(Line* line)
%code{% RETVAL = THIS->perp_distance_to(*line); %};
double ccw(Point* p1, Point* p2)
%code{% RETVAL = THIS->ccw(*p1, *p2); %};
double ccw_angle(Point* p1, Point* p2)