Faster gizmos update
This commit is contained in:
commit
550f6e307f
40 changed files with 1415 additions and 753 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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.)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
402
xs/src/slic3r/Utils/FixModelByWin10.cpp
Normal file
402
xs/src/slic3r/Utils/FixModelByWin10.cpp
Normal 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 */
|
26
xs/src/slic3r/Utils/FixModelByWin10.hpp
Normal file
26
xs/src/slic3r/Utils/FixModelByWin10.hpp
Normal 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_ */
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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); %};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue