Merge remote-tracking branch 'origin/master' into profile_changes_reset

This commit is contained in:
YuSanka 2018-03-22 14:18:48 +01:00
commit 08a8fe84a4
46 changed files with 11102 additions and 6732 deletions

View File

@ -40,10 +40,7 @@ if ($gui) {
Socket 2.016 Socket 2.016
); );
%recommends = qw( %recommends = qw(
Growl::GNTP 0.15
Wx::GLCanvas 0 Wx::GLCanvas 0
LWP::UserAgent 0
Net::Bonjour 0
); );
if ($^O eq 'MSWin32') { if ($^O eq 'MSWin32') {
$recommends{"Win32::TieRegistry"} = 0; $recommends{"Win32::TieRegistry"} = 0;

View File

@ -9,13 +9,11 @@ use List::Util qw(first);
use Slic3r::GUI::2DBed; use Slic3r::GUI::2DBed;
use Slic3r::GUI::AboutDialog; use Slic3r::GUI::AboutDialog;
use Slic3r::GUI::BedShapeDialog; use Slic3r::GUI::BedShapeDialog;
use Slic3r::GUI::BonjourBrowser;
use Slic3r::GUI::ConfigWizard; use Slic3r::GUI::ConfigWizard;
use Slic3r::GUI::Controller; use Slic3r::GUI::Controller;
use Slic3r::GUI::Controller::ManualControlDialog; use Slic3r::GUI::Controller::ManualControlDialog;
use Slic3r::GUI::Controller::PrinterPanel; use Slic3r::GUI::Controller::PrinterPanel;
use Slic3r::GUI::MainFrame; use Slic3r::GUI::MainFrame;
use Slic3r::GUI::Notifier;
use Slic3r::GUI::Plater; use Slic3r::GUI::Plater;
use Slic3r::GUI::Plater::2D; use Slic3r::GUI::Plater::2D;
use Slic3r::GUI::Plater::2DToolpaths; use Slic3r::GUI::Plater::2DToolpaths;
@ -34,7 +32,6 @@ use Slic3r::GUI::SystemInfo;
use Wx::Locale gettext => 'L'; use Wx::Locale gettext => 'L';
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1"; our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
our $have_LWP = eval "use LWP::UserAgent; 1";
use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow :filedialog :font); use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow :filedialog :font);
use Wx::Event qw(EVT_IDLE EVT_COMMAND EVT_MENU); use Wx::Event qw(EVT_IDLE EVT_COMMAND EVT_MENU);
@ -88,7 +85,6 @@ sub OnInit {
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir); Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
Slic3r::GUI::set_wxapp($self); Slic3r::GUI::set_wxapp($self);
$self->{notifier} = Slic3r::GUI::Notifier->new;
$self->{app_config} = Slic3r::GUI::AppConfig->new; $self->{app_config} = Slic3r::GUI::AppConfig->new;
$self->{preset_bundle} = Slic3r::GUI::PresetBundle->new; $self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
@ -177,6 +173,13 @@ sub recreate_GUI{
$topwindow->Destroy; $topwindow->Destroy;
} }
EVT_IDLE($self->{mainframe}, sub {
while (my $cb = shift @cb) {
$cb->();
}
$self->{app_config}->save if $self->{app_config}->dirty;
});
my $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only; my $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only;
if ($run_wizard) { if ($run_wizard) {
# On OSX the UI was not initialized correctly if the wizard was called # On OSX the UI was not initialized correctly if the wizard was called
@ -271,7 +274,9 @@ sub notify {
$frame->RequestUserAttention(&Wx::wxMAC ? wxUSER_ATTENTION_ERROR : wxUSER_ATTENTION_INFO) $frame->RequestUserAttention(&Wx::wxMAC ? wxUSER_ATTENTION_ERROR : wxUSER_ATTENTION_INFO)
unless ($frame->IsActive); unless ($frame->IsActive);
$self->{notifier}->notify($message); # There used to be notifier using a Growl application for OSX, but Growl is dead.
# The notifier also supported the Linux X D-bus notifications, but that support was broken.
#TODO use wxNotificationMessage?
} }
# Called after the Preferences dialog is closed and the program settings are saved. # Called after the Preferences dialog is closed and the program settings are saved.

View File

@ -1,53 +0,0 @@
# A tiny dialog to select an OctoPrint device to print to.
package Slic3r::GUI::BonjourBrowser;
use strict;
use warnings;
use utf8;
use Wx qw(:dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_CLOSE);
use base 'Wx::Dialog';
sub new {
my $class = shift;
my ($parent, $devices) = @_;
my $self = $class->SUPER::new($parent, -1, "Device Browser", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
$self->{devices} = $devices;
# label
my $text = Wx::StaticText->new($self, -1, "Choose an OctoPrint device in your network:", wxDefaultPosition, wxDefaultSize);
# selector
$self->{choice} = my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize,
[ map $_->name, @{$self->{devices}} ]);
my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
$main_sizer->Add($text, 1, wxEXPAND | wxALL, 10);
$main_sizer->Add($choice, 1, wxEXPAND | wxALL, 10);
$main_sizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND);
$self->SetSizer($main_sizer);
$self->SetMinSize($self->GetSize);
$main_sizer->SetSizeHints($self);
# needed to actually free memory
EVT_CLOSE($self, sub {
$self->EndModal(wxID_OK);
$self->Destroy;
});
return $self;
}
sub GetValue {
my ($self) = @_;
return $self->{devices}[ $self->{choice}->GetSelection ]->address;
}
sub GetPort {
my ($self) = @_;
return $self->{devices}[ $self->{choice}->GetSelection ]->port;
}
1;

View File

@ -25,10 +25,6 @@ our $last_config;
our $VALUE_CHANGE_EVENT = Wx::NewEventType; our $VALUE_CHANGE_EVENT = Wx::NewEventType;
# 2) To inform about a preset selection change or a "modified" status change. # 2) To inform about a preset selection change or a "modified" status change.
our $PRESETS_CHANGED_EVENT = Wx::NewEventType; our $PRESETS_CHANGED_EVENT = Wx::NewEventType;
# 3) To inform about a click on Browse button
our $BUTTON_BROWSE_EVENT = Wx::NewEventType;
# 4) To inform about a click on Test button
our $BUTTON_TEST_EVENT = Wx::NewEventType;
sub new { sub new {
my ($class, %params) = @_; my ($class, %params) = @_;
@ -169,59 +165,7 @@ sub _init_tabpanel {
} }
} }
}); });
# The following event is emited by the C++ Tab implementation , Slic3r::GUI::create_preset_tabs($self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT);
# when the Browse button was clicked
EVT_COMMAND($self, -1, $BUTTON_BROWSE_EVENT, sub {
my ($self, $event) = @_;
my $msg = $event->GetString;
# look for devices
my $entries;
{
my $res = Net::Bonjour->new('http');
$res->discover;
$entries = [ $res->entries ];
}
if (@{$entries}) {
my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries);
my $tab = Slic3r::GUI::get_preset_tab("printer");
$tab->load_key_value('octoprint_host', $dlg->GetValue . ":" . $dlg->GetPort)
if $dlg->ShowModal == wxID_OK;
} else {
Wx::MessageDialog->new($self, L('No Bonjour device found'), L('Device Browser'), wxOK | wxICON_INFORMATION)->ShowModal;
}
});
# The following event is emited by the C++ Tab implementation ,
# when the Test button was clicked
EVT_COMMAND($self, -1, $BUTTON_TEST_EVENT, sub {
my ($self, $event) = @_;
my $msg = $event->GetString;
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
my $config = Slic3r::GUI::get_preset_tab("printer")->get_config;
my $res = $ua->get(
"http://" . $config->octoprint_host . "/api/version",
'X-Api-Key' => $config->octoprint_apikey,
);
if ($res->is_success) {
Slic3r::GUI::show_info($self, L("Connection to OctoPrint works correctly."), _L("Success!"));
} else {
Slic3r::GUI::show_error($self,
L("I wasn't able to connect to OctoPrint (") . $res->status_line .
L("). Check hostname and OctoPrint version (at least 1.1.0 is required)."));
}
});
# A variable to inform C++ Tab implementation about disabling of Browse button
$self->{is_disabled_button_browse} = (!eval "use Net::Bonjour; 1") ? 1 : 0 ;
# A variable to inform C++ Tab implementation about user_agent
$self->{is_user_agent} = (eval "use LWP::UserAgent; 1") ? 1 : 0 ;
Slic3r::GUI::create_preset_tabs($self->{no_controller},
$self->{is_disabled_button_browse},
$self->{is_user_agent},
$VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT,
$BUTTON_BROWSE_EVENT, $BUTTON_TEST_EVENT);
$self->{options_tabs} = {}; $self->{options_tabs} = {};
for my $tab_name (qw(print filament printer)) { for my $tab_name (qw(print filament printer)) {
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name"); $self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");

View File

@ -1,46 +0,0 @@
# Notify about the end of slicing.
# The notifications are sent out using the Growl protocol if installed, and using DBus XWindow protocol.
package Slic3r::GUI::Notifier;
use Moo;
has 'growler' => (is => 'rw');
my $icon = Slic3r::var("Slic3r.png");
sub BUILD {
my ($self) = @_;
if (eval 'use Growl::GNTP; 1') {
# register with growl
eval {
$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 $@;
}
}
sub notify {
my ($self, $message) = @_;
my $title = 'Slicing Done!';
eval {
$self->growler->notify(Event => 'SKEIN_DONE', Title => $title, Message => $message)
if $self->growler;
};
# Net::DBus is broken in multithreaded environment
if (0 && eval 'use Net::DBus; 1') {
eval {
my $session = Net::DBus->session;
my $serv = $session->get_service('org.freedesktop.Notifications');
my $notifier = $serv->get_object('/org/freedesktop/Notifications',
'org.freedesktop.Notifications');
$notifier->Notify('Slic3r', 0, $icon, $title, $message, [], {}, -1);
undef $Net::DBus::bus_session;
};
}
}
1;

View File

@ -8,7 +8,6 @@ use utf8;
use File::Basename qw(basename dirname); use File::Basename qw(basename dirname);
use List::Util qw(sum first max); use List::Util qw(sum first max);
use Slic3r::Geometry qw(X Y Z scale unscale deg2rad rad2deg); use Slic3r::Geometry qw(X Y Z scale unscale deg2rad rad2deg);
use LWP::UserAgent;
use threads::shared qw(shared_clone); use threads::shared qw(shared_clone);
use Wx qw(:button :colour :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc use Wx qw(:button :colour :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
:panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap); :panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap);
@ -1334,6 +1333,9 @@ sub export_gcode {
} else { } else {
my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') }; my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
Slic3r::GUI::catch_error($self) and return; Slic3r::GUI::catch_error($self) and return;
# If possible, remove accents from accented latin characters.
# This function is useful for generating file names to be processed by legacy firmwares.
$default_output_file = Slic3r::GUI::fold_utf8_to_ascii($default_output_file);
my $dlg = Wx::FileDialog->new($self, L('Save G-code file as:'), my $dlg = Wx::FileDialog->new($self, L('Save G-code file as:'),
wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)), wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)),
basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
@ -1941,7 +1943,7 @@ sub selection_changed {
$self->{object_info_manifold}->SetToolTipString($message); $self->{object_info_manifold}->SetToolTipString($message);
$self->{object_info_manifold_warning_icon}->SetToolTipString($message); $self->{object_info_manifold_warning_icon}->SetToolTipString($message);
} else { } else {
$self->{object_info_manifold}->SetLabel(L("Yes")); $self->{object_info_manifold}->SetLabel(L("Yes"));
$self->{object_info_manifold_warning_icon}->Hide; $self->{object_info_manifold_warning_icon}->Hide;
} }
} else { } else {

View File

@ -7,7 +7,6 @@ require Exporter;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT_OK = qw(_eq); our @EXPORT_OK = qw(_eq);
use IO::Scalar;
use List::Util qw(first); use List::Util qw(first);
use Slic3r::Geometry qw(epsilon X Y Z); use Slic3r::Geometry qw(epsilon X Y Z);

View File

@ -1,177 +0,0 @@
# 2D cut in the XZ plane through the toolpaths.
# For debugging purposes.
package Slic3r::Test::SectionCut;
use Moo;
use List::Util qw(first min max);
use Slic3r::Geometry qw(unscale);
use Slic3r::Geometry::Clipper qw(intersection_pl);
use SVG;
use Slic3r::SVG;
has 'print' => (is => 'ro', required => 1);
has 'scale' => (is => 'ro', default => sub { 30 });
has 'y_percent' => (is => 'ro', default => sub { 0.5 }); # Y coord of section line expressed as factor
has 'line' => (is => 'rw');
has '_height' => (is => 'rw');
has '_svg' => (is => 'rw');
has '_svg_style' => (is => 'rw', default => sub { {} });
sub BUILD {
my $self = shift;
# calculate the Y coordinate of the section line
my $bb = $self->print->bounding_box;
my $y = ($bb->y_min + $bb->y_max) * $self->y_percent;
# store our section line
$self->line(Slic3r::Line->new([ $bb->x_min, $y ], [ $bb->x_max, $y ]));
}
sub export_svg {
my $self = shift;
my ($filename) = @_;
# get bounding box of print and its height
# (Print should return a BoundingBox3 object instead)
my $bb = $self->print->bounding_box;
my $print_size = $bb->size;
$self->_height(max(map $_->print_z, map @{$_->layers}, @{$self->print->objects}));
# initialize the SVG canvas
$self->_svg(my $svg = SVG->new(
width => $self->scale * unscale($print_size->x),
height => $self->scale * $self->_height,
));
# set default styles
$self->_svg_style->{'stroke-width'} = 1;
$self->_svg_style->{'fill-opacity'} = 0.5;
$self->_svg_style->{'stroke-opacity'} = 0.2;
# plot perimeters
$self->_svg_style->{'stroke'} = '#EE0000';
$self->_svg_style->{'fill'} = '#FF0000';
$self->_plot_group(sub { map @{$_->perimeters}, @{$_[0]->regions} });
# plot infill
$self->_svg_style->{'stroke'} = '#444444';
$self->_svg_style->{'fill'} = '#454545';
$self->_plot_group(sub { map @{$_->fills}, @{$_[0]->regions} });
# plot support material
$self->_svg_style->{'stroke'} = '#12EF00';
$self->_svg_style->{'fill'} = '#22FF00';
$self->_plot_group(sub { $_[0]->isa('Slic3r::Layer::Support') ? ($_[0]->support_fills) : () });
Slic3r::open(\my $fh, '>', $filename);
print $fh $svg->xmlify;
close $fh;
printf "Section cut SVG written to %s\n", $filename;
}
sub _plot_group {
my $self = shift;
my ($filter) = @_;
my $bb = $self->print->bounding_box;
my $g = $self->_svg->group(style => { %{$self->_svg_style} });
foreach my $object (@{$self->print->objects}) {
foreach my $copy (@{$object->_shifted_copies}) {
foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
# get all ExtrusionPath objects
my @paths = map $_->clone,
map { ($_->isa('Slic3r::ExtrusionLoop') || $_->isa('Slic3r::ExtrusionPath::Collection')) ? @$_ : $_ }
grep defined $_,
$filter->($layer);
# move paths to location of copy
$_->polyline->translate(@$copy) for @paths;
if (0) {
# export plan with section line and exit
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"line.svg",
no_arrows => 1,
lines => [ $self->line ],
red_polylines => [ map $_->polyline, @paths ],
);
exit;
}
foreach my $path (@paths) {
foreach my $line (@{$path->lines}) {
my @intersections = @{intersection_pl(
[ $self->line->as_polyline ],
$line->grow(Slic3r::Geometry::scale $path->width/2),
)};
die "Intersection has more than two points!\n"
if defined first { @$_ > 2 } @intersections;
# turn intersections to lines
my @lines = map Slic3r::Line->new(@$_), @intersections;
# align intersections to canvas
$_->translate(-$bb->x_min, 0) for @lines;
# we want lines oriented from left to right in order to draw
# rectangles correctly
foreach my $line (@lines) {
$line->reverse if $line->a->x > $line->b->x;
}
if ($path->is_bridge) {
foreach my $line (@lines) {
my $radius = $path->width / 2;
my $width = unscale abs($line->b->x - $line->a->x);
if ((10 * $radius) < $width) {
# we're cutting the path in the longitudinal direction, so we've got a rectangle
$g->rectangle(
'x' => $self->scale * unscale($line->a->x),
'y' => $self->scale * $self->_y($layer->print_z),
'width' => $self->scale * $width,
'height' => $self->scale * $radius * 2,
'rx' => $self->scale * $radius * 0.35,
'ry' => $self->scale * $radius * 0.35,
);
} else {
$g->circle(
'cx' => $self->scale * (unscale($line->a->x) + $radius),
'cy' => $self->scale * $self->_y($layer->print_z - $radius),
'r' => $self->scale * $radius,
);
}
}
} else {
foreach my $line (@lines) {
my $height = $path->height;
$height = $layer->height if $height == -1;
$g->rectangle(
'x' => $self->scale * unscale($line->a->x),
'y' => $self->scale * $self->_y($layer->print_z),
'width' => $self->scale * unscale($line->b->x - $line->a->x),
'height' => $self->scale * $height,
'rx' => $self->scale * $height * 0.5,
'ry' => $self->scale * $height * 0.5,
);
}
}
}
}
}
}
}
}
sub _y {
my $self = shift;
my ($y) = @_;
return $self->_height - $y;
}
1;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,8 @@ xs/src/slic3r/GUI/2DBed.cpp
xs/src/slic3r/GUI/PresetHints.cpp xs/src/slic3r/GUI/PresetHints.cpp
xs/src/slic3r/GUI/Preferences.hpp xs/src/slic3r/GUI/Preferences.hpp
xs/src/slic3r/GUI/Preferences.cpp xs/src/slic3r/GUI/Preferences.cpp
xs/src/slic3r/GUI/BonjourDialog.cpp
xs/src/slic3r/Utils/OctoPrint.cpp
xs/src/libslic3r/PrintConfig.cpp xs/src/libslic3r/PrintConfig.cpp
xs/src/libslic3r/GCode/PreviewData.cpp xs/src/libslic3r/GCode/PreviewData.cpp
lib/Slic3r/GUI.pm lib/Slic3r/GUI.pm

View File

@ -909,10 +909,10 @@ use_volumetric_e = 0
variable_layer_height = 1 variable_layer_height = 1
wipe = 1 wipe = 1
z_offset = 0 z_offset = 0
printer_model = MK2S #printer_model = MK2S
printer_variant = 0.4 #printer_variant = 0.4
default_print_profile = 0.15mm OPTIMAL #default_print_profile = 0.15mm OPTIMAL
default_filament_profile = Prusa PLA #default_filament_profile = Prusa PLA
[printer:*multimaterial*] [printer:*multimaterial*]
inherits = *common* inherits = *common*
@ -928,7 +928,7 @@ retract_restart_extra = 0
retract_restart_extra_toolchange = 0 retract_restart_extra_toolchange = 0
retract_speed = 80 retract_speed = 80
single_extruder_multi_material = 1 single_extruder_multi_material = 1
printer_model = MK2SMM #printer_model = MK2SMM
[printer:*mm-single*] [printer:*mm-single*]
inherits = *multimaterial* inherits = *multimaterial*
@ -956,15 +956,15 @@ nozzle_diameter = 0.25
retract_length = 1 retract_length = 1
retract_speed = 50 retract_speed = 50
variable_layer_height = 0 variable_layer_height = 0
printer_variant = 0.25 #printer_variant = 0.25
default_print_profile = 0.10mm DETAIL 0.25 nozzle #default_print_profile = 0.10mm DETAIL 0.25 nozzle
[printer:Original Prusa i3 MK2 0.6 nozzle] [printer:Original Prusa i3 MK2 0.6 nozzle]
inherits = *common* inherits = *common*
max_layer_height = 0.35 max_layer_height = 0.35
min_layer_height = 0.1 min_layer_height = 0.1
nozzle_diameter = 0.6 nozzle_diameter = 0.6
printer_variant = 0.6 #printer_variant = 0.6
[printer:Original Prusa i3 MK2 MM Single Mode] [printer:Original Prusa i3 MK2 MM Single Mode]
inherits = *mm-single* inherits = *mm-single*
@ -972,7 +972,7 @@ inherits = *mm-single*
[printer:Original Prusa i3 MK2 MM Single Mode 0.6 nozzle] [printer:Original Prusa i3 MK2 MM Single Mode 0.6 nozzle]
inherits = *mm-single* inherits = *mm-single*
nozzle_diameter = 0.6 nozzle_diameter = 0.6
printer_variant = 0.6 #printer_variant = 0.6
[printer:Original Prusa i3 MK2 MultiMaterial] [printer:Original Prusa i3 MK2 MultiMaterial]
inherits = *mm-multi* inherits = *mm-multi*
@ -981,7 +981,7 @@ nozzle_diameter = 0.4,0.4,0.4,0.4
[printer:Original Prusa i3 MK2 MultiMaterial 0.6 nozzle] [printer:Original Prusa i3 MK2 MultiMaterial 0.6 nozzle]
inherits = *mm-multi* inherits = *mm-multi*
nozzle_diameter = 0.6,0.6,0.6,0.6 nozzle_diameter = 0.6,0.6,0.6,0.6
printer_variant = 0.6 #printer_variant = 0.6
[printer:Original Prusa i3 MK3] [printer:Original Prusa i3 MK3]
inherits = *common* inherits = *common*
@ -989,8 +989,8 @@ end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
retract_lift_below = 209 retract_lift_below = 209
start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif} start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif}
printer_model = MK3 #printer_model = MK3
default_print_profile = 0.15mm OPTIMAL MK3 #default_print_profile = 0.15mm OPTIMAL MK3
[printer:Original Prusa i3 MK3 0.25 nozzle] [printer:Original Prusa i3 MK3 0.25 nozzle]
inherits = *common* inherits = *common*
@ -999,8 +999,8 @@ end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
retract_lift_below = 209 retract_lift_below = 209
start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif} start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif}
printer_model = MK3 #printer_model = MK3
default_print_profile = 0.10mm DETAIL MK3 #default_print_profile = 0.10mm DETAIL MK3
[printer:Original Prusa i3 MK3 0.6 nozzle] [printer:Original Prusa i3 MK3 0.6 nozzle]
inherits = *common* inherits = *common*
@ -1009,5 +1009,10 @@ end_gcode = G4 ; wait\nM221 S100\nM104 S0 ; turn off temperature\nM140 S0 ; turn
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
retract_lift_below = 209 retract_lift_below = 209
start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif} start_gcode = M115 U3.1.1-RC5 ; tell printer latest fw version\nM201 X1000 Y1000 Z200 E5000 ; sets maximum accelerations, mm/sec^2\nM203 X200 Y200 Z12 E120 ; sets maximum feedrates, mm/sec\nM204 S1250 T1250 ; sets acceleration (S) and retract acceleration (T)\nM205 X10 Y10 Z0.4 E2.5 ; sets the jerk limits, mm/sec\nM205 S0 T0 ; sets the minimum extruding and travel feed rate, mm/sec\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif}
printer_model = MK3 #printer_model = MK3
default_print_profile = 0.15mm OPTIMAL MK3 #default_print_profile = 0.15mm OPTIMAL MK3
[presets]
print = 0.15mm OPTIMAL MK3
printer = Original Prusa i3 MK3
filament = Prusa PLA

View File

