Faster gizmos update

This commit is contained in:
Enrico Turri 2018-06-21 08:37:04 +02:00
commit 550f6e307f
40 changed files with 1415 additions and 753 deletions

View file

@ -49,6 +49,22 @@ if(NOT DEFINED CMAKE_PREFIX_PATH)
endif()
endif()
# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory.
# We pick it from environment if it is not defined in another way
if(WIN32)
if(NOT DEFINED WIN10SDK_PATH)
if(DEFINED ENV{WIN10SDK_PATH})
set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
endif()
endif()
if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h")
message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found")
message("STL fixing by the Netfabb service will not be compiled")
unset(WIN10SDK_PATH)
endif()
endif()
add_subdirectory(xs)
get_filename_component(PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY)

View file

@ -7,7 +7,6 @@ use File::Basename qw(basename);
use FindBin;
use List::Util qw(first);
use Slic3r::GUI::2DBed;
use Slic3r::GUI::BedShapeDialog;
use Slic3r::GUI::Controller;
use Slic3r::GUI::Controller::ManualControlDialog;
use Slic3r::GUI::Controller::PrinterPanel;

View file

@ -1,316 +0,0 @@
# The bed shape dialog.
# The dialog opens from Print Settins tab -> Bed Shape: Set...
package Slic3r::GUI::BedShapeDialog;
use strict;
use warnings;
use utf8;
use List::Util qw(min max);
use Slic3r::Geometry qw(X Y unscale);
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, $default) = @_;
my $self = $class->SUPER::new($parent, -1, "Bed Shape", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
$self->{panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $default);
my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
$main_sizer->Add($panel, 1, wxEXPAND);
$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->{panel}->GetValue;
}
package Slic3r::GUI::BedShapePanel;
use List::Util qw(min max sum first);
use Scalar::Util qw(looks_like_number);
use Slic3r::Geometry qw(PI X Y unscale scaled_epsilon);
use Wx qw(:font :id :misc :sizer :choicebook :filedialog :pen :brush wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_CLOSE EVT_CHOICEBOOK_PAGE_CHANGED EVT_BUTTON);
use base 'Wx::Panel';
use constant SHAPE_RECTANGULAR => 0;
use constant SHAPE_CIRCULAR => 1;
use constant SHAPE_CUSTOM => 2;
sub new {
my $class = shift;
my ($parent, $default) = @_;
my $self = $class->SUPER::new($parent, -1);
$self->on_change(undef);
my $box = Wx::StaticBox->new($self, -1, "Shape");
my $sbsizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
# shape options
$self->{shape_options_book} = Wx::Choicebook->new($self, -1, wxDefaultPosition, [300,-1], wxCHB_TOP);
$sbsizer->Add($self->{shape_options_book});
$self->{optgroups} = [];
{
my $optgroup = $self->_init_shape_options_page('Rectangular');
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'rect_size',
type => 'point',
label => 'Size',
tooltip => 'Size in X and Y of the rectangular plate.',
default => [200,200],
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'rect_origin',
type => 'point',
label => 'Origin',
tooltip => 'Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.',
default => [0,0],
));
}
{
my $optgroup = $self->_init_shape_options_page('Circular');
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'diameter',
type => 'f',
label => 'Diameter',
tooltip => 'Diameter of the print bed. It is assumed that origin (0,0) is located in the center.',
sidetext => 'mm',
default => 200,
));
}
{
my $optgroup = $self->_init_shape_options_page('Custom');
$optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new(
full_width => 1,
widget => sub {
my ($parent) = @_;
my $btn = Wx::Button->new($parent, -1, "Load shape from STL...", wxDefaultPosition, wxDefaultSize);
EVT_BUTTON($self, $btn, sub { $self->_load_stl });
return $btn;
}
));
}
EVT_CHOICEBOOK_PAGE_CHANGED($self, -1, sub {
$self->_update_shape;
});
# right pane with preview canvas
my $canvas = $self->{canvas} = Slic3r::GUI::2DBed->new($self);
# main sizer
my $top_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$top_sizer->Add($sbsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
$top_sizer->Add($canvas, 1, wxEXPAND | wxALL, 10) if $canvas;
$self->SetSizerAndFit($top_sizer);
$self->_set_shape($default);
$self->_update_preview;
return $self;
}
sub on_change {
my ($self, $cb) = @_;
$self->{on_change} = $cb // sub {};
}
# Called from the constructor.
# Set the initial bed shape from a list of points.
# Deduce the bed shape type (rect, circle, custom)
# This routine shall be smart enough if the user messes up
# with the list of points in the ini file directly.
sub _set_shape {
my ($self, $points) = @_;
# is this a rectangle?
if (@$points == 4) {
my $polygon = Slic3r::Polygon->new_scale(@$points);
my $lines = $polygon->lines;
if ($lines->[0]->parallel_to_line($lines->[2]) && $lines->[1]->parallel_to_line($lines->[3])) {
# okay, it's a rectangle
# find origin
# the || 0 hack prevents "-0" which might confuse the user
my $x_min = min(map $_->[X], @$points) || 0;
my $x_max = max(map $_->[X], @$points) || 0;
my $y_min = min(map $_->[Y], @$points) || 0;
my $y_max = max(map $_->[Y], @$points) || 0;
my $origin = [-$x_min, -$y_min];
$self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR);
my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR];
$optgroup->set_value('rect_size', [ $x_max-$x_min, $y_max-$y_min ]);
$optgroup->set_value('rect_origin', $origin);
$self->_update_shape;
return;
}
}
# is this a circle?
{
# Analyze the array of points. Do they reside on a circle?
my $polygon = Slic3r::Polygon->new_scale(@$points);
my $center = $polygon->bounding_box->center;
my @vertex_distances = map $center->distance_to($_), @$polygon;
my $avg_dist = sum(@vertex_distances)/@vertex_distances;
if (!defined first { abs($_ - $avg_dist) > 10*scaled_epsilon } @vertex_distances) {
# all vertices are equidistant to center
$self->{shape_options_book}->SetSelection(SHAPE_CIRCULAR);
my $optgroup = $self->{optgroups}[SHAPE_CIRCULAR];
$optgroup->set_value('diameter', sprintf("%.0f", unscale($avg_dist*2)));
$self->_update_shape;
return;
}
}
if (@$points < 3) {
# Invalid polygon. Revert to default bed dimensions.
$self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR);
my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR];
$optgroup->set_value('rect_size', [200, 200]);
$optgroup->set_value('rect_origin', [0, 0]);
$self->_update_shape;
return;
}
# This is a custom bed shape, use the polygon provided.
$self->{shape_options_book}->SetSelection(SHAPE_CUSTOM);
# Copy the polygon to the canvas, make a copy of the array.
$self->{canvas}->bed_shape([@$points]);
$self->_update_shape;
}
# Update the bed shape from the dialog fields.
sub _update_shape {
my ($self) = @_;
my $page_idx = $self->{shape_options_book}->GetSelection;
if ($page_idx == SHAPE_RECTANGULAR) {
my $rect_size = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_size');
my $rect_origin = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_origin');
my ($x, $y) = @$rect_size;
return if !looks_like_number($x) || !looks_like_number($y); # empty strings or '-' or other things
return if !$x || !$y or $x == 0 or $y == 0;
my ($x0, $y0) = (0,0);
my ($x1, $y1) = ($x ,$y);
{
my ($dx, $dy) = @$rect_origin;
return if !looks_like_number($dx) || !looks_like_number($dy); # empty strings or '-' or other things
$x0 -= $dx;
$x1 -= $dx;
$y0 -= $dy;
$y1 -= $dy;
}
$self->{canvas}->bed_shape([
[$x0,$y0],
[$x1,$y0],
[$x1,$y1],
[$x0,$y1],
]);
} elsif ($page_idx == SHAPE_CIRCULAR) {
my $diameter = $self->{optgroups}[SHAPE_CIRCULAR]->get_value('diameter');
return if !$diameter or $diameter == 0;
my $r = $diameter/2;
my $twopi = 2*PI;
my $edges = 60;
my $polygon = Slic3r::Polygon->new_scale(
map [ $r * cos $_, $r * sin $_ ],
map { $twopi/$edges*$_ } 1..$edges
);
$self->{canvas}->bed_shape([
map [ unscale($_->x), unscale($_->y) ], @$polygon #))
]);
}
$self->{on_change}->();
$self->_update_preview;
}
sub _update_preview {
my ($self) = @_;
$self->{canvas}->Refresh if $self->{canvas};
$self->Refresh;
}
# Called from the constructor.
# Create a panel for a rectangular / circular / custom bed shape.
sub _init_shape_options_page {
my ($self, $title) = @_;
my $panel = Wx::Panel->new($self->{shape_options_book});
my $optgroup;
push @{$self->{optgroups}}, $optgroup = Slic3r::GUI::OptionsGroup->new(
parent => $panel,
title => 'Settings',
label_width => 100,
on_change => sub {
my ($opt_id) = @_;
#$self->{"_$opt_id"} = $optgroup->get_value($opt_id);
$self->_update_shape;
},
);
$panel->SetSizerAndFit($optgroup->sizer);
$self->{shape_options_book}->AddPage($panel, $title);
return $optgroup;
}
# Loads an stl file, projects it to the XY plane and calculates a polygon.
sub _load_stl {
my ($self) = @_;
my $dialog = Wx::FileDialog->new($self, 'Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):', "", "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
return;
}
my $input_file = $dialog->GetPaths;
$dialog->Destroy;
my $model = Slic3r::Model->read_from_file($input_file);
my $mesh = $model->mesh;
my $expolygons = $mesh->horizontal_projection;
if (@$expolygons == 0) {
Slic3r::GUI::show_error($self, "The selected file contains no geometry.");
return;
}
if (@$expolygons > 1) {
Slic3r::GUI::show_error($self, "The selected file contains several disjoint areas. This is not supported.");
return;
}
my $polygon = $expolygons->[0]->contour;
$self->{canvas}->bed_shape([ map [ unscale($_->x), unscale($_->y) ], @$polygon ]);
$self->_update_preview();
}
# Returns the resulting bed shape polygon. This value will be stored to the ini file.
sub GetValue {
my ($self) = @_;
return $self->{canvas}->bed_shape;
}
1;

View file

