Merge branch 'master' of https://github.com/prusa3d/Slic3r
This commit is contained in:
commit
a71f05cdc0
12 changed files with 221 additions and 100 deletions
|
@ -556,19 +556,9 @@ std::string Model::propose_export_file_name_and_path() const
|
||||||
for (const ModelObject *model_object : this->objects)
|
for (const ModelObject *model_object : this->objects)
|
||||||
for (ModelInstance *model_instance : model_object->instances)
|
for (ModelInstance *model_instance : model_object->instances)
|
||||||
if (model_instance->is_printable()) {
|
if (model_instance->is_printable()) {
|
||||||
input_file = model_object->input_file;
|
input_file = model_object->get_export_filename();
|
||||||
if (! model_object->name.empty()) {
|
|
||||||
if (input_file.empty())
|
if (!input_file.empty())
|
||||||
// model_object->input_file was empty, just use model_object->name
|
|
||||||
input_file = model_object->name;
|
|
||||||
else {
|
|
||||||
// Replace file name in input_file with model_object->name, but keep the path and file extension.
|
|
||||||
input_file = (boost::filesystem::path(model_object->name).parent_path().empty()) ?
|
|
||||||
(boost::filesystem::path(input_file).parent_path() / model_object->name).make_preferred().string() :
|
|
||||||
model_object->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (! input_file.empty())
|
|
||||||
goto end;
|
goto end;
|
||||||
// Other instances will produce the same name, skip them.
|
// Other instances will produce the same name, skip them.
|
||||||
break;
|
break;
|
||||||
|
@ -1433,6 +1423,26 @@ void ModelObject::print_info() const
|
||||||
cout << "volume = " << mesh.volume() << endl;
|
cout << "volume = " << mesh.volume() << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ModelObject::get_export_filename() const
|
||||||
|
{
|
||||||
|
std::string ret = input_file;
|
||||||
|
|
||||||
|
if (!name.empty())
|
||||||
|
{
|
||||||
|
if (ret.empty())
|
||||||
|
// input_file was empty, just use name
|
||||||
|
ret = name;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Replace file name in input_file with name, but keep the path and file extension.
|
||||||
|
ret = (boost::filesystem::path(name).parent_path().empty()) ?
|
||||||
|
(boost::filesystem::path(ret).parent_path() / name).make_preferred().string() : name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void ModelVolume::set_material_id(t_model_material_id material_id)
|
void ModelVolume::set_material_id(t_model_material_id material_id)
|
||||||
{
|
{
|
||||||
m_material_id = material_id;
|
m_material_id = material_id;
|
||||||
|
|
|
@ -275,6 +275,8 @@ public:
|
||||||
// Print object statistics to console.
|
// Print object statistics to console.
|
||||||
void print_info() const;
|
void print_info() const;
|
||||||
|
|
||||||
|
std::string get_export_filename() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Print;
|
friend class Print;
|
||||||
friend class SLAPrint;
|
friend class SLAPrint;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "GCode/WipeTowerPrusaMM.hpp"
|
#include "GCode/WipeTowerPrusaMM.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
#include "PrintExport.hpp"
|
//#include "PrintExport.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include "Rasterizer/Rasterizer.hpp"
|
#include "Rasterizer/Rasterizer.hpp"
|
||||||
//#include <tbb/parallel_for.h>
|
//#include <tbb/parallel_for.h>
|
||||||
|
@ -72,7 +73,8 @@ public:
|
||||||
void finish_layer();
|
void finish_layer();
|
||||||
|
|
||||||
// Save all the layers into the file (or dir) specified in the path argument
|
// Save all the layers into the file (or dir) specified in the path argument
|
||||||
void save(const std::string& path);
|
// An optional project name can be added to be used for the layer file names
|
||||||
|
void save(const std::string& path, const std::string& projectname = "");
|
||||||
|
|
||||||
// Save only the selected layer to the file specified in path argument.
|
// Save only the selected layer to the file specified in path argument.
|
||||||
void save_layer(unsigned lyr, const std::string& path);
|
void save_layer(unsigned lyr, const std::string& path);
|
||||||
|
@ -86,7 +88,8 @@ template<class T = void> struct VeryFalse { static const bool value = false; };
|
||||||
template<class Fmt> class LayerWriter {
|
template<class Fmt> class LayerWriter {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
LayerWriter(const std::string& /*zipfile_path*/) {
|
LayerWriter(const std::string& /*zipfile_path*/)
|
||||||
|
{
|
||||||
static_assert(VeryFalse<Fmt>::value,
|
static_assert(VeryFalse<Fmt>::value,
|
||||||
"No layer writer implementation provided!");
|
"No layer writer implementation provided!");
|
||||||
}
|
}
|
||||||
|
@ -99,10 +102,6 @@ public:
|
||||||
void binary_entry(const std::string& /*fname*/,
|
void binary_entry(const std::string& /*fname*/,
|
||||||
const std::uint8_t* buf, size_t len);
|
const std::uint8_t* buf, size_t len);
|
||||||
|
|
||||||
// Get the name of the archive but only the name part without the path or
|
|
||||||
// the extension.
|
|
||||||
std::string get_name() { return ""; }
|
|
||||||
|
|
||||||
// Test whether the object can still be used for writing.
|
// Test whether the object can still be used for writing.
|
||||||
bool is_ok() { return false; }
|
bool is_ok() { return false; }
|
||||||
|
|
||||||
|
@ -253,12 +252,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class LyrFmt>
|
template<class LyrFmt>
|
||||||
inline void save(const std::string& path) {
|
inline void save(const std::string& fpath, const std::string& prjname = "")
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
LayerWriter<LyrFmt> writer(path);
|
LayerWriter<LyrFmt> writer(fpath);
|
||||||
if(!writer.is_ok()) return;
|
if(!writer.is_ok()) return;
|
||||||
|
|
||||||
std::string project = writer.get_name();
|
std::string project = prjname.empty()?
|
||||||
|
boost::filesystem::path(fpath).stem().string() : prjname;
|
||||||
|
|
||||||
writer.next_entry("config.ini");
|
writer.next_entry("config.ini");
|
||||||
if(!writer.is_ok()) return;
|
if(!writer.is_ok()) return;
|
||||||
|
|
|
@ -320,10 +320,8 @@ struct SLAPrintStatistics
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SLAminzZipper {};
|
|
||||||
|
|
||||||
// The implementation of creating zipped archives with wxWidgets
|
// The implementation of creating zipped archives with wxWidgets
|
||||||
template<> class LayerWriter<SLAminzZipper> {
|
template<> class LayerWriter<Zipper> {
|
||||||
Zipper m_zip;
|
Zipper m_zip;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -332,16 +330,12 @@ public:
|
||||||
void next_entry(const std::string& fname) { m_zip.add_entry(fname); }
|
void next_entry(const std::string& fname) { m_zip.add_entry(fname); }
|
||||||
|
|
||||||
void binary_entry(const std::string& fname,
|
void binary_entry(const std::string& fname,
|
||||||
const std::uint8_t* buf,
|
const std::uint8_t* buf,
|
||||||
size_t l)
|
size_t l)
|
||||||
{
|
{
|
||||||
m_zip.add_entry(fname, buf, l);
|
m_zip.add_entry(fname, buf, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_name() const {
|
|
||||||
return m_zip.get_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T> inline LayerWriter& operator<<(T&& arg) {
|
template<class T> inline LayerWriter& operator<<(T&& arg) {
|
||||||
m_zip << std::forward<T>(arg); return *this;
|
m_zip << std::forward<T>(arg); return *this;
|
||||||
}
|
}
|
||||||
|
@ -389,9 +383,11 @@ public:
|
||||||
// Returns true if the last step was finished with success.
|
// Returns true if the last step was finished with success.
|
||||||
bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
|
bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
|
||||||
|
|
||||||
template<class Fmt = SLAminzZipper>
|
template<class Fmt = Zipper>
|
||||||
void export_raster(const std::string& fname) {
|
inline void export_raster(const std::string& fpath,
|
||||||
if(m_printer) m_printer->save<Fmt>(fname);
|
const std::string& projectname = "")
|
||||||
|
{
|
||||||
|
if(m_printer) m_printer->save<Fmt>(fpath, projectname);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PrintObjects& objects() const { return m_objects; }
|
const PrintObjects& objects() const { return m_objects; }
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#include "Zipper.hpp"
|
#include "Zipper.hpp"
|
||||||
#include "miniz/miniz_zip.h"
|
#include "miniz/miniz_zip.h"
|
||||||
#include <boost/filesystem/path.hpp>
|
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
@ -213,10 +212,6 @@ void Zipper::finish_entry()
|
||||||
m_entry.clear();
|
m_entry.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Zipper::get_name() const {
|
|
||||||
return boost::filesystem::path(m_impl->m_zipname).stem().string();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Zipper::finalize()
|
void Zipper::finalize()
|
||||||
{
|
{
|
||||||
finish_entry();
|
finish_entry();
|
||||||
|
|
|
@ -81,9 +81,6 @@ public:
|
||||||
/// file is up to minz after the erroneous write.
|
/// file is up to minz after the erroneous write.
|
||||||
void finish_entry();
|
void finish_entry();
|
||||||
|
|
||||||
/// Gets the name of the archive without the path or extension.
|
|
||||||
std::string get_name() const;
|
|
||||||
|
|
||||||
void finalize();
|
void finalize();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -397,8 +397,9 @@ int CLI::run(int argc, char **argv)
|
||||||
outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
|
outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
|
||||||
} else {
|
} else {
|
||||||
outfile = sla_print.output_filepath(outfile);
|
outfile = sla_print.output_filepath(outfile);
|
||||||
sla_print.export_raster(outfile);
|
// We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata
|
||||||
outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
|
outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
|
||||||
|
sla_print.export_raster(outfile_final);
|
||||||
}
|
}
|
||||||
if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
|
if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
|
||||||
boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
|
boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
|
||||||
|
|
|
@ -98,8 +98,9 @@ void BackgroundSlicingProcess::process_sla()
|
||||||
m_print->process();
|
m_print->process();
|
||||||
if (this->set_step_started(bspsGCodeFinalize)) {
|
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||||
if (! m_export_path.empty()) {
|
if (! m_export_path.empty()) {
|
||||||
m_sla_print->export_raster(m_export_path);
|
const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path);
|
||||||
m_print->set_status(100, "Masked SLA file exported to " + m_export_path);
|
m_sla_print->export_raster(export_path);
|
||||||
|
m_print->set_status(100, "Masked SLA file exported to " + export_path);
|
||||||
} else if (! m_upload_job.empty()) {
|
} else if (! m_upload_job.empty()) {
|
||||||
prepare_upload();
|
prepare_upload();
|
||||||
} else {
|
} else {
|
||||||
|
@ -389,7 +390,7 @@ void BackgroundSlicingProcess::prepare_upload()
|
||||||
|
|
||||||
// Generate a unique temp path to which the gcode/zip file is copied/exported
|
// Generate a unique temp path to which the gcode/zip file is copied/exported
|
||||||
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
|
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
|
||||||
/ boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode");
|
/ boost::filesystem::unique_path(".Slic3rPE.upload.%%%%-%%%%-%%%%-%%%%");
|
||||||
|
|
||||||
if (m_print == m_fff_print) {
|
if (m_print == m_fff_print) {
|
||||||
m_print->set_status(95, "Running post-processing scripts");
|
m_print->set_status(95, "Running post-processing scripts");
|
||||||
|
@ -399,8 +400,8 @@ void BackgroundSlicingProcess::prepare_upload()
|
||||||
run_post_process_scripts(source_path.string(), m_fff_print->config());
|
run_post_process_scripts(source_path.string(), m_fff_print->config());
|
||||||
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||||
} else {
|
} else {
|
||||||
m_sla_print->export_raster(source_path.string());
|
m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||||
// TODO: Also finalize upload path like with FFF when there are statistics for SLA print
|
m_sla_print->export_raster(source_path.string(), m_upload_job.upload_data.upload_path.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
|
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
|
||||||
|
|
|
@ -1025,7 +1025,7 @@ void ObjectList::append_menu_item_fix_through_netfabb(wxMenu* menu)
|
||||||
|
|
||||||
void ObjectList::append_menu_item_export_stl(wxMenu* menu) const
|
void ObjectList::append_menu_item_export_stl(wxMenu* menu) const
|
||||||
{
|
{
|
||||||
append_menu_item(menu, wxID_ANY, _(L("Export object as STL")) + dots, "",
|
append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, "",
|
||||||
[](wxCommandEvent&) { wxGetApp().plater()->export_stl(true); }, "", menu);
|
[](wxCommandEvent&) { wxGetApp().plater()->export_stl(true); }, "", menu);
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
}
|
}
|
||||||
|
@ -1101,6 +1101,7 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
|
||||||
#endif // __WXOSX__
|
#endif // __WXOSX__
|
||||||
|
|
||||||
append_menu_item_fix_through_netfabb(menu);
|
append_menu_item_fix_through_netfabb(menu);
|
||||||
|
append_menu_item_export_stl(menu);
|
||||||
|
|
||||||
m_menu_item_split_part = append_menu_item_split(menu);
|
m_menu_item_split_part = append_menu_item_split(menu);
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,6 @@ namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// GLGizmoCut
|
|
||||||
|
|
||||||
class GLGizmoCutPanel : public wxPanel
|
class GLGizmoCutPanel : public wxPanel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -192,7 +186,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co
|
||||||
m_imgui->set_next_window_bg_alpha(0.5f);
|
m_imgui->set_next_window_bg_alpha(0.5f);
|
||||||
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||||
|
|
||||||
ImGui::PushItemWidth(100.0f);
|
ImGui::PushItemWidth(m_imgui->scaled(5.0f));
|
||||||
bool _value_changed = ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
|
bool _value_changed = ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
|
||||||
|
|
||||||
m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper);
|
m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper);
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
#include "libslic3r/SLA/SLARotfinder.hpp"
|
#include "libslic3r/SLA/SLARotfinder.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
|
|
||||||
|
#include "libnest2d/optimizers/nlopt/genetic.hpp"
|
||||||
|
|
||||||
#include "GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
|
@ -1199,7 +1201,7 @@ struct Plater::priv
|
||||||
BoundingBox scaled_bed_shape_bb() const;
|
BoundingBox scaled_bed_shape_bb() const;
|
||||||
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config);
|
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config);
|
||||||
std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects);
|
std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects);
|
||||||
std::unique_ptr<CheckboxFileDialog> get_export_file(GUI::FileType file_type);
|
wxString get_export_file(GUI::FileType file_type);
|
||||||
|
|
||||||
const Selection& get_selection() const;
|
const Selection& get_selection() const;
|
||||||
Selection& get_selection();
|
Selection& get_selection();
|
||||||
|
@ -1617,6 +1619,45 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||||
}
|
}
|
||||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
}
|
}
|
||||||
|
else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf))
|
||||||
|
{
|
||||||
|
bool advanced = false;
|
||||||
|
for (const ModelObject* model_object : model.objects)
|
||||||
|
{
|
||||||
|
// is there more than one instance ?
|
||||||
|
if (model_object->instances.size() > 1)
|
||||||
|
{
|
||||||
|
advanced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is there any modifier ?
|
||||||
|
for (const ModelVolume* model_volume : model_object->volumes)
|
||||||
|
{
|
||||||
|
if (!model_volume->is_model_part())
|
||||||
|
{
|
||||||
|
advanced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advanced)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advanced)
|
||||||
|
{
|
||||||
|
wxMessageDialog dlg(q, _(L("This file cannot be loaded in simple mode. Do you want to switch to expert mode?\n")),
|
||||||
|
_(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO);
|
||||||
|
if (dlg.ShowModal() == wxID_YES)
|
||||||
|
{
|
||||||
|
Slic3r::GUI::wxGetApp().save_mode(comExpert);
|
||||||
|
view3D->set_as_dirty();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return obj_idxs;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
|
||||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
@ -1642,7 +1683,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||||
Slic3r::GUI::show_error(nullptr,
|
Slic3r::GUI::show_error(nullptr,
|
||||||
wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")),
|
wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")),
|
||||||
from_path(filename)));
|
from_path(filename)));
|
||||||
return std::vector<size_t>();
|
return obj_idxs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1784,7 +1825,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
||||||
return obj_idxs;
|
return obj_idxs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CheckboxFileDialog> Plater::priv::get_export_file(GUI::FileType file_type)
|
wxString Plater::priv::get_export_file(GUI::FileType file_type)
|
||||||
{
|
{
|
||||||
wxString wildcard;
|
wxString wildcard;
|
||||||
switch (file_type) {
|
switch (file_type) {
|
||||||
|
@ -1801,34 +1842,56 @@ std::unique_ptr<CheckboxFileDialog> Plater::priv::get_export_file(GUI::FileType
|
||||||
|
|
||||||
// Update printbility state of each of the ModelInstances.
|
// Update printbility state of each of the ModelInstances.
|
||||||
this->update_print_volume_state();
|
this->update_print_volume_state();
|
||||||
// Find the file name of the first printable object.
|
|
||||||
fs::path output_file = this->model.propose_export_file_name_and_path();
|
|
||||||
|
|
||||||
|
const Selection& selection = get_selection();
|
||||||
|
int obj_idx = selection.get_object_idx();
|
||||||
|
|
||||||
|
fs::path output_file;
|
||||||
|
// first try to get the file name from the current selection
|
||||||
|
if ((0 <= obj_idx) && (obj_idx < (int)this->model.objects.size()))
|
||||||
|
output_file = this->model.objects[obj_idx]->get_export_filename();
|
||||||
|
|
||||||
|
if (output_file.empty())
|
||||||
|
// Find the file name of the first printable object.
|
||||||
|
output_file = this->model.propose_export_file_name_and_path();
|
||||||
|
|
||||||
|
wxString dlg_title;
|
||||||
switch (file_type) {
|
switch (file_type) {
|
||||||
case FT_STL: output_file.replace_extension("stl"); break;
|
case FT_STL:
|
||||||
case FT_AMF: output_file.replace_extension("zip.amf"); break; // XXX: Problem on OS X with double extension?
|
{
|
||||||
case FT_3MF: output_file.replace_extension("3mf"); break;
|
output_file.replace_extension("stl");
|
||||||
|
dlg_title = _(L("Export STL file:"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_AMF:
|
||||||
|
{
|
||||||
|
// XXX: Problem on OS X with double extension?
|
||||||
|
output_file.replace_extension("zip.amf");
|
||||||
|
dlg_title = _(L("Export AMF file:"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_3MF:
|
||||||
|
{
|
||||||
|
output_file.replace_extension("3mf");
|
||||||
|
dlg_title = _(L("Save file as:"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dlg = Slic3r::make_unique<CheckboxFileDialog>(q,
|
wxFileDialog* dlg = new wxFileDialog(q, dlg_title,
|
||||||
((file_type == FT_AMF) || (file_type == FT_3MF)) ? _(L("Export print config")) : "",
|
from_path(output_file.parent_path()), from_path(output_file.filename()),
|
||||||
true,
|
wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
_(L("Save file as:")),
|
|
||||||
from_path(output_file.parent_path()),
|
|
||||||
from_path(output_file.filename()),
|
|
||||||
wildcard,
|
|
||||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
|
|
||||||
);
|
|
||||||
|
|
||||||
if (dlg->ShowModal() != wxID_OK) {
|
if (dlg->ShowModal() != wxID_OK) {
|
||||||
return nullptr;
|
return wxEmptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path path(into_path(dlg->GetPath()));
|
wxString out_path = dlg->GetPath();
|
||||||
|
fs::path path(into_path(out_path));
|
||||||
wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
|
wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
|
||||||
|
|
||||||
return dlg;
|
return out_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Selection& Plater::priv::get_selection() const
|
const Selection& Plater::priv::get_selection() const
|
||||||
|
@ -2039,7 +2102,9 @@ void Plater::priv::sla_optimize_rotation() {
|
||||||
rotoptimizing.store(true);
|
rotoptimizing.store(true);
|
||||||
|
|
||||||
int obj_idx = get_selected_object_idx();
|
int obj_idx = get_selected_object_idx();
|
||||||
ModelObject * o = model.objects[obj_idx];
|
if(obj_idx < 0) { rotoptimizing.store(false); return; }
|
||||||
|
|
||||||
|
ModelObject * o = model.objects[size_t(obj_idx)];
|
||||||
|
|
||||||
background_process.stop();
|
background_process.stop();
|
||||||
|
|
||||||
|
@ -2047,7 +2112,7 @@ void Plater::priv::sla_optimize_rotation() {
|
||||||
statusbar()->set_range(100);
|
statusbar()->set_range(100);
|
||||||
|
|
||||||
auto stfn = [this] (unsigned st, const std::string& msg) {
|
auto stfn = [this] (unsigned st, const std::string& msg) {
|
||||||
statusbar()->set_progress(st);
|
statusbar()->set_progress(int(st));
|
||||||
statusbar()->set_status_text(msg);
|
statusbar()->set_status_text(msg);
|
||||||
|
|
||||||
// could be problematic, but we need the cancel button.
|
// could be problematic, but we need the cancel button.
|
||||||
|
@ -2065,8 +2130,59 @@ void Plater::priv::sla_optimize_rotation() {
|
||||||
[this](){ return !rotoptimizing.load(); }
|
[this](){ return !rotoptimizing.load(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
|
||||||
|
assert(bed_shape_opt);
|
||||||
|
|
||||||
|
auto& bedpoints = bed_shape_opt->values;
|
||||||
|
Polyline bed; bed.points.reserve(bedpoints.size());
|
||||||
|
for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||||
|
|
||||||
|
double mindist = 6.0; // FIXME
|
||||||
|
double offs = mindist / 2.0 - EPSILON;
|
||||||
|
|
||||||
if(rotoptimizing.load()) // wasn't canceled
|
if(rotoptimizing.load()) // wasn't canceled
|
||||||
for(ModelInstance * oi : o->instances) oi->set_rotation({r[X], r[Y], r[Z]});
|
for(ModelInstance * oi : o->instances) {
|
||||||
|
oi->set_rotation({r[X], r[Y], r[Z]});
|
||||||
|
|
||||||
|
auto trchull = o->convex_hull_2d(oi->get_transformation().get_matrix());
|
||||||
|
|
||||||
|
namespace opt = libnest2d::opt;
|
||||||
|
opt::StopCriteria stopcr;
|
||||||
|
stopcr.relative_score_difference = 0.01;
|
||||||
|
stopcr.max_iterations = 10000;
|
||||||
|
stopcr.stop_score = 0.0;
|
||||||
|
opt::GeneticOptimizer solver(stopcr);
|
||||||
|
Polygon pbed(bed);
|
||||||
|
|
||||||
|
auto bin = pbed.bounding_box();
|
||||||
|
double binw = bin.size()(X) * SCALING_FACTOR - offs;
|
||||||
|
double binh = bin.size()(Y) * SCALING_FACTOR - offs;
|
||||||
|
|
||||||
|
auto result = solver.optimize_min([&trchull, binw, binh](double rot){
|
||||||
|
auto chull = trchull;
|
||||||
|
chull.rotate(rot);
|
||||||
|
|
||||||
|
auto bb = chull.bounding_box();
|
||||||
|
double bbw = bb.size()(X) * SCALING_FACTOR;
|
||||||
|
double bbh = bb.size()(Y) * SCALING_FACTOR;
|
||||||
|
|
||||||
|
auto wdiff = bbw - binw;
|
||||||
|
auto hdiff = bbh - binh;
|
||||||
|
double diff = 0;
|
||||||
|
if(wdiff < 0 && hdiff < 0) diff = wdiff + hdiff;
|
||||||
|
if(wdiff > 0) diff += wdiff;
|
||||||
|
if(hdiff > 0) diff += hdiff;
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}, opt::initvals(0.0), opt::bound(-PI/2, PI/2));
|
||||||
|
|
||||||
|
double r = std::get<0>(result.optimum);
|
||||||
|
|
||||||
|
Vec3d rt = oi->get_rotation(); rt(Z) += r;
|
||||||
|
oi->set_rotation(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr::find_new_position(model, o->instances, coord_t(mindist/SCALING_FACTOR), bed);
|
||||||
|
|
||||||
// Correct the z offset of the object which was corrupted be the rotation
|
// Correct the z offset of the object which was corrupted be the rotation
|
||||||
o->ensure_on_bed();
|
o->ensure_on_bed();
|
||||||
|
@ -2714,7 +2830,10 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
|
||||||
if (is_part) {
|
if (is_part) {
|
||||||
item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
|
item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
|
||||||
[this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png");
|
[this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png");
|
||||||
} else {
|
|
||||||
|
sidebar->obj_list()->append_menu_item_export_stl(menu);
|
||||||
|
}
|
||||||
|
else {
|
||||||
wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies")) + "\t+", _(L("Place one more copy of the selected object")),
|
wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies")) + "\t+", _(L("Place one more copy of the selected object")),
|
||||||
[this](wxCommandEvent&) { q->increase_instances(); }, "add.png");
|
[this](wxCommandEvent&) { q->increase_instances(); }, "add.png");
|
||||||
wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies")) + "\t-", _(L("Remove one copy of the selected object")),
|
wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies")) + "\t-", _(L("Remove one copy of the selected object")),
|
||||||
|
@ -2747,8 +2866,9 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
|
||||||
|
|
||||||
append_menu_item(menu, wxID_ANY, _(L("Export object as STL")) + dots, _(L("Export this single object as STL file")),
|
append_menu_item(menu, wxID_ANY, _(L("Export object as STL")) + dots, _(L("Export this single object as STL file")),
|
||||||
[this](wxCommandEvent&) { q->export_stl(true); });
|
[this](wxCommandEvent&) { q->export_stl(true); });
|
||||||
|
|
||||||
|
menu->AppendSeparator();
|
||||||
}
|
}
|
||||||
menu->AppendSeparator();
|
|
||||||
|
|
||||||
sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
|
sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
|
||||||
|
|
||||||
|
@ -3243,11 +3363,8 @@ void Plater::export_stl(bool selection_only)
|
||||||
{
|
{
|
||||||
if (p->model.objects.empty()) { return; }
|
if (p->model.objects.empty()) { return; }
|
||||||
|
|
||||||
auto dialog = p->get_export_file(FT_STL);
|
wxString path = p->get_export_file(FT_STL);
|
||||||
if (! dialog) { return; }
|
if (path.empty()) { return; }
|
||||||
|
|
||||||
// Store a binary STL
|
|
||||||
const wxString path = dialog->GetPath();
|
|
||||||
const std::string path_u8 = into_u8(path);
|
const std::string path_u8 = into_u8(path);
|
||||||
|
|
||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
|
@ -3259,8 +3376,17 @@ void Plater::export_stl(bool selection_only)
|
||||||
|
|
||||||
const auto obj_idx = selection.get_object_idx();
|
const auto obj_idx = selection.get_object_idx();
|
||||||
if (obj_idx == -1) { return; }
|
if (obj_idx == -1) { return; }
|
||||||
mesh = p->model.objects[obj_idx]->mesh();
|
|
||||||
} else {
|
if (selection.get_mode() == Selection::Instance)
|
||||||
|
mesh = p->model.objects[obj_idx]->mesh();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||||
|
mesh = p->model.objects[obj_idx]->volumes[volume->volume_idx()]->mesh;
|
||||||
|
mesh.transform(volume->get_volume_transformation().get_matrix());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
mesh = p->model.mesh();
|
mesh = p->model.mesh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3272,15 +3398,14 @@ void Plater::export_amf()
|
||||||
{
|
{
|
||||||
if (p->model.objects.empty()) { return; }
|
if (p->model.objects.empty()) { return; }
|
||||||
|
|
||||||
auto dialog = p->get_export_file(FT_AMF);
|
wxString path = p->get_export_file(FT_AMF);
|
||||||
if (! dialog) { return; }
|
if (path.empty()) { return; }
|
||||||
|
|
||||||
const wxString path = dialog->GetPath();
|
|
||||||
const std::string path_u8 = into_u8(path);
|
const std::string path_u8 = into_u8(path);
|
||||||
|
|
||||||
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
|
||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
if (Slic3r::store_amf(path_u8.c_str(), &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) {
|
bool export_config = true;
|
||||||
|
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||||
|
if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) {
|
||||||
// Success
|
// Success
|
||||||
p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path));
|
p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path));
|
||||||
} else {
|
} else {
|
||||||
|
@ -3297,10 +3422,8 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||||
bool export_config = true;
|
bool export_config = true;
|
||||||
if (output_path.empty())
|
if (output_path.empty())
|
||||||
{
|
{
|
||||||
auto dialog = p->get_export_file(FT_3MF);
|
path = p->get_export_file(FT_3MF);
|
||||||
if (!dialog) { return; }
|
if (path.empty()) { return; }
|
||||||
path = dialog->GetPath();
|
|
||||||
export_config = dialog->get_checkbox_value();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
path = from_path(output_path);
|
path = from_path(output_path);
|
||||||
|
|
Loading…
Add table
Reference in a new issue