@ -10,6 +10,7 @@ BEGIN {
use Slic3r; use Slic3r;
use Slic3r::Test; use Slic3r::Test;
use IO::Scalar;
{ {
my $print = Slic3r::Test::init_print('20mm_cube'); my $print = Slic3r::Test::init_print('20mm_cube');

View File

@ -1,130 +0,0 @@
#!/usr/bin/perl
# This script generates section cuts from a given G-Code file
use strict;
use warnings;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use Getopt::Long qw(:config no_auto_abbrev);
use IO::All;
use List::Util qw(max);
use Slic3r;
use Slic3r::Geometry qw(X Y);
use Slic3r::Geometry::Clipper qw(JT_SQUARE);
use Slic3r::Test;
use SVG;
my %opt = (
layer_height => 0.2,
extrusion_width => 0.5,
scale => 30,
);
{
my %options = (
'help' => sub { usage() },
'layer-height|h=f' => \$opt{layer_height},
'extrusion-width|w=f' => \$opt{extrusion_width},
'scale|s=i' => \$opt{scale},
);
GetOptions(%options) or usage(1);
$ARGV[0] or usage(1);
}
{
my $input_file = $ARGV[0];
my $output_file = $input_file;
$output_file =~ s/\.(?:gcode|gco|ngc|g)$/.svg/;
# read paths
my %paths = (); # z => [ path, path ... ]
Slic3r::GCode::Reader->new->parse(io($input_file)->all, sub {
my ($self, $cmd, $args, $info) = @_;
if ($cmd eq 'G1' && $info->{extruding}) {
$paths{ $self->Z } ||= [];
push @{ $paths{ $self->Z } }, Slic3r::Line->new(
[ $self->X, $self->Y ],
[ $info->{new_X}, $info->{new_Y} ],
);
}
});
# calculate print extents
my $bounding_box = Slic3r::Geometry::BoundingBox->new_from_points([ map @$_, map @$_, values %paths ]);
# calculate section line
my $section_y = $bounding_box->center->[Y];
my $section_line = [
[ $bounding_box->x_min, $section_y ],
[ $bounding_box->x_max, $section_y ],
];
# initialize output
my $max_z = max(keys %paths);
my $svg = SVG->new(
width => $opt{scale} * $bounding_box->size->[X],
height => $opt{scale} * $max_z,
);
# put everything into a group
my $g = $svg->group(style => {
'stroke-width' => 1,
'stroke' => '#444444',
'fill' => 'grey',
});
# draw paths
foreach my $z (sort keys %paths) {
foreach my $line (@{ $paths{$z} }) {
my @intersections = @{intersection_pl(
[ $section_line ],
[ _grow($line, $opt{extrusion_width}/2) ],
)};
$g->rectangle(
'x' => $opt{scale} * ($_->[0][X] - $bounding_box->x_min),
'y' => $opt{scale} * ($max_z - $z),
'width' => $opt{scale} * abs($_->[1][X] - $_->[0][X]),
'height' => $opt{scale} * $opt{layer_height},
'rx' => $opt{scale} * $opt{layer_height} * 0.35,
'ry' => $opt{scale} * $opt{layer_height} * 0.35,
) for @intersections;
}
}
# write output
Slic3r::open(\my $fh, '>', $output_file);
print $fh $svg->xmlify;
close $fh;
printf "Section cut SVG written to %s\n", $output_file;
}
# replace built-in Line->grow method which relies on int_offset()
sub _grow {
my ($line, $distance) = @_;
my $polygon = [ @$line, CORE::reverse @$line[1..($#$line-1)] ];
return @{Math::Clipper::offset([$polygon], $distance, 100000, JT_SQUARE, 2)};
}
sub usage {
my ($exit_code) = @_;
print <<"EOF";
Usage: gcode_sectioncut.pl [ OPTIONS ] file.gcode
--help Output this usage screen and exit
--layer-height, -h Use the specified layer height
--extrusion-width, -w Use the specified extrusion width
--scale Factor for converting G-code units to SVG units
EOF
exit ($exit_code || 0);
}
__END__

View File

@ -201,6 +201,10 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/2DBed.hpp ${LIBDIR}/slic3r/GUI/2DBed.hpp
${LIBDIR}/slic3r/GUI/wxExtensions.cpp ${LIBDIR}/slic3r/GUI/wxExtensions.cpp
${LIBDIR}/slic3r/GUI/wxExtensions.hpp ${LIBDIR}/slic3r/GUI/wxExtensions.hpp
${LIBDIR}/slic3r/GUI/BonjourDialog.cpp
${LIBDIR}/slic3r/GUI/BonjourDialog.hpp
${LIBDIR}/slic3r/Utils/ASCIIFolding.cpp
${LIBDIR}/slic3r/Utils/ASCIIFolding.hpp
${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.cpp
${LIBDIR}/slic3r/Utils/Http.hpp ${LIBDIR}/slic3r/Utils/Http.hpp
${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp

View File

@ -1427,10 +1427,12 @@ Polylines FillTriangles::fill_surface(const Surface *surface, const FillParams &
// Each linear fill covers 1/3 of the target coverage. // Each linear fill covers 1/3 of the target coverage.
FillParams params2 = params; FillParams params2 = params;
params2.density *= 0.333333333f; params2.density *= 0.333333333f;
FillParams params3 = params2;
params3.dont_connect = true;
Polylines polylines_out; Polylines polylines_out;
if (! fill_surface_by_lines(surface, params2, 0.f, 0., polylines_out) || if (! fill_surface_by_lines(surface, params2, 0.f, 0., polylines_out) ||
! fill_surface_by_lines(surface, params2, float(M_PI / 3.), 0., polylines_out) || ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), 0., polylines_out) ||
! fill_surface_by_lines(surface, params2, float(2. * M_PI / 3.), 0., polylines_out)) { ! fill_surface_by_lines(surface, params3, float(2. * M_PI / 3.), 0., polylines_out)) {
printf("FillTriangles::fill_surface() failed to fill a region.\n"); printf("FillTriangles::fill_surface() failed to fill a region.\n");
} }
return polylines_out; return polylines_out;
@ -1441,10 +1443,12 @@ Polylines FillStars::fill_surface(const Surface *surface, const FillParams &para
// Each linear fill covers 1/3 of the target coverage. // Each linear fill covers 1/3 of the target coverage.
FillParams params2 = params; FillParams params2 = params;
params2.density *= 0.333333333f; params2.density *= 0.333333333f;
FillParams params3 = params2;
params3.dont_connect = true;
Polylines polylines_out; Polylines polylines_out;
if (! fill_surface_by_lines(surface, params2, 0.f, 0., polylines_out) || if (! fill_surface_by_lines(surface, params2, 0.f, 0., polylines_out) ||
! fill_surface_by_lines(surface, params2, float(M_PI / 3.), 0., polylines_out) || ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), 0., polylines_out) ||
! fill_surface_by_lines(surface, params2, float(2. * M_PI / 3.), 0.5 * this->spacing / params2.density, polylines_out)) { ! fill_surface_by_lines(surface, params3, float(2. * M_PI / 3.), 0.5 * this->spacing / params2.density, polylines_out)) {
printf("FillStars::fill_surface() failed to fill a region.\n"); printf("FillStars::fill_surface() failed to fill a region.\n");
} }
return polylines_out; return polylines_out;
@ -1455,12 +1459,14 @@ Polylines FillCubic::fill_surface(const Surface *surface, const FillParams &para
// Each linear fill covers 1/3 of the target coverage. // Each linear fill covers 1/3 of the target coverage.
FillParams params2 = params; FillParams params2 = params;
params2.density *= 0.333333333f; params2.density *= 0.333333333f;
FillParams params3 = params2;
params3.dont_connect = true;
Polylines polylines_out; Polylines polylines_out;
coordf_t dx = sqrt(0.5) * z; coordf_t dx = sqrt(0.5) * z;
if (! fill_surface_by_lines(surface, params2, 0.f, dx, polylines_out) || if (! fill_surface_by_lines(surface, params2, 0.f, dx, polylines_out) ||
! fill_surface_by_lines(surface, params2, float(M_PI / 3.), - dx, polylines_out) || ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), - dx, polylines_out) ||
// Rotated by PI*2/3 + PI to achieve reverse sloping wall. // Rotated by PI*2/3 + PI to achieve reverse sloping wall.
! fill_surface_by_lines(surface, params2, float(M_PI * 2. / 3.), dx, polylines_out)) { ! fill_surface_by_lines(surface, params3, float(M_PI * 2. / 3.), dx, polylines_out)) {
printf("FillCubic::fill_surface() failed to fill a region.\n"); printf("FillCubic::fill_surface() failed to fill a region.\n");
} }
return polylines_out; return polylines_out;

View File

@ -107,9 +107,11 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
TYPE_G1 = 1 << 5, TYPE_G1 = 1 << 5,
TYPE_ADJUSTABLE = 1 << 6, TYPE_ADJUSTABLE = 1 << 6,
TYPE_EXTERNAL_PERIMETER = 1 << 7, TYPE_EXTERNAL_PERIMETER = 1 << 7,
TYPE_WIPE = 1 << 8, // The line sets a feedrate.
TYPE_G4 = 1 << 9, TYPE_HAS_F = 1 << 8,
TYPE_G92 = 1 << 10, TYPE_WIPE = 1 << 9,
TYPE_G4 = 1 << 10,
TYPE_G92 = 1 << 11,
}; };
Line(unsigned int type, size_t line_start, size_t line_end) : Line(unsigned int type, size_t line_start, size_t line_end) :
@ -187,9 +189,13 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
(*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1); (*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1);
if (axis != size_t(-1)) { if (axis != size_t(-1)) {
new_pos[axis] = float(atof(++c)); new_pos[axis] = float(atof(++c));
if (axis == 4) if (axis == 4) {
// Convert mm/min to mm/sec. // Convert mm/min to mm/sec.
new_pos[4] /= 60.f; new_pos[4] /= 60.f;
if ((line.type & Adjustment::Line::TYPE_G92) == 0)
// This is G0 or G1 line and it sets the feedrate. This mark is used for reducing the duplicate F calls.
line.type |= Adjustment::Line::TYPE_HAS_F;
}
} }
// Skip this word. // Skip this word.
for (; *c != ' ' && *c != '\t' && *c != 0; ++ c); for (; *c != ' ' && *c != '\t' && *c != 0; ++ c);
@ -227,6 +233,8 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
if ((line.type & Adjustment::Line::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1)) if ((line.type & Adjustment::Line::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1))
line.time_max = (min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / min_print_speed); line.time_max = (min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / min_print_speed);
if (active_speed_modifier < adjustment->lines.size() && (line.type & Adjustment::Line::TYPE_G1)) { if (active_speed_modifier < adjustment->lines.size() && (line.type & Adjustment::Line::TYPE_G1)) {
// Inside the ";_EXTRUDE_SET_SPEED" blocks, there must not be a G1 Fxx entry.
assert((line.type & Adjustment::Line::TYPE_HAS_F) == 0);
Adjustment::Line &sm = adjustment->lines[active_speed_modifier]; Adjustment::Line &sm = adjustment->lines[active_speed_modifier];
sm.length += line.length; sm.length += line.length;
sm.time += line.time; sm.time += line.time;
@ -415,17 +423,20 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
}; };
change_extruder_set_fan(); change_extruder_set_fan();
size_t pos = 0; const char *pos = gcode.c_str();
int current_feedrate = 0;
for (const Adjustment::Line *line : lines) { for (const Adjustment::Line *line : lines) {
if (line->line_start > pos) const char *line_start = gcode.c_str() + line->line_start;
new_gcode.append(gcode.c_str() + pos, line->line_start - pos); const char *line_end = gcode.c_str() + line->line_end;
if (line_start > pos)
new_gcode.append(pos, line_start - pos);
if (line->type & Adjustment::Line::TYPE_SET_TOOL) { if (line->type & Adjustment::Line::TYPE_SET_TOOL) {
unsigned int new_extruder = (unsigned int)atoi(gcode.c_str() + line->line_start + toolchange_prefix.size()); unsigned int new_extruder = (unsigned int)atoi(line_start + toolchange_prefix.size());
if (new_extruder != m_current_extruder) { if (new_extruder != m_current_extruder) {
m_current_extruder = new_extruder; m_current_extruder = new_extruder;
change_extruder_set_fan(); change_extruder_set_fan();
} }
new_gcode.append(gcode.c_str() + line->line_start, line->line_end - line->line_start); new_gcode.append(line_start, line_end - line_start);
} else if (line->type & Adjustment::Line::TYPE_BRIDGE_FAN_START) { } else if (line->type & Adjustment::Line::TYPE_BRIDGE_FAN_START) {
if (bridge_fan_control) if (bridge_fan_control)
new_gcode += m_gcodegen.writer().set_fan(bridge_fan_speed, true); new_gcode += m_gcodegen.writer().set_fan(bridge_fan_speed, true);
@ -434,40 +445,80 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
new_gcode += m_gcodegen.writer().set_fan(fan_speed, true); new_gcode += m_gcodegen.writer().set_fan(fan_speed, true);
} else if (line->type & Adjustment::Line::TYPE_EXTRUDE_END) { } else if (line->type & Adjustment::Line::TYPE_EXTRUDE_END) {
// Just remove this comment. // Just remove this comment.
} else if (line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE)) { } else if (line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE | Adjustment::Line::TYPE_HAS_F)) {
// Start of the comment. The line type indicates there must be some comment present. // Find the start of a comment, or roll to the end of line.
const char *end = strchr(gcode.c_str() + line->line_start, ';'); const char *end = line_start;
for (; end < line_end && *end != ';'; ++ end);
// Find the 'F' word.
const char *fpos = strstr(line_start + 2, " F") + 2;
int new_feedrate = current_feedrate;
bool modify = false;
assert(fpos != nullptr);
if (line->slowdown) { if (line->slowdown) {
// Replace the feedrate. modify = true;
const char *pos = strstr(gcode.c_str() + line->line_start + 2, " F") + 2; new_feedrate = int(floor(60. * (line->length / line->time) + 0.5));
new_gcode.append(gcode.c_str() + line->line_start, pos - gcode.c_str() - line->line_start);
char buf[64];
sprintf(buf, "%d", int(floor(60. * (line->length / line->time) + 0.5)));
new_gcode += buf;
// Skip the non-whitespaces up to the comment.
for (; *pos != ' ' && *pos != ';'; ++ pos);
// Append the rest of the line without the comment.
if (pos < end)
new_gcode.append(pos, end - pos);
} else { } else {
// Append the line without the comment. new_feedrate = atoi(fpos);
new_gcode.append(gcode.c_str() + line->line_start, end - gcode.c_str() - line->line_start); if (new_feedrate != current_feedrate) {
// Append the line without the comment.
new_gcode.append(line_start, end - line_start);
current_feedrate = new_feedrate;
} else if ((line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE)) || line->length == 0.) {
// Feedrate does not change and this line does not move the print head. Skip the complete G-code line including the G-code comment.
end = line_end;
} else {
// Remove the feedrate from the G0/G1 line.
modify = true;
}
}
if (modify) {
if (new_feedrate != current_feedrate) {
// Replace the feedrate.
new_gcode.append(line_start, fpos - line_start);
current_feedrate = new_feedrate;
char buf[64];
sprintf(buf, "%d", int(current_feedrate));
new_gcode += buf;
} else {
// Remove the feedrate word.
const char *f = fpos;
// Roll the pointer before the 'F' word.
for (f -= 2; f > line_start && (*f == ' ' || *f == '\t'); -- f);
// Append up to the F word, without the trailing whitespace.
new_gcode.append(line_start, f - line_start + 1);
}
// Skip the non-whitespaces of the F parameter up the comment or end of line.
for (; fpos != end && *fpos != ' ' && *fpos != ';' && *fpos != '\n'; ++fpos);
// Append the rest of the line without the comment.
if (fpos < end)
new_gcode.append(fpos, end - fpos);
// There should never be an empty G1 statement emited by the filter. Such lines should be removed completely.
assert(new_gcode.size() < 4 || new_gcode.substr(new_gcode.size() - 4) != "G1 \n");
}
// Process the rest of the line.
if (end < line_end) {
if (line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE)) {
// Process comments, remove ";_EXTRUDE_SET_SPEED", ";_EXTERNAL_PERIMETER", ";_WIPE"
std::string comment(end, line_end);
boost::replace_all(comment, ";_EXTRUDE_SET_SPEED", "");
if (line->type & Adjustment::Line::TYPE_EXTERNAL_PERIMETER)
boost::replace_all(comment, ";_EXTERNAL_PERIMETER", "");
if (line->type & Adjustment::Line::TYPE_WIPE)
boost::replace_all(comment, ";_WIPE", "");
new_gcode += comment;
} else {
// Just attach the rest of the source line.
new_gcode.append(end, line_end - end);
}
} }
// Process the comments, remove ";_EXTRUDE_SET_SPEED", ";_EXTERNAL_PERIMETER", ";_WIPE"
std::string comment(end, gcode.c_str() + line->line_end);
boost::replace_all(comment, ";_EXTRUDE_SET_SPEED", "");
if (line->type & Adjustment::Line::TYPE_EXTERNAL_PERIMETER)
boost::replace_all(comment, ";_EXTERNAL_PERIMETER", "");
if (line->type & Adjustment::Line::TYPE_WIPE)
boost::replace_all(comment, ";_WIPE", "");
new_gcode += comment;
} else { } else {
new_gcode.append(gcode.c_str() + line->line_start, line->line_end - line->line_start); new_gcode.append(line_start, line_end - line_start);
} }
pos = line->line_end; pos = line_end;
} }
if (pos < gcode.size()) const char *gcode_end = gcode.c_str() + gcode.size();
new_gcode.append(gcode.c_str() + pos, gcode.size() - pos); if (pos < gcode_end)
new_gcode.append(pos, gcode_end - pos);
return new_gcode; return new_gcode;
} }

