diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 54f30c276..d0f003b17 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -44,16 +44,16 @@ use Wx::Event qw(EVT_IDLE EVT_COMMAND); use base 'Wx::App'; use constant FILE_WILDCARDS => { - known => 'Known files (*.stl, *.obj, *.amf, *.xml, *.prus)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prus;*.PRUS', + known => 'Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA', stl => 'STL files (*.stl)|*.stl;*.STL', obj => 'OBJ files (*.obj)|*.obj;*.OBJ', amf => 'AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML', - prus => 'PRUS files (*.prus)|*.prus;*.PRUS', + prusa => 'Prusa Control files (*.prusa)|*.prusa;*.PRUSA', ini => 'INI files *.ini|*.ini;*.INI', gcode => 'G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC', svg => 'SVG files *.svg|*.svg;*.SVG', }; -use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prus)}; +use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prusa)}; our $datadir; # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. @@ -370,7 +370,7 @@ sub open_model { || $Slic3r::GUI::Settings->{recent}{config_directory} || ''; - my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, 'Choose one or more files (STL/OBJ/AMF/PRUS):', $dir, "", + my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, 'Choose one or more files (STL/OBJ/AMF/PRUSA):', $dir, "", MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); if ($dialog->ShowModal != wxID_OK) { $dialog->Destroy; diff --git a/lib/Slic3r/GUI/BedShapeDialog.pm b/lib/Slic3r/GUI/BedShapeDialog.pm index 065f165fe..5e7a7008c 100644 --- a/lib/Slic3r/GUI/BedShapeDialog.pm +++ b/lib/Slic3r/GUI/BedShapeDialog.pm @@ -281,7 +281,7 @@ sub _init_shape_options_page { sub _load_stl { my ($self) = @_; - my $dialog = Wx::FileDialog->new($self, 'Choose a file to import bed shape from (STL/OBJ/AMF/PRUS):', "", "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + 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; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index d10a39bbe..30ec15e91 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -387,7 +387,7 @@ sub quick_slice { my $input_file; my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || ''; if (!$params{reslice}) { - my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUS):', $dir, "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):', $dir, "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if ($dialog->ShowModal != wxID_OK) { $dialog->Destroy; return; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 080ab7b89..1446c5023 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -2113,7 +2113,7 @@ sub OnDropFiles { @_ = (); # only accept STL, OBJ and AMF files - return 0 if grep !/\.(?:stl|obj|amf(?:\.xml)?|prus)$/i, @$filenames; + return 0 if grep !/\.(?:stl|obj|amf(?:\.xml)?|prusa)$/i, @$filenames; $self->{window}->load_file($_) for @$filenames; } diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 466303222..280688f84 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -12,7 +12,7 @@ sub read_from_file { my $model = $input_file =~ /\.stl$/i ? Slic3r::Model->load_stl(Slic3r::encode_path($input_file), basename($input_file)) : $input_file =~ /\.obj$/i ? Slic3r::Model->load_obj(Slic3r::encode_path($input_file), basename($input_file)) : $input_file =~ /\.amf(\.xml)?$/i ? Slic3r::Model->load_amf(Slic3r::encode_path($input_file)) - : $input_file =~ /\.prus$/i ? Slic3r::Model->load_prus(Slic3r::encode_path($input_file)) + : $input_file =~ /\.prusa$/i ? Slic3r::Model->load_prus(Slic3r::encode_path($input_file)) : die "Input file must have .stl, .obj or .amf(.xml) extension\n"; die "The supplied file couldn't be read because it's empty.\n" diff --git a/utils/zsh/functions/_slic3r b/utils/zsh/functions/_slic3r index 56a198ae9..cea887cc6 100644 --- a/utils/zsh/functions/_slic3r +++ b/utils/zsh/functions/_slic3r @@ -111,7 +111,7 @@ _arguments -S \ '--support-material-extrusion-width[specify extrusion width for support material]:support material extrusion width in mm or % of --layer-height' \ '--bridge-flow-ratio[specify multiplier for extrusion when bridging]:bridge extrusion multiplier' \ \ - '*:input file:_files -g "*.(#i)(stl|obj|amf|xml|prus)(-.)"' + '*:input file:_files -g "*.(#i)(stl|obj|amf|xml|prusa)(-.)"' # Local Variables: *** # mode:sh *** diff --git a/xs/src/libslic3r/Format/PRUS.cpp b/xs/src/libslic3r/Format/PRUS.cpp index d21a5537d..024610645 100644 --- a/xs/src/libslic3r/Format/PRUS.cpp +++ b/xs/src/libslic3r/Format/PRUS.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "../libslic3r.h" #include "../Model.hpp" @@ -29,6 +31,8 @@ struct StlHeader uint32_t nTriangles; }; +static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct"); + // Buffered line reader for the wxInputStream. class LineReader { @@ -89,7 +93,7 @@ public: } } - int next_line_scanf(char *format, ...) + int next_line_scanf(const char *format, ...) { const char *line = next_line(); if (line == nullptr) @@ -147,8 +151,13 @@ bool load_prus(const char *path, Model *model) char model_name_tag[1024]; sprintf(model_name_tag, "", name_utf8.data()); const char *model_xml = strstr(scene_xml_data.data(), model_name_tag); - float trafo[3][4] = { 0 }; - bool trafo_set = false; + const char *zero_tag = ""; + const char *zero_xml = strstr(scene_xml_data.data(), zero_tag); + float trafo[3][4] = { 0 }; + double instance_rotation = 0.; + double instance_scaling_factor = 1.f; + Pointf instance_offset(0., 0.); + bool trafo_set = false; if (model_xml != nullptr) { model_xml += strlen(model_name_tag); const char *position_tag = ""; @@ -157,27 +166,37 @@ bool load_prus(const char *path, Model *model) const char *rotation_xml = strstr(model_xml, rotation_tag); const char *scale_tag = ""; const char *scale_xml = strstr(model_xml, scale_tag); - float position[3], rotation[3][3], scale[3][3]; - if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && + float position[3], rotation[3], scale[3], zero[3]; + if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && zero_xml != nullptr && sscanf(position_xml+strlen(position_tag), "[%f, %f, %f]", position, position+1, position+2) == 3 && sscanf(rotation_xml+strlen(rotation_tag), - "[[%f, %f, %f], [%f, %f, %f], [%f, %f, %f]]", - rotation[0], rotation[0]+1, rotation[0]+2, - rotation[1], rotation[1]+1, rotation[1]+2, - rotation[2], rotation[2]+1, rotation[2]+2) == 9 && - sscanf(scale_xml+strlen(scale_tag), - "[[%f, %f, %f], [%f, %f, %f], [%f, %f, %f]]", - scale[0], scale[0]+1, scale[0]+2, - scale[1], scale[1]+1, scale[1]+2, - scale[2], scale[2]+1, scale[2]+2) == 9) { - for (size_t r = 0; r < 3; ++ r) { - for (size_t c = 0; c < 3; ++ c) { - for (size_t i = 0; i < 3; ++ i) - trafo[r][c] += rotation[r][i]*scale[i][c]; - } - trafo[r][3] = position[r]; + "[%f, %f, %f]", rotation, rotation+1, rotation+2) == 3 && + sscanf(scale_xml+strlen(scale_tag), + "[%f, %f, %f]", scale, scale+1, scale+2) == 3 && + sscanf(zero_xml+strlen(zero_tag), + "[%f, %f, %f]", zero, zero+1, zero+2) == 3) { + if (scale[0] == scale[1] && scale[1] == scale[2]) { + instance_scaling_factor = scale[0]; + scale[0] = scale[1] = scale[2] = 1.; } + if (rotation[0] == 0. && rotation[1] == 0.) { + instance_rotation = - rotation[2]; + rotation[2] = 0.; + } + Eigen::Matrix3f mat_rot, mat_scale, mat_trafo; + mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) * + Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) * + Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX()); + mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]); + mat_trafo = mat_rot * mat_scale; + for (size_t r = 0; r < 3; ++ r) { + for (size_t c = 0; c < 3; ++ c) + trafo[r][c] += mat_trafo(r, c); + } + instance_offset.x = position[0] - zero[0]; + instance_offset.y = position[1] - zero[1]; + trafo[2][3] = position[2] / instance_scaling_factor; trafo_set = true; } } @@ -197,15 +216,27 @@ bool load_prus(const char *path, Model *model) stl.stats.number_of_facets = header.nTriangles; stl.stats.original_num_facets = header.nTriangles; stl_allocate(&stl); - if (zip.ReadAll((void*)stl.facet_start, 50 * header.nTriangles)) { + if (header.nTriangles > 0 && zip.ReadAll((void*)stl.facet_start, 50 * header.nTriangles)) { + if (sizeof(stl_facet) > SIZEOF_STL_FACET) { + // The stl.facet_start is not packed tightly. Unpack the array of stl_facets. + unsigned char *data = (unsigned char*)stl.facet_start; + for (size_t i = header.nTriangles - 1; i > 0; -- i) + memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET); + } // All the faces have been read. stl_get_size(&stl); mesh.repair(); // Transform the model. stl_transform(&stl, &trafo[0][0]); + if (std::abs(stl.stats.min.z) < EPSILON) + stl.stats.min.z = 0.; // Add a mesh to a model. if (mesh.facets_count() > 0) { - model->add_object(name_utf8.data(), path, std::move(mesh)); + ModelObject *object = model->add_object(name_utf8.data(), path, std::move(mesh)); + ModelInstance *instance = object->add_instance(); + instance->rotation = instance_rotation; + instance->scaling_factor = instance_scaling_factor; + instance->offset = instance_offset; ++ num_models; } }