@ -116,6 +116,8 @@ sub new {
my $model_object = $self->{model}->objects->[$obj_idx];
my $model_instance = $model_object->instances->[0];
$self->stop_background_process;
my $variation = $scale / $model_instance->scaling_factor;
#FIXME Scale the layer height profile?
@ -127,7 +129,6 @@ sub new {
$object->transform_thumbnail($self->{model}, $obj_idx);
#update print and start background processing
$self->stop_background_process;
$self->{print}->add_model_object($model_object, $obj_idx);
$self->selection_changed(1); # refresh info (size, volume etc.)

View file

@ -22,8 +22,8 @@ struct PrintBoxDetection
{
vec3 min;
vec3 max;
// xyz contains the offset, if w == 1.0 detection needs to be performed
vec4 volume_origin;
bool volume_detection;
mat4 volume_world_matrix;
};
uniform PrintBoxDetection print_box;
@ -54,9 +54,9 @@ void main()
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
// compute deltas for out of print volume detection (world coordinates)
if (print_box.volume_origin.w == 1.0)
if (print_box.volume_detection)
{
vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz;
vec3 v = (print_box.volume_world_matrix * gl_Vertex).xyz;
delta_box_min = v - print_box.min;
delta_box_max = v - print_box.max;
}

View file

@ -14,6 +14,8 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define INTENSITY_AMBIENT 0.3
uniform mat4 volume_world_matrix;
// x = tainted, y = specular;
varying vec2 intensity;
@ -38,9 +40,8 @@ void main()
NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
// Scaled to widths of the Z texture.
object_z = gl_Vertex.z;
// Scaled to widths of the Z texture.
object_z = (volume_world_matrix * gl_Vertex).z;
gl_Position = ftransform();
}

View file

@ -27,6 +27,13 @@ if(WIN32)
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB)
# -D_ITERATOR_DEBUG_LEVEL)
if(WIN10SDK_PATH)
message("Building with Win10 Netfabb STL fixing service support")
add_definitions(-DHAS_WIN10SDK)
include_directories("${WIN10SDK_PATH}/Include")
else()
message("Building without Win10 Netfabb STL fixing service support")
endif()
endif()
add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE -DWXINTL_NO_GETTEXT_MACRO)
@ -240,6 +247,8 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp
${LIBDIR}/slic3r/Utils/Http.cpp
${LIBDIR}/slic3r/Utils/Http.hpp
${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp
${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp
${LIBDIR}/slic3r/Utils/OctoPrint.cpp
${LIBDIR}/slic3r/Utils/OctoPrint.hpp
${LIBDIR}/slic3r/Utils/Bonjour.cpp
@ -344,8 +353,6 @@ add_library(semver STATIC
)
add_subdirectory(src/avrdude)
# Generate the Slic3r Perl module (XS) typemap file.
set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap)
add_custom_command(
@ -508,12 +515,12 @@ if (WIN32 AND ";${PerlEmbed_CCFLAGS};" MATCHES ";[-/]Od;")
message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}")
message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}")
message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG")
set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG")
set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG")
set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32")
set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32")
set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32")
set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32")
endif()
# The following line will add -fPIC on Linux to make the XS.so rellocable.
add_definitions(${PerlEmbed_CCCDLFLAGS})
@ -521,6 +528,8 @@ if (WIN32)
target_link_libraries(XS ${PERL_LIBRARY})
endif()
add_subdirectory(src/avrdude)
## REQUIRED packages
# Find and configure boost

View file