View File

@ -987,101 +987,227 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
} }
} }
} }
// Build a map of lines by edge_a_id and a_id. struct OpenPolyline {
std::vector<IntersectionLine*> by_edge_a_id; OpenPolyline() {};
std::vector<IntersectionLine*> by_a_id; OpenPolyline(const IntersectionReference &start, const IntersectionReference &end, Points &&points) :
by_edge_a_id.reserve(lines.size()); start(start), end(end), points(std::move(points)), consumed(false) {}
by_a_id.reserve(lines.size()); void reverse() {
for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line) { std::swap(start, end);
if (! line->skip) { std::reverse(points.begin(), points.end());
if (line->edge_a_id != -1) }
by_edge_a_id.push_back(&(*line)); // [line->edge_a_id].push_back(); IntersectionReference start;
if (line->a_id != -1) IntersectionReference end;
by_a_id.push_back(&(*line)); // [line->a_id].push_back(&(*line)); Points points;
bool consumed;
};
std::vector<OpenPolyline> open_polylines;
{
// Build a map of lines by edge_a_id and a_id.
std::vector<IntersectionLine*> by_edge_a_id;
std::vector<IntersectionLine*> by_a_id;
by_edge_a_id.reserve(lines.size());
by_a_id.reserve(lines.size());
for (IntersectionLine &line : lines) {
if (! line.skip) {
if (line.edge_a_id != -1)
by_edge_a_id.emplace_back(&line);
if (line.a_id != -1)
by_a_id.emplace_back(&line);
}
}
auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; };
auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; };
std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower);
std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower);
// Chain the segments with a greedy algorithm, collect the loops and unclosed polylines.
IntersectionLines::iterator it_line_seed = lines.begin();
for (;;) {
// take first spare line and start a new loop
IntersectionLine *first_line = nullptr;
for (; it_line_seed != lines.end(); ++ it_line_seed)
if (! it_line_seed->skip) {
first_line = &(*it_line_seed ++);
break;
}
if (first_line == nullptr)
break;
first_line->skip = true;
Points loop_pts;
loop_pts.emplace_back(first_line->a);
IntersectionLine *last_line = first_line;
/*
printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id,
first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y);
*/
IntersectionLine key;
for (;;) {
// find a line starting where last one finishes
IntersectionLine* next_line = nullptr;
if (last_line->edge_b_id != -1) {
key.edge_a_id = last_line->edge_b_id;
auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower);
if (it_begin != by_edge_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
if (! (*it_line)->skip) {
next_line = *it_line;
break;
}
}
}
if (next_line == nullptr && last_line->b_id != -1) {
key.a_id = last_line->b_id;
auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower);
if (it_begin != by_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
if (! (*it_line)->skip) {
next_line = *it_line;
break;
}
}
}
if (next_line == nullptr) {
// Check whether we closed this loop.
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) ||
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) {
// The current loop is complete. Add it to the output.
loops->emplace_back(std::move(loop_pts));
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
#endif
} else {
// This is an open polyline. Add it to the list of open polylines. These open polylines will processed later.
loop_pts.emplace_back(last_line->b);
open_polylines.emplace_back(OpenPolyline(
IntersectionReference(first_line->a_id, first_line->edge_a_id),
IntersectionReference(last_line->b_id, last_line->edge_b_id), std::move(loop_pts)));
}
break;
}
/*
printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
*/
loop_pts.emplace_back(next_line->a);
last_line = next_line;
next_line->skip = true;
}
} }
} }
auto by_edge_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->edge_a_id < il2->edge_a_id; };
auto by_vertex_lower = [](const IntersectionLine* il1, const IntersectionLine *il2) { return il1->a_id < il2->a_id; };
std::sort(by_edge_a_id.begin(), by_edge_a_id.end(), by_edge_lower);
std::sort(by_a_id.begin(), by_a_id.end(), by_vertex_lower);
IntersectionLines::iterator it_line_seed = lines.begin(); // Now process the open polylines.
CYCLE: while (1) { if (! open_polylines.empty()) {
// take first spare line and start a new loop // Store the end points of open_polylines into vectors sorted
IntersectionLine *first_line = nullptr; struct OpenPolylineEnd {
for (; it_line_seed != lines.end(); ++ it_line_seed) OpenPolylineEnd(OpenPolyline *polyline, bool start) : polyline(polyline), start(start) {}
if (! it_line_seed->skip) { OpenPolyline *polyline;
first_line = &(*it_line_seed ++); // Is it the start or end point?
break; bool start;
} const IntersectionReference& ipref() const { return start ? polyline->start : polyline->end; }
if (first_line == nullptr) int point_id() const { return ipref().point_id; }
break; int edge_id () const { return ipref().edge_id; }
first_line->skip = true; };
Points loop_pts; auto by_edge_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.edge_id() < ope2.edge_id(); };
loop_pts.push_back(first_line->a); auto by_point_lower = [](const OpenPolylineEnd &ope1, const OpenPolylineEnd &ope2) { return ope1.point_id() < ope2.point_id(); };
IntersectionLine *last_line = first_line; std::vector<OpenPolylineEnd> by_edge_id;
std::vector<OpenPolylineEnd> by_point_id;
/* by_edge_id.reserve(2 * open_polylines.size());
printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", by_point_id.reserve(2 * open_polylines.size());
first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id, for (OpenPolyline &opl : open_polylines) {
first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y); if (opl.start.edge_id != -1)
*/ by_edge_id .emplace_back(OpenPolylineEnd(&opl, true));
if (opl.end.edge_id != -1)
IntersectionLine key; by_edge_id .emplace_back(OpenPolylineEnd(&opl, false));
for (;;) { if (opl.start.point_id != -1)
// find a line starting where last one finishes by_point_id.emplace_back(OpenPolylineEnd(&opl, true));
IntersectionLine* next_line = nullptr; if (opl.end.point_id != -1)
if (last_line->edge_b_id != -1) { by_point_id.emplace_back(OpenPolylineEnd(&opl, false));
key.edge_a_id = last_line->edge_b_id; }
auto it_begin = std::lower_bound(by_edge_a_id.begin(), by_edge_a_id.end(), &key, by_edge_lower); std::sort(by_edge_id .begin(), by_edge_id .end(), by_edge_lower);
if (it_begin != by_edge_a_id.end()) { std::sort(by_point_id.begin(), by_point_id.end(), by_point_lower);
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line) // Try to connect the loops.
if (! (*it_line)->skip) { for (OpenPolyline &opl : open_polylines) {
next_line = *it_line; if (opl.consumed)
break; continue;
} opl.consumed = true;
OpenPolylineEnd end(&opl, false);
for (;;) {
// find a line starting where last one finishes
OpenPolylineEnd* next_start = nullptr;
if (end.edge_id() != -1) {
auto it_begin = std::lower_bound(by_edge_id.begin(), by_edge_id.end(), end, by_edge_lower);
if (it_begin != by_edge_id.end()) {
auto it_end = std::upper_bound(it_begin, by_edge_id.end(), end, by_edge_lower);
for (auto it_edge = it_begin; it_edge != it_end; ++ it_edge)
if (! it_edge->polyline->consumed) {
next_start = &(*it_edge);
break;
}
}
} }
} if (next_start == nullptr && end.point_id() != -1) {
if (next_line == nullptr && last_line->b_id != -1) { auto it_begin = std::lower_bound(by_point_id.begin(), by_point_id.end(), end, by_point_lower);
key.a_id = last_line->b_id; if (it_begin != by_point_id.end()) {
auto it_begin = std::lower_bound(by_a_id.begin(), by_a_id.end(), &key, by_vertex_lower); auto it_end = std::upper_bound(it_begin, by_point_id.end(), end, by_point_lower);
if (it_begin != by_a_id.end()) { for (auto it_point = it_begin; it_point != it_end; ++ it_point)
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower); if (! it_point->polyline->consumed) {
for (auto it_line = it_begin; it_line != it_end; ++ it_line) next_start = &(*it_point);
if (! (*it_line)->skip) { break;
next_line = *it_line; }
break; }
}
} }
} if (next_start == nullptr) {
if (next_line == nullptr) { // The current loop could not be closed. Unmark the segment.
// check whether we closed this loop opl.consumed = false;
if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) || break;
(first_line->a_id != -1 && first_line->a_id == last_line->b_id)) { }
// loop is complete // Attach this polyline to the end of the initial polyline.
loops->emplace_back(std::move(loop_pts)); if (next_start->start) {
#ifdef SLIC3R_TRIANGLEMESH_DEBUG auto it = next_start->polyline->points.begin();
printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size()); std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
#endif //opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.end());
goto CYCLE; } else {
auto it = next_start->polyline->points.rbegin();
std::copy(++ it, next_start->polyline->points.rend(), back_inserter(opl.points));
//opl.points.insert(opl.points.back(), ++ it, next_start->polyline->points.rend());
} }
// we can't close this loop! end = *next_start;
//// push @failed_loops, [@loop]; end.start = !end.start;
//#ifdef SLIC3R_TRIANGLEMESH_DEBUG next_start->polyline->points.clear();
printf(" Unable to close this loop having %d points\n", (int)loop_pts.size()); next_start->polyline->consumed = true;
//#endif // Check whether we closed this loop.
goto CYCLE; const IntersectionReference &ip1 = opl.start;
const IntersectionReference &ip2 = end.ipref();
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
// The current loop is complete. Add it to the output.
assert(opl.points.front().point_id == opl.points.back().point_id);
assert(opl.points.front().edge_id == opl.points.back().edge_id);
// Remove the duplicate last point.
opl.points.pop_back();
if (opl.points.size() >= 3) {
// The closed polygon is patched from pieces with messed up orientation, therefore
// the orientation of the patched up polygon is not known.
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
double area = 0.;
for (size_t i = 0, j = opl.points.size() - 1; i < opl.points.size(); j = i ++)
area += double(opl.points[j].x + opl.points[i].x) * double(opl.points[i].y - opl.points[j].y);
if (area < 0)
std::reverse(opl.points.begin(), opl.points.end());
loops->emplace_back(std::move(opl.points));
}
opl.points.clear();
break;
}
// Continue with the current loop.
} }
/*
printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n",
next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
*/
loop_pts.push_back(next_line->a);
last_line = next_line;
next_line->skip = true;
} }
} }
} }

View File

@ -85,11 +85,11 @@ enum FacetEdgeType {
feHorizontal feHorizontal
}; };
class IntersectionPoint : public Point class IntersectionReference
{ {
public: public:
IntersectionPoint() : point_id(-1), edge_id(-1) {}; IntersectionReference() : point_id(-1), edge_id(-1) {};
// Inherits coord_t x, y IntersectionReference(int point_id, int edge_id) : point_id(point_id), edge_id(edge_id) {}
// Where is this intersection point located? On mesh vertex or mesh edge? // Where is this intersection point located? On mesh vertex or mesh edge?
// Only one of the following will be set, the other will remain set to -1. // Only one of the following will be set, the other will remain set to -1.
// Index of the mesh vertex. // Index of the mesh vertex.
@ -98,6 +98,15 @@ public:
int edge_id; int edge_id;
}; };
class IntersectionPoint : public Point, public IntersectionReference
{
public:
IntersectionPoint() {};
IntersectionPoint(int point_id, int edge_id, const Point &pt) : IntersectionReference(point_id, edge_id), Point(pt) {}
IntersectionPoint(const IntersectionReference &ir, const Point &pt) : IntersectionReference(ir), Point(pt) {}
// Inherits coord_t x, y
};
class IntersectionLine : public Line class IntersectionLine : public Line
{ {
public: public:

View File

@ -0,0 +1,200 @@
#include "slic3r/Utils/Bonjour.hpp" // On Windows, boost needs to be included before wxWidgets headers
#include "BonjourDialog.hpp"
#include <set>
#include <mutex>
#include <wx/sizer.h>
#include <wx/button.h>
#include <wx/listctrl.h>
#include <wx/stattext.h>
#include <wx/timer.h>
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/Utils/Bonjour.hpp"
namespace Slic3r {
struct BonjourReplyEvent : public wxEvent
{
BonjourReply reply;
BonjourReplyEvent(wxEventType eventType, int winid, BonjourReply &&reply) :
wxEvent(winid, eventType),
reply(std::move(reply))
{}
virtual wxEvent *Clone() const
{
return new BonjourReplyEvent(*this);
}
};
wxDEFINE_EVENT(EVT_BONJOUR_REPLY, BonjourReplyEvent);
wxDECLARE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent);
wxDEFINE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent);
class ReplySet: public std::set<BonjourReply> {};
struct LifetimeGuard
{
std::mutex mutex;
BonjourDialog *dialog;
LifetimeGuard(BonjourDialog *dialog) : dialog(dialog) {}
};
BonjourDialog::BonjourDialog(wxWindow *parent) :
wxDialog(parent, wxID_ANY, _(L("Network lookup"))),
list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300))),
replies(new ReplySet),
label(new wxStaticText(this, wxID_ANY, "")),
timer(new wxTimer()),
timer_state(0)
{
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
list->SetSingleStyle(wxLC_SINGLE_SEL);
list->SetSingleStyle(wxLC_SORT_DESCENDING);
list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 50);
list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 100);
list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 200);
list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50);
vsizer->Add(list, 1, wxEXPAND | wxALL, 10);
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, 10);
button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, 10);
// ^ Note: The Ok/Cancel labels are translated by wxWidgets
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
SetSizerAndFit(vsizer);
Bind(EVT_BONJOUR_REPLY, &BonjourDialog::on_reply, this);
Bind(EVT_BONJOUR_COMPLETE, [this](wxCommandEvent &) {
this->timer_state = 0;
});
Bind(wxEVT_TIMER, &BonjourDialog::on_timer, this);
}
BonjourDialog::~BonjourDialog()
{
// Needed bacuse of forward defs
}
bool BonjourDialog::show_and_lookup()
{
Show(); // Because we need GetId() to work before ShowModal()
timer->Stop();
timer->SetOwner(this);
timer_state = 1;
timer->Start(1000);
wxTimerEvent evt_dummy;
on_timer(evt_dummy);
// The background thread needs to queue messages for this dialog
// and for that it needs a valid pointer to it (mandated by the wxWidgets API).
// Here we put the pointer under a shared_ptr and protect it by a mutex,
// so that both threads can access it safely.
auto dguard = std::make_shared<LifetimeGuard>(this);
bonjour = std::move(Bonjour("octoprint")
.set_retries(3)
.set_timeout(4)
.on_reply([dguard](BonjourReply &&reply) {
std::lock_guard<std::mutex> lock_guard(dguard->mutex);
auto dialog = dguard->dialog;
if (dialog != nullptr) {
auto evt = new BonjourReplyEvent(EVT_BONJOUR_REPLY, dialog->GetId(), std::move(reply));
wxQueueEvent(dialog, evt);
}
})
.on_complete([dguard]() {
std::lock_guard<std::mutex> lock_guard(dguard->mutex);
auto dialog = dguard->dialog;
if (dialog != nullptr) {
auto evt = new wxCommandEvent(EVT_BONJOUR_COMPLETE, dialog->GetId());
wxQueueEvent(dialog, evt);
}
})
.lookup()
);
bool res = ShowModal() == wxID_OK && list->GetFirstSelected() >= 0;
{
// Tell the background thread the dialog is going away...
std::lock_guard<std::mutex> lock_guard(dguard->mutex);
dguard->dialog = nullptr;
}
return res;
}
wxString BonjourDialog::get_selected() const
{
auto sel = list->GetFirstSelected();
return sel >= 0 ? list->GetItemText(sel) : wxString();
}
// Private
void BonjourDialog::on_reply(BonjourReplyEvent &e)
{
if (replies->find(e.reply) != replies->end()) {
// We already have this reply
return;
}
replies->insert(std::move(e.reply));
auto selected = get_selected();
list->DeleteAllItems();
// The whole list is recreated so that we benefit from it already being sorted in the set.
// (And also because wxListView's sorting API is bananas.)
for (const auto &reply : *replies) {
auto item = list->InsertItem(0, reply.full_address);
list->SetItem(item, 1, reply.hostname);
list->SetItem(item, 2, reply.service_name);
list->SetItem(item, 3, reply.version);
}
for (int i = 0; i < 4; i++) {
this->list->SetColumnWidth(i, wxLIST_AUTOSIZE);
if (this->list->GetColumnWidth(i) < 100) { this->list->SetColumnWidth(i, 100); }
}
if (!selected.IsEmpty()) {
// Attempt to preserve selection
auto hit = list->FindItem(-1, selected);
if (hit >= 0) { list->SetItemState(hit, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); }
}
}
void BonjourDialog::on_timer(wxTimerEvent &)
{
const auto search_str = _(L("Searching for devices"));
if (timer_state > 0) {
const std::string dots(timer_state, '.');
label->SetLabel(wxString::Format("%s %s", search_str, dots));
timer_state = (timer_state) % 3 + 1;
} else {
label->SetLabel(wxString::Format("%s: %s", search_str, _(L("Finished."))));
timer->Stop();
}
}
}

View File

@ -0,0 +1,49 @@
#ifndef slic3r_BonjourDialog_hpp_
#define slic3r_BonjourDialog_hpp_
#include <memory>
#include <wx/dialog.h>
class wxListView;
class wxStaticText;
class wxTimer;
class wxTimerEvent;
namespace Slic3r {
class Bonjour;
class BonjourReplyEvent;
class ReplySet;
class BonjourDialog: public wxDialog
{
public:
BonjourDialog(wxWindow *parent);
BonjourDialog(BonjourDialog &&) = delete;
BonjourDialog(const BonjourDialog &) = delete;
BonjourDialog &operator=(BonjourDialog &&) = delete;
BonjourDialog &operator=(const BonjourDialog &) = delete;
~BonjourDialog();
bool show_and_lookup();
wxString get_selected() const;
private:
wxListView *list;
std::unique_ptr<ReplySet> replies;
wxStaticText *label;
std::shared_ptr<Bonjour> bonjour;
std::unique_ptr<wxTimer> timer;
unsigned timer_state;
void on_reply(BonjourReplyEvent &);
void on_timer(wxTimerEvent &);
};
}
#endif

View File

@ -273,7 +273,7 @@ void SpinCtrl::BUILD() {
// # when it was changed from the text control, so the on_change callback // # when it was changed from the text control, so the on_change callback
// # gets the old one, and on_kill_focus resets the control to the old value. // # gets the old one, and on_kill_focus resets the control to the old value.
// # As a workaround, we get the new value from $event->GetString and store // # As a workaround, we get the new value from $event->GetString and store
// # here temporarily so that we can return it from $self->get_value // # here temporarily so that we can return it from $self->get_value
std::string value = e.GetString().utf8_str().data(); std::string value = e.GetString().utf8_str().data();
if (is_matched(value, "^\\d+$")) if (is_matched(value, "^\\d+$"))
tmp_value = std::stoi(value); tmp_value = std::stoi(value);
@ -377,9 +377,9 @@ void Choice::set_selection()
} }
} }
void Choice::set_value(const std::string value) //! Redundant? void Choice::set_value(const std::string value, bool change_event) //! Redundant?
{ {
m_disable_change_event = true; m_disable_change_event = !change_event;
size_t idx=0; size_t idx=0;
for (auto el : m_opt.enum_values) for (auto el : m_opt.enum_values)
@ -396,9 +396,9 @@ void Choice::set_value(const std::string value) //! Redundant?
m_disable_change_event = false; m_disable_change_event = false;
} }
void Choice::set_value(boost::any value) void Choice::set_value(boost::any value, bool change_event)
{ {
m_disable_change_event = true; m_disable_change_event = !change_event;
switch (m_opt.type){ switch (m_opt.type){
case coInt: case coInt:
@ -441,7 +441,7 @@ void Choice::set_values(const std::vector<std::string> values)
return; return;
m_disable_change_event = true; m_disable_change_event = true;
// # it looks that Clear() also clears the text field in recent wxWidgets versions, // # it looks that Clear() also clears the text field in recent wxWidgets versions,
// # but we want to preserve it // # but we want to preserve it
auto ww = dynamic_cast<wxComboBox*>(window); auto ww = dynamic_cast<wxComboBox*>(window);
auto value = ww->GetValue(); auto value = ww->GetValue();
@ -553,9 +553,9 @@ void PointCtrl::BUILD()
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
} }
void PointCtrl::set_value(const Pointf value) void PointCtrl::set_value(const Pointf value, bool change_event)
{ {
m_disable_change_event = true; m_disable_change_event = !change_event;
double val = value.x; double val = value.x;
x_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None)); x_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None));
@ -565,7 +565,7 @@ void PointCtrl::set_value(const Pointf value)
m_disable_change_event = false; m_disable_change_event = false;
} }
void PointCtrl::set_value(boost::any value) void PointCtrl::set_value(boost::any value, bool change_event)
{ {
Pointf pt; Pointf pt;
Pointf *ptf = boost::any_cast<Pointf>(&value); Pointf *ptf = boost::any_cast<Pointf>(&value);
@ -591,7 +591,7 @@ void PointCtrl::set_value(boost::any value)
// return; // return;
// } // }
// } // }
set_value(pt); set_value(pt, change_event);
} }
boost::any PointCtrl::get_value() boost::any PointCtrl::get_value()

View File