@ -80,6 +80,49 @@ static int arduino_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
return 3;
}
static int prusa_init_external_flash(PROGRAMMER * pgm)
{
// Note: send/receive as in _the firmare_ send & receives
const char entry_magic_send [] = "start\n";
const char entry_magic_receive[] = "w25x20cl_enter\n";
const char entry_magic_cfm [] = "w25x20cl_cfm\n";
const size_t buffer_len = 32; // Should be large enough for the above messages
int res;
size_t recv_size;
char *buffer = alloca(buffer_len);
// 1. receive the "start" command
recv_size = sizeof(entry_magic_send) - 1;
res = serial_recv(&pgm->fd, buffer, recv_size);
if (res < 0) {
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
return -1;
} else if (strncmp(buffer, entry_magic_send, recv_size) != 0) {
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname);
return -1;
}
// 2. Send the external flash programmer enter command
if (serial_send(&pgm->fd, entry_magic_receive, sizeof(entry_magic_receive) - 1) < 0) {
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): Failed to send command to the printer\n",progname);
return -1;
}
// 3. Receive the entry confirmation command
recv_size = sizeof(entry_magic_cfm) - 1;
res = serial_recv(&pgm->fd, buffer, recv_size);
if (res < 0) {
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
return -1;
} else if (strncmp(buffer, entry_magic_cfm, recv_size) != 0) {
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname);
return -1;
}
return 0;
}
static int arduino_open(PROGRAMMER * pgm, char * port)
{
union pinfo pinfo;
@ -102,6 +145,12 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
*/
stk500_drain(pgm, 0);
// Initialization sequence for programming the external FLASH on the Prusa MK3
if (prusa_init_external_flash(pgm) < 0) {
avrdude_message(MSG_INFO, "%s: arduino_open(): Failed to initialize MK3 external flash programming mode\n", progname);
return -1;
}
if (stk500_getsync(pgm) < 0)
return -1;

View file

@ -1,5 +1,6 @@
#include "avrdude-slic3r.hpp"
#include <deque>
#include <thread>
extern "C" {
@ -33,17 +34,22 @@ static void avrdude_progress_handler_closure(const char *task, unsigned progress
struct AvrDude::priv
{
std::string sys_config;
std::vector<std::string> args;
std::deque<std::vector<std::string>> args;
size_t current_args_set = 0;
RunFn run_fn;
MessageFn message_fn;
ProgressFn progress_fn;
CompleteFn complete_fn;
std::thread avrdude_thread;
priv(std::string &&sys_config) : sys_config(sys_config) {}
int run_one(const std::vector<std::string> &args);
int run();
};
int AvrDude::priv::run() {
int AvrDude::priv::run_one(const std::vector<std::string> &args) {
std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
for (const auto &arg : args) {
c_args.push_back(const_cast<char*>(arg.data()));
@ -68,10 +74,22 @@ int AvrDude::priv::run() {
return res;
}
int AvrDude::priv::run() {
for (; args.size() > 0; current_args_set++) {
int res = run_one(args.front());
args.pop_front();
if (res != 0) {
return res;
}
}
return 0;
}
// Public
AvrDude::AvrDude() : p(new priv()) {}
AvrDude::AvrDude(std::string sys_config) : p(new priv(std::move(sys_config))) {}
AvrDude::AvrDude(AvrDude &&other) : p(std::move(other.p)) {}
@ -82,15 +100,15 @@ AvrDude::~AvrDude()
}
}
AvrDude& AvrDude::sys_config(std::string sys_config)
AvrDude& AvrDude::push_args(std::vector<std::string> args)
{
if (p) { p->sys_config = std::move(sys_config); }
if (p) { p->args.push_back(std::move(args)); }
return *this;
}
AvrDude& AvrDude::args(std::vector<std::string> args)
AvrDude& AvrDude::on_run(RunFn fn)
{
if (p) { p->args = std::move(args); }
if (p) { p->run_fn = std::move(fn); }
return *this;
}
@ -123,11 +141,17 @@ AvrDude::Ptr AvrDude::run()
if (self->p) {
auto avrdude_thread = std::thread([self]() {
auto res = self->p->run();
if (self->p->complete_fn) {
self->p->complete_fn(res);
}
});
if (self->p->run_fn) {
self->p->run_fn();
}
auto res = self->p->run();
if (self->p->complete_fn) {
self->p->complete_fn(res, self->p->current_args_set);
}
});
self->p->avrdude_thread = std::move(avrdude_thread);
}

View file

@ -12,22 +12,28 @@ class AvrDude
{
public:
typedef std::shared_ptr<AvrDude> Ptr;
typedef std::function<void()> RunFn;
typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
typedef std::function<void(int /* exit status */)> CompleteFn;
typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn;
AvrDude();
// Main c-tor, sys_config is the location of avrdude's main configuration file
AvrDude(std::string sys_config);
AvrDude(AvrDude &&);
AvrDude(const AvrDude &) = delete;
AvrDude &operator=(AvrDude &&) = delete;
AvrDude &operator=(const AvrDude &) = delete;
~AvrDude();
// Set location of avrdude's main configuration file
AvrDude& sys_config(std::string sys_config);
// Push a set of avrdude cli arguments
// Each set makes one avrdude invocation - use this method multiple times to push
// more than one avrdude invocations.
AvrDude& push_args(std::vector<std::string> args);
// Set avrdude cli arguments
AvrDude& args(std::vector<std::string> args);
// Set a callback to be called just after run() before avrdude is ran
// This can be used to perform any needed setup tasks from the background thread.
// This has no effect when using run_sync().
AvrDude& on_run(RunFn fn);
// Set message output callback
AvrDude& on_message(MessageFn fn);
@ -36,7 +42,10 @@ public:
// Progress is reported per each task (reading / writing) in percents.
AvrDude& on_progress(ProgressFn fn);
// Called when avrdude's main function finishes
// Called when the last avrdude invocation finishes with the exit status of zero,
// or earlier, if one of the invocations return a non-zero status.
// The second argument contains the sequential id of the last avrdude invocation argument set.
// This has no effect when using run_sync().
AvrDude& on_complete(CompleteFn fn);
int run_sync();

View file

@ -378,7 +378,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type,
char * optr;
if (m == NULL) {
fprintf(f,
avrdude_message(MSG_INFO,
"%s Block Poll Page Polled\n"
"%sMemory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack\n"
"%s----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------\n",
@ -386,13 +386,13 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type,
}
else {
if (verbose > 2) {
fprintf(f,
avrdude_message(MSG_INFO,
"%s Block Poll Page Polled\n"
"%sMemory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack\n"
"%s----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------\n",
prefix, prefix, prefix);
}
fprintf(f,
avrdude_message(MSG_INFO,
"%s%-11s %4d %5d %5d %4d %-6s %6d %4d %6d %5d %5d 0x%02x 0x%02x\n",
prefix, m->desc, m->mode, m->delay, m->blocksize, m->pollindex,
m->paged ? "yes" : "no",
@ -415,7 +415,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type,
optr = avr_op_str(i);
else
optr = " ";
fprintf(f,
avrdude_message(MSG_INFO,
"%s %-11s %8d %8s %5d %5d\n",
prefix, optr, j,
bittype(m->op[i]->bit[j].type),
@ -620,7 +620,7 @@ void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose)
LNODEID ln;
AVRMEM * m;
fprintf(f,
avrdude_message(MSG_INFO,
"%sAVR Part : %s\n"
"%sChip Erase delay : %d us\n"
"%sPAGEL : P%02X\n"

View file

@ -45,6 +45,8 @@
#define MAX_LINE_LEN 256 /* max line length for ASCII format input files */
#define MAX_MODE_LEN 32 // For fopen_and_seek()
struct ihexrec {
unsigned char reclen;
@ -96,10 +98,42 @@ static int fileio_num(struct fioparms * fio,
char * filename, FILE * f, AVRMEM * mem, int size,
FILEFMT fmt);
static int fmt_autodetect(char * fname);
static int fmt_autodetect(char * fname, size_t offset);
static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offset)
{
FILE *file;
// On Windows we need to convert the filename to UTF-16
#if defined(WIN32NATIVE)
static wchar_t fname_buffer[PATH_MAX];
static wchar_t mode_buffer[MAX_MODE_LEN];
if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; }
if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; }
file = _wfopen(fname_buffer, mode_buffer);
#else
file = fopen(filename, mode);
#endif
if (file != NULL) {
// Some systems allow seeking past the end of file, so we need check for that first and disallow
if (fseek(file, 0, SEEK_END) != 0
|| offset >= ftell(file)
|| fseek(file, offset, SEEK_SET) != 0
) {
fclose(file);
file = NULL;
errno = EINVAL;
}
}
return file;
}
char * fmtstr(FILEFMT format)
{
switch (format) {
@ -1358,7 +1392,7 @@ int fileio_setparms(int op, struct fioparms * fp,
static int fmt_autodetect(char * fname)
static int fmt_autodetect(char * fname, size_t offset)
{
FILE * f;
unsigned char buf[MAX_LINE_LEN];
@ -1368,10 +1402,11 @@ static int fmt_autodetect(char * fname)
int first = 1;
#if defined(WIN32NATIVE)
f = fopen(fname, "r");
f = fopen_and_seek(fname, "r", offset);
#else
f = fopen(fname, "rb");
f = fopen_and_seek(fname, "rb", offset);
#endif
if (f == NULL) {
avrdude_message(MSG_INFO, "%s: error opening %s: %s\n",
progname, fname, strerror(errno));
@ -1445,7 +1480,7 @@ static int fmt_autodetect(char * fname)
int fileio(int op, char * filename, FILEFMT format,
struct avrpart * p, char * memtype, int size)
struct avrpart * p, char * memtype, int size, size_t offset)
{
int rc;
FILE * f;
@ -1477,15 +1512,17 @@ int fileio(int op, char * filename, FILEFMT format,
using_stdio = 0;
if (strcmp(filename, "-")==0) {
if (fio.op == FIO_READ) {
fname = "<stdin>";
f = stdin;
}
else {
fname = "<stdout>";
f = stdout;
}
using_stdio = 1;
return -1;
// Note: we don't want to read stdin or write to stdout as part of Slic3r
// if (fio.op == FIO_READ) {
// fname = "<stdin>";
// f = stdin;
// }
// else {
// fname = "<stdout>";
// f = stdout;
// }
// using_stdio = 1;
}
else {
fname = filename;
@ -1502,7 +1539,7 @@ int fileio(int op, char * filename, FILEFMT format,
return -1;
}
format_detect = fmt_autodetect(fname);
format_detect = fmt_autodetect(fname, offset);
if (format_detect < 0) {
avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n",
progname, fname);
@ -1533,7 +1570,7 @@ int fileio(int op, char * filename, FILEFMT format,
if (format != FMT_IMM) {
if (!using_stdio) {
f = fopen(fname, fio.mode);
f = fopen_and_seek(fname, fio.mode, offset);
if (f == NULL) {
avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n",
progname, fio.iodesc, fname, strerror(errno));

View file

@ -737,7 +737,7 @@ extern bool cancel_flag;
#define RETURN_IF_CANCEL() \
do { \
if (cancel_flag) { \
avrdude_message(MSG_INFO, "%s(): Cancelled, exiting...\n", __func__); \
avrdude_message(MSG_INFO, "avrdude: %s(): Cancelled, exiting...\n", __func__); \
return -99; \
} \
} while (0)
@ -821,7 +821,7 @@ extern "C" {
char * fmtstr(FILEFMT format);
int fileio(int op, char * filename, FILEFMT format,
struct avrpart * p, char * memtype, int size);
struct avrpart * p, char * memtype, int size, size_t offset);
#ifdef __cplusplus
}
@ -870,6 +870,7 @@ enum updateflags {
typedef struct update_t {
char * memtype;
int op;
size_t offset;
char * filename;
int format;
} UPDATE;
@ -881,7 +882,7 @@ extern "C" {
extern UPDATE * parse_op(char * s);
extern UPDATE * dup_update(UPDATE * upd);
extern UPDATE * new_update(int op, char * memtype, int filefmt,
char * filename);
char * filename, size_t offset);
extern void free_update(UPDATE * upd);
extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd,
enum updateflags flags);

View file

@ -194,7 +194,7 @@ static void usage(void)
" -F Override invalid signature check.\n"
" -e Perform a chip erase.\n"
" -O Perform RC oscillator calibration (see AVR053). \n"
" -U <memtype>:r|w|v:<filename>[:format]\n"
" -U <memtype>:r|w|v:<offset>:<filename>[:format]\n"
" Memory operation specification.\n"
" Multiple -U options are allowed, each request\n"
" is performed in the order specified.\n"
@ -374,7 +374,7 @@ static void list_parts(FILE * f, const char *prefix, LISTID avrparts)
static int cleanup_main(int status)
{
if (pgm_setup && pgm->teardown) {
if (pgm_setup && pgm != NULL && pgm->teardown) {
pgm->teardown(pgm);
}

View file

@ -376,6 +376,10 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
FD_SET(fd->ifd, &rfds);
nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &to2);
// FIXME: The timeout has different behaviour on Linux vs other Unices
// On Linux, the timeout is modified by subtracting the time spent,
// on OS X (for example), it is not modified.
// POSIX recommends re-initializing it before selecting.
if (nfds == 0) {
avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n",
progname);

View file

@ -716,11 +716,14 @@ static int stk500_loadaddr(PROGRAMMER * pgm, AVRMEM * mem, unsigned int addr)
}
buf[0] = Cmnd_STK_LOAD_ADDRESS;
buf[1] = addr & 0xff;
buf[2] = (addr >> 8) & 0xff;
buf[3] = Sync_CRC_EOP;
stk500_send(pgm, buf, 4);
// Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
// Send the binary data by nibbles to avoid transmitting the ';' character.
buf[1] = addr & 0x0f;
buf[2] = addr & 0xf0;
buf[3] = (addr >> 8) & 0x0f;
buf[4] = (addr >> 8) & 0xf0;
buf[5] = Sync_CRC_EOP;
stk500_send(pgm, buf, 6);
if (stk500_recv(pgm, buf, 1) < 0)
return -1;
@ -765,7 +768,9 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
int block_size;
int tries;
unsigned int n;
unsigned int i;
unsigned int i, j;
unsigned int prusa3d_semicolon_workaround_round = 0;
bool has_semicolon = false;
if (strcmp(m->desc, "flash") == 0) {
memtype = 'F';
@ -806,44 +811,64 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
tries++;
stk500_loadaddr(pgm, m, addr/a_div);
/* build command block and avoid multiple send commands as it leads to a crash
of the silabs usb serial driver on mac os x */
i = 0;
buf[i++] = Cmnd_STK_PROG_PAGE;
buf[i++] = (block_size >> 8) & 0xff;
buf[i++] = block_size & 0xff;
buf[i++] = memtype;
memcpy(&buf[i], &m->buf[addr], block_size);
i += block_size;
buf[i++] = Sync_CRC_EOP;
stk500_send( pgm, buf, i);
if (stk500_recv(pgm, buf, 1) < 0)
return -1;
if (buf[0] == Resp_STK_NOSYNC) {
if (tries > 33) {
avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): can't get into sync\n",
progname);
return -3;
for (i = 0; i < n_bytes; ++ i)
if (m->buf[addr + i] == ';') {
has_semicolon = true;
break;
}
for (prusa3d_semicolon_workaround_round = 0; prusa3d_semicolon_workaround_round < (has_semicolon ? 2 : 1); ++ prusa3d_semicolon_workaround_round) {
/* build command block and avoid multiple send commands as it leads to a crash
of the silabs usb serial driver on mac os x */
i = 0;
buf[i++] = Cmnd_STK_PROG_PAGE;
// Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
// Send the binary data by nibbles to avoid transmitting the ';' character.
buf[i++] = (block_size >> 8) & 0xf0;
buf[i++] = (block_size >> 8) & 0x0f;
buf[i++] = block_size & 0xf0;
buf[i++] = block_size & 0x0f;
buf[i++] = memtype;
if (has_semicolon) {
for (j = 0; j < block_size; ++i, ++ j) {
buf[i] = m->buf[addr + j];
if (buf[i] == ';')
buf[i] |= (prusa3d_semicolon_workaround_round ? 0xf0 : 0x0f);
}
} else {
memcpy(&buf[i], &m->buf[addr], block_size);
i += block_size;
}
buf[i++] = Sync_CRC_EOP;
stk500_send( pgm, buf, i);
if (stk500_recv(pgm, buf, 1) < 0)
return -1;
if (buf[0] == Resp_STK_NOSYNC) {
if (tries > 33) {
avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): can't get into sync\n",
progname);
return -3;
}
if (stk500_getsync(pgm) < 0)
return -1;
goto retry;
}
else if (buf[0] != Resp_STK_INSYNC) {
avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, "
"expect=0x%02x, resp=0x%02x\n",
progname, Resp_STK_INSYNC, buf[0]);
return -4;
}
if (stk500_recv(pgm, buf, 1) < 0)
return -1;
if (buf[0] != Resp_STK_OK) {
avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, "
"expect=0x%02x, resp=0x%02x\n",
progname, Resp_STK_INSYNC, buf[0]);
return -5;
}
if (stk500_getsync(pgm) < 0)
return -1;
goto retry;
}
else if (buf[0] != Resp_STK_INSYNC) {
avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, "
"expect=0x%02x, resp=0x%02x\n",
progname, Resp_STK_INSYNC, buf[0]);
return -4;
}
if (stk500_recv(pgm, buf, 1) < 0)
return -1;
if (buf[0] != Resp_STK_OK) {
avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, "
"expect=0x%02x, resp=0x%02x\n",
progname, Resp_STK_INSYNC, buf[0]);
return -5;
}
}
@ -893,11 +918,15 @@ static int stk500_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
tries++;
stk500_loadaddr(pgm, m, addr/a_div);
buf[0] = Cmnd_STK_READ_PAGE;
buf[1] = (block_size >> 8) & 0xff;
buf[2] = block_size & 0xff;
buf[3] = memtype;
buf[4] = Sync_CRC_EOP;
stk500_send(pgm, buf, 5);
// Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
// Send the binary data by nibbles to avoid transmitting the ';' character.
buf[1] = (block_size >> 8) & 0xf0;
buf[2] = (block_size >> 8) & 0x0f;
buf[3] = block_size & 0xf0;
buf[4] = block_size & 0x0f;
buf[5] = memtype;
buf[6] = Sync_CRC_EOP;
stk500_send(pgm, buf, 7);
if (stk500_recv(pgm, buf, 1) < 0)
return -1;

View file

@ -79,7 +79,7 @@
#define SERIAL_TIMEOUT 2
// Retry count
#define RETRIES 5
#define RETRIES 0
#if 0
#define DEBUG(...) avrdude_message(MSG_INFO, __VA_ARGS__)
@ -745,7 +745,7 @@ static int stk500v2_recv(PROGRAMMER * pgm, unsigned char *msg, size_t maxsize) {
static int stk500v2_getsync_internal(PROGRAMMER * pgm, int retries) {
int stk500v2_getsync(PROGRAMMER * pgm) {
int tries = 0;
unsigned char buf[1], resp[32];
int status;
@ -804,7 +804,7 @@ retry:
progname, pgmname[PDATA(pgm)->pgmtype]);
return 0;
} else {
if (tries > retries) {
if (tries > RETRIES) {
avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): can't communicate with device: resp=0x%02x\n",
progname, resp[0]);
return -6;
@ -814,7 +814,7 @@ retry:
// or if we got a timeout
} else if (status == -1) {
if (tries > retries) {
if (tries > RETRIES) {
avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): timeout communicating with programmer\n",
progname);
return -1;
@ -823,7 +823,7 @@ retry:
// or any other error
} else {
if (tries > retries) {
if (tries > RETRIES) {
avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): error communicating with programmer: (%d)\n",
progname,status);
} else
@ -833,11 +833,6 @@ retry:
return 0;
}
int stk500v2_getsync(PROGRAMMER * pgm) {
// This is to avoid applying RETRIES exponentially
return stk500v2_getsync_internal(pgm, RETRIES);
}
static int stk500v2_command(PROGRAMMER * pgm, unsigned char * buf,
size_t len, size_t maxlen) {
int i;
@ -947,7 +942,7 @@ retry:
}
// otherwise try to sync up again
status = stk500v2_getsync_internal(pgm, 1);
status = stk500v2_getsync(pgm);
if (status != 0) {
if (tries > RETRIES) {
avrdude_message(MSG_INFO, "%s: stk500v2_command(): failed miserably to execute command 0x%02x\n",

View file

@ -101,6 +101,24 @@ UPDATE * parse_op(char * s)
p++;
// Extension: Parse file contents offset
size_t offset = 0;
for (; *p != ':'; p++) {
if (*p >= '0' && *p <= '9') {
offset *= 10;
offset += *p - 0x30;
} else {
avrdude_message(MSG_INFO, "%s: invalid update specification: offset is not a number\n", progname);
free(upd->memtype);
free(upd);
return NULL;
}
}
upd->offset = offset;
p++;
/*
* Now, parse the filename component. Instead of looking for the
* leftmost possible colon delimiter, we look for the rightmost one.
@ -176,7 +194,7 @@ UPDATE * dup_update(UPDATE * upd)
return u;
}
UPDATE * new_update(int op, char * memtype, int filefmt, char * filename)
UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t offset)
{
UPDATE * u;
@ -190,6 +208,7 @@ UPDATE * new_update(int op, char * memtype, int filefmt, char * filename)
u->filename = strdup(filename);
u->op = op;
u->format = filefmt;
u->offset = offset;
return u;
}
@ -250,7 +269,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
progname,
strcmp(upd->filename, "-")==0 ? "<stdout>" : upd->filename);
}
rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size);
rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size, 0);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: write to file '%s' failed\n",
progname, upd->filename);
@ -267,7 +286,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
progname,
strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
}
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1);
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
progname, upd->filename);
@ -296,11 +315,11 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
report_progress(1,1,NULL);
}
else {
/*
* test mode, don't actually write to the chip, output the buffer
* to stdout in intel hex instead
*/
rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size);
// /*
// * test mode, don't actually write to the chip, output the buffer
// * to stdout in intel hex instead
// */
// rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size, 0);
}
if (rc < 0) {
@ -332,7 +351,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
progname, mem->desc, upd->filename);
}
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1);
rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
progname, upd->filename);

View file

@ -2,6 +2,8 @@
#include <algorithm>
#include <assert.h>
#include <Eigen/Dense>
namespace Slic3r {
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points);
@ -251,4 +253,41 @@ void BoundingBox::align_to_grid(const coord_t cell_size)
}
}
BoundingBoxf3 BoundingBoxf3::transformed(const std::vector<float>& matrix) const
{
Eigen::Matrix<float, 3, 8> vertices;
vertices(0, 0) = (float)min.x; vertices(1, 0) = (float)min.y; vertices(2, 0) = (float)min.z;
vertices(0, 1) = (float)max.x; vertices(1, 1) = (float)min.y; vertices(2, 1) = (float)min.z;
vertices(0, 2) = (float)max.x; vertices(1, 2) = (float)max.y; vertices(2, 2) = (float)min.z;
vertices(0, 3) = (float)min.x; vertices(1, 3) = (float)max.y; vertices(2, 3) = (float)min.z;
vertices(0, 4) = (float)min.x; vertices(1, 4) = (float)min.y; vertices(2, 4) = (float)max.z;
vertices(0, 5) = (float)max.x; vertices(1, 5) = (float)min.y; vertices(2, 5) = (float)max.z;
vertices(0, 6) = (float)max.x; vertices(1, 6) = (float)max.y; vertices(2, 6) = (float)max.z;
vertices(0, 7) = (float)min.x; vertices(1, 7) = (float)max.y; vertices(2, 7) = (float)max.z;
Eigen::Transform<float, 3, Eigen::Affine> m;
::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
Eigen::Matrix<float, 3, 8> transf_vertices = m * vertices.colwise().homogeneous();
float min_x = transf_vertices(0, 0);
float max_x = transf_vertices(0, 0);
float min_y = transf_vertices(1, 0);
float max_y = transf_vertices(1, 0);
float min_z = transf_vertices(2, 0);
float max_z = transf_vertices(2, 0);
for (int i = 1; i < 8; ++i)
{
min_x = std::min(min_x, transf_vertices(0, i));
max_x = std::max(max_x, transf_vertices(0, i));
min_y = std::min(min_y, transf_vertices(1, i));
max_y = std::max(max_y, transf_vertices(1, i));
min_z = std::min(min_z, transf_vertices(2, i));
max_z = std::max(max_z, transf_vertices(2, i));
}
return BoundingBoxf3(Pointf3((coordf_t)min_x, (coordf_t)min_y, (coordf_t)min_z), Pointf3((coordf_t)max_x, (coordf_t)max_y, (coordf_t)max_z));
}
}

View file

@ -148,6 +148,8 @@ public:
BoundingBoxf3() : BoundingBox3Base<Pointf3>() {};
BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {};
BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {};
BoundingBoxf3 transformed(const std::vector<float>& matrix) const;
};
template<typename VT>

View file

@ -1271,6 +1271,7 @@ namespace Slic3r {
if ((std::abs(sx - sy) > 0.00001) || (std::abs(sx - sz) > 0.00001))
return;
#if 0 // use quaternions
// rotations (extracted using quaternion)
double inv_sx = 1.0 / sx;
double inv_sy = 1.0 / sy;
@ -1331,6 +1332,25 @@ namespace Slic3r {
if (angle_z < 0.0)
angle_z += 2.0 * PI;
}
#else // use eigen library
double inv_sx = 1.0 / sx;
double inv_sy = 1.0 / sy;
double inv_sz = 1.0 / sz;
Eigen::Matrix3d m3x3;
m3x3 << (double)matrix(0, 0) * inv_sx, (double)matrix(0, 1) * inv_sy, (double)matrix(0, 2) * inv_sz,
(double)matrix(1, 0) * inv_sx, (double)matrix(1, 1) * inv_sy, (double)matrix(1, 2) * inv_sz,
(double)matrix(2, 0) * inv_sx, (double)matrix(2, 1) * inv_sy, (double)matrix(2, 2) * inv_sz;
Eigen::AngleAxisd rotation;
rotation.fromRotationMatrix(m3x3);
// invalid rotation axis, we currently handle only rotations around Z axis
if ((rotation.angle() != 0.0) && (rotation.axis() != Eigen::Vector3d::UnitZ()) && (rotation.axis() != -Eigen::Vector3d::UnitZ()))
return;
double angle_z = (rotation.axis() == Eigen::Vector3d::UnitZ()) ? rotation.angle() : -rotation.angle();
#endif
instance.offset.x = offset_x;
instance.offset.y = offset_y;

View file

@ -13,6 +13,7 @@
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/nowide/fstream.hpp>
#include <miniz/miniz_zip.h>
#if 0
@ -666,10 +667,21 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
// If bundle is not a null pointer, updates it if the amf file/archive contains config data
bool load_amf(const char *path, PresetBundle* bundle, Model *model)
{
if (boost::iends_with(path, ".zip.amf"))
return load_amf_archive(path, bundle, model);
else if (boost::iends_with(path, ".amf") || boost::iends_with(path, ".amf.xml"))
if (boost::iends_with(path, ".amf.xml"))
// backward compatibility with older slic3r output
return load_amf_file(path, bundle, model);
else if (boost::iends_with(path, ".amf"))
{
boost::nowide::ifstream file(path, boost::nowide::ifstream::binary);
if (!file.good())
return false;
std::string zip_mask(2, '\0');
file.read(const_cast<char*>(zip_mask.data()), 2);
file.close();
return (zip_mask == "PK") ? load_amf_archive(path, bundle, model) : load_amf_file(path, bundle, model);
}
else
return false;
}

View file

@ -14,6 +14,8 @@
#include <boost/nowide/iostream.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <Eigen/Dense>
namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
@ -603,10 +605,7 @@ void ModelObject::clear_instances()
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
//========================================================================================================
const BoundingBoxf3& ModelObject::bounding_box() const
//const BoundingBoxf3& ModelObject::bounding_box()
//========================================================================================================
{
if (! m_bounding_box_valid) {
BoundingBoxf3 raw_bbox;
@ -1048,32 +1047,16 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
{
// rotate around mesh origin
double c = cos(this->rotation);
double s = sin(this->rotation);
Pointf3 pts[4] = {
bbox.min,
bbox.max,
Pointf3(bbox.min.x, bbox.max.y, bbox.min.z),
Pointf3(bbox.max.x, bbox.min.y, bbox.max.z)
};
BoundingBoxf3 out;
for (int i = 0; i < 4; ++ i) {
Pointf3 &v = pts[i];
double xold = v.x;
double yold = v.y;
v.x = float(c * xold - s * yold);
v.y = float(s * xold + c * yold);
v.x *= this->scaling_factor;
v.y *= this->scaling_factor;
v.z *= this->scaling_factor;
if (! dont_translate) {
v.x += this->offset.x;
v.y += this->offset.y;
}
out.merge(v);
}
return out;
Eigen::Transform<float, 3, Eigen::Affine> matrix = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
if (!dont_translate)
matrix.translate(Eigen::Vector3f((float)offset.x, (float)offset.y, 0.0f));
matrix.rotate(Eigen::AngleAxisf(rotation, Eigen::Vector3f::UnitZ()));
matrix.scale(scaling_factor);
std::vector<float> m(16, 0.0f);
::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
return bbox.transformed(m);
}
void ModelInstance::transform_polygon(Polygon* polygon) const

View file

@ -103,10 +103,7 @@ public:
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
// This bounding box is being cached.
//========================================================================================================
const BoundingBoxf3& bounding_box() const;
// const BoundingBoxf3& bounding_box();
//========================================================================================================
void invalidate_bounding_box() { m_bounding_box_valid = false; }
// Returns a snug bounding box of the transformed instances.
// This bounding box is not being cached.
@ -148,10 +145,9 @@ private:
// Parent object, owning this ModelObject.
Model *m_model;
// Bounding box, cached.
//========================================================================================================
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_valid;
//========================================================================================================
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.

View file

@ -103,7 +103,7 @@ double Polygon::area() const
double a = 0.;
for (size_t i = 0, j = n - 1; i < n; ++i) {
a += double(points[j].x + points[i].x) * double(points[i].y - points[j].y);
a += ((double)points[j].x + (double)points[i].x) * ((double)points[i].y - (double)points[j].y);
j = i;
}
return 0.5 * a;

View file

@ -2,7 +2,6 @@
#include "3DScene.hpp"
#include "../../libslic3r/libslic3r.h"
#include "../../libslic3r/ExtrusionEntity.hpp"
#include "../../libslic3r/ExtrusionEntityCollection.hpp"
#include "../../libslic3r/Geometry.hpp"
@ -28,8 +27,15 @@
#include <wx/image.h>
#include <wx/settings.h>
#include <Eigen/Dense>
#include "GUI.hpp"
static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
namespace Slic3r {
void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
@ -198,6 +204,34 @@ const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f };
const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f };
const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
GLVolume::GLVolume(float r, float g, float b, float a)
: m_angle_z(0.0f)
, m_scale_factor(1.0f)
, m_dirty(true)
, composite_id(-1)
, select_group_id(-1)
, drag_group_id(-1)
, extruder_id(0)
, selected(false)
, is_active(true)
, zoom_to_volumes(true)
, outside_printer_detection_enabled(true)
, is_outside(false)
, hover(false)
, is_modifier(false)
, is_wipe_tower(false)
, tverts_range(0, size_t(-1))
, qverts_range(0, size_t(-1))
{
m_world_mat = std::vector<float>(UNIT_MATRIX, std::end(UNIT_MATRIX));
color[0] = r;
color[1] = g;
color[2] = b;
color[3] = a;
set_render_color(r, g, b, a);
}
void GLVolume::set_render_color(float r, float g, float b, float a)
{
render_color[0] = r;
@ -218,12 +252,7 @@ void GLVolume::set_render_color(const float* rgba, unsigned int size)
void GLVolume::set_render_color()
{
if (selected)
{
if (is_outside)
set_render_color(SELECTED_OUTSIDE_COLOR, 4);
else
set_render_color(SELECTED_COLOR, 4);
}
set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
else if (hover)
set_render_color(HOVER_COLOR, 4);
else if (is_outside)
@ -232,6 +261,52 @@ void GLVolume::set_render_color()
set_render_color(color, 4);
}
const Pointf3& GLVolume::get_origin() const
{
return m_origin;
}
void GLVolume::set_origin(const Pointf3& origin)
{
m_origin = origin;
m_dirty = true;
}
void GLVolume::set_angle_z(float angle_z)
{
m_angle_z = angle_z;
m_dirty = true;
}
void GLVolume::set_scale_factor(float scale_factor)
{
m_scale_factor = scale_factor;
m_dirty = true;
}
const std::vector<float>& GLVolume::world_matrix() const
{
if (m_dirty)
{
Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
m.translate(Eigen::Vector3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z));
m.rotate(Eigen::AngleAxisf(m_angle_z, Eigen::Vector3f::UnitZ()));
m.scale(m_scale_factor);
::memcpy((void*)m_world_mat.data(), (const void*)m.data(), 16 * sizeof(float));
m_dirty = false;
}
return m_world_mat;
}
BoundingBoxf3 GLVolume::transformed_bounding_box() const
{
if (m_dirty)
m_transformed_bounding_box = bounding_box.transformed(world_matrix());
return m_transformed_bounding_box;
}
void GLVolume::set_range(double min_z, double max_z)
{
this->qverts_range.first = 0;
@ -272,14 +347,16 @@ void GLVolume::render() const
if (!is_active)
return;
glCullFace(GL_BACK);
glPushMatrix();
glTranslated(this->origin.x, this->origin.y, this->origin.z);
::glCullFace(GL_BACK);
::glPushMatrix();
::glTranslated(m_origin.x, m_origin.y, m_origin.z);
::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
if (this->indexed_vertex_array.indexed())
this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
else
this->indexed_vertex_array.render();
glPopMatrix();
::glPopMatrix();
}
void GLVolume::render_using_layer_height() const
@ -297,6 +374,7 @@ void GLVolume::render_using_layer_height() const
GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1;
GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1;
GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1;
GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1;
if (z_to_texture_row_id >= 0)
glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id());
@ -310,14 +388,19 @@ void GLVolume::render_using_layer_height() const
if (z_cursor_band_width_id >= 0)
glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width);
unsigned int w = layer_height_texture_width();
unsigned int h = layer_height_texture_height();
if (world_matrix_id >= 0)
::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
GLsizei w = (GLsizei)layer_height_texture_width();
GLsizei h = (GLsizei)layer_height_texture_height();
GLsizei half_w = w / 2;
GLsizei half_h = h / 2;
glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, w / 2, h / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0());
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, w / 2, h / 2, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
render();
@ -327,6 +410,128 @@ void GLVolume::render_using_layer_height() const
glUseProgram(current_program_id);
}
void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const
{
if (!is_active)
return;
if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
return;
if (layer_height_texture_data.can_use())
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
render_using_layer_height();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
return;
}
GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
if (n_triangles + n_quads == 0)
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
if (color_id >= 0)
{
float color[4];
::memcpy((void*)color, (const void*)render_color, 4 * sizeof(float));
::glUniform4fv(color_id, 1, (const GLfloat*)color);
}
else
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
if (detection_id != -1)
::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0);
if (worldmatrix_id != -1)
::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
return;
}
if (color_id >= 0)
::glUniform4fv(color_id, 1, (const GLfloat*)render_color);
else
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
if (detection_id != -1)
::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0);
if (worldmatrix_id != -1)
::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
::glPushMatrix();
::glTranslated(m_origin.x, m_origin.y, m_origin.z);
::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
if (n_triangles > 0)
{
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.triangle_indices_VBO_id);
::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4));
}
if (n_quads > 0)
{
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.quad_indices_VBO_id);
::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4));
}
::glPopMatrix();
}
void GLVolume::render_legacy() const
{
assert(!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
if (!is_active)
return;
GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
if (n_triangles + n_quads == 0)
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
return;
}
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data());
::glPushMatrix();
::glTranslated(m_origin.x, m_origin.y, m_origin.z);
::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
if (n_triangles > 0)
::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first);
if (n_quads > 0)
::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first);
::glPopMatrix();
}
double GLVolume::layer_height_texture_z_to_row_id() const
{
return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max.z);
@ -399,7 +604,6 @@ std::vector<int> GLVolumeCollection::load_object(
for (int instance_idx : instance_idxs) {
const ModelInstance *instance = model_object->instances[instance_idx];
TriangleMesh mesh = model_volume->mesh;
instance->transform_mesh(&mesh);
volumes_idx.push_back(int(this->volumes.size()));
float color[4];
memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
@ -433,13 +637,15 @@ std::vector<int> GLVolumeCollection::load_object(
v.extruder_id = extruder_id;
}
v.is_modifier = model_volume->modifier;
v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0));
v.set_angle_z(instance->rotation);
v.set_scale_factor(instance->scaling_factor);
}
}
return volumes_idx;
}
int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs)
{
@ -460,7 +666,8 @@ int GLVolumeCollection::load_wipe_tower_preview(
else
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
v.origin = Pointf3(pos_x, pos_y, 0.);
v.set_origin(Pointf3(pos_x, pos_y, 0.));
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(use_VBOs);
@ -485,102 +692,23 @@ void GLVolumeCollection::render_VBOs() const
GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1;
GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1;
GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1;
GLint print_box_origin_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_origin") : -1;
GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
GLint print_box_worldmatrix_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1;
for (GLVolume *volume : this->volumes) {
if (!volume->is_active)
continue;
if (print_box_min_id != -1)
::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
if (!volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
continue;
if (print_box_max_id != -1)
::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
for (GLVolume *volume : this->volumes)
{
if (volume->layer_height_texture_data.can_use())
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
volume->generate_layer_height_texture(volume->layer_height_texture_data.print_object, false);
volume->render_using_layer_height();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
continue;
}
volume->set_render_color();
GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
if (n_triangles + n_quads == 0)
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
if (color_id >= 0)
{
float color[4];
::memcpy((void*)color, (const void*)volume->render_color, 4 * sizeof(float));
::glUniform4fv(color_id, 1, (const GLfloat*)color);
}
else
::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
if (print_box_min_id != -1)
::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
if (print_box_max_id != -1)
::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
if (print_box_origin_id != -1)
{
float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
}
volume->render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
continue;
}
if (color_id >= 0)
::glUniform4fv(color_id, 1, (const GLfloat*)volume->render_color);
else
::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
volume->set_render_color();
if (print_box_min_id != -1)
::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
if (print_box_max_id != -1)
::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
if (print_box_origin_id != -1)
{
float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
}
::glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
bool has_offset = (volume->origin.x != 0) || (volume->origin.y != 0) || (volume->origin.z != 0);
if (has_offset) {
::glPushMatrix();
::glTranslated(volume->origin.x, volume->origin.y, volume->origin.z);
}
if (n_triangles > 0) {
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id);
::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(volume->tverts_range.first * 4));
}
if (n_quads > 0) {
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id);
::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(volume->qverts_range.first * 4));
}
if (has_offset)
::glPopMatrix();
volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
}
::glBindBuffer(GL_ARRAY_BUFFER, 0);
@ -601,43 +729,10 @@ void GLVolumeCollection::render_legacy() const
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
for (GLVolume *volume : this->volumes) {
assert(! volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
if (!volume->is_active)
continue;
for (GLVolume *volume : this->volumes)
{
volume->set_render_color();
GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
if (n_triangles + n_quads == 0)
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
volume->render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
continue;
}
glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
glNormalPointer(GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data());
bool has_offset = volume->origin.x != 0 || volume->origin.y != 0 || volume->origin.z != 0;
if (has_offset) {
glPushMatrix();
glTranslated(volume->origin.x, volume->origin.y, volume->origin.z);
}
if (n_triangles > 0)
glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, volume->indexed_vertex_array.triangle_indices.data() + volume->tverts_range.first);
if (n_quads > 0)
glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, volume->indexed_vertex_array.quad_indices.data() + volume->qverts_range.first);
if (has_offset)
glPopMatrix();
volume->render_legacy();
}
glDisableClientState(GL_VERTEX_ARRAY);