@ -81,7 +81,7 @@ public:
/// Sets a value for this control. /// Sets a value for this control.
/// subclasses should overload with a specific version /// subclasses should overload with a specific version
/// Postcondition: Method does not fire the on_change event. /// Postcondition: Method does not fire the on_change event.
virtual void set_value(boost::any value) = 0; virtual void set_value(boost::any value, bool change_event) = 0;
/// Gets a boost::any representing this control. /// Gets a boost::any representing this control.
/// subclasses should overload with a specific version /// subclasses should overload with a specific version
@ -141,13 +141,13 @@ public:
void BUILD(); void BUILD();
wxWindow* window {nullptr}; wxWindow* window {nullptr};
virtual void set_value(std::string value) { virtual void set_value(std::string value, bool change_event = false) {
m_disable_change_event = true; m_disable_change_event = !change_event;
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value)); dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
m_disable_change_event = false; m_disable_change_event = false;
} }
virtual void set_value(boost::any value) { virtual void set_value(boost::any value, bool change_event = false) {
m_disable_change_event = true; m_disable_change_event = !change_event;
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value)); dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
m_disable_change_event = false; m_disable_change_event = false;
} }
@ -168,13 +168,13 @@ public:
wxWindow* window{ nullptr }; wxWindow* window{ nullptr };
void BUILD() override; void BUILD() override;
void set_value(const bool value) { void set_value(const bool value, bool change_event = false) {
m_disable_change_event = true; m_disable_change_event = !change_event;
dynamic_cast<wxCheckBox*>(window)->SetValue(value); dynamic_cast<wxCheckBox*>(window)->SetValue(value);
m_disable_change_event = false; m_disable_change_event = false;
} }
void set_value(boost::any value) { void set_value(boost::any value, bool change_event = false) {
m_disable_change_event = true; m_disable_change_event = !change_event;
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value)); dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
m_disable_change_event = false; m_disable_change_event = false;
} }
@ -196,13 +196,13 @@ public:
wxWindow* window{ nullptr }; wxWindow* window{ nullptr };
void BUILD() override; void BUILD() override;
void set_value(const std::string value) { void set_value(const std::string value, bool change_event = false) {
m_disable_change_event = true; m_disable_change_event = !change_event;
dynamic_cast<wxSpinCtrl*>(window)->SetValue(value); dynamic_cast<wxSpinCtrl*>(window)->SetValue(value);
m_disable_change_event = false; m_disable_change_event = false;
} }
void set_value(boost::any value) { void set_value(boost::any value, bool change_event = false) {
m_disable_change_event = true; m_disable_change_event = !change_event;
dynamic_cast<wxSpinCtrl*>(window)->SetValue(boost::any_cast<int>(value)); dynamic_cast<wxSpinCtrl*>(window)->SetValue(boost::any_cast<int>(value));
m_disable_change_event = false; m_disable_change_event = false;
} }
@ -225,8 +225,8 @@ public:
void BUILD() override; void BUILD() override;
void set_selection(); void set_selection();
void set_value(const std::string value); void set_value(const std::string value, bool change_event = false);
void set_value(boost::any value); void set_value(boost::any value, bool change_event = false);
void set_values(const std::vector<std::string> values); void set_values(const std::vector<std::string> values);
boost::any get_value() override; boost::any get_value() override;
@ -244,13 +244,13 @@ public:
wxWindow* window{ nullptr }; wxWindow* window{ nullptr };
void BUILD() override; void BUILD() override;
void set_value(const std::string value) { void set_value(const std::string value, bool change_event = false) {
m_disable_change_event = true; m_disable_change_event = !change_event;
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value); dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
m_disable_change_event = false; m_disable_change_event = false;
} }
void set_value(boost::any value) { void set_value(boost::any value, bool change_event = false) {
m_disable_change_event = true; m_disable_change_event = !change_event;
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value)); dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value));
m_disable_change_event = false; m_disable_change_event = false;
} }
@ -274,8 +274,8 @@ public:
void BUILD() override; void BUILD() override;
void set_value(const Pointf value); void set_value(const Pointf value, bool change_event = false);
void set_value(boost::any value); void set_value(boost::any value, bool change_event = false);
boost::any get_value() override; boost::any get_value() override;
void enable() override { void enable() override {

View File

@ -358,24 +358,17 @@ void open_preferences_dialog(int event_preferences)
dlg->ShowModal(); dlg->ShowModal();
} }
void create_preset_tabs(bool no_controller, bool is_disabled_button_browse, bool is_user_agent, void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
int event_value_change, int event_presets_changed,
int event_button_browse, int event_button_test)
{ {
add_created_tab(new TabPrint (g_wxTabPanel, no_controller)); add_created_tab(new TabPrint (g_wxTabPanel, no_controller));
add_created_tab(new TabFilament (g_wxTabPanel, no_controller)); add_created_tab(new TabFilament (g_wxTabPanel, no_controller));
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller, is_disabled_button_browse, is_user_agent)); add_created_tab(new TabPrinter (g_wxTabPanel, no_controller));
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) { for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i)); Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
if (! tab) if (! tab)
continue; continue;
tab->set_event_value_change(wxEventType(event_value_change)); tab->set_event_value_change(wxEventType(event_value_change));
tab->set_event_presets_changed(wxEventType(event_presets_changed)); tab->set_event_presets_changed(wxEventType(event_presets_changed));
if (tab->name() == "printer"){
TabPrinter* tab_printer = static_cast<TabPrinter*>(tab);
tab_printer->set_event_button_browse(wxEventType(event_button_browse));
tab_printer->set_event_button_test(wxEventType(event_button_test));
}
} }
} }
@ -594,19 +587,6 @@ wxString from_u8(const std::string &str)
return wxString::FromUTF8(str.c_str()); return wxString::FromUTF8(str.c_str());
} }
wxWindow *get_widget_by_id(int id)
{
if (g_wxMainFrame == nullptr) {
throw std::runtime_error("Main frame not set");
}
wxWindow *window = g_wxMainFrame->FindWindow(id);
if (window == nullptr) {
throw std::runtime_error((boost::format("Could not find widget by ID: %1%") % id).str());
}
return window;
}
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer) void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer)
{ {

View File

@ -87,9 +87,7 @@ void add_debug_menu(wxMenuBar *menu, int event_language_change);
void open_preferences_dialog(int event_preferences); void open_preferences_dialog(int event_preferences);
// Create a new preset tab (print, filament and printer), // Create a new preset tab (print, filament and printer),
void create_preset_tabs(bool no_controller, bool is_disabled_button_browse, bool is_user_agent, void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed);
int event_value_change, int event_presets_changed,
int event_button_browse, int event_button_test);
TabIface* get_preset_tab_iface(char *name); TabIface* get_preset_tab_iface(char *name);
// add it at the end of the tab panel. // add it at the end of the tab panel.
@ -128,7 +126,6 @@ wxString L_str(const std::string &str);
// Return wxString from std::string in UTF8 // Return wxString from std::string in UTF8
wxString from_u8(const std::string &str); wxString from_u8(const std::string &str);
wxWindow *get_widget_by_id(int id);
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer); void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);

View File

@ -102,9 +102,9 @@ public:
if (m_fields.find(id) == m_fields.end()) return nullptr; if (m_fields.find(id) == m_fields.end()) return nullptr;
return m_fields.at(id).get(); return m_fields.at(id).get();
} }
bool set_value(t_config_option_key id, boost::any value) { bool set_value(t_config_option_key id, boost::any value, bool change_event = false) {
if (m_fields.find(id) == m_fields.end()) return false; if (m_fields.find(id) == m_fields.end()) return false;
m_fields.at(id)->set_value(value); m_fields.at(id)->set_value(value, change_event);
return true; return true;
} }
boost::any get_value(t_config_option_key id) { boost::any get_value(t_config_option_key id) {

View File

@ -3,6 +3,9 @@
#include "PresetBundle.hpp" #include "PresetBundle.hpp"
#include "PresetHints.hpp" #include "PresetHints.hpp"
#include "../../libslic3r/Utils.hpp" #include "../../libslic3r/Utils.hpp"
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/OctoPrint.hpp"
#include "BonjourDialog.hpp"
#include <wx/app.h> #include <wx/app.h>
#include <wx/button.h> #include <wx/button.h>
@ -14,6 +17,7 @@
#include <wx/treectrl.h> #include <wx/treectrl.h>
#include <wx/imaglist.h> #include <wx/imaglist.h>
#include <wx/settings.h> #include <wx/settings.h>
#include <wx/filedlg.h>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
@ -1388,39 +1392,18 @@ void TabPrinter::build()
} }
optgroup = page->new_optgroup(_(L("OctoPrint upload"))); optgroup = page->new_optgroup(_(L("OctoPrint upload")));
// # append two buttons to the Host line
auto octoprint_host_browse = [this] (wxWindow* parent) { auto octoprint_host_browse = [this, optgroup] (wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
// btn->SetFont($Slic3r::GUI::small_font);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn); sizer->Add(btn);
if (m_is_disabled_button_browse) btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) {
btn->Disable(); BonjourDialog dialog(parent);
if (dialog.show_and_lookup()) {
btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e){ optgroup->set_value("octoprint_host", std::move(dialog.get_selected()), true);
if (m_event_button_browse > 0){
wxCommandEvent event(m_event_button_browse);
event.SetString("Button BROWSE was clicked!");
g_wxMainFrame->ProcessWindowEvent(event);
} }
// // # look for devices
// auto entries;
// {
// my $res = Net::Bonjour->new('http');
// $res->discover;
// $entries = [$res->entries];
// }
// if (@{$entries}) {
// my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries);
// $self->_load_key_value('octoprint_host', $dlg->GetValue . ":".$dlg->GetPort)
// if $dlg->ShowModal == wxID_OK;
// }
// else {
// auto msg_window = new wxMessageDialog(parent, "No Bonjour device found", "Device Browser", wxOK | wxICON_INFORMATION);
// msg_window->ShowModal();
// }
}); });
return sizer; return sizer;
@ -1429,33 +1412,23 @@ void TabPrinter::build()
auto octoprint_host_test = [this](wxWindow* parent) { auto octoprint_host_test = [this](wxWindow* parent) {
auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), auto btn = m_octoprint_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
// btn->SetFont($Slic3r::GUI::small_font);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn); sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e) { btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
if (m_event_button_test > 0){ OctoPrint octoprint(m_config);
wxCommandEvent event(m_event_button_test); wxString msg;
event.SetString("Button TEST was clicked!"); if (octoprint.test(msg)) {
g_wxMainFrame->ProcessWindowEvent(event); show_info(this, _(L("Connection to OctoPrint works correctly.")), _(L("Success!")));
} else {
const auto text = wxString::Format("%s: %s\n\n%s",
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))
);
show_error(this, text);
} }
// my $ua = LWP::UserAgent->new; });
// $ua->timeout(10);
//
// my $res = $ua->get(
// "http://".$self->{config}->octoprint_host . "/api/version",
// 'X-Api-Key' = > $self->{config}->octoprint_apikey,
// );
// if ($res->is_success) {
// show_info(parent, "Connection to OctoPrint works correctly.", "Success!");
// }
// else {
// show_error(parent,
// "I wasn't able to connect to OctoPrint (".$res->status_line . "). "
// . "Check hostname and OctoPrint version (at least 1.1.0 is required).");
// }
});
return sizer; return sizer;
}; };
@ -1465,6 +1438,45 @@ void TabPrinter::build()
optgroup->append_line(host_line); optgroup->append_line(host_line);
optgroup->append_single_option_line("octoprint_apikey"); optgroup->append_single_option_line("octoprint_apikey");
if (Http::ca_file_supported()) {
Line cafile_line = optgroup->create_single_option_line("octoprint_cafile");
auto octoprint_cafile_browse = [this, optgroup] (wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e){
static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"));
wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() != wxID_CANCEL) {
optgroup->set_value("octoprint_cafile", std::move(openFileDialog.GetPath()), true);
}
});
return sizer;
};
cafile_line.append_widget(octoprint_cafile_browse);
optgroup->append_line(cafile_line);
auto octoprint_cafile_hint = [this, optgroup] (wxWindow* parent) {
auto txt = new wxStaticText(parent, wxID_ANY,
_(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(txt);
return sizer;
};
Line cafile_hint { "", "" };
cafile_hint.full_width = 1;
cafile_hint.widget = std::move(octoprint_cafile_hint);
optgroup->append_line(cafile_hint);
}
optgroup = page->new_optgroup(_(L("Firmware"))); optgroup = page->new_optgroup(_(L("Firmware")));
optgroup->append_single_option_line("gcode_flavor"); optgroup->append_single_option_line("gcode_flavor");
@ -1632,13 +1644,8 @@ void TabPrinter::update(){
m_serial_test_btn->Disable(); m_serial_test_btn->Disable();
} }
en = !m_config->opt_string("octoprint_host").empty(); m_octoprint_host_test_btn->Enable(!m_config->opt_string("octoprint_host").empty());
if ( en && m_is_user_agent)
m_octoprint_host_test_btn->Enable();
else
m_octoprint_host_test_btn->Disable();
get_field("octoprint_apikey")->toggle(en);
bool have_multiple_extruders = m_extruders_count > 1; bool have_multiple_extruders = m_extruders_count > 1;
get_field("toolchange_gcode")->toggle(have_multiple_extruders); get_field("toolchange_gcode")->toggle(have_multiple_extruders);
get_field("single_extruder_multi_material")->toggle(have_multiple_extruders); get_field("single_extruder_multi_material")->toggle(have_multiple_extruders);

View File

@ -234,11 +234,6 @@ public:
//Slic3r::GUI::Tab::Printer; //Slic3r::GUI::Tab::Printer;
class TabPrinter : public Tab class TabPrinter : public Tab
{ {
bool m_is_disabled_button_browse;
bool m_is_user_agent;
// similar event by clicking Buttons "Browse" & "Test"
wxEventType m_event_button_browse = 0;
wxEventType m_event_button_test = 0;
public: public:
wxButton* m_serial_test_btn; wxButton* m_serial_test_btn;
wxButton* m_octoprint_host_test_btn; wxButton* m_octoprint_host_test_btn;
@ -249,10 +244,7 @@ public:
std::vector<PageShp> m_extruder_pages; std::vector<PageShp> m_extruder_pages;
TabPrinter() {} TabPrinter() {}
TabPrinter(wxNotebook* parent, bool no_controller, bool is_disabled_btn_browse, bool is_user_agent) : TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {}
Tab(parent, _(L("Printer Settings")), "printer", no_controller),
m_is_disabled_button_browse(is_disabled_btn_browse),
m_is_user_agent(is_user_agent) {}
~TabPrinter(){} ~TabPrinter(){}
void build() override; void build() override;
@ -261,10 +253,6 @@ public:
void extruders_count_changed(size_t extruders_count); void extruders_count_changed(size_t extruders_count);
void build_extruder_pages(); void build_extruder_pages();
void on_preset_loaded() override; void on_preset_loaded() override;
// Set the events to the callbacks posted to the main frame window (currently implemented in Perl).
void set_event_button_browse(wxEventType evt) { m_event_button_browse = evt; }
void set_event_button_test(wxEventType evt) { m_event_button_test = evt; }
}; };
class SavePresetWindow :public wxDialog class SavePresetWindow :public wxDialog

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
#ifndef slic3r_ASCIIFolding_hpp_
#define slic3r_ASCIIFolding_hpp_
#include <string>
namespace Slic3r {
// If possible, remove accents from accented latin characters.
// This function is useful for generating file names to be processed by legacy firmwares.
extern std::string fold_utf8_to_ascii(const char *src);
extern std::string fold_utf8_to_ascii(const std::string &src);
}; // namespace Slic3r
#endif /* slic3r_ASCIIFolding_hpp_ */

View File

@ -1,9 +1,7 @@
#include "Bonjour.hpp" #include "Bonjour.hpp"
#include <iostream> // XXX
#include <cstdint> #include <cstdint>
#include <algorithm> #include <algorithm>
#include <unordered_map>
#include <array> #include <array>
#include <vector> #include <vector>
#include <string> #include <string>
@ -23,16 +21,18 @@ namespace asio = boost::asio;
using boost::asio::ip::udp; using boost::asio::ip::udp;
// TODO: Fuzzing test (done without TXT)
// FIXME: check char retype to unsigned
namespace Slic3r { namespace Slic3r {
// Minimal implementation of a MDNS/DNS-SD client // Minimal implementation of a MDNS/DNS-SD client
// This implementation is extremely simple, only the bits that are useful // This implementation is extremely simple, only the bits that are useful
// for very basic MDNS discovery are present. // for basic MDNS discovery of OctoPi devices are present.
// However, the bits that are present are implemented with security in mind.
// Only fully correct DNS replies are allowed through.
// While decoding the decoder will bail the moment it encounters anything fishy.
// At least that's the idea. To help prove this is actually the case,
// the implementations has been tested with AFL.
struct DnsName: public std::string struct DnsName: public std::string
{ {
@ -48,8 +48,7 @@ struct DnsName: public std::string
return boost::none; return boost::none;
} }
// Check for recursion depth to prevent parsing names that are nested too deeply // Check for recursion depth to prevent parsing names that are nested too deeply or end up cyclic:
// or end up cyclic:
if (depth >= MAX_RECURSION) { if (depth >= MAX_RECURSION) {
return boost::none; return boost::none;
} }
@ -443,6 +442,30 @@ private:
} }
}; };
std::ostream& operator<<(std::ostream &os, const DnsMessage &msg)
{
os << "DnsMessage(ID: " << msg.header.id << ", "
<< "Q: " << (msg.question ? msg.question->name.c_str() : "none") << ", "
<< "A: " << (msg.rr_a ? msg.rr_a->ip.to_string() : "none") << ", "
<< "AAAA: " << (msg.rr_aaaa ? msg.rr_aaaa->ip.to_string() : "none") << ", "
<< "services: [";
enum { SRV_PRINT_MAX = 3 };
unsigned i = 0;
for (const auto &sdpair : msg.sdmap) {
os << sdpair.first << ", ";
if (++i >= SRV_PRINT_MAX) {
os << "...";
break;
}
}
os << "])";
return os;
}
struct BonjourRequest struct BonjourRequest
{ {
@ -515,6 +538,7 @@ struct Bonjour::priv
const std::string protocol; const std::string protocol;
const std::string service_dn; const std::string service_dn;
unsigned timeout; unsigned timeout;
unsigned retries;
uint16_t rq_id; uint16_t rq_id;
std::vector<char> buffer; std::vector<char> buffer;
@ -524,6 +548,7 @@ struct Bonjour::priv
priv(std::string service, std::string protocol); priv(std::string service, std::string protocol);
std::string strip_service_dn(const std::string &service_name) const;
void udp_receive(udp::endpoint from, size_t bytes); void udp_receive(udp::endpoint from, size_t bytes);
void lookup_perform(); void lookup_perform();
}; };
@ -533,11 +558,26 @@ Bonjour::priv::priv(std::string service, std::string protocol) :
protocol(std::move(protocol)), protocol(std::move(protocol)),
service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()), service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()),
timeout(10), timeout(10),
retries(1),
rq_id(0) rq_id(0)
{ {
buffer.resize(DnsMessage::MAX_SIZE); buffer.resize(DnsMessage::MAX_SIZE);
} }
std::string Bonjour::priv::strip_service_dn(const std::string &service_name) const
{
if (service_name.size() <= service_dn.size()) {
return service_name;
}
auto needle = service_name.rfind(service_dn);
if (needle == service_name.size() - service_dn.size()) {
return service_name.substr(0, needle - 1);
} else {
return service_name;
}
}
void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes) void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
{ {
if (bytes == 0 || !replyfn) { if (bytes == 0 || !replyfn) {
@ -557,7 +597,10 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
} }
const auto &srv = *sdpair.second.srv; const auto &srv = *sdpair.second.srv;
BonjourReply reply(ip, sdpair.first, srv.hostname); auto service_name = strip_service_dn(sdpair.first);
std::string path;
std::string version;
if (sdpair.second.txt) { if (sdpair.second.txt) {
static const std::string tag_path = "path="; static const std::string tag_path = "path=";
@ -565,13 +608,14 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
for (const auto &value : sdpair.second.txt->values) { for (const auto &value : sdpair.second.txt->values) {
if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) { if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) {
reply.path = value.substr(tag_path.size()); path = std::move(value.substr(tag_path.size()));
} else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) { } else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) {
reply.version = value.substr(tag_version.size()); version = std::move(value.substr(tag_version.size()));
} }
} }
} }
BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(path), std::move(version));
replyfn(std::move(reply)); replyfn(std::move(reply));
} }
} }
@ -595,15 +639,26 @@ void Bonjour::priv::lookup_perform()
udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT); udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT);
socket.send_to(asio::buffer(brq->data), mcast); socket.send_to(asio::buffer(brq->data), mcast);
bool timeout = false; bool expired = false;
bool retry = false;
asio::deadline_timer timer(io_service); asio::deadline_timer timer(io_service);
timer.expires_from_now(boost::posix_time::seconds(10)); retries--;
timer.async_wait([=, &timeout](const error_code &error) { std::function<void(const error_code &)> timer_handler = [&](const error_code &error) {
timeout = true; if (retries == 0 || error) {
if (self->completefn) { expired = true;
self->completefn(); if (self->completefn) {
self->completefn();
}
} else {
retry = true;
retries--;
timer.expires_from_now(boost::posix_time::seconds(timeout));
timer.async_wait(timer_handler);
} }
}); };
timer.expires_from_now(boost::posix_time::seconds(timeout));
timer.async_wait(timer_handler);
udp::endpoint recv_from; udp::endpoint recv_from;
const auto recv_handler = [&](const error_code &error, size_t bytes) { const auto recv_handler = [&](const error_code &error, size_t bytes) {
@ -612,8 +667,11 @@ void Bonjour::priv::lookup_perform()
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler); socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
while (io_service.run_one()) { while (io_service.run_one()) {
if (timeout) { if (expired) {
socket.cancel(); socket.cancel();
} else if (retry) {
retry = false;
socket.send_to(asio::buffer(brq->data), mcast);
} else { } else {
buffer.resize(DnsMessage::MAX_SIZE); buffer.resize(DnsMessage::MAX_SIZE);
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler); socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
@ -626,13 +684,39 @@ void Bonjour::priv::lookup_perform()
// API - public part // API - public part
BonjourReply::BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname) : BonjourReply::BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version) :
ip(std::move(ip)), ip(std::move(ip)),
port(port),
service_name(std::move(service_name)), service_name(std::move(service_name)),
hostname(std::move(hostname)), hostname(std::move(hostname)),
path("/"), path(path.empty() ? std::move(std::string("/")) : std::move(path)),
version("Unknown") version(version.empty() ? std::move(std::string("Unknown")) : std::move(version))
{} {
std::string proto;
std::string port_suffix;
if (port == 443) { proto = "https://"; }
if (port != 443 && port != 80) { port_suffix = std::to_string(port).insert(0, 1, ':'); }
if (this->path[0] != '/') { this->path.insert(0, 1, '/'); }
full_address = proto + ip.to_string() + port_suffix;
if (this->path != "/") { full_address += path; }
}
bool BonjourReply::operator==(const BonjourReply &other) const
{
return this->full_address == other.full_address
&& this->service_name == other.service_name;
}
bool BonjourReply::operator<(const BonjourReply &other) const
{
if (this->ip != other.ip) {
// So that the common case doesn't involve string comparison
return this->ip < other.ip;
} else {
auto cmp = this->full_address.compare(other.full_address);
return cmp != 0 ? cmp < 0 : this->service_name < other.service_name;
}
}
std::ostream& operator<<(std::ostream &os, const BonjourReply &reply) std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
{ {
@ -641,6 +725,7 @@ std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
return os; return os;
} }
Bonjour::Bonjour(std::string service, std::string protocol) : Bonjour::Bonjour(std::string service, std::string protocol) :
p(new priv(std::move(service), std::move(protocol))) p(new priv(std::move(service), std::move(protocol)))
{} {}
@ -660,6 +745,12 @@ Bonjour& Bonjour::set_timeout(unsigned timeout)
return *this; return *this;
} }
Bonjour& Bonjour::set_retries(unsigned retries)
{
if (p && retries > 0) { p->retries = retries; }
return *this;
}
Bonjour& Bonjour::on_reply(ReplyFn fn) Bonjour& Bonjour::on_reply(ReplyFn fn)
{ {
if (p) { p->replyfn = std::move(fn); } if (p) { p->replyfn = std::move(fn); }
@ -677,7 +768,7 @@ Bonjour::Ptr Bonjour::lookup()
auto self = std::make_shared<Bonjour>(std::move(*this)); auto self = std::make_shared<Bonjour>(std::move(*this));
if (self->p) { if (self->p) {
auto io_thread = std::thread([self](){ auto io_thread = std::thread([self]() {
self->p->lookup_perform(); self->p->lookup_perform();
}); });
self->p->io_thread = std::move(io_thread); self->p->io_thread = std::move(io_thread);
@ -687,18 +778,4 @@ Bonjour::Ptr Bonjour::lookup()
} }
void Bonjour::pokus() // XXX
{
auto bonjour = Bonjour("octoprint")
.set_timeout(15)
.on_reply([](BonjourReply &&reply) {
std::cerr << "BonjourReply: " << reply << std::endl;
})
.on_complete([](){
std::cerr << "MDNS lookup complete" << std::endl;
})
.lookup();
}
} }

View File