View file

@ -240,7 +240,7 @@ class GLVolume {
edit_band_width = 0.0f;
}
bool can_use() { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); }
bool can_use() const { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); }
};
public:
@ -249,44 +249,27 @@ public:
static const float OUTSIDE_COLOR[4];
static const float SELECTED_OUTSIDE_COLOR[4];
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) :
composite_id(-1),
select_group_id(-1),
drag_group_id(-1),
extruder_id(0),
selected(false),
is_active(true),
zoom_to_volumes(true),
outside_printer_detection_enabled(true),
is_outside(false),
hover(false),
is_modifier(false),
is_wipe_tower(false),
tverts_range(0, size_t(-1)),
qverts_range(0, size_t(-1))
{
color[0] = r;
color[1] = g;
color[2] = b;
color[3] = a;
set_render_color(r, g, b, a);
}
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
std::vector<int> load_object(
const ModelObject *model_object,
const std::vector<int> &instance_idxs,
const std::string &color_by,
const std::string &select_by,
const std::string &drag_by);
private:
// Offset of the volume to be rendered.
Pointf3 m_origin;
// Rotation around Z axis of the volume to be rendered.
float m_angle_z;
// Scale factor of the volume to be rendered.
float m_scale_factor;
// World matrix of the volume to be rendered.
std::vector<float> m_world_mat;
// Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_transformed_bounding_box;
// Whether or not is needed to recalculate the world matrix.
mutable bool m_dirty;
int load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
public:
// Bounding box of this volume, in unscaled coordinates.
BoundingBoxf3 bounding_box;
// Offset of the volume to be rendered.
Pointf3 origin;
// Color of the triangles / quads held by this volume.
float color[4];
// Color used to render this volume.
@ -333,10 +316,17 @@ public:
// Sets render color in dependence of current state
void set_render_color();
const Pointf3& get_origin() const;
void set_origin(const Pointf3& origin);
void set_angle_z(float angle_z);
void set_scale_factor(float scale_factor);
int object_idx() const { return this->composite_id / 1000000; }
int volume_idx() const { return (this->composite_id / 1000) % 1000; }
int instance_idx() const { return this->composite_id % 1000; }
BoundingBoxf3 transformed_bounding_box() const { BoundingBoxf3 bb = this->bounding_box; bb.translate(this->origin); return bb; }
const std::vector<float>& world_matrix() const;
BoundingBoxf3 transformed_bounding_box() const;
bool empty() const { return this->indexed_vertex_array.empty(); }
bool indexed() const { return this->indexed_vertex_array.indexed(); }
@ -344,6 +334,9 @@ public:
void set_range(coordf_t low, coordf_t high);
void render() const;
void render_using_layer_height() const;
void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
void render_legacy() const;
void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }

View file

@ -113,6 +113,11 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons
sizer->Add(all_none_sizer, 0, wxEXPAND);
SetSizer(sizer);
if (cboxes.size() > 0) {
cboxes[0]->SetValue(true);
on_checkbox(cboxes[0], true);
}
}
void PrinterPicker::select_all(bool select)
@ -598,10 +603,10 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
static const std::unordered_map<std::string, std::pair<std::string, std::string>> legacy_preset_map {{
{ "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") },
{ "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2S", "0.4") },
{ "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
{ "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2S", "0.4") },
{ "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
{ "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2SMM", "0.4") },
{ "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") },
{ "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2SMM", "0.4") },
{ "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") },
{ "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") },
{ "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
{ "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") },
@ -809,8 +814,8 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
topsizer->AddSpacer(INDEX_MARGIN);
topsizer->Add(p->hscroll, 1, wxEXPAND);
p->btn_prev = new wxButton(this, wxID_BACKWARD);
p->btn_next = new wxButton(this, wxID_FORWARD);
p->btn_prev = new wxButton(this, wxID_NONE, _(L("< &Back")));
p->btn_next = new wxButton(this, wxID_NONE, _(L("&Next >")));
p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish")));
p->btn_cancel = new wxButton(this, wxID_CANCEL);
p->btnsizer->AddStretchSpacer();

View file