@ -1,26 +1,31 @@
#ifndef slic3r_Bonjour_hpp_ #ifndef slic3r_Bonjour_hpp_
#define slic3r_Bonjour_hpp_ #define slic3r_Bonjour_hpp_
#include <cstdint>
#include <memory> #include <memory>
#include <string> #include <string>
#include <functional> #include <functional>
// #include <ostream>
#include <boost/asio/ip/address.hpp> #include <boost/asio/ip/address.hpp>
namespace Slic3r { namespace Slic3r {
// TODO: reply data structure
struct BonjourReply struct BonjourReply
{ {
boost::asio::ip::address ip; boost::asio::ip::address ip;
uint16_t port;
std::string service_name; std::string service_name;
std::string hostname; std::string hostname;
std::string full_address;
std::string path; std::string path;
std::string version; std::string version;
BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname); BonjourReply() = delete;
BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version);
bool operator==(const BonjourReply &other) const;
bool operator<(const BonjourReply &other) const;
}; };
std::ostream& operator<<(std::ostream &, const BonjourReply &); std::ostream& operator<<(std::ostream &, const BonjourReply &);
@ -32,7 +37,7 @@ private:
struct priv; struct priv;
public: public:
typedef std::shared_ptr<Bonjour> Ptr; typedef std::shared_ptr<Bonjour> Ptr;
typedef std::function<void(BonjourReply &&reply)> ReplyFn; typedef std::function<void(BonjourReply &&)> ReplyFn;
typedef std::function<void()> CompleteFn; typedef std::function<void()> CompleteFn;
Bonjour(std::string service, std::string protocol = "tcp"); Bonjour(std::string service, std::string protocol = "tcp");
@ -40,12 +45,15 @@ public:
~Bonjour(); ~Bonjour();
Bonjour& set_timeout(unsigned timeout); Bonjour& set_timeout(unsigned timeout);
Bonjour& set_retries(unsigned retries);
// ^ Note: By default there is 1 retry (meaning 1 broadcast is sent).
// Timeout is per one retry, ie. total time spent listening = retries * timeout.
// If retries > 1, then care needs to be taken as more than one reply from the same service may be received.
Bonjour& on_reply(ReplyFn fn); Bonjour& on_reply(ReplyFn fn);
Bonjour& on_complete(CompleteFn fn); Bonjour& on_complete(CompleteFn fn);
Ptr lookup(); Ptr lookup();
static void pokus(); // XXX: remove
private: private:
std::unique_ptr<priv> p; std::unique_ptr<priv> p;
}; };

View File

@ -3,7 +3,6 @@
#include <cstdlib> #include <cstdlib>
#include <functional> #include <functional>
#include <thread> #include <thread>
#include <iostream>
#include <tuple> #include <tuple>
#include <boost/format.hpp> #include <boost/format.hpp>
@ -45,7 +44,9 @@ struct Http::priv
priv(const std::string &url); priv(const std::string &url);
~priv(); ~priv();
static bool ca_file_supported(::CURL *curl);
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp); static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
std::string curl_error(CURLcode curlcode);
std::string body_size_error(); std::string body_size_error();
void http_perform(); void http_perform();
}; };
@ -71,6 +72,29 @@ Http::priv::~priv()
::curl_slist_free_all(headerlist); ::curl_slist_free_all(headerlist);
} }
bool Http::priv::ca_file_supported(::CURL *curl)
{
#ifdef _WIN32
bool res = false;
#else
bool res = true;
#endif
if (curl == nullptr) { return res; }
#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 48
::curl_tlssessioninfo *tls;
if (::curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &tls) == CURLE_OK) {
if (tls->backend == CURLSSLBACKEND_SCHANNEL || tls->backend == CURLSSLBACKEND_DARWINSSL) {
// With Windows and OS X native SSL support, cert files cannot be set
res = false;
}
}
#endif
return res;
}
size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp) size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
{ {
auto self = static_cast<priv*>(userp); auto self = static_cast<priv*>(userp);
@ -88,6 +112,14 @@ size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
return realsize; return realsize;
} }
std::string Http::priv::curl_error(CURLcode curlcode)
{
return (boost::format("%1% (%2%)")
% ::curl_easy_strerror(curlcode)
% curlcode
).str();
}
std::string Http::priv::body_size_error() std::string Http::priv::body_size_error()
{ {
return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str(); return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str();
@ -121,7 +153,7 @@ void Http::priv::http_perform()
if (res == CURLE_WRITE_ERROR) { if (res == CURLE_WRITE_ERROR) {
error = std::move(body_size_error()); error = std::move(body_size_error());
} else { } else {
error = ::curl_easy_strerror(res); error = std::move(curl_error(res));
}; };
if (errorfn) { if (errorfn) {
@ -180,7 +212,7 @@ Http& Http::remove_header(std::string name)
Http& Http::ca_file(const std::string &name) Http& Http::ca_file(const std::string &name)
{ {
if (p) { if (p && priv::ca_file_supported(p->curl)) {
::curl_easy_setopt(p->curl, CURLOPT_CAINFO, name.c_str()); ::curl_easy_setopt(p->curl, CURLOPT_CAINFO, name.c_str());
} }
@ -257,5 +289,13 @@ Http Http::post(std::string url)
return http; return http;
} }
bool Http::ca_file_supported()
{
::CURL *curl = ::curl_easy_init();
bool res = priv::ca_file_supported(curl);
if (curl != nullptr) { ::curl_easy_cleanup(curl); }
return res;
}
} }

View File

@ -41,6 +41,7 @@ public:
Ptr perform(); Ptr perform();
void perform_sync(); void perform_sync();
static bool ca_file_supported();
private: private:
Http(const std::string &url); Http(const std::string &url);

View File

@ -20,16 +20,19 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) :
cafile(config->opt_string("octoprint_cafile")) cafile(config->opt_string("octoprint_cafile"))
{} {}
std::string OctoPrint::test() const bool OctoPrint::test(wxString &msg) const
{ {
// Since the request is performed synchronously here, // Since the request is performed synchronously here,
// it is ok to refer to `res` from within the closure // it is ok to refer to `msg` from within the closure
std::string res;
auto http = Http::get(std::move(make_url("api/version"))); bool res = true;
auto url = std::move(make_url("api/version"));
auto http = Http::get(std::move(url));
set_auth(http); set_auth(http);
http.on_error([&](std::string, std::string error, unsigned status) { http.on_error([&](std::string, std::string error, unsigned status) {
res = format_error(error, status); res = false;
msg = format_error(error, status);
}) })
.perform_sync(); .perform_sync();
@ -43,21 +46,26 @@ void OctoPrint::send_gcode(int windowId, int completeEvt, int errorEvt, const st
http.form_add("print", print ? "true" : "false") http.form_add("print", print ? "true" : "false")
.form_add_file("file", filename) .form_add_file("file", filename)
.on_complete([=](std::string body, unsigned status) { .on_complete([=](std::string body, unsigned status) {
wxWindow *window = GUI::get_widget_by_id(windowId); wxWindow *window = wxWindow::FindWindowById(windowId);
if (window == nullptr) { return; }
wxCommandEvent* evt = new wxCommandEvent(completeEvt); wxCommandEvent* evt = new wxCommandEvent(completeEvt);
evt->SetString("G-code file successfully uploaded to the OctoPrint server"); evt->SetString(_(L("G-code file successfully uploaded to the OctoPrint server")));
evt->SetInt(100); evt->SetInt(100);
wxQueueEvent(window, evt); wxQueueEvent(window, evt);
}) })
.on_error([=](std::string body, std::string error, unsigned status) { .on_error([=](std::string body, std::string error, unsigned status) {
wxWindow *window = GUI::get_widget_by_id(windowId); wxWindow *window = wxWindow::FindWindowById(windowId);
if (window == nullptr) { return; }
wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt); wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt);
evt_complete->SetInt(100); evt_complete->SetInt(100);
wxQueueEvent(window, evt_complete); wxQueueEvent(window, evt_complete);
wxCommandEvent* evt_error = new wxCommandEvent(errorEvt); wxCommandEvent* evt_error = new wxCommandEvent(errorEvt);
evt_error->SetString(wxString::Format("Error while uploading to the OctoPrint server: %s", format_error(error, status))); evt_error->SetString(wxString::Format("%s: %s",
_(L("Error while uploading to the OctoPrint server")),
format_error(error, status)));
wxQueueEvent(window, evt_error); wxQueueEvent(window, evt_error);
}) })
.perform(); .perform();
@ -85,19 +93,15 @@ std::string OctoPrint::make_url(const std::string &path) const
} }
} }
std::string OctoPrint::format_error(std::string error, unsigned status) wxString OctoPrint::format_error(std::string error, unsigned status)
{ {
const wxString wxerror = error;
if (status != 0) { if (status != 0) {
std::string res{"HTTP "}; return wxString::Format("HTTP %u: %s", status,
res.append(std::to_string(status)); (status == 401 ? _(L("Invalid API key")) : wxerror));
if (status == 401) {
res.append(": Invalid API key");
}
return std::move(res);
} else { } else {
return std::move(error); return std::move(wxerror);
} }
} }

View File

@ -2,8 +2,8 @@
#define slic3r_OctoPrint_hpp_ #define slic3r_OctoPrint_hpp_
#include <string> #include <string>
#include <wx/string.h>
// #include "Http.hpp" // XXX: ?
namespace Slic3r { namespace Slic3r {
@ -16,8 +16,7 @@ class OctoPrint
public: public:
OctoPrint(DynamicPrintConfig *config); OctoPrint(DynamicPrintConfig *config);
std::string test() const; bool test(wxString &curl_msg) const;
// XXX: style
void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const; void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const;
private: private:
std::string host; std::string host;
@ -26,7 +25,7 @@ private:
void set_auth(Http &http) const; void set_auth(Http &http) const;
std::string make_url(const std::string &path) const; std::string make_url(const std::string &path) const;
static std::string format_error(std::string error, unsigned status); static wxString format_error(std::string error, unsigned status);
}; };

View File

@ -23,7 +23,7 @@
try { try {
THIS->do_export(print, path); THIS->do_export(print, path);
} catch (std::exception& e) { } catch (std::exception& e) {
croak(e.what()); croak("%s\n", e.what());
} }
%}; %};
void do_export_w_preview(Print *print, const char *path, GCodePreviewData *preview_data) void do_export_w_preview(Print *print, const char *path, GCodePreviewData *preview_data)

View File

@ -3,6 +3,7 @@
%{ %{
#include <xsinit.h> #include <xsinit.h>
#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI.hpp"
#include "slic3r/Utils/ASCIIFolding.hpp"
%} %}
@ -35,12 +36,8 @@ void set_tab_panel(SV *ui)
void add_debug_menu(SV *ui, int event_language_change) void add_debug_menu(SV *ui, int event_language_change)
%code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %}; %code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %};
void create_preset_tabs(bool no_controller, bool is_disabled_button_browse, bool is_user_agent, void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
int event_value_change, int event_presets_changed, %code%{ Slic3r::GUI::create_preset_tabs(no_controller, event_value_change, event_presets_changed); %};
int event_button_browse, int event_button_test)
%code%{ Slic3r::GUI::create_preset_tabs(no_controller, is_disabled_button_browse, is_user_agent,
event_value_change, event_presets_changed,
event_button_browse, event_button_test); %};
Ref<TabIface> get_preset_tab(char *name) Ref<TabIface> get_preset_tab(char *name)
%code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %}; %code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %};
@ -67,3 +64,6 @@ void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_siz
%code%{ Slic3r::GUI::add_frequently_changed_parameters((wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"), %code%{ Slic3r::GUI::add_frequently_changed_parameters((wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
(wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"), (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"),
(wxFlexGridSizer*)wxPli_sv_2_object(aTHX_ ui_p_sizer, "Wx::FlexGridSizer")); %}; (wxFlexGridSizer*)wxPli_sv_2_object(aTHX_ ui_p_sizer, "Wx::FlexGridSizer")); %};
std::string fold_utf8_to_ascii(const char *src)
%code%{ RETVAL = Slic3r::fold_utf8_to_ascii(src); %};

View File

@ -18,7 +18,7 @@
try { try {
RETVAL = THIS->process(str, 0); RETVAL = THIS->process(str, 0);
} catch (std::exception& e) { } catch (std::exception& e) {
croak(e.what()); croak("%s\n", e.what());
} }
%}; %};
@ -27,7 +27,7 @@
try { try {
RETVAL = THIS->evaluate_boolean_expression(str, THIS->config()); RETVAL = THIS->evaluate_boolean_expression(str, THIS->config());
} catch (std::exception& e) { } catch (std::exception& e) {
croak(e.what()); croak("%s\n", e.what());
} }
%}; %};
}; };

View File

@ -212,7 +212,7 @@ _constant()
try { try {
RETVAL = THIS->output_filepath(path); RETVAL = THIS->output_filepath(path);
} catch (std::exception& e) { } catch (std::exception& e) {
croak(e.what()); croak("%s\n", e.what());
} }
%}; %};

View File

@ -9,6 +9,5 @@
OctoPrint(DynamicPrintConfig *config); OctoPrint(DynamicPrintConfig *config);
~OctoPrint(); ~OctoPrint();
std::string test() const;
void send_gcode(int windowId, int completeEvt, int errorEvt, std::string filename, bool print = false) const; void send_gcode(int windowId, int completeEvt, int errorEvt, std::string filename, bool print = false) const;
}; };