@ -4,12 +4,14 @@
#include <algorithm>
#include <boost/format.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/log/trivial.hpp>
#include <wx/app.h>
#include <wx/event.h>
#include <wx/sizer.h>
#include <wx/settings.h>
#include <wx/timer.h>
#include <wx/panel.h>
#include <wx/button.h>
#include <wx/filepicker.h>
@ -36,7 +38,7 @@ namespace Slic3r {
enum AvrdudeEvent
{
AE_MESSAGE,
AE_PRORGESS,
AE_PROGRESS,
AE_EXIT,
};
@ -62,7 +64,6 @@ struct FirmwareDialog::priv
std::vector<Utils::SerialPortInfo> ports;
wxFilePickerCtrl *hex_picker;
wxStaticText *txt_status;
wxStaticText *txt_progress;
wxGauge *progressbar;
wxCollapsiblePane *spoiler;
wxTextCtrl *txt_stdout;
@ -72,6 +73,8 @@ struct FirmwareDialog::priv
wxString btn_flash_label_ready;
wxString btn_flash_label_flashing;
wxTimer timer_pulse;
// This is a shared pointer holding the background AvrDude task
// also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset).
AvrDude::Ptr avrdude;
@ -83,13 +86,16 @@ struct FirmwareDialog::priv
q(q),
btn_flash_label_ready(_(L("Flash!"))),
btn_flash_label_flashing(_(L("Cancel"))),
timer_pulse(q),
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
progress_tasks_done(0),
cancelled(false)
{}
void find_serial_ports();
void flashing_status(bool flashing, AvrDudeComplete complete = AC_NONE);
void flashing_start(bool flashing_l10n);
void flashing_done(AvrDudeComplete complete);
size_t hex_lang_offset(const wxString &path);
void perform_upload();
void cancel();
void on_avrdude(const wxCommandEvent &evt);
@ -116,42 +122,76 @@ void FirmwareDialog::priv::find_serial_ports()
}
}
void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete)
void FirmwareDialog::priv::flashing_start(bool flashing_l10n)
{
if (value) {
txt_stdout->Clear();
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
txt_status->SetForegroundColour(GUI::get_label_clr_modified());
port_picker->Disable();
btn_rescan->Disable();
hex_picker->Disable();
btn_close->Disable();
btn_flash->SetLabel(btn_flash_label_flashing);
progressbar->SetRange(200); // See progress callback below
progressbar->SetValue(0);
progress_tasks_done = 0;
cancelled = false;
} else {
auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
port_picker->Enable();
btn_rescan->Enable();
hex_picker->Enable();
btn_close->Enable();
btn_flash->SetLabel(btn_flash_label_ready);
txt_status->SetForegroundColour(text_color);
progressbar->SetValue(200);
txt_stdout->Clear();
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
txt_status->SetForegroundColour(GUI::get_label_clr_modified());
port_picker->Disable();
btn_rescan->Disable();
hex_picker->Disable();
btn_close->Disable();
btn_flash->SetLabel(btn_flash_label_flashing);
progressbar->SetRange(flashing_l10n ? 500 : 200); // See progress callback below
progressbar->SetValue(0);
progress_tasks_done = 0;
cancelled = false;
timer_pulse.Start(50);
}
switch (complete) {
case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break;
case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break;
case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break;
void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete)
{
auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
port_picker->Enable();
btn_rescan->Enable();
hex_picker->Enable();
btn_close->Enable();
btn_flash->SetLabel(btn_flash_label_ready);
txt_status->SetForegroundColour(text_color);
timer_pulse.Stop();
progressbar->SetValue(progressbar->GetRange());
switch (complete) {
case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break;
case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break;
case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break;
}
}
size_t FirmwareDialog::priv::hex_lang_offset(const wxString &path)
{
fs::ifstream file(fs::path(path.wx_str()));
if (! file.good()) {
return 0;
}
static const char *hex_terminator = ":00000001FF\r";
size_t res = 0;
std::string line;
while (getline(file, line, '\n').good()) {
// Account for LF vs CRLF
if (!line.empty() && line.back() != '\r') {
line.push_back('\r');
}
if (line == hex_terminator) {
if (res == 0) {
// This is the first terminator seen, save the position
res = file.tellg();
} else {
// We've found another terminator, return the offset just after the first one
// which is the start of the second 'section'.
return res;
}
}
}
return 0;
}
void FirmwareDialog::priv::perform_upload()
{
auto filename = hex_picker->GetPath();
auto filename = hex_picker->GetPath();
std::string port = port_picker->GetValue().ToStdString();
int selection = port_picker->GetSelection();
if (selection != -1) {
@ -161,16 +201,32 @@ void FirmwareDialog::priv::perform_upload()
}
if (filename.IsEmpty() || port.empty()) { return; }
flashing_status(true);
const bool extra_verbose = false; // For debugging
const auto lang_offset = hex_lang_offset(filename);
const auto filename_utf8 = filename.utf8_str();
flashing_start(lang_offset > 0);
// It is ok here to use the q-pointer to the FirmwareDialog
// because the dialog ensures it doesn't exit before the background thread is done.
auto q = this->q;
// Init the avrdude object
AvrDude avrdude(avrdude_config);
// Build argument list(s)
std::vector<std::string> args {{
"-v",
extra_verbose ? "-vvvvv" : "-v",
"-p", "atmega2560",
// Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500).
// The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
// is flashed with a buggy firmware.
"-c", "wiring",
"-P", port,
"-b", "115200", // XXX: is this ok to hardcode?
"-b", "115200", // TODO: Allow other rates? Ditto below.
"-D",
"-U", (boost::format("flash:w:%1%:i") % filename.ToStdString()).str()
// XXX: Safe mode?
"-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(),
}};
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
@ -178,26 +234,51 @@ void FirmwareDialog::priv::perform_upload()
return a + ' ' + b;
});
// It is ok here to use the q-pointer to the FirmwareDialog
// because the dialog ensures it doesn't exit before the background thread is done.
auto q = this->q;
avrdude.push_args(std::move(args));
if (lang_offset > 0) {
// The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy)
// This is done via another avrdude invocation, here we build arg list for that:
std::vector<std::string> args_l10n {{
extra_verbose ? "-vvvvv" : "-v",
"-p", "atmega2560",
// Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
// The Prusa's avrdude is patched again to never send semicolons inside the data packets.
"-c", "arduino",
"-P", port,
"-b", "115200",
"-D",
"-u", // disable safe mode
"-U", (boost::format("flash:w:%1%:%2%:i") % lang_offset % filename_utf8.data()).str(),
}};
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
<< std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) {
return a + ' ' + b;
});
avrdude.push_args(std::move(args_l10n));
}
this->avrdude = avrdude
.on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) {
if (extra_verbose) {
BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg;
}
avrdude = AvrDude()
.sys_config(avrdude_config)
.args(args)
.on_message(std::move([q](const char *msg, unsigned /* size */) {
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
auto wxmsg = wxString::FromUTF8(msg);
evt->SetExtraLong(AE_MESSAGE);
evt->SetString(msg);
evt->SetString(std::move(wxmsg));
wxQueueEvent(q, evt);
}))
.on_progress(std::move([q](const char * /* task */, unsigned progress) {
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
evt->SetExtraLong(AE_PRORGESS);
evt->SetExtraLong(AE_PROGRESS);
evt->SetInt(progress);
wxQueueEvent(q, evt);
}))
.on_complete(std::move([q](int status) {
.on_complete(std::move([q](int status, size_t /* args_id */) {
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
evt->SetExtraLong(AE_EXIT);
evt->SetInt(status);
@ -224,19 +305,19 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
txt_stdout->AppendText(evt.GetString());
break;
case AE_PRORGESS:
case AE_PROGRESS:
// We try to track overall progress here.
// When uploading the firmware, avrdude first reads a littlebit of status data,
// then performs write, then reading (verification).
// We Pulse() during the first read and combine progress of the latter two tasks.
// Avrdude performs 3 tasks per one memory operation ("-U" arg),
// first of which is reading of status data (very short).
// We use the timer_pulse during the very first task to indicate intialization
// and then display overall progress during the latter tasks.
if (progress_tasks_done == 0) {
progressbar->Pulse();
} else {
if (progress_tasks_done > 0) {
progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt());
}
if (evt.GetInt() == 100) {
timer_pulse.Stop();
progress_tasks_done += 100;
}
@ -246,7 +327,7 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt();
complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
flashing_status(false, complete_kind);
flashing_done(complete_kind);
// Make sure the background thread is collected and the AvrDude object reset
if (avrdude) { avrdude->join(); }
@ -374,6 +455,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
}
});
Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->p->progressbar->Pulse(); });
Bind(EVT_AVRDUDE, [this](wxCommandEvent &evt) { this->p->on_avrdude(evt); });
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) {

View file

@ -1,5 +1,6 @@
#include "GLCanvas3D.hpp"
#include "../../libslic3r/libslic3r.h"
#include "../../slic3r/GUI/3DScene.hpp"
#include "../../slic3r/GUI/GLShader.hpp"
#include "../../slic3r/GUI/GUI.hpp"
@ -41,6 +42,11 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f };
static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f;
static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
namespace Slic3r {
namespace GUI {
@ -719,6 +725,12 @@ void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const
m_shader->set_uniform(name.c_str(), value);
}
void GLCanvas3D::Shader::set_uniform(const std::string& name, const float* matrix) const
{
if (m_shader != nullptr)
m_shader->set_uniform(name.c_str(), matrix);
}
const GLShader* GLCanvas3D::Shader::get_shader() const
{
return m_shader;
@ -952,6 +964,8 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height());
m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas));
m_shader.set_uniform("z_cursor_band_width", band_width);
// The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX);
GLsizei w = (GLsizei)volume.layer_height_texture_width();
GLsizei h = (GLsizei)volume.layer_height_texture_height();
@ -1042,7 +1056,9 @@ const Pointf3 GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MA
GLCanvas3D::Mouse::Drag::Drag()
: start_position_2D(Invalid_2D_Point)
, start_position_3D(Invalid_3D_Point)
, volume_idx(-1)
, move_with_ctrl(false)
, move_volume_idx(-1)
, gizmo_volume_idx(-1)
{
}
@ -2765,6 +2781,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
{
update_gizmos_data();
m_gizmos.start_dragging();
m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id();
m_dirty = true;
}
else
@ -2812,7 +2829,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (volume_bbox.contains(pos3d))
{
// The dragging operation is initiated.
m_mouse.drag.volume_idx = volume_idx;
m_mouse.drag.move_with_ctrl = evt.ControlDown();
m_mouse.drag.move_volume_idx = volume_idx;
m_mouse.drag.start_position_3D = pos3d;
// Remember the shift to to the object center.The object center will later be used
// to limit the object placement close to the bed.
@ -2828,7 +2846,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
}
else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1))
else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1))
{
m_mouse.dragging = true;
@ -2851,24 +2869,30 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Calculate the translation vector.
Vectorf3 vector = m_mouse.drag.start_position_3D.vector_to(cur_pos);
// Get the volume being dragged.
GLVolume* volume = m_volumes.volumes[m_mouse.drag.volume_idx];
GLVolume* volume = m_volumes.volumes[m_mouse.drag.move_volume_idx];
// Get all volumes belonging to the same group, if any.
std::vector<GLVolume*> volumes;
if (volume->drag_group_id == -1)
int group_id = m_mouse.drag.move_with_ctrl ? volume->select_group_id : volume->drag_group_id;
if (group_id == -1)
volumes.push_back(volume);
else
{
for (GLVolume* v : m_volumes.volumes)
{
if ((v != nullptr) && (v->drag_group_id == volume->drag_group_id))
volumes.push_back(v);
if (v != nullptr)
{
if ((m_mouse.drag.move_with_ctrl && (v->select_group_id == group_id)) || (v->drag_group_id == group_id))
volumes.push_back(v);
}
}
}
// Apply new temporary volume origin and ignore Z.
for (GLVolume* v : volumes)
{
v->origin.translate(vector.x, vector.y, 0.0);
Pointf3 origin = v->get_origin();
origin.translate(vector.x, vector.y, 0.0);
v->set_origin(origin);
}
m_mouse.drag.start_position_3D = cur_pos;
@ -2882,16 +2906,43 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
const Pointf3& cur_pos = _mouse_to_bed_3d(pos);
m_gizmos.update(Pointf(cur_pos.x, cur_pos.y));
std::vector<GLVolume*> volumes;
if (m_mouse.drag.gizmo_volume_idx != -1)
{
GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx];
// Get all volumes belonging to the same group, if any.
if (volume->select_group_id == -1)
volumes.push_back(volume);
else
{
for (GLVolume* v : m_volumes.volumes)
{
if ((v != nullptr) && (v->select_group_id == volume->select_group_id))
volumes.push_back(v);
}
}
}
switch (m_gizmos.get_current_type())
{
case Gizmos::Scale:
{
m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
// Apply new temporary scale factor
float scale_factor = m_gizmos.get_scale();
for (GLVolume* v : volumes)
{
v->set_scale_factor(scale_factor);
}
break;
}
case Gizmos::Rotate:
{
m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z());
// Apply new temporary angle_z
float angle_z = m_gizmos.get_angle_z();
for (GLVolume* v : volumes)
{
v->set_angle_z(angle_z);
}
break;
}
default:
@ -2954,19 +3005,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (layer_editing_object_idx != -1)
m_on_model_update_callback.call();
}
else if ((m_mouse.drag.volume_idx != -1) && m_mouse.dragging)
else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging)
{
// get all volumes belonging to the same group, if any
std::vector<int> volume_idxs;
int vol_id = m_mouse.drag.volume_idx;
int group_id = m_volumes.volumes[vol_id]->drag_group_id;
int vol_id = m_mouse.drag.move_volume_idx;
int group_id = m_mouse.drag.move_with_ctrl ? m_volumes.volumes[vol_id]->select_group_id : m_volumes.volumes[vol_id]->drag_group_id;
if (group_id == -1)
volume_idxs.push_back(vol_id);
else
{
for (int i = 0; i < (int)m_volumes.volumes.size(); ++i)
{
if (m_volumes.volumes[i]->drag_group_id == group_id)
if ((m_mouse.drag.move_with_ctrl && (m_volumes.volumes[i]->select_group_id == group_id)) || (m_volumes.volumes[i]->drag_group_id == group_id))
volume_idxs.push_back(i);
}
}
@ -2984,10 +3035,26 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
else if (evt.LeftUp() && m_gizmos.is_dragging())
{
switch (m_gizmos.get_current_type())
{
case Gizmos::Scale:
{
m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
break;
}
case Gizmos::Rotate:
{
m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z());
break;
}
default:
break;
}
m_gizmos.stop_dragging();
}
m_mouse.drag.volume_idx = -1;
m_mouse.drag.move_volume_idx = -1;
m_mouse.drag.gizmo_volume_idx = -1;
m_mouse.set_start_position_3D_as_invalid();
m_mouse.set_start_position_2D_as_invalid();
m_mouse.dragging = false;
@ -3706,6 +3773,35 @@ int GLCanvas3D::_get_first_selected_object_id() const
return -1;
}
int GLCanvas3D::_get_first_selected_volume_id() const
{
if (m_print != nullptr)
{
int objects_count = (int)m_print->objects.size();
for (const GLVolume* vol : m_volumes.volumes)
{
if ((vol != nullptr) && vol->selected)
{
int object_id = vol->select_group_id / 1000000;
// Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy.
if (object_id < 10000)
{
int volume_id = 0;
for (int i = 0; i < object_id; ++i)
{
const PrintObject* obj = m_print->objects[i];
const ModelObject* model = obj->model_object();
volume_id += model->instances.size();
}
return volume_id;
}
}
}
}
return -1;
}
static inline int hex_digit_to_int(const char c)
{
return
@ -4311,13 +4407,14 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
{
// Move a regular object.
ModelObject* model_object = m_model->objects[obj_idx];
model_object->instances[instance_idx]->offset.translate(volume->origin.x, volume->origin.y);
const Pointf3& origin = volume->get_origin();
model_object->instances[instance_idx]->offset = Pointf(origin.x, origin.y);
model_object->invalidate_bounding_box();
object_moved = true;
}
else if (obj_idx == 1000)
// Move a wipe tower proxy.
wipe_tower_origin = volume->origin;
wipe_tower_origin = volume->get_origin();
}
if (object_moved)

View file

@ -225,6 +225,7 @@ public:
void stop_using() const;
void set_uniform(const std::string& name, float value) const;
void set_uniform(const std::string& name, const float* matrix) const;
const GLShader* get_shader() const;
@ -302,7 +303,10 @@ public:
Point start_position_2D;
Pointf3 start_position_3D;
Vectorf3 volume_center_offset;
int volume_idx;
bool move_with_ctrl;
int move_volume_idx;
int gizmo_volume_idx;
public:
Drag();
@ -617,6 +621,7 @@ private:
void _stop_timer();
int _get_first_selected_object_id() const;
int _get_first_selected_volume_id() const;
// generates gcode extrusion paths geometry
void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);

View file

@ -214,6 +214,17 @@ bool GLShader::set_uniform(const char *name, float value) const
return false;
}
bool GLShader::set_uniform(const char* name, const float* matrix) const
{
int id = get_uniform_location(name);
if (id >= 0)
{
::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix);
return true;
}
return false;
}
/*
# Set shader vector
sub SetVector

View file

@ -25,6 +25,7 @@ public:
int get_uniform_location(const char *name) const;
bool set_uniform(const char *name, float value) const;
bool set_uniform(const char* name, const float* matrix) const;
void enable() const;
void disable() const;

View file

@ -423,7 +423,7 @@ bool check_unsaved_changes()
bool config_wizard_startup(bool app_config_exists)
{
if (! app_config_exists || g_PresetBundle->has_defauls_only()) {
if (! app_config_exists || g_PresetBundle->printers.size() <= 1) {
config_wizard(ConfigWizard::RR_DATA_EMPTY);
return true;
} else if (g_AppConfig->legacy_datadir()) {

View file

@ -0,0 +1,402 @@
#ifdef HAS_WIN10SDK
#ifndef NOMINMAX
# define NOMINMAX
#endif
#include "FixModelByWin10.hpp"
#include <atomic>
#include <chrono>
#include <cstdint>
#include <condition_variable>
#include <exception>
#include <string>
#include <thread>
#include <boost/filesystem.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/cstdio.hpp>
#include <roapi.h>
// for ComPtr
#include <wrl/client.h>
// from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/
#include <winrt/robuffer.h>
#include <winrt/windows.storage.provider.h>
#include <winrt/windows.graphics.printing3d.h>
#include "libslic3r/Model.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/Format/3mf.hpp"
#include "../GUI/GUI.hpp"
#include "../GUI/PresetBundle.hpp"
#include <wx/msgdlg.h>
#include <wx/progdlg.h>
extern "C"{
// from rapi.h
typedef HRESULT (__stdcall* FunctionRoInitialize)(int);
typedef HRESULT (__stdcall* FunctionRoUninitialize)();
typedef HRESULT (__stdcall* FunctionRoActivateInstance)(HSTRING activatableClassId, IInspectable **instance);
typedef HRESULT (__stdcall* FunctionRoGetActivationFactory)(HSTRING activatableClassId, REFIID iid, void **factory);
// from winstring.h
typedef HRESULT (__stdcall* FunctionWindowsCreateString)(LPCWSTR sourceString, UINT32 length, HSTRING *string);
typedef HRESULT (__stdcall* FunctionWindowsDelteString)(HSTRING string);
}
namespace Slic3r {
HMODULE s_hRuntimeObjectLibrary = nullptr;
FunctionRoInitialize s_RoInitialize = nullptr;
FunctionRoUninitialize s_RoUninitialize = nullptr;
FunctionRoActivateInstance s_RoActivateInstance = nullptr;
FunctionRoGetActivationFactory s_RoGetActivationFactory = nullptr;
FunctionWindowsCreateString s_WindowsCreateString = nullptr;
FunctionWindowsDelteString s_WindowsDeleteString = nullptr;
bool winrt_load_runtime_object_library()
{
if (s_hRuntimeObjectLibrary == nullptr)
s_hRuntimeObjectLibrary = LoadLibrary(L"ComBase.dll");
if (s_hRuntimeObjectLibrary != nullptr) {
s_RoInitialize = (FunctionRoInitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoInitialize");
s_RoUninitialize = (FunctionRoUninitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoUninitialize");
s_RoActivateInstance = (FunctionRoActivateInstance) GetProcAddress(s_hRuntimeObjectLibrary, "RoActivateInstance");
s_RoGetActivationFactory = (FunctionRoGetActivationFactory) GetProcAddress(s_hRuntimeObjectLibrary, "RoGetActivationFactory");
s_WindowsCreateString = (FunctionWindowsCreateString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsCreateString");
s_WindowsDeleteString = (FunctionWindowsDelteString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsDeleteString");
}
return s_RoInitialize && s_RoUninitialize && s_RoActivateInstance && s_WindowsCreateString && s_WindowsDeleteString;
}
static HRESULT winrt_activate_instance(const std::wstring &class_name, IInspectable **pinst)
{
HSTRING hClassName;
HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName);
if (S_OK != hr)
return hr;
hr = (*s_RoActivateInstance)(hClassName, pinst);
(*s_WindowsDeleteString)(hClassName);
return hr;
}
template<typename TYPE>
static HRESULT winrt_activate_instance(const std::wstring &class_name, TYPE **pinst)
{
IInspectable *pinspectable = nullptr;
HRESULT hr = winrt_activate_instance(class_name, &pinspectable);
if (S_OK != hr)
return hr;
hr = pinspectable->QueryInterface(__uuidof(TYPE), (void**)pinst);
pinspectable->Release();
return hr;
}
static HRESULT winrt_get_activation_factory(const std::wstring &class_name, REFIID iid, void **pinst)
{
HSTRING hClassName;
HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName);
if (S_OK != hr)
return hr;
hr = (*s_RoGetActivationFactory)(hClassName, iid, pinst);
(*s_WindowsDeleteString)(hClassName);
return hr;
}
template<typename TYPE>
static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE **pinst)
{
return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast<void**>(pinst));
}
// To be called often to test whether to cancel the operation.
typedef std::function<void ()> ThrowOnCancelFn;
template<typename T>
static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, ThrowOnCancelFn throw_on_cancel, int blocking_tick_ms = 100)
{
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
asyncAction.As(&asyncInfo);
AsyncStatus status;
// Ugly blocking loop until the RepairAsync call finishes.
//FIXME replace with a callback.
// https://social.msdn.microsoft.com/Forums/en-US/a5038fb4-b7b7-4504-969d-c102faa389fb/trying-to-block-an-async-operation-and-wait-for-a-particular-time?forum=vclanguage
for (;;) {
asyncInfo->get_Status(&status);
if (status != AsyncStatus::Started)
return status;
throw_on_cancel();
::Sleep(blocking_tick_ms);
}
}
static HRESULT winrt_open_file_stream(
const std::wstring &path,
ABI::Windows::Storage::FileAccessMode mode,
ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream,
ThrowOnCancelFn throw_on_cancel)
{
// Get the file factory.
Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFileStatics> fileFactory;
HRESULT hr = winrt_get_activation_factory(L"Windows.Storage.StorageFile", fileFactory.GetAddressOf());
if (FAILED(hr)) return hr;
// Open the file asynchronously.
HSTRING hstr_path;
hr = (*s_WindowsCreateString)(path.c_str(), path.size(), &hstr_path);
if (FAILED(hr)) return hr;
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFile*>> fileOpenAsync;
hr = fileFactory->GetFileFromPathAsync(hstr_path, fileOpenAsync.GetAddressOf());
if (FAILED(hr)) return hr;
(*s_WindowsDeleteString)(hstr_path);
// Wait until the file gets open, get the actual file.
AsyncStatus status = winrt_async_await(fileOpenAsync, throw_on_cancel);
Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFile> storageFile;
if (status == AsyncStatus::Completed) {
hr = fileOpenAsync->GetResults(storageFile.GetAddressOf());
} else {
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
hr = fileOpenAsync.As(&asyncInfo);
if (FAILED(hr)) return hr;
HRESULT err;
hr = asyncInfo->get_ErrorCode(&err);
return FAILED(hr) ? hr : err;
}
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> fileStreamAsync;
hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf());
if (FAILED(hr)) return hr;
status = winrt_async_await(fileStreamAsync, throw_on_cancel);
if (status == AsyncStatus::Completed) {
hr = fileStreamAsync->GetResults(fileStream);
} else {
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
hr = fileStreamAsync.As(&asyncInfo);
if (FAILED(hr)) return hr;
HRESULT err;
hr = asyncInfo->get_ErrorCode(&err);
if (!FAILED(hr))
hr = err;
}
return hr;
}
bool is_windows10()
{
HKEY hKey;
LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey);
if (lRes == ERROR_SUCCESS) {
WCHAR szBuffer[512];
DWORD dwBufferSize = sizeof(szBuffer);
lRes = RegQueryValueExW(hKey, L"ProductName", 0, nullptr, (LPBYTE)szBuffer, &dwBufferSize);
if (lRes == ERROR_SUCCESS)
return wcsncmp(szBuffer, L"Windows 10", 10) == 0;
RegCloseKey(hKey);
}
return false;
}
// Progress function, to be called regularly to update the progress.
typedef std::function<void (const char * /* message */, unsigned /* progress */)> ProgressFn;
void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst, ProgressFn on_progress, ThrowOnCancelFn throw_on_cancel)
{
if (! is_windows10())
throw std::runtime_error("fix_model_by_win10_sdk called on non Windows 10 system");
if (! winrt_load_runtime_object_library())
throw std::runtime_error("Failed to initialize the WinRT library.");
HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED);
{
on_progress(L("Exporting the source model"), 20);
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> fileStream;
hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf(), throw_on_cancel);
Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage;
hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf());
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Graphics::Printing3D::Printing3DModel*>> modelAsync;
hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf());
AsyncStatus status = winrt_async_await(modelAsync, throw_on_cancel);
Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3DModel> model;
if (status == AsyncStatus::Completed)
hr = modelAsync->GetResults(model.GetAddressOf());
else
throw std::runtime_error(L("Failed loading the input model."));
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes;
hr = model->get_Meshes(meshes.GetAddressOf());
unsigned num_meshes = 0;
hr = meshes->get_Size(&num_meshes);
on_progress(L("Repairing the model by the Netfabb service"), 40);
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> repairAsync;
hr = model->RepairAsync(repairAsync.GetAddressOf());
status = winrt_async_await(repairAsync, throw_on_cancel);
if (status != AsyncStatus::Completed)
throw std::runtime_error(L("Mesh repair failed."));
repairAsync->GetResults();
on_progress(L("Loading the repaired model"), 60);
// Verify the number of meshes returned after the repair action.
meshes.Reset();
hr = model->get_Meshes(meshes.GetAddressOf());
hr = meshes->get_Size(&num_meshes);
// Save model to this class' Printing3D3MFPackage.
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> saveToPackageAsync;
hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf());
status = winrt_async_await(saveToPackageAsync, throw_on_cancel);
if (status != AsyncStatus::Completed)
throw std::runtime_error(L("Saving mesh into the 3MF container failed."));
hr = saveToPackageAsync->GetResults();
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> generatorStreamAsync;
hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf());
status = winrt_async_await(generatorStreamAsync, throw_on_cancel);
if (status != AsyncStatus::Completed)
throw std::runtime_error(L("Saving mesh into the 3MF container failed."));
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream;
hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf());
// Go to the beginning of the stream.
generatorStream->Seek(0);
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IInputStream> inputStream;
hr = generatorStream.As(&inputStream);
// Get the buffer factory.
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory;
hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf());
// Open the destination file.
FILE *fout = boost::nowide::fopen(path_dst.c_str(), "wb");
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
byte *buffer_ptr;
bufferFactory->Create(65536 * 2048, buffer.GetAddressOf());
{
Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
buffer.As(&bufferByteAccess);
hr = bufferByteAccess->Buffer(&buffer_ptr);
}
uint32_t length;
hr = buffer->get_Length(&length);
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer*, UINT32>> asyncRead;
for (;;) {
hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf());
status = winrt_async_await(asyncRead, throw_on_cancel);
if (status != AsyncStatus::Completed)
throw std::runtime_error(L("Saving mesh into the 3MF container failed."));
hr = buffer->get_Length(&length);
if (length == 0)
break;
fwrite(buffer_ptr, length, 1, fout);
}
fclose(fout);
// Here all the COM objects will be released through the ComPtr destructors.
}
(*s_RoUninitialize)();
}
class RepairCanceledException : public std::exception {
public:
const char* what() const throw() { return "Model repair has been canceled"; }
};
void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result)
{
std::mutex mutex;
std::condition_variable condition;
std::unique_lock<std::mutex> lock(mutex);
struct Progress {
std::string message;
int percent = 0;
bool updated = false;
} progress;
std::atomic<bool> canceled = false;
std::atomic<bool> finished = false;
// Open a progress dialog.
wxProgressDialog progress_dialog(
_(L("Model fixing")),
_(L("Exporting model...")),
100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
// Executing the calculation in a background thread, so that the COM context could be created with its own threading model.
// (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context).
bool success = false;
auto on_progress = [&mutex, &condition, &progress](const char *msg, unsigned prcnt) {
std::lock_guard<std::mutex> lk(mutex);
progress.message = msg;
progress.percent = prcnt;
progress.updated = true;
condition.notify_all();
};
auto worker_thread = boost::thread([&model_object, &print, &result, on_progress, &success, &canceled, &finished]() {
try {
on_progress(L("Exporting the source model"), 0);
boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
path_src += ".3mf";
Model model;
model.add_object(model_object);
if (! Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast<Print*>(&print), false)) {
boost::filesystem::remove(path_src);
throw std::runtime_error(L("Export of a temporary 3mf file failed"));
}
model.clear_objects();
model.clear_materials();
boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
path_dst += ".3mf";
fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string(), on_progress,
[&canceled]() { if (canceled) throw RepairCanceledException(); });
boost::filesystem::remove(path_src);
PresetBundle bundle;
on_progress(L("Loading the repaired model"), 80);
bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result);
boost::filesystem::remove(path_dst);
if (! loaded)
throw std::runtime_error(L("Import of the repaired 3mf file failed"));
success = true;
finished = true;
on_progress(L("Model repair finished"), 100);
} catch (RepairCanceledException &ex) {
canceled = true;
finished = true;
on_progress(L("Model repair canceled"), 100);
} catch (std::exception &ex) {
success = false;
finished = true;
on_progress(ex.what(), 100);
}
});
while (! finished) {
condition.wait_for(lock, std::chrono::milliseconds(500), [&progress]{ return progress.updated; });
if (! progress_dialog.Update(progress.percent, _(progress.message)))
canceled = true;
progress.updated = false;
}
if (canceled) {
// Nothing to show.
} else if (success) {
wxMessageDialog dlg(nullptr, _(L("Model repaired successfully")), _(L("Model Repair by the Netfabb service")), wxICON_INFORMATION | wxOK_DEFAULT);
dlg.ShowModal();
} else {
wxMessageDialog dlg(nullptr, _(L("Model repair failed: \n")) + _(progress.message), _(L("Model Repair by the Netfabb service")), wxICON_ERROR | wxOK_DEFAULT);
dlg.ShowModal();
}
worker_thread.join();
}
} // namespace Slic3r
#endif /* HAS_WIN10SDK */

View file

@ -0,0 +1,26 @@
#ifndef slic3r_GUI_Utils_FixModelByWin10_hpp_
#define slic3r_GUI_Utils_FixModelByWin10_hpp_
#include <string>
namespace Slic3r {
class Model;
class ModelObject;
class Print;
#ifdef HAS_WIN10SDK
extern bool is_windows10();
extern void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result);
#else /* HAS_WIN10SDK */
inline bool is_windows10() { return false; }
inline void fix_model_by_win10_sdk_gui(const ModelObject &, const Print &, Model &) {}
#endif /* HAS_WIN10SDK */
} // namespace Slic3r
#endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */

View file

@ -259,7 +259,7 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors) con
}
const auto recommended = recommended_it->config_version;
BOOST_LOG_TRIVIAL(debug) << boost::format("New index for vendor: %1%: current version: %2%, recommended version: %3%")
BOOST_LOG_TRIVIAL(debug) << boost::format("Got index for vendor: %1%: current version: %2%, recommended version: %3%")
% vendor.name
% vendor.config_version.to_string()
% recommended.to_string();
@ -352,20 +352,25 @@ Updates PresetUpdater::priv::get_config_updates() const
continue;
}
auto path_in_cache = cache_path / (idx.vendor() + ".ini");
if (! fs::exists(path_in_cache)) {
BOOST_LOG_TRIVIAL(warning) << "Index indicates update, but new bundle not found in cache: " << path_in_cache.string();
continue;
auto path_src = cache_path / (idx.vendor() + ".ini");
if (! fs::exists(path_src)) {
auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini");
if (! fs::exists(path_in_rsrc)) {
BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update, but bundle found in neither cache nor resources")
% idx.vendor();;
continue;
} else {
path_src = std::move(path_in_rsrc);
}
}
const auto cached_vp = VendorProfile::from_ini(path_in_cache, false);
if (cached_vp.config_version == recommended->config_version) {
updates.updates.emplace_back(std::move(path_in_cache), std::move(bundle_path), *recommended);
const auto new_vp = VendorProfile::from_ini(path_src, false);
if (new_vp.config_version == recommended->config_version) {
updates.updates.emplace_back(std::move(path_src), std::move(bundle_path), *recommended);
} else {
BOOST_LOG_TRIVIAL(warning) << boost::format("Vendor: %1%: Index indicates update (%2%) but cached bundle has a different version: %3%")
BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources")
% idx.vendor()
% recommended->config_version.to_string()
% cached_vp.config_version.to_string();
% recommended->config_version.to_string();
}
}
}

View file

@ -4,6 +4,7 @@
#include <xsinit.h>
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/Utils/ASCIIFolding.hpp"
#include "slic3r/Utils/FixModelByWin10.hpp"
#include "slic3r/Utils/Serial.hpp"
%}
@ -28,6 +29,9 @@ bool debugged()
void break_to_debugger()
%code{% Slic3r::GUI::break_to_debugger(); %};
bool is_windows10()
%code{% RETVAL=Slic3r::is_windows10(); %};
void set_wxapp(SV *ui)
%code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %};
@ -94,3 +98,6 @@ int get_export_option(SV *ui)
void desktop_open_datadir_folder()
%code%{ Slic3r::GUI::desktop_open_datadir_folder(); %};
void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst)
%code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %};

View file

@ -56,9 +56,13 @@
int volume_idx() const;
int instance_idx() const;
Clone<Pointf3> origin() const
%code%{ RETVAL = THIS->origin; %};
%code%{ RETVAL = THIS->get_origin(); %};
void translate(double x, double y, double z)
%code%{ THIS->origin.translate(x, y, z); %};
%code%{
Pointf3 o = THIS->get_origin();
o.translate(x, y, z);
THIS->set_origin(o);
%};
Clone<BoundingBoxf3> bounding_box() const
%code%{ RETVAL = THIS->bounding_box; %};
Clone<BoundingBoxf3> transformed_bounding_box() const;

View file

@ -54,7 +54,6 @@ _constant()
int region_volumes_count()
%code%{ RETVAL = THIS->region_volumes.size(); %};
Ref<Print> print();
Ref<ModelObject> model_object();
Ref<StaticPrintConfig> config()