From 696ade15ca22b746d52386caba01937f179b079e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 13 May 2019 18:58:56 +0200 Subject: [PATCH 01/44] New way of starting arrange and rotation optimization. To prevent segfaults when exiting while processing is running. --- src/libslic3r/SLA/SLARotfinder.cpp | 4 +- src/slic3r/GUI/MainFrame.cpp | 6 +- src/slic3r/GUI/MainFrame.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 279 +++++++++++++++++---------- src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/ProgressStatusBar.cpp | 5 + src/slic3r/GUI/ProgressStatusBar.hpp | 1 + 7 files changed, 191 insertions(+), 109 deletions(-) diff --git a/src/libslic3r/SLA/SLARotfinder.cpp b/src/libslic3r/SLA/SLARotfinder.cpp index 1a91041b7..2e64059d6 100644 --- a/src/libslic3r/SLA/SLARotfinder.cpp +++ b/src/libslic3r/SLA/SLARotfinder.cpp @@ -44,7 +44,7 @@ std::array find_best_rotation(const ModelObject& modelobj, // call the status callback in each iteration but the actual value may be // the same for subsequent iterations (status goes from 0 to 100 but // iterations can be many more) - auto objfunc = [&emesh, &status, &statuscb, max_tries] + auto objfunc = [&emesh, &status, &statuscb, &stopcond, max_tries] (double rx, double ry, double rz) { EigenMesh3D& m = emesh; @@ -91,7 +91,7 @@ std::array find_best_rotation(const ModelObject& modelobj, } // report status - statuscb( unsigned(++status * 100.0/max_tries) ); + if(!stopcond()) statuscb( unsigned(++status * 100.0/max_tries) ); return score; }; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 128aba16d..7e897f9a0 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -54,7 +54,7 @@ DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_BUILD_ID) + " " + _(L("based on Slic3r" #endif // _WIN32 // initialize status bar - m_statusbar = new ProgressStatusBar(this); + m_statusbar.reset(new ProgressStatusBar(this)); m_statusbar->embed(this); m_statusbar->set_status_text(_(L("Version")) + " " + SLIC3R_VERSION + @@ -107,6 +107,8 @@ DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_BUILD_ID) + " " + _(L("based on Slic3r" // Also the application closes much faster without these unnecessary screen refreshes. // In addition, there were some crashes due to the Paint events sent to already destructed windows. this->Show(false); + + if(m_plater) m_plater->stop_jobs(); // Save the slic3r.ini.Usually the ini file is saved from "on idle" callback, // but in rare cases it may not have been called yet. @@ -136,6 +138,8 @@ DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_BUILD_ID) + " " + _(L("based on Slic3r" update_ui_from_settings(); // FIXME (?) } +MainFrame::~MainFrame() {} + void MainFrame::init_tabpanel() { diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index f3d582681..321fbe61b 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -86,7 +86,7 @@ protected: public: MainFrame(); - ~MainFrame() {} + ~MainFrame(); Plater* plater() { return m_plater; } @@ -121,7 +121,7 @@ public: Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; - ProgressStatusBar* m_statusbar { nullptr }; + std::unique_ptr m_statusbar; }; } // GUI diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fd29bcfa8..f485c4016 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5,6 +5,8 @@ #include #include #include +#include + #include #include #include @@ -1219,8 +1221,107 @@ struct Plater::priv wxString project_filename; BackgroundSlicingProcess background_process; - bool arranging; - bool rotoptimizing; + + class Job: public wxEvtHandler { + int m_range = 100; + std::future m_ftr; + priv *m_plater = nullptr; + std::atomic m_running {false}, m_canceled {false}; + + void run() { + m_running.store(true); process(); m_running.store(false); + + // ensure to call the last status to finalize the job + update_status(status_range(), ""); + } + + protected: + + // status range for a particular job + virtual int status_range() const { return 100; } + + void update_status(int st, const wxString& msg = "") { + auto evt = new wxThreadEvent(); evt->SetInt(st); evt->SetString(msg); + wxQueueEvent(this, evt); + } + + priv& plater() { return *m_plater; } + bool was_canceled() const { return m_canceled.load(); } + + public: + + Job(priv *_plater): m_plater(_plater) { + Bind(wxEVT_THREAD, [this](const wxThreadEvent& evt){ + auto msg = evt.GetString(); + if(! msg.empty()) plater().statusbar()->set_status_text(msg); + + if(! m_range) return; + + plater().statusbar()->set_progress(evt.GetInt()); + if(evt.GetInt() == status_range()) { + + // set back the original range and cancel callback + plater().statusbar()->set_range(m_range); + plater().statusbar()->set_cancel_callback(); + wxEndBusyCursor(); + + // Do a full refresh of scene tree, including regenerating + // all the GLVolumes. FIXME The update function shall just + // reload the modified matrices. + if(! was_canceled()) plater().update(true); + + // dont do finalization again for the same process + m_range = 0; + } + }); + } + + virtual void process() = 0; + + void start() { // only if not running + if(! m_running.load()) { + m_range = plater().statusbar()->get_range(); + m_canceled.store(false); + plater().statusbar()->set_range(status_range()); + plater().statusbar()->set_cancel_callback( [this](){ + m_canceled.store(true); + }); + wxBeginBusyCursor(); + m_ftr = std::async(std::launch::async, &Job::run, this); + } + } + + bool join(int timeout_ms = 0) { + if(!m_ftr.valid()) return true; + + if(timeout_ms <= 0) + m_ftr.wait(); + else if(m_ftr.wait_for(std::chrono::milliseconds(timeout_ms)) == + std::future_status::timeout) + return false; + + return true; + } + + bool is_running() const { return m_running.load(); } + void cancel() { m_canceled.store(true); } + }; + + class ArrangeJob: public Job { + int count = 0; + public: + using Job::Job; + int status_range() const override { return count; } + void set_count(int c) { count = c; } + void process() override; + } arrange_job {this}; + + class RotoptimizeJob: public Job { + public: + using Job::Job; + void process() override; + } rotoptimize_job {this}; + bool delayed_scene_refresh; std::string delayed_error_message; @@ -1389,8 +1490,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) { this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font()); - arranging = false; - rotoptimizing = false; background_process.set_fff_print(&fff_print); background_process.set_sla_print(&sla_print); background_process.set_gcode_preview_data(&gcode_preview_data); @@ -1564,7 +1663,7 @@ void Plater::priv::update_ui_from_settings() ProgressStatusBar* Plater::priv::statusbar() { - return main_frame->m_statusbar; + return main_frame->m_statusbar.get(); } std::string Plater::priv::get_config(const std::string &key) const @@ -2089,46 +2188,37 @@ void Plater::priv::mirror(Axis axis) void Plater::priv::arrange() { - if (arranging) { return; } - arranging = true; - Slic3r::ScopeGuard arranging_guard([this]() { arranging = false; }); + if(!arrange_job.is_running()) { + int count = 0; + for(auto obj : model.objects) count += int(obj->instances.size()); + arrange_job.set_count(count); + arrange_job.start(); + } +} - wxBusyCursor wait; +// This method will find an optimal orientation for the currently selected item +// Very similar in nature to the arrange method above... +void Plater::priv::sla_optimize_rotation() { + + rotoptimize_job.start(); +} - this->background_process.stop(); - - unsigned count = 0; - for(auto obj : model.objects) count += obj->instances.size(); - - auto prev_range = statusbar()->get_range(); - statusbar()->set_range(count); - - auto statusfn = [this, count] (unsigned st, const std::string& msg) { - /* // In case we would run the arrange asynchronously - wxCommandEvent event(EVT_PROGRESS_BAR); - event.SetInt(st); - event.SetString(msg); - wxQueueEvent(this->q, event.Clone()); */ - statusbar()->set_progress(count - st); - statusbar()->set_status_text(_(msg)); - - // ok, this is dangerous, but we are protected by the flag - // 'arranging' and the arrange button is also disabled. - // This call is needed for the cancel button to work. - wxYieldIfNeeded(); - }; - - statusbar()->set_cancel_callback([this, statusfn](){ - arranging = false; - statusfn(0, L("Arranging canceled")); - }); +void Plater::priv::ArrangeJob::process() { + + // TODO: we should decide whether to allow arrange when the search is + // running we should probably disable explicit slicing and background + // processing static const std::string arrangestr = L("Arranging"); + + auto& config = plater().config; + auto& view3D = plater().view3D; + auto& model = plater().model; // FIXME: I don't know how to obtain the minimum distance, it depends // on printer technology. I guess the following should work but it crashes. double dist = 6; //PrintConfig::min_object_distance(config); - if(printer_technology == ptFFF) { + if(plater().printer_technology == ptFFF) { dist = PrintConfig::min_object_distance(config); } @@ -2141,7 +2231,7 @@ void Plater::priv::arrange() Polyline bed; bed.points.reserve(bedpoints.size()); for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1))); - statusfn(0, arrangestr); + update_status(0, arrangestr); arr::WipeTowerInfo wti = view3D->get_canvas3d()->get_wipe_tower_info(); @@ -2157,67 +2247,34 @@ void Plater::priv::arrange() bed, hint, false, // create many piles not just one pile - [statusfn](unsigned st) { statusfn(st, arrangestr); }, - [this] () { return !arranging; }); + [this](unsigned st) { if(st > 0) update_status(count - int(st), arrangestr); }, + [this] () { return was_canceled(); }); } catch(std::exception& /*e*/) { - GUI::show_error(this->q, L("Could not arrange model objects! " - "Some geometries may be invalid.")); + GUI::show_error(plater().q, L("Could not arrange model objects! " + "Some geometries may be invalid.")); } + + update_status(count, was_canceled() ? L("Arranging canceled.") : L("Arranging done.")); // it remains to move the wipe tower: - view3D->get_canvas3d()->arrange_wipe_tower(wti); - - statusfn(0, L("Arranging done.")); - statusbar()->set_range(prev_range); - statusbar()->set_cancel_callback(); // remove cancel button - - // Do a full refresh of scene tree, including regenerating all the GLVolumes. - //FIXME The update function shall just reload the modified matrices. - update(true); + view3D->get_canvas3d()->arrange_wipe_tower(wti); } -// This method will find an optimal orientation for the currently selected item -// Very similar in nature to the arrange method above... -void Plater::priv::sla_optimize_rotation() { +void Plater::priv::RotoptimizeJob::process() +{ - // TODO: we should decide whether to allow arrange when the search is - // running we should probably disable explicit slicing and background - // processing - - if (rotoptimizing) { return; } - rotoptimizing = true; - Slic3r::ScopeGuard rotoptimizing_guard([this]() { rotoptimizing = false; }); - - int obj_idx = get_selected_object_idx(); + int obj_idx = plater().get_selected_object_idx(); if (obj_idx < 0) { return; } - ModelObject * o = model.objects[size_t(obj_idx)]; - - background_process.stop(); - - auto prev_range = statusbar()->get_range(); - statusbar()->set_range(100); - - auto stfn = [this] (unsigned st, const std::string& msg) { - statusbar()->set_progress(int(st)); - statusbar()->set_status_text(msg); - - // could be problematic, but we need the cancel button. - wxYieldIfNeeded(); - }; - - statusbar()->set_cancel_callback([this, stfn](){ - rotoptimizing = false; - stfn(0, L("Orientation search canceled")); - }); - + ModelObject * o = plater().model.objects[size_t(obj_idx)]; + auto r = sla::find_best_rotation( *o, .005f, - [stfn](unsigned s) { stfn(s, L("Searching for optimal orientation")); }, - [this](){ return !rotoptimizing; } + [this](unsigned s) { if(s < 100) update_status(int(s), L("Searching for optimal orientation")); }, + [this](){ return was_canceled(); } ); - const auto *bed_shape_opt = config->opt("bed_shape"); + const auto *bed_shape_opt = plater().config->opt("bed_shape"); assert(bed_shape_opt); auto& bedpoints = bed_shape_opt->values; @@ -2227,7 +2284,7 @@ void Plater::priv::sla_optimize_rotation() { double mindist = 6.0; // FIXME double offs = mindist / 2.0 - EPSILON; - if(rotoptimizing) // wasn't canceled + if(! was_canceled()) // wasn't canceled for(ModelInstance * oi : o->instances) { oi->set_rotation({r[X], r[Y], r[Z]}); @@ -2267,19 +2324,18 @@ void Plater::priv::sla_optimize_rotation() { Vec3d rt = oi->get_rotation(); rt(Z) += r; oi->set_rotation(rt); + + arr::WipeTowerInfo wti; // useless in SLA context + arr::find_new_position(plater().model, o->instances, + coord_t(mindist/SCALING_FACTOR), bed, wti); + + // Correct the z offset of the object which was corrupted be the rotation + o->ensure_on_bed(); + + update_status(100, L("Orientation found.")); + } else { + update_status(100, L("Orientation search canceled.")); } - - arr::WipeTowerInfo wti; // useless in SLA context - arr::find_new_position(model, o->instances, coord_t(mindist/SCALING_FACTOR), bed, wti); - - // Correct the z offset of the object which was corrupted be the rotation - o->ensure_on_bed(); - - stfn(0, L("Orientation found.")); - statusbar()->set_range(prev_range); - statusbar()->set_cancel_callback(); - - update(true); } void Plater::priv::split_object() @@ -2455,7 +2511,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation) // Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState. bool Plater::priv::restart_background_process(unsigned int state) { - if (arranging || rotoptimizing) { + if (arrange_job.is_running() || rotoptimize_job.is_running()) { // Avoid a race condition return false; } @@ -2686,7 +2742,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) { if (evt.status.percent >= -1) { - if (arranging || rotoptimizing) { + if (arrange_job.is_running() || rotoptimize_job.is_running()) { // Avoid a race condition return; } @@ -3140,7 +3196,7 @@ bool Plater::priv::can_fix_through_netfabb() const bool Plater::priv::can_increase_instances() const { - if (arranging || rotoptimizing) { + if (arrange_job.is_running() || rotoptimize_job.is_running()) { return false; } @@ -3150,7 +3206,7 @@ bool Plater::priv::can_increase_instances() const bool Plater::priv::can_decrease_instances() const { - if (arranging || rotoptimizing) { + if (arrange_job.is_running() || rotoptimize_job.is_running()) { return false; } @@ -3170,7 +3226,7 @@ bool Plater::priv::can_split_to_volumes() const bool Plater::priv::can_arrange() const { - return !model.objects.empty() && !arranging; + return !model.objects.empty() && !arrange_job.is_running(); } bool Plater::priv::can_layers_editing() const @@ -3292,6 +3348,21 @@ void Plater::load_files(const std::vector& input_files, bool load_m void Plater::update() { p->update(); } +void Plater::stop_jobs() +{ + static const int ABORT_WAIT_MAX_MS = 10000; + bool aborted = false; + + p->rotoptimize_job.cancel(); + aborted = p->rotoptimize_job.join(ABORT_WAIT_MAX_MS); + + p->arrange_job.cancel(); + aborted &= p->arrange_job.join(ABORT_WAIT_MAX_MS); + + if(!aborted) + BOOST_LOG_TRIVIAL(error) << "Could not abort an optimization job!"; +} + void Plater::update_ui_from_settings() { p->update_ui_from_settings(); } void Plater::select_view(const std::string& direction) { p->select_view(direction); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 3e7003686..8c62b742f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -143,6 +143,7 @@ public: void load_files(const std::vector& input_files, bool load_model = true, bool load_config = true); void update(); + void stop_jobs(); void select_view(const std::string& direction); void select_view_3D(const std::string& name); diff --git a/src/slic3r/GUI/ProgressStatusBar.cpp b/src/slic3r/GUI/ProgressStatusBar.cpp index b48c5732b..f848e663d 100644 --- a/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/src/slic3r/GUI/ProgressStatusBar.cpp @@ -168,6 +168,11 @@ void ProgressStatusBar::set_status_text(const char *txt) this->set_status_text(wxString::FromUTF8(txt)); } +wxString ProgressStatusBar::get_status_text() const +{ + return self->GetStatusText(); +} + void ProgressStatusBar::show_cancel_button() { if(m_cancelbutton) m_cancelbutton->Show(); diff --git a/src/slic3r/GUI/ProgressStatusBar.hpp b/src/slic3r/GUI/ProgressStatusBar.hpp index 225b0331e..fa21ba47c 100644 --- a/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/src/slic3r/GUI/ProgressStatusBar.hpp @@ -51,6 +51,7 @@ public: void set_status_text(const wxString& txt); void set_status_text(const std::string& txt); void set_status_text(const char *txt); + wxString get_status_text() const; // Temporary methods to satisfy Perl side void show_cancel_button(); From 170789a78715ba27303c38ac93687f1d3878f525 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 14 May 2019 10:34:11 +0200 Subject: [PATCH 02/44] Some comments and avoid race condition with background process. --- src/slic3r/GUI/Plater.cpp | 45 +++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f485c4016..f82174ea9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1222,11 +1222,22 @@ struct Plater::priv BackgroundSlicingProcess background_process; + // A class to handle UI jobs like arranging and optimizing rotation. + // These are not instant jobs, the user has to be informed about their + // state in the status progress indicator. On the other hand they are + // separated from the background slicing process. Ideally, these jobs should + // run when the background process is not running. + // + // TODO: A mechanism would be useful for blocking the plater interactions: + // objects would be frozen for the user. In case of arrange, an animation + // could be shown, or with the optimize orientations, partial results + // could be displayed. class Job: public wxEvtHandler { int m_range = 100; std::future m_ftr; priv *m_plater = nullptr; std::atomic m_running {false}, m_canceled {false}; + bool m_stop_slicing = false; void run() { m_running.store(true); process(); m_running.store(false); @@ -1240,6 +1251,7 @@ struct Plater::priv // status range for a particular job virtual int status_range() const { return 100; } + // status update, to be used from the work thread (process() method) void update_status(int st, const wxString& msg = "") { auto evt = new wxThreadEvent(); evt->SetInt(st); evt->SetString(msg); wxQueueEvent(this, evt); @@ -1250,7 +1262,9 @@ struct Plater::priv public: - Job(priv *_plater): m_plater(_plater) { + Job(priv *_plater, bool stop_slicing = false): + m_plater(_plater), m_stop_slicing(stop_slicing) + { Bind(wxEVT_THREAD, [this](const wxThreadEvent& evt){ auto msg = evt.GetString(); if(! msg.empty()) plater().statusbar()->set_status_text(msg); @@ -1278,19 +1292,39 @@ struct Plater::priv virtual void process() = 0; - void start() { // only if not running + void start() { // Start the job. No effect if the job is already running if(! m_running.load()) { + + if(m_stop_slicing) plater().background_process.stop(); + + // Save the current status indicatior range and push the new one m_range = plater().statusbar()->get_range(); - m_canceled.store(false); plater().statusbar()->set_range(status_range()); + + // init cancellation flag and set the cancel callback + m_canceled.store(false); plater().statusbar()->set_cancel_callback( [this](){ m_canceled.store(true); }); + + // Changing cursor to busy wxBeginBusyCursor(); - m_ftr = std::async(std::launch::async, &Job::run, this); + + try { // Execute the job + m_ftr = std::async(std::launch::async, &Job::run, this); + } catch(std::exception& ) { + update_status(status_range(), + _(L("ERROR: not enough resources to execute a new job."))); + } + + // The state changes will be undone when the process hits the + // last status value, in the status update handler (see ctor) } } + // To wait for the running job and join the threads. False is returned + // if the timeout has been reached and the job is still running. Call + // cancel() before this fn if you want to explicitly end the job. bool join(int timeout_ms = 0) { if(!m_ftr.valid()) return true; @@ -3676,6 +3710,9 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) void Plater::reslice() { + // Stop arrange and (or) optimize rotation tasks. + this->stop_jobs(); + //FIXME Don't reslice if export of G-code or sending to OctoPrint is running. // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->p->update_background_process(true); From baab5e49f1a95f48f4bafcdd9060b0c6f896cb00 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 18 May 2019 16:56:46 +0200 Subject: [PATCH 03/44] Mirroring parameters prepared for UI. Actual mirroring disabled, it will be refactored to maintain clarity of code. --- src/libslic3r/PrintConfig.cpp | 12 ++++++ src/libslic3r/PrintConfig.hpp | 4 ++ src/libslic3r/PrintExport.hpp | 22 ++++++++++ src/libslic3r/Rasterizer/Rasterizer.hpp | 7 +++- src/libslic3r/SLAPrint.cpp | 54 +++++++++++++------------ 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 87ea26301..bd29916af 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2258,6 +2258,18 @@ void PrintConfigDef::init_sla_params() def->min = 100; def->set_default_value(new ConfigOptionInt(1440)); + def = this->add("display_mirror_x", coBool); + def->full_label = L("Display mirroring in X axis"); + def->label = L("Mirror X"); + def->tooltip = L("Enable mirroring of output images in the X axis"); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("display_mirror_y", coBool); + def->full_label = L("Display mirroring in Y axis"); + def->label = L("Mirror Y"); + def->tooltip = L("Enable mirroring of output images in the Y axis"); + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("display_orientation", coEnum); def->label = L("Display orientation"); def->tooltip = L("Set the actual LCD display orientation inside the SLA printer." diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 1da22b377..248b89e32 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1083,6 +1083,8 @@ public: ConfigOptionInt display_pixels_x; ConfigOptionInt display_pixels_y; ConfigOptionEnum display_orientation; + ConfigOptionBool display_mirror_x; + ConfigOptionBool display_mirror_y; ConfigOptionFloats relative_correction; ConfigOptionFloat absolute_correction; ConfigOptionFloat gamma_correction; @@ -1099,6 +1101,8 @@ protected: OPT_PTR(display_height); OPT_PTR(display_pixels_x); OPT_PTR(display_pixels_y); + OPT_PTR(display_mirror_x); + OPT_PTR(display_mirror_y); OPT_PTR(display_orientation); OPT_PTR(relative_correction); OPT_PTR(absolute_correction); diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index f6537ed32..c7d462b4f 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -210,6 +210,28 @@ public: { } + inline FilePrinter(const SLAPrinterConfig& cfg, const SLAMaterialConfig& mcfg, double layer_height) + { + double w = cfg.display_width.getFloat(); + double h = cfg.display_height.getFloat(); + auto pw = unsigned(cfg.display_pixels_x.getInt()); + auto ph = unsigned(cfg.display_pixels_y.getInt()); + + m_res = Raster::Resolution(pw, ph); + m_pxdim = Raster::PixelDim(w/pw, h/ph); + m_exp_time_s = mcfg.exposure_time.getFloat(); + m_exp_time_first_s = mcfg.initial_exposure_time.getFloat(); + m_layer_height = layer_height; + + auto ro = cfg.display_orientation.getInt(); + + // Here is the trick with the orientation. + m_o = ro == RO_LANDSCAPE? Raster::Origin::BOTTOM_LEFT : + Raster::Origin::TOP_LEFT; + + m_gamma = cfg.gamma_correction.getFloat(); + } + FilePrinter(const FilePrinter& ) = delete; FilePrinter(FilePrinter&& m): m_layers_rst(std::move(m.m_layers_rst)), diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/Rasterizer/Rasterizer.hpp index 3fffe1a36..d338a5e3b 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.hpp +++ b/src/libslic3r/Rasterizer/Rasterizer.hpp @@ -75,7 +75,10 @@ public: struct Resolution { unsigned width_px; unsigned height_px; - inline Resolution(unsigned w, unsigned h): width_px(w), height_px(h) {} + + inline Resolution(unsigned w = 0, unsigned h = 0): + width_px(w), height_px(h) {} + inline unsigned pixels() const /*noexcept*/ { return width_px * height_px; } @@ -85,7 +88,7 @@ public: struct PixelDim { double w_mm; double h_mm; - inline PixelDim(double px_width_mm, double px_height_mm ): + inline PixelDim(double px_width_mm = 0.0, double px_height_mm = 0.0): w_mm(px_width_mm), h_mm(px_height_mm) {} }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 457be23ba..6d2318bea 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1008,7 +1008,7 @@ void SLAPrint::process() namespace sl = libnest2d::shapelike; // For algorithms // If the raster has vertical orientation, we will flip the coordinates - bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait; +// bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait; // Set up custom union and diff functions for clipper polygons auto polyunion = [] (const ClipperPolygons& subjects) @@ -1066,9 +1066,9 @@ void SLAPrint::process() // get polygons for all instances in the object auto get_all_polygons = - [flpXY](const ExPolygons& input_polygons, - const std::vector& instances, - bool is_lefthanded) + [](const ExPolygons& input_polygons, + const std::vector& instances, + bool is_lefthanded) { ClipperPolygons polygons; polygons.reserve(input_polygons.size() * instances.size()); @@ -1082,7 +1082,7 @@ void SLAPrint::process() // We need to reverse if flpXY OR is_lefthanded is true but // not if both are true which is a logical inequality (XOR) - bool needreverse = flpXY != is_lefthanded; + bool needreverse = /*flpXY !=*/ is_lefthanded; // should be a move poly.Contour.reserve(polygon.contour.size() + 1); @@ -1117,10 +1117,10 @@ void SLAPrint::process() sl::translate(poly, ClipperPoint{instances[i].shift(X), instances[i].shift(Y)}); - if (flpXY) { - for(auto& p : poly.Contour) std::swap(p.X, p.Y); - for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y); - } +// if (flpXY) { +// for(auto& p : poly.Contour) std::swap(p.X, p.Y); +// for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y); +// } polygons.emplace_back(std::move(poly)); } @@ -1289,27 +1289,29 @@ void SLAPrint::process() { // create a raster printer for the current print parameters // I don't know any better - auto& ocfg = m_objects.front()->m_config; - auto& matcfg = m_material_config; - auto& printcfg = m_printer_config; +// auto& ocfg = m_objects.front()->m_config; +// auto& matcfg = m_material_config; +// auto& printcfg = m_printer_config; - double w = printcfg.display_width.getFloat(); - double h = printcfg.display_height.getFloat(); - auto pw = unsigned(printcfg.display_pixels_x.getInt()); - auto ph = unsigned(printcfg.display_pixels_y.getInt()); - double lh = ocfg.layer_height.getFloat(); - double exp_t = matcfg.exposure_time.getFloat(); - double iexp_t = matcfg.initial_exposure_time.getFloat(); +// double w = printcfg.display_width.getFloat(); +// double h = printcfg.display_height.getFloat(); +// auto pw = unsigned(printcfg.display_pixels_x.getInt()); +// auto ph = unsigned(printcfg.display_pixels_y.getInt()); +// double lh = ocfg.layer_height.getFloat(); +// double exp_t = matcfg.exposure_time.getFloat(); +// double iexp_t = matcfg.initial_exposure_time.getFloat(); - double gamma = m_printer_config.gamma_correction.getFloat(); +// double gamma = m_printer_config.gamma_correction.getFloat(); - if(flpXY) { std::swap(w, h); std::swap(pw, ph); } +// if(flpXY) { std::swap(w, h); std::swap(pw, ph); } - m_printer.reset( - new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t, - flpXY? SLAPrinter::RO_PORTRAIT : - SLAPrinter::RO_LANDSCAPE, - gamma)); +// m_printer.reset( +// new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t, +// flpXY? SLAPrinter::RO_PORTRAIT : +// SLAPrinter::RO_LANDSCAPE, +// gamma)); + + m_printer.reset(new SLAPrinter(m_printer_config, m_material_config, m_default_object_config.layer_height.getFloat())); } // Allocate space for all the layers From bb73b59aa672632eab7e6f87514cb94688c5fb02 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Sat, 18 May 2019 22:45:24 +0200 Subject: [PATCH 04/44] Mirroring refactored. --- src/libslic3r/CMakeLists.txt | 7 +- src/libslic3r/PrintConfig.cpp | 4 +- src/libslic3r/PrintExport.hpp | 349 ------------------ src/libslic3r/Rasterizer/bicubic.h | 186 ---------- .../Rasterizer.cpp => SLA/SLARaster.cpp} | 99 +++-- .../Rasterizer.hpp => SLA/SLARaster.hpp} | 64 ++-- src/libslic3r/SLA/SLARasterWriter.cpp | 136 +++++++ src/libslic3r/SLA/SLARasterWriter.hpp | 139 +++++++ src/libslic3r/SLAPrint.cpp | 34 +- src/libslic3r/SLAPrint.hpp | 42 +-- 10 files changed, 380 insertions(+), 680 deletions(-) delete mode 100644 src/libslic3r/PrintExport.hpp delete mode 100644 src/libslic3r/Rasterizer/bicubic.h rename src/libslic3r/{Rasterizer/Rasterizer.cpp => SLA/SLARaster.cpp} (77%) rename src/libslic3r/{Rasterizer/Rasterizer.hpp => SLA/SLARaster.hpp} (68%) create mode 100644 src/libslic3r/SLA/SLARasterWriter.cpp create mode 100644 src/libslic3r/SLA/SLARasterWriter.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index ce93d95fa..3c32a22ed 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -130,13 +130,10 @@ add_library(libslic3r STATIC Print.hpp PrintBase.cpp PrintBase.hpp - PrintExport.hpp PrintConfig.cpp PrintConfig.hpp PrintObject.cpp PrintRegion.cpp - Rasterizer/Rasterizer.hpp - Rasterizer/Rasterizer.cpp SLAPrint.cpp SLAPrint.hpp SLA/SLAAutoSupports.hpp @@ -173,6 +170,10 @@ add_library(libslic3r STATIC SLA/SLARotfinder.cpp SLA/SLABoostAdapter.hpp SLA/SLASpatIndex.hpp + SLA/SLARaster.hpp + SLA/SLARaster.cpp + SLA/SLARasterWriter.hpp + SLA/SLARasterWriter.cpp ) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index bd29916af..d000c2c2c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2262,13 +2262,13 @@ void PrintConfigDef::init_sla_params() def->full_label = L("Display mirroring in X axis"); def->label = L("Mirror X"); def->tooltip = L("Enable mirroring of output images in the X axis"); - def->set_default_value(new ConfigOptionBool(false)); + def->set_default_value(new ConfigOptionBool(true)); def = this->add("display_mirror_y", coBool); def->full_label = L("Display mirroring in Y axis"); def->label = L("Mirror Y"); def->tooltip = L("Enable mirroring of output images in the Y axis"); - def->set_default_value(new ConfigOptionBool(true)); + def->set_default_value(new ConfigOptionBool(false)); def = this->add("display_orientation", coEnum); def->label = L("Display orientation"); diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp deleted file mode 100644 index c7d462b4f..000000000 --- a/src/libslic3r/PrintExport.hpp +++ /dev/null @@ -1,349 +0,0 @@ -#ifndef PRINTEXPORT_HPP -#define PRINTEXPORT_HPP - -// For png export of the sliced model -#include -#include -#include - -#include -#include - -#include "Rasterizer/Rasterizer.hpp" -//#include -//#include //#include "tbb/mutex.h" - -namespace Slic3r { - -// Used for addressing parameters of FilePrinter::set_statistics() -enum ePrintStatistics -{ - psUsedMaterial = 0, - psNumFade, - psNumSlow, - psNumFast, - - psCnt -}; - -enum class FilePrinterFormat { - SLA_PNGZIP, - SVG -}; - -/* - * Interface for a file printer of the slices. Implementation can be an SVG - * or PNG printer or any other format. - * - * The format argument specifies the output format of the printer and it enables - * different implementations of this class template for each supported format. - * - */ -template -class FilePrinter { -public: - - // Draw a polygon which is a polygon inside a slice on the specified layer. - void draw_polygon(const ExPolygon& p, unsigned lyr); - void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr); - - // Tell the printer how many layers should it consider. - void layers(unsigned layernum); - - // Get the number of layers in the print. - unsigned layers() const; - - /* Switch to a particular layer. If there where less layers then the - * specified layer number than an appropriate number of layers will be - * allocated in the printer. - */ - void begin_layer(unsigned layer); - - // Allocate a new layer on top of the last and switch to it. - void begin_layer(); - - /* - * Finish the selected layer. It means that no drawing is allowed on that - * layer anymore. This fact can be used to prepare the file system output - * data like png comprimation and so on. - */ - void finish_layer(unsigned layer); - - // Finish the top layer. - void finish_layer(); - - // Save all the layers into the file (or dir) specified in the path argument - // 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. - void save_layer(unsigned lyr, const std::string& path); -}; - -// Provokes static_assert in the right way. -template struct VeryFalse { static const bool value = false; }; - -// This can be explicitly implemented in the gui layer or the default Zipper -// API in libslic3r with minz. -template class LayerWriter { -public: - - LayerWriter(const std::string& /*zipfile_path*/) - { - static_assert(VeryFalse::value, - "No layer writer implementation provided!"); - } - - // Should create a new file within the zip with the given filename. It - // should also finish any previous entry. - void next_entry(const std::string& /*fname*/) {} - - // Should create a new file within the archive and write the provided data. - void binary_entry(const std::string& /*fname*/, - const std::uint8_t* buf, size_t len); - - // Test whether the object can still be used for writing. - bool is_ok() { return false; } - - // Write some data (text) into the current file (entry) within the archive. - template LayerWriter& operator<<(T&& /*arg*/) { - return *this; - } - - // Flush the current entry into the archive. - void finalize() {} -}; - -// Implementation for PNG raster output -// Be aware that if a large number of layers are allocated, it can very well -// exhaust the available memory especially on 32 bit platform. -template<> class FilePrinter -{ - struct Layer { - Raster raster; - RawBytes rawbytes; - - Layer() {} - - Layer(const Layer&) = delete; - Layer(Layer&& m): - raster(std::move(m.raster)) {} - }; - - // We will save the compressed PNG data into stringstreams which can be done - // in parallel. Later we can write every layer to the disk sequentially. - std::vector m_layers_rst; - Raster::Resolution m_res; - Raster::PixelDim m_pxdim; - double m_exp_time_s = .0, m_exp_time_first_s = .0; - double m_layer_height = .0; - Raster::Origin m_o = Raster::Origin::TOP_LEFT; - double m_gamma; - - double m_used_material = 0.0; - int m_cnt_fade_layers = 0; - int m_cnt_slow_layers = 0; - int m_cnt_fast_layers = 0; - - std::string createIniContent(const std::string& projectname) { - using std::string; - using std::to_string; - - auto expt_str = to_string(m_exp_time_s); - auto expt_first_str = to_string(m_exp_time_first_s); - auto layerh_str = to_string(m_layer_height); - - const std::string cnt_fade_layers = to_string(m_cnt_fade_layers); - const std::string cnt_slow_layers = to_string(m_cnt_slow_layers); - const std::string cnt_fast_layers = to_string(m_cnt_fast_layers); - const std::string used_material = to_string(m_used_material); - - return string( - "action = print\n" - "jobDir = ") + projectname + "\n" + - "expTime = " + expt_str + "\n" - "expTimeFirst = " + expt_first_str + "\n" - "numFade = " + cnt_fade_layers + "\n" - "layerHeight = " + layerh_str + "\n" - "usedMaterial = " + used_material + "\n" - "numSlow = " + cnt_slow_layers + "\n" - "numFast = " + cnt_fast_layers + "\n"; - } - -public: - - enum RasterOrientation { - RO_LANDSCAPE, - RO_PORTRAIT - }; - - // We will play with the raster's coordinate origin parameter. When the - // printer should print in landscape mode it should have the Y axis flipped - // because the layers should be displayed upside down. PNG has its - // coordinate origin in the top-left corner so normally the Raster objects - // should be instantiated with the TOP_LEFT flag. However, in landscape mode - // we do want the pictures to be upside down so we will make BOTTOM_LEFT - // type rasters and the PNG format will do the flipping automatically. - - // In case of portrait images, we have to rotate the image by a 90 degrees - // and flip the y axis. To get the correct upside-down orientation of the - // slice images, we can flip the x and y coordinates of the input polygons - // and do the Y flipping of the image. This will generate the correct - // orientation in portrait mode. - - inline FilePrinter(double width_mm, double height_mm, - unsigned width_px, unsigned height_px, - double layer_height, - double exp_time, double exp_time_first, - RasterOrientation ro = RO_PORTRAIT, - double gamma = 1.0): - m_res(width_px, height_px), - m_pxdim(width_mm/width_px, height_mm/height_px), - m_exp_time_s(exp_time), - m_exp_time_first_s(exp_time_first), - m_layer_height(layer_height), - - // Here is the trick with the orientation. - m_o(ro == RO_LANDSCAPE? Raster::Origin::BOTTOM_LEFT : - Raster::Origin::TOP_LEFT ), - m_gamma(gamma) - { - } - - inline FilePrinter(const SLAPrinterConfig& cfg, const SLAMaterialConfig& mcfg, double layer_height) - { - double w = cfg.display_width.getFloat(); - double h = cfg.display_height.getFloat(); - auto pw = unsigned(cfg.display_pixels_x.getInt()); - auto ph = unsigned(cfg.display_pixels_y.getInt()); - - m_res = Raster::Resolution(pw, ph); - m_pxdim = Raster::PixelDim(w/pw, h/ph); - m_exp_time_s = mcfg.exposure_time.getFloat(); - m_exp_time_first_s = mcfg.initial_exposure_time.getFloat(); - m_layer_height = layer_height; - - auto ro = cfg.display_orientation.getInt(); - - // Here is the trick with the orientation. - m_o = ro == RO_LANDSCAPE? Raster::Origin::BOTTOM_LEFT : - Raster::Origin::TOP_LEFT; - - m_gamma = cfg.gamma_correction.getFloat(); - } - - FilePrinter(const FilePrinter& ) = delete; - FilePrinter(FilePrinter&& m): - m_layers_rst(std::move(m.m_layers_rst)), - m_res(m.m_res), - m_pxdim(m.m_pxdim) {} - - inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } - inline unsigned layers() const { return unsigned(m_layers_rst.size()); } - - inline void draw_polygon(const ExPolygon& p, unsigned lyr) { - assert(lyr < m_layers_rst.size()); - m_layers_rst[lyr].raster.draw(p); - } - - inline void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr) { - assert(lyr < m_layers_rst.size()); - m_layers_rst[lyr].raster.draw(p); - } - - inline void begin_layer(unsigned lyr) { - if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1); - m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_o, m_gamma); - } - - inline void begin_layer() { - m_layers_rst.emplace_back(); - m_layers_rst.front().raster.reset(m_res, m_pxdim, m_o, m_gamma); - } - - inline void finish_layer(unsigned lyr_id) { - assert(lyr_id < m_layers_rst.size()); - m_layers_rst[lyr_id].rawbytes = - m_layers_rst[lyr_id].raster.save(Raster::Compression::PNG); - m_layers_rst[lyr_id].raster.reset(); - } - - inline void finish_layer() { - if(!m_layers_rst.empty()) { - m_layers_rst.back().rawbytes = - m_layers_rst.back().raster.save(Raster::Compression::PNG); - m_layers_rst.back().raster.reset(); - } - } - - template - inline void save(const std::string& fpath, const std::string& prjname = "") - { - try { - LayerWriter writer(fpath); - if(!writer.is_ok()) return; - - std::string project = prjname.empty()? - boost::filesystem::path(fpath).stem().string() : prjname; - - writer.next_entry("config.ini"); - if(!writer.is_ok()) return; - - writer << createIniContent(project); - - for(unsigned i = 0; i < m_layers_rst.size() && writer.is_ok(); i++) - { - if(m_layers_rst[i].rawbytes.size() > 0) { - char lyrnum[6]; - std::sprintf(lyrnum, "%.5d", i); - auto zfilename = project + lyrnum + ".png"; - if(!writer.is_ok()) break; - - writer.binary_entry(zfilename, - m_layers_rst[i].rawbytes.data(), - m_layers_rst[i].rawbytes.size()); - } - } - - writer.finalize(); - } catch(std::exception& e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - // Rethrow the exception - throw; - } - } - - void save_layer(unsigned lyr, const std::string& path) { - unsigned i = lyr; - assert(i < m_layers_rst.size()); - - char lyrnum[6]; - std::sprintf(lyrnum, "%.5d", lyr); - std::string loc = path + "layer" + lyrnum + ".png"; - - std::fstream out(loc, std::fstream::out | std::fstream::binary); - if(out.good()) { - m_layers_rst[i].raster.save(out, Raster::Compression::PNG); - } else { - BOOST_LOG_TRIVIAL(error) << "Can't create file for layer"; - } - - out.close(); - m_layers_rst[i].raster.reset(); - } - - void set_statistics(const std::vector statistics) - { - if (statistics.size() != psCnt) - return; - - m_used_material = statistics[psUsedMaterial]; - m_cnt_fade_layers = int(statistics[psNumFade]); - m_cnt_slow_layers = int(statistics[psNumSlow]); - m_cnt_fast_layers = int(statistics[psNumFast]); - } -}; - -} - -#endif // PRINTEXPORT_HPP diff --git a/src/libslic3r/Rasterizer/bicubic.h b/src/libslic3r/Rasterizer/bicubic.h deleted file mode 100644 index 870d00dbd..000000000 --- a/src/libslic3r/Rasterizer/bicubic.h +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef BICUBIC_HPP -#define BICUBIC_HPP - -#include -#include -#include - -#include - -namespace Slic3r { - -namespace BicubicInternal { - // Linear kernel, to be able to test cubic methods with hat kernels. - template - struct LinearKernel - { - typedef T FloatType; - - static T a00() { return T(0.); } - static T a01() { return T(0.); } - static T a02() { return T(0.); } - static T a03() { return T(0.); } - static T a10() { return T(1.); } - static T a11() { return T(-1.); } - static T a12() { return T(0.); } - static T a13() { return T(0.); } - static T a20() { return T(0.); } - static T a21() { return T(1.); } - static T a22() { return T(0.); } - static T a23() { return T(0.); } - static T a30() { return T(0.); } - static T a31() { return T(0.); } - static T a32() { return T(0.); } - static T a33() { return T(0.); } - }; - - // Interpolation kernel aka Catmul-Rom aka Keyes kernel. - template - struct CubicCatmulRomKernel - { - typedef T FloatType; - - static T a00() { return 0; } - static T a01() { return (T)-0.5; } - static T a02() { return (T) 1.; } - static T a03() { return (T)-0.5; } - static T a10() { return (T) 1.; } - static T a11() { return 0; } - static T a12() { return (T)-5./2.; } - static T a13() { return (T) 3./2.; } - static T a20() { return 0; } - static T a21() { return (T) 0.5; } - static T a22() { return (T) 2.; } - static T a23() { return (T)-3./2.; } - static T a30() { return 0; } - static T a31() { return 0; } - static T a32() { return (T)-0.5; } - static T a33() { return (T) 0.5; } - }; - - // B-spline kernel - template - struct CubicBSplineKernel - { - typedef T FloatType; - - static T a00() { return (T) 1./6.; } - static T a01() { return (T) -3./6.; } - static T a02() { return (T) 3./6.; } - static T a03() { return (T) -1./6.; } - static T a10() { return (T) 4./6.; } - static T a11() { return 0; } - static T a12() { return (T) -6./6.; } - static T a13() { return (T) 3./6.; } - static T a20() { return (T) 1./6.; } - static T a21() { return (T) 3./6.; } - static T a22() { return (T) 3./6.; } - static T a23() { return (T)- 3./6.; } - static T a30() { return 0; } - static T a31() { return 0; } - static T a32() { return 0; } - static T a33() { return (T) 1./6.; } - }; - - template - inline T clamp(T a, T lower, T upper) - { - return (a < lower) ? lower : - (a > upper) ? upper : a; - } -} - -template -struct CubicKernel -{ - typedef typename KERNEL KernelInternal; - typedef typename KERNEL::FloatType FloatType; - - static FloatType kernel(FloatType x) - { - x = fabs(x); - if (x >= (FloatType)2.) - return 0.0f; - if (x <= (FloatType)1.) { - FloatType x2 = x * x; - FloatType x3 = x2 * x; - return KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3; - } - assert(x > (FloatType)1. && x < (FloatType)2.); - x -= (FloatType)1.; - FloatType x2 = x * x; - FloatType x3 = x2 * x; - return KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3; - } - - static FloatType interpolate(FloatType f0, FloatType f1, FloatType f2, FloatType f3, FloatType x) - { - const FloatType x2 = x*x; - const FloatType x3 = x*x*x; - return f0*(KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3) + - f1*(KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3) + - f2*(KERNEL::a20() + KERNEL::a21() * x + KERNEL::a22() * x2 + KERNEL::a23() * x3) + - f3*(KERNEL::a30() + KERNEL::a31() * x + KERNEL::a32() * x2 + KERNEL::a33() * x3); - } -}; - -// Linear splines -typedef CubicKernel> LinearKernelf; -typedef CubicKernel> LinearKerneld; -// Catmul-Rom splines -typedef CubicKernel> CubicCatmulRomKernelf; -typedef CubicKernel> CubicCatmulRomKerneld; -typedef CubicKernel> CubicInterpolationKernelf; -typedef CubicKernel> CubicInterpolationKerneld; -// Cubic B-splines -typedef CubicKernel> CubicBSplineKernelf; -typedef CubicKernel> CubicBSplineKerneld; - -template -static float cubic_interpolate(const Eigen::ArrayBase &F, const typename KERNEL::FloatType pt, const typename KERNEL::FloatType dx) -{ - typedef typename KERNEL::FloatType T; - const int w = int(F.size()); - const int ix = (int)floor(pt); - const T s = pt - (T)ix; - - if (ix > 1 && ix + 2 < w) { - // Inside the fully interpolated region. - return KERNEL::interpolate(F[ix - 1], F[ix], F[ix + 1], F[ix + 2], s); - } - // Transition region. Extend with a constant function. - auto f = [&F, w](x) { return F[BicubicInternal::clamp(x, 0, w - 1)]; } - return KERNEL::interpolate(f(ix - 1), f(ix), f(ix + 1), f(ix + 2), s); -} - -template -static float bicubic_interpolate(const Eigen::MatrixBase &F, const Eigen::Matrix &pt, const typename KERNEL::FloatType dx) -{ - typedef typename KERNEL::FloatType T; - const int w = F.cols(); - const int h = F.rows(); - const int ix = (int)floor(pt[0]); - const int iy = (int)floor(pt[1]); - const T s = pt[0] - (T)ix; - const T t = pt[1] - (T)iy; - - if (ix > 1 && ix + 2 < w && iy > 1 && iy + 2 < h) { - // Inside the fully interpolated region. - return KERNEL::interpolate( - KERNEL::interpolate(F(ix-1,iy-1),F(ix ,iy-1),F(ix+1,iy-1),F(ix+2,iy-1),s), - KERNEL::interpolate(F(ix-1,iy ),F(ix ,iy ),F(ix+1,iy ),F(ix+2,iy ),s), - KERNEL::interpolate(F(ix-1,iy+1),F(ix ,iy+1),F(ix+1,iy+1),F(ix+2,iy+1),s), - KERNEL::interpolate(F(ix-1,iy+2),F(ix ,iy+2),F(ix+1,iy+2),F(ix+2,iy+2),s),t); - } - // Transition region. Extend with a constant function. - auto f = [&f, w, h](int x, int y) { return F(BicubicInternal::clamp(x,0,w-1),BicubicInternal::clamp(y,0,h-1)); } - return KERNEL::interpolate( - KERNEL::interpolate(f(ix-1,iy-1),f(ix ,iy-1),f(ix+1,iy-1),f(ix+2,iy-1),s), - KERNEL::interpolate(f(ix-1,iy ),f(ix ,iy ),f(ix+1,iy ),f(ix+2,iy ),s), - KERNEL::interpolate(f(ix-1,iy+1),f(ix ,iy+1),f(ix+1,iy+1),f(ix+2,iy+1),s), - KERNEL::interpolate(f(ix-1,iy+2),f(ix ,iy+2),f(ix+1,iy+2),f(ix+2,iy+2),s),t); -} - -} // namespace Slic3r - -#endif /* BICUBIC_HPP */ diff --git a/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/SLA/SLARaster.cpp similarity index 77% rename from src/libslic3r/Rasterizer/Rasterizer.cpp rename to src/libslic3r/SLA/SLARaster.cpp index 6384a241f..fba1f46f3 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.cpp +++ b/src/libslic3r/SLA/SLARaster.cpp @@ -1,5 +1,8 @@ -#include "Rasterizer.hpp" -#include +#ifndef SLARASTER_CPP +#define SLARASTER_CPP + +#include "SLARaster.hpp" +#include "libslic3r/ExPolygon.hpp" #include // For rasterizing @@ -19,11 +22,13 @@ namespace Slic3r { -const Polygon& contour(const ExPolygon& p) { return p.contour; } -const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; } +inline const Polygon& contour(const ExPolygon& p) { return p.contour; } +inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; } -const Polygons& holes(const ExPolygon& p) { return p.holes; } -const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; } +inline const Polygons& holes(const ExPolygon& p) { return p.holes; } +inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; } + +namespace sla { class Raster::Impl { public: @@ -39,7 +44,7 @@ public: static const TPixel ColorWhite; static const TPixel ColorBlack; - using Origin = Raster::Origin; + using Format = Raster::Format; private: Raster::Resolution m_resolution; @@ -52,16 +57,20 @@ private: TRendererAA m_renderer; std::function m_gammafn; - Origin m_o; + std::array m_mirror; inline void flipy(agg::path_storage& path) const { path.flip_y(0, m_resolution.height_px); } + + inline void flipx(agg::path_storage& path) const { + path.flip_x(0, m_resolution.width_px); + } public: inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd, - Origin o, double gamma = 1.0): + const std::array& mirror, double gamma = 1.0): m_resolution(res), // m_pxdim(pd), m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm), @@ -72,7 +81,7 @@ public: m_pixfmt(m_rbuf), m_raw_renderer(m_pixfmt), m_renderer(m_raw_renderer), - m_o(o) + m_mirror(mirror) { m_renderer.color(ColorWhite); @@ -81,6 +90,18 @@ public: clear(); } + + inline Impl(const Raster::Resolution& res, + const Raster::PixelDim &pd, + Format fmt, + double gamma = 1.0): + Impl(res, pd, {false, false}, gamma) + { + switch (fmt) { + case Format::PNG: m_mirror = {false, true}; break; + case Format::RAW: m_mirror = {false, false}; break; + } + } template void draw(const P &poly) { agg::rasterizer_scanline_aa<> ras; @@ -89,14 +110,16 @@ public: ras.gamma(m_gammafn); auto&& path = to_path(contour(poly)); - - if(m_o == Origin::TOP_LEFT) flipy(path); + + if(m_mirror[X]) flipx(path); + if(m_mirror[Y]) flipy(path); ras.add_path(path); for(auto& h : holes(poly)) { auto&& holepath = to_path(h); - if(m_o == Origin::TOP_LEFT) flipy(holepath); + if(m_mirror[X]) flipx(holepath); + if(m_mirror[Y]) flipy(holepath); ras.add_path(holepath); } @@ -110,9 +133,7 @@ public: inline TBuffer& buffer() { return m_buf; } inline const Raster::Resolution resolution() { return m_resolution; } - - inline Origin origin() const /*noexcept*/ { return m_o; } - + private: inline double getPx(const Point& p) { return p(0) * m_pxdim_scaled.w_mm; @@ -154,30 +175,23 @@ private: const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255); const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0); -Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o, double g): - m_impl(new Impl(r, pd, o, g)) {} +Raster::Raster() = default; +Raster::~Raster() = default; +Raster::Raster(Raster &&m) = default; +Raster& Raster::operator=(Raster&&) = default; -Raster::Raster() {} - -Raster::~Raster() {} - -Raster::Raster(Raster &&m): - m_impl(std::move(m.m_impl)) {} - -void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, - double g) +void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, + Format fmt, double gamma) { - // Free up the unnecessary memory and make sure it stays clear after - // an exception - auto o = m_impl? m_impl->origin() : Origin::TOP_LEFT; - reset(r, pd, o, g); + m_impl.reset(); + m_impl.reset(new Impl(r, pd, fmt, gamma)); } void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, - Raster::Origin o, double gamma) + const std::array& mirror, double gamma) { m_impl.reset(); - m_impl.reset(new Impl(r, pd, o, gamma)); + m_impl.reset(new Impl(r, pd, mirror, gamma)); } void Raster::reset() @@ -208,13 +222,13 @@ void Raster::draw(const ClipperLib::Polygon &poly) m_impl->draw(poly); } -void Raster::save(std::ostream& stream, Compression comp) +void Raster::save(std::ostream& stream, Format fmt) { assert(m_impl); if(!stream.good()) return; - switch(comp) { - case Compression::PNG: { + switch(fmt) { + case Format::PNG: { auto& b = m_impl->buffer(); size_t out_len = 0; void * rawdata = tdefl_write_image_to_png_file_in_memory( @@ -231,7 +245,7 @@ void Raster::save(std::ostream& stream, Compression comp) break; } - case Compression::RAW: { + case Format::RAW: { stream << "P5 " << m_impl->resolution().width_px << " " << m_impl->resolution().height_px << " " @@ -244,14 +258,14 @@ void Raster::save(std::ostream& stream, Compression comp) } } -RawBytes Raster::save(Raster::Compression comp) +RawBytes Raster::save(Format fmt) { assert(m_impl); std::vector data; size_t s = 0; - switch(comp) { - case Compression::PNG: { + switch(fmt) { + case Format::PNG: { void *rawdata = tdefl_write_image_to_png_file_in_memory( m_impl->buffer().data(), int(resolution().width_px), @@ -265,7 +279,7 @@ RawBytes Raster::save(Raster::Compression comp) MZ_FREE(rawdata); break; } - case Compression::RAW: { + case Format::RAW: { auto header = std::string("P5 ") + std::to_string(m_impl->resolution().width_px) + " " + std::to_string(m_impl->resolution().height_px) + " " + "255 "; @@ -287,3 +301,6 @@ RawBytes Raster::save(Raster::Compression comp) } } +} + +#endif // SLARASTER_CPP diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/SLA/SLARaster.hpp similarity index 68% rename from src/libslic3r/Rasterizer/Rasterizer.hpp rename to src/libslic3r/SLA/SLARaster.hpp index d338a5e3b..03fb06d6c 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.hpp +++ b/src/libslic3r/SLA/SLARaster.hpp @@ -1,5 +1,5 @@ -#ifndef RASTERIZER_HPP -#define RASTERIZER_HPP +#ifndef SLARASTER_HPP +#define SLARASTER_HPP #include #include @@ -8,10 +8,12 @@ namespace ClipperLib { struct Polygon; } -namespace Slic3r { +namespace Slic3r { class ExPolygon; +namespace sla { + // Raw byte buffer paired with its size. Suitable for compressed PNG data. class RawBytes { @@ -23,19 +25,24 @@ public: size_t size() const { return m_buffer.size(); } const uint8_t * data() { return m_buffer.data(); } + + RawBytes(const RawBytes&) = delete; + RawBytes(RawBytes&&) = default; + RawBytes& operator=(const RawBytes&) = delete; + RawBytes& operator=(RawBytes&&) = default; // ///////////////////////////////////////////////////////////////////////// // FIXME: the following is needed for MSVC2013 compatibility // ///////////////////////////////////////////////////////////////////////// - RawBytes(const RawBytes&) = delete; - RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} +// RawBytes(const RawBytes&) = delete; +// RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} - RawBytes& operator=(const RawBytes&) = delete; - RawBytes& operator=(RawBytes&& mv) { - m_buffer = std::move(mv.m_buffer); - return *this; - } +// RawBytes& operator=(const RawBytes&) = delete; +// RawBytes& operator=(RawBytes&& mv) { +// m_buffer = std::move(mv.m_buffer); +// return *this; +// } // ///////////////////////////////////////////////////////////////////////// }; @@ -54,23 +61,11 @@ class Raster { public: /// Supported compression types - enum class Compression { + enum class Format { RAW, //!> Uncompressed pixel data PNG //!> PNG compression }; - /// The Rasterizer expects the input polygons to have their coordinate - /// system origin in the bottom left corner. If the raster is then - /// configured with the TOP_LEFT origin parameter (in the constructor) than - /// it will flip the Y axis in output to maintain the correct orientation. - /// This is the default case with PNG images. They have the origin in the - /// top left corner. Without the flipping, the image would be upside down - /// with the scaled (clipper) coordinate system of the input polygons. - enum class Origin { - TOP_LEFT, - BOTTOM_LEFT - }; - /// Type that represents a resolution in pixels. struct Resolution { unsigned width_px; @@ -93,19 +88,21 @@ public: }; /// Constructor taking the resolution and the pixel dimension. - Raster(const Resolution& r, const PixelDim& pd, - Origin o = Origin::BOTTOM_LEFT, double gamma = 1.0); + template Raster(Args...args) { + reset(std::forward(args)...); + } Raster(); Raster(const Raster& cpy) = delete; Raster& operator=(const Raster& cpy) = delete; Raster(Raster&& m); + Raster& operator=(Raster&&); ~Raster(); /// Reallocated everything for the given resolution and pixel dimension. - void reset(const Resolution& r, const PixelDim& pd, double gamma = 1.0); - void reset(const Resolution& r, const PixelDim& pd, Origin o, double gamma); - + void reset(const Resolution&, const PixelDim&, const std::array& mirror, double gamma = 1.0); + void reset(const Resolution& r, const PixelDim& pd, Format o, double gamma = 1.0); + /** * Release the allocated resources. Drawing in this state ends in * unspecified behavior. @@ -123,10 +120,13 @@ public: void draw(const ClipperLib::Polygon& poly); /// Save the raster on the specified stream. - void save(std::ostream& stream, Compression comp = Compression::RAW); + void save(std::ostream& stream, Format = Format::PNG); - RawBytes save(Compression comp = Compression::RAW); + /// Save into a continuous byte stream which is returned. + RawBytes save(Format fmt = Format::PNG); }; -} -#endif // RASTERIZER_HPP +} // sla +} // Slic3r + +#endif // SLARASTER_HPP diff --git a/src/libslic3r/SLA/SLARasterWriter.cpp b/src/libslic3r/SLA/SLARasterWriter.cpp new file mode 100644 index 000000000..b2fe0c72c --- /dev/null +++ b/src/libslic3r/SLA/SLARasterWriter.cpp @@ -0,0 +1,136 @@ +#include "SLARasterWriter.hpp" +#include "libslic3r/Zipper.hpp" +#include "ExPolygon.hpp" +#include + +#include +#include + +namespace Slic3r { namespace sla { + +std::string SLARasterWriter::createIniContent(const std::string& projectname) const +{ + auto expt_str = std::to_string(m_exp_time_s); + auto expt_first_str = std::to_string(m_exp_time_first_s); + auto layerh_str = std::to_string(m_layer_height); + + const std::string cnt_fade_layers = std::to_string(m_cnt_fade_layers); + const std::string cnt_slow_layers = std::to_string(m_cnt_slow_layers); + const std::string cnt_fast_layers = std::to_string(m_cnt_fast_layers); + const std::string used_material = std::to_string(m_used_material); + + return std::string( + "action = print\n" + "jobDir = ") + projectname + "\n" + + "expTime = " + expt_str + "\n" + "expTimeFirst = " + expt_first_str + "\n" + "numFade = " + cnt_fade_layers + "\n" + "layerHeight = " + layerh_str + "\n" + "usedMaterial = " + used_material + "\n" + "numSlow = " + cnt_slow_layers + "\n" + "numFast = " + cnt_fast_layers + "\n"; +} + +void SLARasterWriter::flpXY(ClipperLib::Polygon &poly) +{ + for(auto& p : poly.Contour) std::swap(p.X, p.Y); + std::reverse(poly.Contour.begin(), poly.Contour.end()); + + for(auto& h : poly.Holes) { + for(auto& p : h) std::swap(p.X, p.Y); + std::reverse(h.begin(), h.end()); + } +} + +void SLARasterWriter::flpXY(ExPolygon &poly) +{ + for(auto& p : poly.contour.points) p = {p.y(), p.x()}; + std::reverse(poly.contour.points.begin(), poly.contour.points.end()); + + for(auto& h : poly.holes) { + for(auto& p : h.points) p = {p.y(), p.x()}; + std::reverse(h.points.begin(), h.points.end()); + } +} + +SLARasterWriter::SLARasterWriter(const SLAPrinterConfig &cfg, + const SLAMaterialConfig &mcfg, + double layer_height) +{ + double w = cfg.display_width.getFloat(); + double h = cfg.display_height.getFloat(); + auto pw = unsigned(cfg.display_pixels_x.getInt()); + auto ph = unsigned(cfg.display_pixels_y.getInt()); + + m_mirror[X] = cfg.display_mirror_x.getBool(); + + // PNG raster will implicitly do an Y mirror + m_mirror[Y] = ! cfg.display_mirror_y.getBool(); + + auto ro = cfg.display_orientation.getInt(); + + if(ro == roPortrait) { + std::swap(w, h); + std::swap(pw, ph); + m_o = roPortrait; + + // XY flipping implicitly does an X mirror + m_mirror[X] = ! m_mirror[X]; + } else m_o = roLandscape; + + m_res = Raster::Resolution(pw, ph); + m_pxdim = Raster::PixelDim(w/pw, h/ph); + m_exp_time_s = mcfg.exposure_time.getFloat(); + m_exp_time_first_s = mcfg.initial_exposure_time.getFloat(); + m_layer_height = layer_height; + + m_gamma = cfg.gamma_correction.getFloat(); +} + +void SLARasterWriter::save(const std::string &fpath, const std::string &prjname) +{ + try { + Zipper zipper(fpath); // zipper with no compression + + std::string project = prjname.empty()? + boost::filesystem::path(fpath).stem().string() : prjname; + + zipper.add_entry("config.ini"); + + zipper << createIniContent(project); + + for(unsigned i = 0; i < m_layers_rst.size(); i++) + { + if(m_layers_rst[i].rawbytes.size() > 0) { + char lyrnum[6]; + std::sprintf(lyrnum, "%.5d", i); + auto zfilename = project + lyrnum + ".png"; + + // Add binary entry to the zipper + zipper.add_entry(zfilename, + m_layers_rst[i].rawbytes.data(), + m_layers_rst[i].rawbytes.size()); + } + } + + zipper.finalize(); + } catch(std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + // Rethrow the exception + throw; + } +} + +void SLARasterWriter::set_statistics(const std::vector statistics) +{ + if (statistics.size() != psCnt) + return; + + m_used_material = statistics[psUsedMaterial]; + m_cnt_fade_layers = int(statistics[psNumFade]); + m_cnt_slow_layers = int(statistics[psNumSlow]); + m_cnt_fast_layers = int(statistics[psNumFast]); +} + +} // namespace sla +} // namespace Slic3r diff --git a/src/libslic3r/SLA/SLARasterWriter.hpp b/src/libslic3r/SLA/SLARasterWriter.hpp new file mode 100644 index 000000000..9fc23840e --- /dev/null +++ b/src/libslic3r/SLA/SLARasterWriter.hpp @@ -0,0 +1,139 @@ +#ifndef SLARASTERWRITER_HPP +#define SLARASTERWRITER_HPP + +// For png export of the sliced model +#include +#include +#include + +#include "libslic3r/PrintConfig.hpp" + +#include "SLARaster.hpp" + +namespace Slic3r { namespace sla { + +// Implementation for PNG raster output +// Be aware that if a large number of layers are allocated, it can very well +// exhaust the available memory especially on 32 bit platform. +// This class is designed to be used in parallel mode. Layers have an ID and +// each layer can be written and compressed independently (in parallel). +// At the end when all layers where written, the save method can be used to +// write out the result into a zipped archive. +class SLARasterWriter +{ +public: + enum RasterOrientation { + roLandscape, + roPortrait + }; + + // Used for addressing parameters of set_statistics() + enum ePrintStatistics + { + psUsedMaterial = 0, + psNumFade, + psNumSlow, + psNumFast, + + psCnt + }; + +private: + + // A struct to bind the raster image data and its compressed bytes together. + struct Layer { + Raster raster; + RawBytes rawbytes; + + Layer() = default; + Layer(const Layer&) = delete; // The image is big, do not copy by accident + Layer& operator=(const Layer&) = delete; + + Layer(Layer&& m) = default; + Layer& operator=(Layer&&) = default; + }; + + // We will save the compressed PNG data into RawBytes type buffers in + // parallel. Later we can write every layer to the disk sequentially. + std::vector m_layers_rst; + Raster::Resolution m_res; + Raster::PixelDim m_pxdim; + double m_exp_time_s = .0, m_exp_time_first_s = .0; + double m_layer_height = .0; + RasterOrientation m_o = roPortrait; + std::array m_mirror; + + double m_gamma; + + double m_used_material = 0.0; + int m_cnt_fade_layers = 0; + int m_cnt_slow_layers = 0; + int m_cnt_fast_layers = 0; + + std::string createIniContent(const std::string& projectname) const; + + static void flpXY(ClipperLib::Polygon& poly); + static void flpXY(ExPolygon& poly); + +public: + + SLARasterWriter(const SLAPrinterConfig& cfg, + const SLAMaterialConfig& mcfg, + double layer_height); + + SLARasterWriter(const SLARasterWriter& ) = delete; + SLARasterWriter& operator=(const SLARasterWriter&) = delete; + SLARasterWriter(SLARasterWriter&& m) = default; + SLARasterWriter& operator=(SLARasterWriter&&) = default; +// SLARasterWriter(SLARasterWriter&& m) = default; +// SLARasterWriter(SLARasterWriter&& m): +// m_layers_rst(std::move(m.m_layers_rst)), +// m_res(m.m_res), +// m_pxdim(m.m_pxdim) {} + + inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } + inline unsigned layers() const { return unsigned(m_layers_rst.size()); } + + template void draw_polygon(const Poly& p, unsigned lyr) { + assert(lyr < m_layers_rst.size()); + if(m_o == roPortrait) { + Poly poly(p); flpXY(poly); + m_layers_rst[lyr].raster.draw(poly); + } + else m_layers_rst[lyr].raster.draw(p); + } + + inline void begin_layer(unsigned lyr) { + if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1); + m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_mirror, m_gamma); + } + + inline void begin_layer() { + m_layers_rst.emplace_back(); + m_layers_rst.front().raster.reset(m_res, m_pxdim, m_mirror, m_gamma); + } + + inline void finish_layer(unsigned lyr_id) { + assert(lyr_id < m_layers_rst.size()); + m_layers_rst[lyr_id].rawbytes = + m_layers_rst[lyr_id].raster.save(Raster::Format::PNG); + m_layers_rst[lyr_id].raster.reset(); + } + + inline void finish_layer() { + if(!m_layers_rst.empty()) { + m_layers_rst.back().rawbytes = + m_layers_rst.back().raster.save(Raster::Format::PNG); + m_layers_rst.back().raster.reset(); + } + } + + void save(const std::string& fpath, const std::string& prjname = ""); + + void set_statistics(const std::vector statistics); +}; + +} // namespace sla +} // namespace Slic3r + +#endif // SLARASTERWRITER_HPP diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 6d2318bea..fba974822 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1281,37 +1281,11 @@ void SLAPrint::process() auto rasterize = [this, max_objstatus]() { if(canceled()) return; - // collect all the keys - - // If the raster has vertical orientation, we will flip the coordinates - bool flpXY = m_printer_config.display_orientation.getInt() == - SLADisplayOrientation::sladoPortrait; - { // create a raster printer for the current print parameters - // I don't know any better -// auto& ocfg = m_objects.front()->m_config; -// auto& matcfg = m_material_config; -// auto& printcfg = m_printer_config; - -// double w = printcfg.display_width.getFloat(); -// double h = printcfg.display_height.getFloat(); -// auto pw = unsigned(printcfg.display_pixels_x.getInt()); -// auto ph = unsigned(printcfg.display_pixels_y.getInt()); -// double lh = ocfg.layer_height.getFloat(); -// double exp_t = matcfg.exposure_time.getFloat(); -// double iexp_t = matcfg.initial_exposure_time.getFloat(); - -// double gamma = m_printer_config.gamma_correction.getFloat(); - -// if(flpXY) { std::swap(w, h); std::swap(pw, ph); } - -// m_printer.reset( -// new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t, -// flpXY? SLAPrinter::RO_PORTRAIT : -// SLAPrinter::RO_LANDSCAPE, -// gamma)); - - m_printer.reset(new SLAPrinter(m_printer_config, m_material_config, m_default_object_config.layer_height.getFloat())); + double layerh = m_default_object_config.layer_height.getFloat(); + m_printer.reset(new SLAPrinter(m_printer_config, + m_material_config, + layerh)); } // Allocate space for all the layers diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index dea468e7a..1ad40ef15 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -3,11 +3,11 @@ #include #include "PrintBase.hpp" -#include "PrintExport.hpp" +//#include "PrintExport.hpp" +#include "SLA/SLARasterWriter.hpp" #include "Point.hpp" #include "MTUtils.hpp" #include -#include "Zipper.hpp" namespace Slic3r { @@ -322,37 +322,6 @@ struct SLAPrintStatistics } }; -// The implementation of creating zipped archives with wxWidgets -template<> class LayerWriter { - Zipper m_zip; -public: - - LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {} - - void next_entry(const std::string& fname) { m_zip.add_entry(fname); } - - void binary_entry(const std::string& fname, - const std::uint8_t* buf, - size_t l) - { - m_zip.add_entry(fname, buf, l); - } - - template inline LayerWriter& operator<<(T&& arg) { - m_zip << std::forward(arg); return *this; - } - - bool is_ok() const { - return true; // m_zip blows up if something goes wrong... - } - - // After finalize, no writing to the archive will have an effect. The only - // valid operation is to dispose the object calling the destructor which - // should close the file. This method can throw and signal potential errors - // when flushing the archive. This is why its present. - void finalize() { m_zip.finalize(); } -}; - /** * @brief This class is the high level FSM for the SLA printing process. * @@ -385,11 +354,10 @@ public: // 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); } - template inline void export_raster(const std::string& fpath, - const std::string& projectname = "") + const std::string& projectname = "") { - if(m_printer) m_printer->save(fpath, projectname); + if(m_printer) m_printer->save(fpath, projectname); } const PrintObjects& objects() const { return m_objects; } @@ -450,7 +418,7 @@ public: const std::vector& print_layers() const { return m_printer_input; } private: - using SLAPrinter = FilePrinter; + using SLAPrinter = sla::SLARasterWriter; using SLAPrinterPtr = std::unique_ptr; // Implement same logic as in SLAPrintObject From 38d54d779a1b5ca2d7bd992ec5920d6963f94420 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Sat, 18 May 2019 23:21:59 +0200 Subject: [PATCH 05/44] Mirror checkboxes added to the UI. Everything seems to work fine. --- src/libslic3r/PrintConfig.cpp | 14 ++++++++------ src/libslic3r/SLA/SLARaster.cpp | 14 ++++++++++++++ src/libslic3r/SLA/SLARaster.hpp | 23 +++++++++++++++++++---- src/libslic3r/SLAPrint.cpp | 2 ++ src/slic3r/GUI/Preset.cpp | 1 + src/slic3r/GUI/Tab.cpp | 4 ++++ 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d000c2c2c..89e21934a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2259,15 +2259,17 @@ void PrintConfigDef::init_sla_params() def->set_default_value(new ConfigOptionInt(1440)); def = this->add("display_mirror_x", coBool); - def->full_label = L("Display mirroring in X axis"); - def->label = L("Mirror X"); - def->tooltip = L("Enable mirroring of output images in the X axis"); + def->full_label = L("Display horizontal mirroring"); + def->label = L("Mirror horizontally"); + def->tooltip = L("Enable horizontal mirroring of output images"); + def->mode = comExpert; def->set_default_value(new ConfigOptionBool(true)); def = this->add("display_mirror_y", coBool); - def->full_label = L("Display mirroring in Y axis"); - def->label = L("Mirror Y"); - def->tooltip = L("Enable mirroring of output images in the Y axis"); + def->full_label = L("Display vertical mirroring"); + def->label = L("Mirror vertically"); + def->tooltip = L("Enable vertical mirroring of output images"); + def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); def = this->add("display_orientation", coEnum); diff --git a/src/libslic3r/SLA/SLARaster.cpp b/src/libslic3r/SLA/SLARaster.cpp index fba1f46f3..f1b1c8c42 100644 --- a/src/libslic3r/SLA/SLARaster.cpp +++ b/src/libslic3r/SLA/SLARaster.cpp @@ -58,6 +58,7 @@ private: std::function m_gammafn; std::array m_mirror; + Format m_fmt = Format::PNG; inline void flipy(agg::path_storage& path) const { path.flip_y(0, m_resolution.height_px); @@ -101,6 +102,7 @@ public: case Format::PNG: m_mirror = {false, true}; break; case Format::RAW: m_mirror = {false, false}; break; } + m_fmt = fmt; } template void draw(const P &poly) { @@ -131,6 +133,8 @@ public: } inline TBuffer& buffer() { return m_buf; } + + inline Format format() const { return m_fmt; } inline const Raster::Resolution resolution() { return m_resolution; } @@ -258,6 +262,11 @@ void Raster::save(std::ostream& stream, Format fmt) } } +void Raster::save(std::ostream &stream) +{ + save(stream, m_impl->format()); +} + RawBytes Raster::save(Format fmt) { assert(m_impl); @@ -300,6 +309,11 @@ RawBytes Raster::save(Format fmt) return {std::move(data)}; } +RawBytes Raster::save() +{ + return save(m_impl->format()); +} + } } diff --git a/src/libslic3r/SLA/SLARaster.hpp b/src/libslic3r/SLA/SLARaster.hpp index 03fb06d6c..5051498c5 100644 --- a/src/libslic3r/SLA/SLARaster.hpp +++ b/src/libslic3r/SLA/SLARaster.hpp @@ -100,8 +100,17 @@ public: ~Raster(); /// Reallocated everything for the given resolution and pixel dimension. - void reset(const Resolution&, const PixelDim&, const std::array& mirror, double gamma = 1.0); - void reset(const Resolution& r, const PixelDim& pd, Format o, double gamma = 1.0); + /// The third parameter is either the X, Y mirroring or a supported format + /// for which the correct mirroring will be configured. + void reset(const Resolution&, + const PixelDim&, + const std::array& mirror, + double gamma = 1.0); + + void reset(const Resolution& r, + const PixelDim& pd, + Format o, + double gamma = 1.0); /** * Release the allocated resources. Drawing in this state ends in @@ -119,11 +128,17 @@ public: void draw(const ExPolygon& poly); void draw(const ClipperLib::Polygon& poly); + // Saving the raster: + // It is possible to override the format given in the constructor but + // be aware that the mirroring will not be modified. + /// Save the raster on the specified stream. - void save(std::ostream& stream, Format = Format::PNG); + void save(std::ostream& stream, Format); + void save(std::ostream& stream); /// Save into a continuous byte stream which is returned. - RawBytes save(Format fmt = Format::PNG); + RawBytes save(Format fmt); + RawBytes save(); }; } // sla diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index fba974822..13df2fa79 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1461,6 +1461,8 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector& Preset::sla_printer_options() "printer_technology", "bed_shape", "max_print_height", "display_width", "display_height", "display_pixels_x", "display_pixels_y", + "display_mirror_x", "display_mirror_y", "display_orientation", "fast_tilt_time", "slow_tilt_time", "area_fill", "relative_correction", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b6bdb7a4b..8fa34e37a 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2056,6 +2056,10 @@ void TabPrinter::build_sla() line.append_option(optgroup->get_option("display_pixels_y")); optgroup->append_line(line); optgroup->append_single_option_line("display_orientation"); + + // FIXME: This should be on one line in the UI + optgroup->append_single_option_line("display_mirror_x"); + optgroup->append_single_option_line("display_mirror_y"); optgroup = page->new_optgroup(_(L("Tilt"))); line = { _(L("Tilt time")), "" }; From 4e2ef09a506409697cc5f1f9340e2bf04343ede7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 May 2019 11:19:43 +0200 Subject: [PATCH 06/44] Fixing build on Win and OSX --- src/libslic3r/SLA/SLARaster.cpp | 15 +++++++-- src/libslic3r/SLA/SLARaster.hpp | 23 ++++++++------ src/libslic3r/SLA/SLARasterWriter.cpp | 4 +-- src/libslic3r/SLA/SLARasterWriter.hpp | 46 +++++++++++++++++++++------ src/libslic3r/SLAPrint.cpp | 2 +- 5 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/SLA/SLARaster.cpp b/src/libslic3r/SLA/SLARaster.cpp index f1b1c8c42..20891c3d4 100644 --- a/src/libslic3r/SLA/SLARaster.cpp +++ b/src/libslic3r/SLA/SLARaster.cpp @@ -1,6 +1,8 @@ #ifndef SLARASTER_CPP #define SLARASTER_CPP +#include + #include "SLARaster.hpp" #include "libslic3r/ExPolygon.hpp" #include @@ -179,10 +181,17 @@ private: const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255); const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0); -Raster::Raster() = default; +template<> Raster::Raster() { reset(); }; Raster::~Raster() = default; -Raster::Raster(Raster &&m) = default; -Raster& Raster::operator=(Raster&&) = default; + +// Raster::Raster(Raster &&m) = default; +// Raster& Raster::operator=(Raster&&) = default; + +// FIXME: remove after migrating to higher version of windows compiler +Raster::Raster(Raster &&m): m_impl(std::move(m.m_impl)) {} +Raster& Raster::operator=(Raster &&m) { + m_impl = std::move(m.m_impl); return *this; +} void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, Format fmt, double gamma) diff --git a/src/libslic3r/SLA/SLARaster.hpp b/src/libslic3r/SLA/SLARaster.hpp index 5051498c5..d3bd52d92 100644 --- a/src/libslic3r/SLA/SLARaster.hpp +++ b/src/libslic3r/SLA/SLARaster.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include namespace ClipperLib { struct Polygon; } @@ -27,22 +29,20 @@ public: const uint8_t * data() { return m_buffer.data(); } RawBytes(const RawBytes&) = delete; - RawBytes(RawBytes&&) = default; RawBytes& operator=(const RawBytes&) = delete; - RawBytes& operator=(RawBytes&&) = default; // ///////////////////////////////////////////////////////////////////////// // FIXME: the following is needed for MSVC2013 compatibility // ///////////////////////////////////////////////////////////////////////// -// RawBytes(const RawBytes&) = delete; -// RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} + // RawBytes(RawBytes&&) = default; + // RawBytes& operator=(RawBytes&&) = default; -// RawBytes& operator=(const RawBytes&) = delete; -// RawBytes& operator=(RawBytes&& mv) { -// m_buffer = std::move(mv.m_buffer); -// return *this; -// } + RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} + RawBytes& operator=(RawBytes&& mv) { + m_buffer = std::move(mv.m_buffer); + return *this; + } // ///////////////////////////////////////////////////////////////////////// }; @@ -92,7 +92,6 @@ public: reset(std::forward(args)...); } - Raster(); Raster(const Raster& cpy) = delete; Raster& operator=(const Raster& cpy) = delete; Raster(Raster&& m); @@ -141,6 +140,10 @@ public: RawBytes save(); }; +// This prevents the duplicate default constructor warning on MSVC2013 +template<> Raster::Raster(); + + } // sla } // Slic3r diff --git a/src/libslic3r/SLA/SLARasterWriter.cpp b/src/libslic3r/SLA/SLARasterWriter.cpp index b2fe0c72c..f7c3925ac 100644 --- a/src/libslic3r/SLA/SLARasterWriter.cpp +++ b/src/libslic3r/SLA/SLARasterWriter.cpp @@ -44,11 +44,11 @@ void SLARasterWriter::flpXY(ClipperLib::Polygon &poly) void SLARasterWriter::flpXY(ExPolygon &poly) { - for(auto& p : poly.contour.points) p = {p.y(), p.x()}; + for(auto& p : poly.contour.points) p = Point(p.y(), p.x()); std::reverse(poly.contour.points.begin(), poly.contour.points.end()); for(auto& h : poly.holes) { - for(auto& p : h.points) p = {p.y(), p.x()}; + for(auto& p : h.points) p = Point(p.y(), p.x()); std::reverse(h.points.begin(), h.points.end()); } } diff --git a/src/libslic3r/SLA/SLARasterWriter.hpp b/src/libslic3r/SLA/SLARasterWriter.hpp index 9fc23840e..7133d2dde 100644 --- a/src/libslic3r/SLA/SLARasterWriter.hpp +++ b/src/libslic3r/SLA/SLARasterWriter.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "libslic3r/PrintConfig.hpp" @@ -49,8 +50,18 @@ private: Layer(const Layer&) = delete; // The image is big, do not copy by accident Layer& operator=(const Layer&) = delete; - Layer(Layer&& m) = default; - Layer& operator=(Layer&&) = default; + // ///////////////////////////////////////////////////////////////////// + // FIXME: the following is needed for MSVC2013 compatibility + // ///////////////////////////////////////////////////////////////////// + + // Layer(Layer&& m) = default; + // Layer& operator=(Layer&&) = default; + Layer(Layer &&m): + raster(std::move(m.raster)), rawbytes(std::move(m.rawbytes)) {} + Layer& operator=(Layer &&m) { + raster = std::move(m.raster); rawbytes = std::move(m.rawbytes); + return *this; + } }; // We will save the compressed PNG data into RawBytes type buffers in @@ -83,13 +94,30 @@ public: SLARasterWriter(const SLARasterWriter& ) = delete; SLARasterWriter& operator=(const SLARasterWriter&) = delete; - SLARasterWriter(SLARasterWriter&& m) = default; - SLARasterWriter& operator=(SLARasterWriter&&) = default; -// SLARasterWriter(SLARasterWriter&& m) = default; -// SLARasterWriter(SLARasterWriter&& m): -// m_layers_rst(std::move(m.m_layers_rst)), -// m_res(m.m_res), -// m_pxdim(m.m_pxdim) {} + + // ///////////////////////////////////////////////////////////////////////// + // FIXME: the following is needed for MSVC2013 compatibility + // ///////////////////////////////////////////////////////////////////////// + + // SLARasterWriter(SLARasterWriter&& m) = default; + // SLARasterWriter& operator=(SLARasterWriter&&) = default; + SLARasterWriter(SLARasterWriter&& m): + m_layers_rst(std::move(m.m_layers_rst)), + m_res(m.m_res), + m_pxdim(m.m_pxdim), + m_exp_time_s(m.m_exp_time_s), + m_exp_time_first_s(m.m_exp_time_first_s), + m_layer_height(m.m_layer_height), + m_o(m.m_o), + m_mirror(std::move(m.m_mirror)), + m_gamma(m.m_gamma), + m_used_material(m.m_used_material), + m_cnt_fade_layers(m.m_cnt_fade_layers), + m_cnt_slow_layers(m.m_cnt_slow_layers), + m_cnt_fast_layers(m.m_cnt_fast_layers) + {} + + // ///////////////////////////////////////////////////////////////////////// inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } inline unsigned layers() const { return unsigned(m_layers_rst.size()); } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 13df2fa79..d07eba2b8 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -742,7 +742,7 @@ void SLAPrint::process() // We apply the printer correction offset here. if(clpr_offs != 0) po.m_model_slices[id] = - offset_ex(po.m_model_slices[id], clpr_offs); + offset_ex(po.m_model_slices[id], float(clpr_offs)); mit->set_model_slice_idx(po, id); ++mit; } From 3ab886b747c4742d1662a415d683857003c0388b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 4 Jun 2019 18:25:53 +0200 Subject: [PATCH 07/44] Fix of mesh decimation (the admesh library). Fixes "Unable to save project (#2445)" --- src/admesh/connect.cpp | 474 ++++++++++++++++----------------- src/admesh/shared.cpp | 417 ++++++++++++++--------------- src/admesh/stl.h | 2 + src/admesh/util.cpp | 47 ++++ src/libslic3r/TriangleMesh.cpp | 36 ++- 5 files changed, 495 insertions(+), 481 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index fb3213219..3069251d3 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -37,7 +37,6 @@ static void stl_match_neighbors_nearby(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b); static void stl_record_neighbors(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b); -static void stl_initialize_facet_check_exact(stl_file *stl); static void stl_initialize_facet_check_nearby(stl_file *stl); static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b); static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, @@ -47,63 +46,90 @@ static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, stl_hash_edge *edge_a, stl_hash_edge *edge_b)); static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b); static void stl_free_edges(stl_file *stl); -static void stl_remove_facet(stl_file *stl, int facet_number); static void stl_change_vertices(stl_file *stl, int facet_num, int vnot, stl_vertex new_vertex); static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b, int *facet1, int *vertex1, int *facet2, int *vertex2, stl_vertex *new_vertex1, stl_vertex *new_vertex2); -static void stl_remove_degenerate(stl_file *stl, int facet); extern int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag); -static void stl_update_connects_remove_1(stl_file *stl, int facet_num); + +static inline size_t hash_size_from_nr_faces(const size_t nr_faces) +{ + // Good primes for addressing a cca. 30 bit space. + // https://planetmath.org/goodhashtableprimes + static std::vector primes{ 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 }; + // Find a prime number for 50% filling of the shared triangle edges in the mesh. + auto it = std::upper_bound(primes.begin(), primes.end(), nr_faces * 3 * 2 - 1); + return (it == primes.end()) ? primes.back() : *it; +} // This function builds the neighbors list. No modifications are made // to any of the facets. The edges are said to match only if all six // floats of the first edge matches all six floats of the second edge. void stl_check_facets_exact(stl_file *stl) { - if (stl->error) - return; + if (stl->error) + return; - stl->stats.connected_edges = 0; - stl->stats.connected_facets_1_edge = 0; - stl->stats.connected_facets_2_edge = 0; - stl->stats.connected_facets_3_edge = 0; + stl->stats.connected_edges = 0; + stl->stats.connected_facets_1_edge = 0; + stl->stats.connected_facets_2_edge = 0; + stl->stats.connected_facets_3_edge = 0; - // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. - // Do it before the next step, as the next step stores references to the face indices in the hash tables and removing a facet - // will break the references. - for (int i = 0; i < stl->stats.number_of_facets;) { - stl_facet &facet = stl->facet_start[i]; - if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) { - // Remove the degenerate facet. - facet = stl->facet_start[--stl->stats.number_of_facets]; - stl->stats.facets_removed += 1; - stl->stats.degenerate_facets += 1; - } else - ++ i; - } + // If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. + // Do it before the next step, as the next step stores references to the face indices in the hash tables and removing a facet + // will break the references. + for (uint32_t i = 0; i < stl->stats.number_of_facets;) { + stl_facet &facet = stl->facet_start[i]; + if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) { + // Remove the degenerate facet. + facet = stl->facet_start[--stl->stats.number_of_facets]; + stl->stats.facets_removed += 1; + stl->stats.degenerate_facets += 1; + } else + ++ i; + } - // Connect neighbor edges. - stl_initialize_facet_check_exact(stl); - for (int i = 0; i < stl->stats.number_of_facets; i++) { - const stl_facet &facet = stl->facet_start[i]; - for (int j = 0; j < 3; j++) { - stl_hash_edge edge; - edge.facet_number = i; - edge.which_edge = j; - stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); - insert_hash_edge(stl, edge, stl_record_neighbors); - } - } - stl_free_edges(stl); + // Initialize hash table. + stl->stats.malloced = 0; + stl->stats.freed = 0; + stl->stats.collisions = 0; + stl->M = (int)hash_size_from_nr_faces(stl->stats.number_of_facets); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + // initialize neighbors list to -1 to mark unconnected edges + stl->neighbors_start[i].neighbor[0] = -1; + stl->neighbors_start[i].neighbor[1] = -1; + stl->neighbors_start[i].neighbor[2] = -1; + } + stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); + if (stl->heads == NULL) + perror("stl_initialize_facet_check_exact"); + stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); + if (stl->tail == NULL) + perror("stl_initialize_facet_check_exact"); + stl->tail->next = stl->tail; + for (int i = 0; i < stl->M; ++ i) + stl->heads[i] = stl->tail; + + // Connect neighbor edges. + for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { + const stl_facet &facet = stl->facet_start[i]; + for (int j = 0; j < 3; ++ j) { + stl_hash_edge edge; + edge.facet_number = i; + edge.which_edge = j; + stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); + insert_hash_edge(stl, edge, stl_record_neighbors); + } + } + stl_free_edges(stl); #if 0 - printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n", - stl->stats.number_of_facets, stl->stats.number_of_facets * 3, - stl->stats.connected_edges, stl->stats.number_of_facets * 3 - stl->stats.connected_edges); + printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n", + stl->stats.number_of_facets, stl->stats.number_of_facets * 3, + stl->stats.connected_edges, stl->stats.number_of_facets * 3 - stl->stats.connected_edges); #endif } @@ -141,48 +167,6 @@ static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_ve } } -static inline size_t hash_size_from_nr_faces(const size_t nr_faces) -{ - // Good primes for addressing a cca. 30 bit space. - // https://planetmath.org/goodhashtableprimes - static std::vector primes{ 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 }; - // Find a prime number for 50% filling of the shared triangle edges in the mesh. - auto it = std::upper_bound(primes.begin(), primes.end(), nr_faces * 3 * 2 - 1); - return (it == primes.end()) ? primes.back() : *it; -} - -static void -stl_initialize_facet_check_exact(stl_file *stl) { - int i; - - if (stl->error) return; - - stl->stats.malloced = 0; - stl->stats.freed = 0; - stl->stats.collisions = 0; - - stl->M = hash_size_from_nr_faces(stl->stats.number_of_facets); - - for (i = 0; i < stl->stats.number_of_facets ; i++) { - /* initialize neighbors list to -1 to mark unconnected edges */ - stl->neighbors_start[i].neighbor[0] = -1; - stl->neighbors_start[i].neighbor[1] = -1; - stl->neighbors_start[i].neighbor[2] = -1; - } - - stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); - if(stl->heads == NULL) perror("stl_initialize_facet_check_exact"); - - stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); - if(stl->tail == NULL) perror("stl_initialize_facet_check_exact"); - - stl->tail->next = stl->tail; - - for(i = 0; i < stl->M; i++) { - stl->heads[i] = stl->tail; - } -} - static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, void (*match_neighbors)(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b)) @@ -264,7 +248,7 @@ void stl_check_facets_nearby(stl_file *stl, float tolerance) stl_initialize_facet_check_nearby(stl); - for (int i = 0; i < stl->stats.number_of_facets; ++ i) { + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { //FIXME is the copy necessary? stl_facet facet = stl->facet_start[i]; for (int j = 0; j < 3; j++) { @@ -348,7 +332,7 @@ static void stl_initialize_facet_check_nearby(stl_file *stl) /* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/ /* tolerance *= 0.5;*/ - stl->M = hash_size_from_nr_faces(stl->stats.number_of_facets); + stl->M = (int)hash_size_from_nr_faces(stl->stats.number_of_facets); stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby"); @@ -611,181 +595,170 @@ stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, } } -static void -stl_remove_facet(stl_file *stl, int facet_number) { - int neighbor[3]; - int vnot[3]; - int i; - int j; +static void remove_facet(stl_file *stl, int facet_number) +{ + assert(! stl->error); + ++ stl->stats.facets_removed; + /* Update list of connected edges */ + stl_neighbors &neighbors = stl->neighbors_start[facet_number]; + // Update statistics on unconnected triangle edges. + switch ((neighbors.neighbor[0] == -1) + (neighbors.neighbor[1] == -1) + (neighbors.neighbor[2] == -1)) { + case 0: // Facet has 3 neighbors + -- stl->stats.connected_facets_3_edge; + -- stl->stats.connected_facets_2_edge; + -- stl->stats.connected_facets_1_edge; + break; + case 1: // Facet has 2 neighbors + -- stl->stats.connected_facets_2_edge; + -- stl->stats.connected_facets_1_edge; + break; + case 2: // Facet has 1 neighbor + -- stl->stats.connected_facets_1_edge; + case 3: // Facet has 0 neighbors + break; + default: + assert(false); + } - if (stl->error) return; + if (facet_number == -- stl->stats.number_of_facets) + // Removing the last face is easy, just forget the last face. + return; - stl->stats.facets_removed += 1; - /* Update list of connected edges */ - j = ((stl->neighbors_start[facet_number].neighbor[0] == -1) + - (stl->neighbors_start[facet_number].neighbor[1] == -1) + - (stl->neighbors_start[facet_number].neighbor[2] == -1)); - if(j == 2) { - stl->stats.connected_facets_1_edge -= 1; - } else if(j == 1) { - stl->stats.connected_facets_2_edge -= 1; - stl->stats.connected_facets_1_edge -= 1; - } else if(j == 0) { - stl->stats.connected_facets_3_edge -= 1; - stl->stats.connected_facets_2_edge -= 1; - stl->stats.connected_facets_1_edge -= 1; - } + // Copy the face and neighborship from the last face to facet_number. + stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets]; + neighbors = stl->neighbors_start[stl->stats.number_of_facets]; + // Update neighborship of faces, which used to point to the last face, now moved to facet_number. + for (int i = 0; i < 3; ++ i) + if (neighbors.neighbor[i] != -1) { + int &other_face_idx = stl->neighbors_start[neighbors.neighbor[i]].neighbor[(neighbors.which_vertex_not[i] + 1) % 3]; + if (other_face_idx != stl->stats.number_of_facets) { + printf("in remove_facet: neighbor = %d numfacets = %d this is wrong\n", other_face_idx, stl->stats.number_of_facets); + return; + } + other_face_idx = facet_number; + } +} - stl->facet_start[facet_number] = - stl->facet_start[stl->stats.number_of_facets - 1]; - /* I could reallocate at this point, but it is not really necessary. */ - stl->neighbors_start[facet_number] = - stl->neighbors_start[stl->stats.number_of_facets - 1]; - stl->stats.number_of_facets -= 1; +static void remove_degenerate(stl_file *stl, int facet) +{ + assert(! stl->error); - for(i = 0; i < 3; i++) { - neighbor[i] = stl->neighbors_start[facet_number].neighbor[i]; - vnot[i] = stl->neighbors_start[facet_number].which_vertex_not[i]; - } + // Update statistics on face connectivity. + auto stl_update_connects_remove_1 = [stl](int facet_num) { + assert(! stl->error); + //FIXME when decreasing 3_edge, should I increase 2_edge etc? + switch ((stl->neighbors_start[facet_num].neighbor[0] == -1) + (stl->neighbors_start[facet_num].neighbor[1] == -1) + (stl->neighbors_start[facet_num].neighbor[2] == -1)) { + case 0: // Facet has 3 neighbors + -- stl->stats.connected_facets_3_edge; break; + case 1: // Facet has 2 neighbors + -- stl->stats.connected_facets_2_edge; break; + case 2: // Facet has 1 neighbor + -- stl->stats.connected_facets_1_edge; break; + case 3: // Facet has 0 neighbors + break; + default: + assert(false); + } + }; - for(i = 0; i < 3; i++) { - if(neighbor[i] != -1) { - if(stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] != - stl->stats.number_of_facets) { - printf("\ -in stl_remove_facet: neighbor = %d numfacets = %d this is wrong\n", - stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3], - stl->stats.number_of_facets); - return; - } - stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] - = facet_number; - } - } + int edge_to_collapse = 0; + if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1]) { + if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { + // All 3 vertices are equal. Collapse the edge with no neighbor if it exists. + const int *nbr = stl->neighbors_start[facet].neighbor; + edge_to_collapse = (nbr[0] == -1) ? 0 : (nbr[1] == -1) ? 1 : 2; + } else { + edge_to_collapse = 0; + } + } else if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { + edge_to_collapse = 1; + } else if (stl->facet_start[facet].vertex[2] == stl->facet_start[facet].vertex[0]) { + edge_to_collapse = 2; + } else { + // No degenerate. Function shouldn't have been called. + return; + } + + int edge[3] = { (edge_to_collapse + 1) % 3, (edge_to_collapse + 2) % 3, edge_to_collapse }; + int neighbor[] = { + stl->neighbors_start[facet].neighbor[edge[0]], + stl->neighbors_start[facet].neighbor[edge[1]], + stl->neighbors_start[facet].neighbor[edge[2]] + }; + int vnot[] = { + stl->neighbors_start[facet].which_vertex_not[edge[0]], + stl->neighbors_start[facet].which_vertex_not[edge[1]], + stl->neighbors_start[facet].which_vertex_not[edge[2]] + }; + // Update statistics on edge connectivity. + if (neighbor[0] == -1) + stl_update_connects_remove_1(neighbor[1]); + if (neighbor[1] == -1) + stl_update_connects_remove_1(neighbor[0]); + + if (neighbor[0] >= 0) { + if (neighbor[1] >= 0) { + // Adjust the "flip" flag for the which_vertex_not values. + if (vnot[0] > 2) { + if (vnot[1] > 2) { + // The face to be removed has its normal flipped compared to the left & right neighbors, therefore after removing this face + // the two remaining neighbors will be oriented correctly. + vnot[0] -= 3; + vnot[1] -= 3; + } else + // One neighbor has its normal inverted compared to the face to be removed, the other is oriented equally. + // After removal, the two neighbors will have their normals flipped. + vnot[1] += 3; + } else if (vnot[1] > 2) + // One neighbor has its normal inverted compared to the face to be removed, the other is oriented equally. + // After removal, the two neighbors will have their normals flipped. + vnot[0] += 3; + } + stl->neighbors_start[neighbor[0]].neighbor[(vnot[0] + 1) % 3] = (neighbor[0] == neighbor[1]) ? -1 : neighbor[1]; + stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = vnot[1]; + } + if (neighbor[1] >= 0) { + stl->neighbors_start[neighbor[1]].neighbor[(vnot[1] + 1) % 3] = (neighbor[0] == neighbor[1]) ? -1 : neighbor[0]; + stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = vnot[0]; + } + if (neighbor[2] >= 0) { + stl_update_connects_remove_1(neighbor[2]); + stl->neighbors_start[neighbor[2]].neighbor[(vnot[2] + 1) % 3] = -1; + } + + remove_facet(stl, facet); } void stl_remove_unconnected_facets(stl_file *stl) { - /* A couple of things need to be done here. One is to remove any */ - /* completely unconnected facets (0 edges connected) since these are */ - /* useless and could be completely wrong. The second thing that needs to */ - /* be done is to remove any degenerate facets that were created during */ - /* stl_check_facets_nearby(). */ - if (stl->error) - return; + // A couple of things need to be done here. One is to remove any completely unconnected facets (0 edges connected) since these are + // useless and could be completely wrong. The second thing that needs to be done is to remove any degenerate facets that were created during + // stl_check_facets_nearby(). + if (stl->error) + return; - // remove degenerate facets - for (int i = 0; i < stl->stats.number_of_facets; ++ i) { - if(stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[1] || - stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[2] || - stl->facet_start[i].vertex[1] == stl->facet_start[i].vertex[2]) { - stl_remove_degenerate(stl, i); - i--; - } - } + // remove degenerate facets + for (uint32_t i = 0; i < stl->stats.number_of_facets;) + if (stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[1] || + stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[2] || + stl->facet_start[i].vertex[1] == stl->facet_start[i].vertex[2]) { + remove_degenerate(stl, i); +// assert(stl_validate(stl)); + } else + ++ i; - if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) { - // remove completely unconnected facets - for (int i = 0; i < stl->stats.number_of_facets; i++) { - if (stl->neighbors_start[i].neighbor[0] == -1 && - stl->neighbors_start[i].neighbor[1] == -1 && - stl->neighbors_start[i].neighbor[2] == -1) { - // This facet is completely unconnected. Remove it. - stl_remove_facet(stl, i); - -- i; - } - } - } -} - -static void -stl_remove_degenerate(stl_file *stl, int facet) { - int edge1; - int edge2; - int edge3; - int neighbor1; - int neighbor2; - int neighbor3; - int vnot1; - int vnot2; - int vnot3; - - if (stl->error) return; - - if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1] && - stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { - /* all 3 vertices are equal. Just remove the facet. I don't think*/ - /* this is really possible, but just in case... */ - printf("removing a facet in stl_remove_degenerate\n"); - stl_remove_facet(stl, facet); - return; - } - - if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1]) { - edge1 = 1; - edge2 = 2; - edge3 = 0; - } else if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { - edge1 = 0; - edge2 = 2; - edge3 = 1; - } else if (stl->facet_start[facet].vertex[2] == stl->facet_start[facet].vertex[0]) { - edge1 = 0; - edge2 = 1; - edge3 = 2; - } else { - /* No degenerate. Function shouldn't have been called. */ - return; - } - neighbor1 = stl->neighbors_start[facet].neighbor[edge1]; - neighbor2 = stl->neighbors_start[facet].neighbor[edge2]; - - if(neighbor1 == -1) { - stl_update_connects_remove_1(stl, neighbor2); - } - if(neighbor2 == -1) { - stl_update_connects_remove_1(stl, neighbor1); - } - - - neighbor3 = stl->neighbors_start[facet].neighbor[edge3]; - vnot1 = stl->neighbors_start[facet].which_vertex_not[edge1]; - vnot2 = stl->neighbors_start[facet].which_vertex_not[edge2]; - vnot3 = stl->neighbors_start[facet].which_vertex_not[edge3]; - - if(neighbor1 >= 0){ - stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2; - stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2; - } - if(neighbor2 >= 0){ - stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1; - stl->neighbors_start[neighbor2].which_vertex_not[(vnot2 + 1) % 3] = vnot1; - } - - stl_remove_facet(stl, facet); - - if(neighbor3 >= 0) { - stl_update_connects_remove_1(stl, neighbor3); - stl->neighbors_start[neighbor3].neighbor[(vnot3 + 1) % 3] = -1; - } -} - -void -stl_update_connects_remove_1(stl_file *stl, int facet_num) { - int j; - - if (stl->error) return; - /* Update list of connected edges */ - j = ((stl->neighbors_start[facet_num].neighbor[0] == -1) + - (stl->neighbors_start[facet_num].neighbor[1] == -1) + - (stl->neighbors_start[facet_num].neighbor[2] == -1)); - if(j == 0) { /* Facet has 3 neighbors */ - stl->stats.connected_facets_3_edge -= 1; - } else if(j == 1) { /* Facet has 2 neighbors */ - stl->stats.connected_facets_2_edge -= 1; - } else if(j == 2) { /* Facet has 1 neighbor */ - stl->stats.connected_facets_1_edge -= 1; - } + if (stl->stats.connected_facets_1_edge < (int)stl->stats.number_of_facets) { + // remove completely unconnected facets + for (uint32_t i = 0; i < stl->stats.number_of_facets;) + if (stl->neighbors_start[i].neighbor[0] == -1 && + stl->neighbors_start[i].neighbor[1] == -1 && + stl->neighbors_start[i].neighbor[2] == -1) { + // This facet is completely unconnected. Remove it. + remove_facet(stl, i); + assert(stl_validate(stl)); + } else + ++ i; + } } void @@ -801,7 +774,6 @@ stl_fill_holes(stl_file *stl) { int next_edge; int pivot_vertex; int next_facet; - int i; int j; int k; @@ -809,7 +781,7 @@ stl_fill_holes(stl_file *stl) { /* Insert all unconnected edges into hash list */ stl_initialize_facet_check_nearby(stl); - for(i = 0; i < stl->stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { facet = stl->facet_start[i]; for(j = 0; j < 3; j++) { if(stl->neighbors_start[i].neighbor[j] != -1) continue; @@ -822,7 +794,7 @@ stl_fill_holes(stl_file *stl) { } } - for(i = 0; i < stl->stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { facet = stl->facet_start[i]; neighbors_initial[0] = stl->neighbors_start[i].neighbor[0]; neighbors_initial[1] = stl->neighbors_start[i].neighbor[1]; @@ -900,7 +872,7 @@ stl_add_facet(stl_file *stl, stl_facet *new_facet) { if (stl->error) return; stl->stats.facets_added += 1; - if(stl->stats.facets_malloced < stl->stats.number_of_facets + 1) { + if(stl->stats.facets_malloced < (int)stl->stats.number_of_facets + 1) { stl->facet_start = (stl_facet*)realloc(stl->facet_start, (sizeof(stl_facet) * (stl->stats.facets_malloced + 256))); if(stl->facet_start == NULL) perror("stl_add_facet"); diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index c8c17ccd5..2ad270903 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -23,242 +23,239 @@ #include #include +#include + #include #include "stl.h" -void -stl_invalidate_shared_vertices(stl_file *stl) { - if (stl->error) return; +void stl_invalidate_shared_vertices(stl_file *stl) +{ + if (stl->error) + return; - if (stl->v_indices != NULL) { - free(stl->v_indices); - stl->v_indices = NULL; - } - if (stl->v_shared != NULL) { - free(stl->v_shared); - stl->v_shared = NULL; - } + if (stl->v_indices != nullptr) { + free(stl->v_indices); + stl->v_indices = nullptr; + } + if (stl->v_shared != nullptr) { + free(stl->v_shared); + stl->v_shared = nullptr; + } } -void -stl_generate_shared_vertices(stl_file *stl) { - int i; - int j; - int first_facet; - int direction; - int facet_num; - int vnot; - int next_edge; - int pivot_vertex; - int next_facet; - int reversed; +void stl_generate_shared_vertices(stl_file *stl) +{ + if (stl->error) + return; - if (stl->error) return; + /* make sure this function is idempotent and does not leak memory */ + stl_invalidate_shared_vertices(stl); - /* make sure this function is idempotent and does not leak memory */ - stl_invalidate_shared_vertices(stl); + // 3 indices to vertex per face + stl->v_indices = (v_indices_struct*)calloc(stl->stats.number_of_facets, sizeof(v_indices_struct)); + if (stl->v_indices == nullptr) + perror("stl_generate_shared_vertices"); + // Shared vertices (3D coordinates) + stl->v_shared = (stl_vertex*)calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex)); + if (stl->v_shared == nullptr) + perror("stl_generate_shared_vertices"); + stl->stats.shared_malloced = stl->stats.number_of_facets / 2; + stl->stats.shared_vertices = 0; - stl->v_indices = (v_indices_struct*) - calloc(stl->stats.number_of_facets, sizeof(v_indices_struct)); - if(stl->v_indices == NULL) perror("stl_generate_shared_vertices"); - stl->v_shared = (stl_vertex*) - calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex)); - if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); - stl->stats.shared_malloced = stl->stats.number_of_facets / 2; - stl->stats.shared_vertices = 0; + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + // vertex index -1 means no shared vertex was assigned yet. + stl->v_indices[i].vertex[0] = -1; + stl->v_indices[i].vertex[1] = -1; + stl->v_indices[i].vertex[2] = -1; + } - for(i = 0; i < stl->stats.number_of_facets; i++) { - stl->v_indices[i].vertex[0] = -1; - stl->v_indices[i].vertex[1] = -1; - stl->v_indices[i].vertex[2] = -1; - } + // A degenerate mesh may contain loops: Traversing a fan will end up in an endless loop + // while never reaching the starting face. To avoid these endless loops, traversed faces at each fan traversal + // are marked with a unique fan_traversal_stamp. + unsigned int fan_traversal_stamp = 0; + std::vector fan_traversal_facet_visited(stl->stats.number_of_facets, 0); + for (uint32_t facet_idx = 0; facet_idx < stl->stats.number_of_facets; ++ facet_idx) { + for (int j = 0; j < 3; ++ j) { + if (stl->v_indices[facet_idx].vertex[j] != -1) + // Shared vertex was already assigned. + continue; + // Create a new shared vertex. + if (stl->stats.shared_vertices == stl->stats.shared_malloced) { + stl->stats.shared_malloced += 1024; + stl->v_shared = (stl_vertex*)realloc(stl->v_shared, stl->stats.shared_malloced * sizeof(stl_vertex)); + if(stl->v_shared == nullptr) + perror("stl_generate_shared_vertices"); + } + stl->v_shared[stl->stats.shared_vertices] = stl->facet_start[facet_idx].vertex[j]; + // Traverse the fan around the j-th vertex of the i-th face, assign the newly created shared vertex index to all the neighboring triangles in the triangle fan. + int facet_in_fan_idx = facet_idx; + bool edge_direction = false; + bool traversal_reversed = false; + int vnot = (j + 2) % 3; + // Increase the + ++ fan_traversal_stamp; + for (;;) { + // Next edge on facet_in_fan_idx to be traversed. The edge is indexed by its starting vertex index. + int next_edge = 0; + // Vertex index in facet_in_fan_idx, which is being pivoted around, and which is being assigned a new shared vertex. + int pivot_vertex = 0; + if (vnot > 2) { + // The edge of facet_in_fan_idx opposite to vnot is equally oriented, therefore + // the neighboring facet is flipped. + if (! edge_direction) { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } else { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot % 3; + } + edge_direction = ! edge_direction; + } else { + // The neighboring facet is correctly oriented. + if (! edge_direction) { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot; + } else { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + } + stl->v_indices[facet_in_fan_idx].vertex[pivot_vertex] = stl->stats.shared_vertices; + fan_traversal_facet_visited[facet_in_fan_idx] = fan_traversal_stamp; - for(i = 0; i < stl->stats.number_of_facets; i++) { - first_facet = i; - for(j = 0; j < 3; j++) { - if(stl->v_indices[i].vertex[j] != -1) { - continue; - } - if(stl->stats.shared_vertices == stl->stats.shared_malloced) { - stl->stats.shared_malloced += 1024; - stl->v_shared = (stl_vertex*)realloc(stl->v_shared, - stl->stats.shared_malloced * sizeof(stl_vertex)); - if(stl->v_shared == NULL) perror("stl_generate_shared_vertices"); - } + // next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge! + int next_facet = stl->neighbors_start[facet_in_fan_idx].neighbor[next_edge]; + if (next_facet == -1) { + // No neighbor going in the current direction. + if (traversal_reversed) { + // Went to one limit, then turned back and reached the other limit. Quit the fan traversal. + break; + } else { + // Reached the first limit. Now try to reverse and traverse up to the other limit. + edge_direction = true; + vnot = (j + 1) % 3; + traversal_reversed = true; + facet_in_fan_idx = facet_idx; + } + } else if (next_facet == facet_idx) { + // Traversed a closed fan all around. +// assert(! traversal_reversed); + break; + } else if (next_facet >= (int)stl->stats.number_of_facets) { + // The mesh is not valid! + // assert(false); + break; + } else if (fan_traversal_facet_visited[next_facet] == fan_traversal_stamp) { + // Traversed a closed fan all around, but did not reach the starting face. + // This indicates an invalid geometry (non-manifold). + //assert(false); + break; + } else { + // Continue traversal. + // next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge! + vnot = stl->neighbors_start[facet_in_fan_idx].which_vertex_not[next_edge]; + facet_in_fan_idx = next_facet; + } + } - stl->v_shared[stl->stats.shared_vertices] = - stl->facet_start[i].vertex[j]; - - direction = 0; - reversed = 0; - facet_num = i; - vnot = (j + 2) % 3; - - for(;;) { - if(vnot > 2) { - if(direction == 0) { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - direction = 1; - } else { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot % 3; - direction = 0; - } - } else { - if(direction == 0) { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot; - } else { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - } - } - stl->v_indices[facet_num].vertex[pivot_vertex] = - stl->stats.shared_vertices; - - next_facet = stl->neighbors_start[facet_num].neighbor[next_edge]; - if(next_facet == -1) { - if(reversed) { - break; - } else { - direction = 1; - vnot = (j + 1) % 3; - reversed = 1; - facet_num = first_facet; - } - } else if(next_facet != first_facet) { - vnot = stl->neighbors_start[facet_num]. - which_vertex_not[next_edge]; - facet_num = next_facet; - } else { - break; - } - } - stl->stats.shared_vertices += 1; - } - } + ++ stl->stats.shared_vertices; + } + } } -void -stl_write_off(stl_file *stl, const char *file) { - int i; - FILE *fp; - char *error_msg; +void stl_write_off(stl_file *stl, const char *file) +{ + if (stl->error) + return; - if (stl->error) return; + /* Open the file */ + FILE *fp = boost::nowide::fopen(file, "w"); + if (fp == nullptr) { + char *error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ + sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); + perror(error_msg); + free(error_msg); + stl->error = 1; + return; + } - /* Open the file */ - fp = boost::nowide::fopen(file, "w"); - if(fp == NULL) { - error_msg = (char*) - malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", - file); - perror(error_msg); - free(error_msg); - stl->error = 1; - return; - } - - fprintf(fp, "OFF\n"); - fprintf(fp, "%d %d 0\n", - stl->stats.shared_vertices, stl->stats.number_of_facets); - - for(i = 0; i < stl->stats.shared_vertices; i++) { - fprintf(fp, "\t%f %f %f\n", - stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); - } - for(i = 0; i < stl->stats.number_of_facets; i++) { - fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], - stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); - } - fclose(fp); + fprintf(fp, "OFF\n"); + fprintf(fp, "%d %d 0\n", stl->stats.shared_vertices, stl->stats.number_of_facets); + for (int i = 0; i < stl->stats.shared_vertices; ++ i) + fprintf(fp, "\t%f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); + fclose(fp); } -void -stl_write_vrml(stl_file *stl, const char *file) { - int i; - FILE *fp; - char *error_msg; +void stl_write_vrml(stl_file *stl, const char *file) +{ + if (stl->error) + return; - if (stl->error) return; + /* Open the file */ + FILE *fp = boost::nowide::fopen(file, "w"); + if (fp == nullptr) { + char *error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ + sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); + perror(error_msg); + free(error_msg); + stl->error = 1; + return; + } - /* Open the file */ - fp = boost::nowide::fopen(file, "w"); - if(fp == NULL) { - error_msg = (char*) - malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", - file); - perror(error_msg); - free(error_msg); - stl->error = 1; - return; - } + fprintf(fp, "#VRML V1.0 ascii\n\n"); + fprintf(fp, "Separator {\n"); + fprintf(fp, "\tDEF STLShape ShapeHints {\n"); + fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n"); + fprintf(fp, "\t\tfaceType CONVEX\n"); + fprintf(fp, "\t\tshapeType SOLID\n"); + fprintf(fp, "\t\tcreaseAngle 0.0\n"); + fprintf(fp, "\t}\n"); + fprintf(fp, "\tDEF STLModel Separator {\n"); + fprintf(fp, "\t\tDEF STLColor Material {\n"); + fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n"); + fprintf(fp, "\t\t}\n"); + fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n"); + fprintf(fp, "\t\t\tpoint [\n"); - fprintf(fp, "#VRML V1.0 ascii\n\n"); - fprintf(fp, "Separator {\n"); - fprintf(fp, "\tDEF STLShape ShapeHints {\n"); - fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n"); - fprintf(fp, "\t\tfaceType CONVEX\n"); - fprintf(fp, "\t\tshapeType SOLID\n"); - fprintf(fp, "\t\tcreaseAngle 0.0\n"); - fprintf(fp, "\t}\n"); - fprintf(fp, "\tDEF STLModel Separator {\n"); - fprintf(fp, "\t\tDEF STLColor Material {\n"); - fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n"); - fprintf(fp, "\t\t}\n"); - fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n"); - fprintf(fp, "\t\t\tpoint [\n"); + int i = 0; + for (; i < (stl->stats.shared_vertices - 1); i++) + fprintf(fp, "\t\t\t\t%f %f %f,\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); + fprintf(fp, "\t\t\t\t%f %f %f]\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); + fprintf(fp, "\t\t}\n"); + fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n"); + fprintf(fp, "\t\t\tcoordIndex [\n"); - for(i = 0; i < (stl->stats.shared_vertices - 1); i++) { - fprintf(fp, "\t\t\t\t%f %f %f,\n", - stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); - } - fprintf(fp, "\t\t\t\t%f %f %f]\n", - stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); - fprintf(fp, "\t\t}\n"); - fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n"); - fprintf(fp, "\t\t\tcoordIndex [\n"); - - for(i = 0; i < (stl->stats.number_of_facets - 1); i++) { - fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0], - stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); - } - fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0], - stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); - fprintf(fp, "\t\t}\n"); - fprintf(fp, "\t}\n"); - fprintf(fp, "}\n"); - fclose(fp); + for (int i = 0; i + 1 < (int)stl->stats.number_of_facets; ++ i) + fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); + fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); + fprintf(fp, "\t\t}\n"); + fprintf(fp, "\t}\n"); + fprintf(fp, "}\n"); + fclose(fp); } -void stl_write_obj (stl_file *stl, const char *file) { - int i; - FILE* fp; +void stl_write_obj (stl_file *stl, const char *file) +{ + if (stl->error) + return; - if (stl->error) return; + FILE *fp = boost::nowide::fopen(file, "w"); + if (fp == nullptr) { + char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ + sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); + perror(error_msg); + free(error_msg); + stl->error = 1; + return; + } - /* Open the file */ - fp = boost::nowide::fopen(file, "w"); - if (fp == NULL) { - char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); - perror(error_msg); - free(error_msg); - stl->error = 1; - return; - } - - for (i = 0; i < stl->stats.shared_vertices; i++) { - fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); - } - for (i = 0; i < stl->stats.number_of_facets; i++) { - fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1); - } - - fclose(fp); + for (int i = 0; i < stl->stats.shared_vertices; ++ i) + fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1); + fclose(fp); } diff --git a/src/admesh/stl.h b/src/admesh/stl.h index f867e197b..5ecd94bd1 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -277,5 +277,7 @@ extern void stl_add_facet(stl_file *stl, stl_facet *new_facet); extern void stl_clear_error(stl_file *stl); extern int stl_get_error(stl_file *stl); extern void stl_exit_on_error(stl_file *stl); +// Validate the mesh, assert on error. +extern bool stl_validate(stl_file *stl); #endif diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index 305a58e22..c2d2c2726 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -457,3 +457,50 @@ All facets connected. No further nearby check necessary.\n"); stl_verify_neighbors(stl); } } + +// Check validity of the mesh, assert on error. +bool stl_validate(stl_file *stl) +{ + assert(! stl->error); + assert(stl->fp == nullptr); + assert(stl->facet_start != nullptr); + assert(stl->heads == nullptr); + assert(stl->tail == nullptr); + assert(stl->neighbors_start != nullptr); + assert((stl->v_indices == nullptr) == (stl->v_shared == nullptr)); + assert(stl->stats.number_of_facets > 0); + +#ifdef _DEBUG + // Verify validity of neighborship data. + for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) { + const stl_neighbors &nbr = stl->neighbors_start[facet_idx]; + const int *vertices = (stl->v_indices == nullptr) ? nullptr : stl->v_indices[facet_idx].vertex; + for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) { + int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx]; + assert(nbr_face < (int)stl->stats.number_of_facets); + if (nbr_face != -1) { + int nbr_vnot = nbr.which_vertex_not[nbr_idx]; + assert(nbr_vnot >= 0 && nbr_vnot < 6); + // Neighbor of the neighbor is the original face. + assert(stl->neighbors_start[nbr_face].neighbor[(nbr_vnot + 1) % 3] == facet_idx); + int vnot_back = stl->neighbors_start[nbr_face].which_vertex_not[(nbr_vnot + 1) % 3]; + assert(vnot_back >= 0 && vnot_back < 6); + assert((nbr_vnot < 3) == (vnot_back < 3)); + assert(vnot_back % 3 == (nbr_idx + 2) % 3); + if (vertices != nullptr) { + // Has shared vertices. + if (nbr_vnot < 3) { + // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are correctly oriented. + assert((stl->v_indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[(nbr_idx + 1) % 3] && stl->v_indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[nbr_idx])); + } else { + // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped. + assert((stl->v_indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[(nbr_idx + 1) % 3] && stl->v_indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[nbr_idx])); + } + } + } + } + } +#endif /* _DEBUG */ + + return true; +} diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 4d35cabca..b7c6c07a7 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -104,18 +104,21 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other) void TriangleMesh::repair() { - if (this->repaired) return; + if (this->repaired) + return; // admesh fails when repairing empty meshes - if (this->stl.stats.number_of_facets == 0) return; + if (this->stl.stats.number_of_facets == 0) + return; BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started"; - + // checking exact #ifdef SLIC3R_TRACE_REPAIR BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact"; #endif /* SLIC3R_TRACE_REPAIR */ stl_check_facets_exact(&stl); + assert(stl_validate(&this->stl)); stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge); stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge); stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge); @@ -141,6 +144,7 @@ void TriangleMesh::repair() } } } + assert(stl_validate(&this->stl)); // remove_unconnected if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) { @@ -148,6 +152,7 @@ void TriangleMesh::repair() BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets"; #endif /* SLIC3R_TRACE_REPAIR */ stl_remove_unconnected_facets(&stl); + assert(stl_validate(&this->stl)); } // fill_holes @@ -168,24 +173,28 @@ void TriangleMesh::repair() BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions"; #endif /* SLIC3R_TRACE_REPAIR */ stl_fix_normal_directions(&stl); + assert(stl_validate(&this->stl)); // normal_values #ifdef SLIC3R_TRACE_REPAIR BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values"; #endif /* SLIC3R_TRACE_REPAIR */ stl_fix_normal_values(&stl); + assert(stl_validate(&this->stl)); // always calculate the volume and reverse all normals if volume is negative #ifdef SLIC3R_TRACE_REPAIR BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume"; #endif /* SLIC3R_TRACE_REPAIR */ stl_calculate_volume(&stl); + assert(stl_validate(&this->stl)); // neighbors #ifdef SLIC3R_TRACE_REPAIR BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors"; #endif /* SLIC3R_TRACE_REPAIR */ stl_verify_neighbors(&stl); + assert(stl_validate(&this->stl)); this->repaired = true; @@ -594,27 +603,14 @@ TriangleMesh TriangleMesh::convex_hull_3d() const void TriangleMesh::require_shared_vertices() { BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start"; - if (!this->repaired) + assert(stl_validate(&this->stl)); + if (! this->repaired) this->repair(); - if (this->stl.v_shared == NULL) { + if (this->stl.v_shared == nullptr) { BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices"; stl_generate_shared_vertices(&(this->stl)); } -#ifdef _DEBUG - // Verify validity of neighborship data. - for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) { - const stl_neighbors &nbr = stl.neighbors_start[facet_idx]; - const int *vertices = stl.v_indices[facet_idx].vertex; - for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) { - int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx]; - if (nbr_face != -1) { - assert( - (stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3] && stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]) || - (stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[(nbr_idx + 1) % 3] && stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[nbr_idx])); - } - } - } -#endif /* _DEBUG */ + assert(stl_validate(&this->stl)); BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; } From 8da54139c463a09ec899a9df6bccae16802dccdf Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 4 Jun 2019 22:06:42 +0200 Subject: [PATCH 08/44] WIP: Admesh - replacement of C memory allocation with std vectors --- CMakeLists.txt | 2 +- src/admesh/connect.cpp | 401 ++++++++----------- src/admesh/normals.cpp | 26 +- src/admesh/shared.cpp | 37 +- src/admesh/stl.h | 187 +++++---- src/admesh/stl_io.cpp | 30 +- src/admesh/stlinit.cpp | 104 ++--- src/admesh/util.cpp | 104 +++-- src/libslic3r/Format/3mf.cpp | 2 +- src/libslic3r/Format/AMF.cpp | 2 +- src/libslic3r/Format/PRUS.cpp | 6 +- src/libslic3r/Model.cpp | 18 +- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 17 +- src/libslic3r/SlicingAdaptive.cpp | 4 +- src/libslic3r/TriangleMesh.cpp | 83 ++-- src/libslic3r/TriangleMesh.hpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 8 +- 17 files changed, 450 insertions(+), 585 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 719bdd04e..3d7157d5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ if (MSVC) # /bigobj (Increase Number of Sections in .Obj file) # error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater # Generate symbols at every build target, even for the release. - add_compile_options(-bigobj -Zm316 /Zi) + add_compile_options(-bigobj -Zm520 /Zi) endif () # Display and check CMAKE_PREFIX_PATH diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index 3069251d3..b99f93f3d 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -97,18 +97,10 @@ void stl_check_facets_exact(stl_file *stl) stl->stats.freed = 0; stl->stats.collisions = 0; stl->M = (int)hash_size_from_nr_faces(stl->stats.number_of_facets); - for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { - // initialize neighbors list to -1 to mark unconnected edges - stl->neighbors_start[i].neighbor[0] = -1; - stl->neighbors_start[i].neighbor[1] = -1; - stl->neighbors_start[i].neighbor[2] = -1; - } - stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); - if (stl->heads == NULL) - perror("stl_initialize_facet_check_exact"); - stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); - if (stl->tail == NULL) - perror("stl_initialize_facet_check_exact"); + for (auto &neighbor : stl->neighbors_start) + neighbor.reset(); + stl->heads.assign(stl->M, nullptr); + stl->tail = new stl_hash_edge; stl->tail->next = stl->tail; for (int i = 0; i < stl->M; ++ i) stl->heads[i] = stl->tail; @@ -180,7 +172,7 @@ static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, stl_hash_edge *temp; if(link == stl->tail) { /* This list doesn't have any edges currently in it. Add this one. */ - new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); + new_edge = new stl_hash_edge; if(new_edge == NULL) perror("insert_hash_edge"); stl->stats.malloced++; *new_edge = edge; @@ -192,7 +184,7 @@ static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, match_neighbors(stl, &edge, link); /* Delete the matched edge from the list. */ stl->heads[chain_number] = link->next; - free(link); + delete link; stl->stats.freed++; return; } else { @@ -200,7 +192,7 @@ static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, for(;;) { if(link->next == stl->tail) { /* This is the last item in the list. Insert a new edge. */ - new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); + new_edge = new stl_hash_edge; if(new_edge == NULL) perror("insert_hash_edge"); stl->stats.malloced++; *new_edge = edge; @@ -215,7 +207,7 @@ static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, /* Delete the matched edge from the list. */ temp = link->next; link->next = link->next->next; - free(temp); + delete temp; stl->stats.freed++; return; } else { @@ -307,48 +299,38 @@ static void stl_free_edges(stl_file *stl) for (int i = 0; i < stl->M; i++) { for (stl_hash_edge *temp = stl->heads[i]; stl->heads[i] != stl->tail; temp = stl->heads[i]) { stl->heads[i] = stl->heads[i]->next; - free(temp); + delete temp; ++ stl->stats.freed; } } } - free(stl->heads); - stl->heads = nullptr; - free(stl->tail); + stl->heads.clear(); + delete stl->tail; stl->tail = nullptr; } static void stl_initialize_facet_check_nearby(stl_file *stl) { - int i; + if (stl->error) + return; - if (stl->error) return; + stl->stats.malloced = 0; + stl->stats.freed = 0; + stl->stats.collisions = 0; - stl->stats.malloced = 0; - stl->stats.freed = 0; - stl->stats.collisions = 0; + /* tolerance = STL_MAX(stl->stats.shortest_edge, tolerance);*/ + /* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/ + /* tolerance *= 0.5;*/ + stl->M = (int)hash_size_from_nr_faces(stl->stats.number_of_facets); - /* tolerance = STL_MAX(stl->stats.shortest_edge, tolerance);*/ - /* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/ - /* tolerance *= 0.5;*/ + stl->heads.assign(stl->M, nullptr); + stl->tail = new stl_hash_edge; + stl->tail->next = stl->tail; - stl->M = (int)hash_size_from_nr_faces(stl->stats.number_of_facets); - - stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads)); - if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby"); - - stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge)); - if(stl->tail == NULL) perror("stl_initialize_facet_check_nearby"); - - stl->tail->next = stl->tail; - - for(i = 0; i < stl->M; i++) { - stl->heads[i] = stl->tail; - } + for (int i = 0; i < stl->M; ++ i) + stl->heads[i] = stl->tail; } - - static void stl_record_neighbors(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b) { @@ -358,29 +340,19 @@ stl_record_neighbors(stl_file *stl, if (stl->error) return; /* Facet a's neighbor is facet b */ - stl->neighbors_start[edge_a->facet_number].neighbor[edge_a->which_edge % 3] = - edge_b->facet_number; /* sets the .neighbor part */ - - stl->neighbors_start[edge_a->facet_number]. - which_vertex_not[edge_a->which_edge % 3] = - (edge_b->which_edge + 2) % 3; /* sets the .which_vertex_not part */ + stl->neighbors_start[edge_a->facet_number].neighbor[edge_a->which_edge % 3] = edge_b->facet_number; /* sets the .neighbor part */ + stl->neighbors_start[edge_a->facet_number].which_vertex_not[edge_a->which_edge % 3] = (edge_b->which_edge + 2) % 3; /* sets the .which_vertex_not part */ /* Facet b's neighbor is facet a */ - stl->neighbors_start[edge_b->facet_number].neighbor[edge_b->which_edge % 3] = - edge_a->facet_number; /* sets the .neighbor part */ - - stl->neighbors_start[edge_b->facet_number]. - which_vertex_not[edge_b->which_edge % 3] = - (edge_a->which_edge + 2) % 3; /* sets the .which_vertex_not part */ + stl->neighbors_start[edge_b->facet_number].neighbor[edge_b->which_edge % 3] = edge_a->facet_number; /* sets the .neighbor part */ + stl->neighbors_start[edge_b->facet_number].which_vertex_not[edge_b->which_edge % 3] = (edge_a->which_edge + 2) % 3; /* sets the .which_vertex_not part */ if( ((edge_a->which_edge < 3) && (edge_b->which_edge < 3)) || ((edge_a->which_edge > 2) && (edge_b->which_edge > 2))) { /* these facets are oriented in opposite directions. */ /* their normals are probably messed up. */ - stl->neighbors_start[edge_a->facet_number]. - which_vertex_not[edge_a->which_edge % 3] += 3; - stl->neighbors_start[edge_b->facet_number]. - which_vertex_not[edge_b->which_edge % 3] += 3; + stl->neighbors_start[edge_a->facet_number].which_vertex_not[edge_a->which_edge % 3] += 3; + stl->neighbors_start[edge_b->facet_number].which_vertex_not[edge_b->which_edge % 3] += 3; } @@ -561,8 +533,7 @@ stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, *facet1 = -1; } else { if( (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1) - && (stl->neighbors_start[edge_a->facet_number]. - neighbor[(v1a + 2) % 3] == -1)) { + && (stl->neighbors_start[edge_a->facet_number].neighbor[(v1a + 2) % 3] == -1)) { /* This vertex has no neighbors. This is a good one to change */ *facet1 = edge_a->facet_number; *vertex1 = v1a; @@ -581,8 +552,7 @@ stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, *facet2 = -1; } else { if( (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1) - && (stl->neighbors_start[edge_a->facet_number]. - neighbor[(v2a + 2) % 3] == -1)) { + && (stl->neighbors_start[edge_a->facet_number].neighbor[(v2a + 2) % 3] == -1)) { /* This vertex has no neighbors. This is a good one to change */ *facet2 = edge_a->facet_number; *vertex2 = v2a; @@ -595,140 +565,6 @@ stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, } } -static void remove_facet(stl_file *stl, int facet_number) -{ - assert(! stl->error); - ++ stl->stats.facets_removed; - /* Update list of connected edges */ - stl_neighbors &neighbors = stl->neighbors_start[facet_number]; - // Update statistics on unconnected triangle edges. - switch ((neighbors.neighbor[0] == -1) + (neighbors.neighbor[1] == -1) + (neighbors.neighbor[2] == -1)) { - case 0: // Facet has 3 neighbors - -- stl->stats.connected_facets_3_edge; - -- stl->stats.connected_facets_2_edge; - -- stl->stats.connected_facets_1_edge; - break; - case 1: // Facet has 2 neighbors - -- stl->stats.connected_facets_2_edge; - -- stl->stats.connected_facets_1_edge; - break; - case 2: // Facet has 1 neighbor - -- stl->stats.connected_facets_1_edge; - case 3: // Facet has 0 neighbors - break; - default: - assert(false); - } - - if (facet_number == -- stl->stats.number_of_facets) - // Removing the last face is easy, just forget the last face. - return; - - // Copy the face and neighborship from the last face to facet_number. - stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets]; - neighbors = stl->neighbors_start[stl->stats.number_of_facets]; - // Update neighborship of faces, which used to point to the last face, now moved to facet_number. - for (int i = 0; i < 3; ++ i) - if (neighbors.neighbor[i] != -1) { - int &other_face_idx = stl->neighbors_start[neighbors.neighbor[i]].neighbor[(neighbors.which_vertex_not[i] + 1) % 3]; - if (other_face_idx != stl->stats.number_of_facets) { - printf("in remove_facet: neighbor = %d numfacets = %d this is wrong\n", other_face_idx, stl->stats.number_of_facets); - return; - } - other_face_idx = facet_number; - } -} - -static void remove_degenerate(stl_file *stl, int facet) -{ - assert(! stl->error); - - // Update statistics on face connectivity. - auto stl_update_connects_remove_1 = [stl](int facet_num) { - assert(! stl->error); - //FIXME when decreasing 3_edge, should I increase 2_edge etc? - switch ((stl->neighbors_start[facet_num].neighbor[0] == -1) + (stl->neighbors_start[facet_num].neighbor[1] == -1) + (stl->neighbors_start[facet_num].neighbor[2] == -1)) { - case 0: // Facet has 3 neighbors - -- stl->stats.connected_facets_3_edge; break; - case 1: // Facet has 2 neighbors - -- stl->stats.connected_facets_2_edge; break; - case 2: // Facet has 1 neighbor - -- stl->stats.connected_facets_1_edge; break; - case 3: // Facet has 0 neighbors - break; - default: - assert(false); - } - }; - - int edge_to_collapse = 0; - if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1]) { - if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { - // All 3 vertices are equal. Collapse the edge with no neighbor if it exists. - const int *nbr = stl->neighbors_start[facet].neighbor; - edge_to_collapse = (nbr[0] == -1) ? 0 : (nbr[1] == -1) ? 1 : 2; - } else { - edge_to_collapse = 0; - } - } else if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { - edge_to_collapse = 1; - } else if (stl->facet_start[facet].vertex[2] == stl->facet_start[facet].vertex[0]) { - edge_to_collapse = 2; - } else { - // No degenerate. Function shouldn't have been called. - return; - } - - int edge[3] = { (edge_to_collapse + 1) % 3, (edge_to_collapse + 2) % 3, edge_to_collapse }; - int neighbor[] = { - stl->neighbors_start[facet].neighbor[edge[0]], - stl->neighbors_start[facet].neighbor[edge[1]], - stl->neighbors_start[facet].neighbor[edge[2]] - }; - int vnot[] = { - stl->neighbors_start[facet].which_vertex_not[edge[0]], - stl->neighbors_start[facet].which_vertex_not[edge[1]], - stl->neighbors_start[facet].which_vertex_not[edge[2]] - }; - // Update statistics on edge connectivity. - if (neighbor[0] == -1) - stl_update_connects_remove_1(neighbor[1]); - if (neighbor[1] == -1) - stl_update_connects_remove_1(neighbor[0]); - - if (neighbor[0] >= 0) { - if (neighbor[1] >= 0) { - // Adjust the "flip" flag for the which_vertex_not values. - if (vnot[0] > 2) { - if (vnot[1] > 2) { - // The face to be removed has its normal flipped compared to the left & right neighbors, therefore after removing this face - // the two remaining neighbors will be oriented correctly. - vnot[0] -= 3; - vnot[1] -= 3; - } else - // One neighbor has its normal inverted compared to the face to be removed, the other is oriented equally. - // After removal, the two neighbors will have their normals flipped. - vnot[1] += 3; - } else if (vnot[1] > 2) - // One neighbor has its normal inverted compared to the face to be removed, the other is oriented equally. - // After removal, the two neighbors will have their normals flipped. - vnot[0] += 3; - } - stl->neighbors_start[neighbor[0]].neighbor[(vnot[0] + 1) % 3] = (neighbor[0] == neighbor[1]) ? -1 : neighbor[1]; - stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = vnot[1]; - } - if (neighbor[1] >= 0) { - stl->neighbors_start[neighbor[1]].neighbor[(vnot[1] + 1) % 3] = (neighbor[0] == neighbor[1]) ? -1 : neighbor[0]; - stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = vnot[0]; - } - if (neighbor[2] >= 0) { - stl_update_connects_remove_1(neighbor[2]); - stl->neighbors_start[neighbor[2]].neighbor[(vnot[2] + 1) % 3] = -1; - } - - remove_facet(stl, facet); -} - void stl_remove_unconnected_facets(stl_file *stl) { // A couple of things need to be done here. One is to remove any completely unconnected facets (0 edges connected) since these are @@ -737,12 +573,143 @@ void stl_remove_unconnected_facets(stl_file *stl) if (stl->error) return; + auto remove_facet = [stl](int facet_number) + { + ++ stl->stats.facets_removed; + /* Update list of connected edges */ + stl_neighbors &neighbors = stl->neighbors_start[facet_number]; + // Update statistics on unconnected triangle edges. + switch ((neighbors.neighbor[0] == -1) + (neighbors.neighbor[1] == -1) + (neighbors.neighbor[2] == -1)) { + case 0: // Facet has 3 neighbors + -- stl->stats.connected_facets_3_edge; + -- stl->stats.connected_facets_2_edge; + -- stl->stats.connected_facets_1_edge; + break; + case 1: // Facet has 2 neighbors + -- stl->stats.connected_facets_2_edge; + -- stl->stats.connected_facets_1_edge; + break; + case 2: // Facet has 1 neighbor + -- stl->stats.connected_facets_1_edge; + case 3: // Facet has 0 neighbors + break; + default: + assert(false); + } + + if (facet_number == -- stl->stats.number_of_facets) + // Removing the last face is easy, just forget the last face. + return; + + // Copy the face and neighborship from the last face to facet_number. + stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets]; + neighbors = stl->neighbors_start[stl->stats.number_of_facets]; + // Update neighborship of faces, which used to point to the last face, now moved to facet_number. + for (int i = 0; i < 3; ++ i) + if (neighbors.neighbor[i] != -1) { + int &other_face_idx = stl->neighbors_start[neighbors.neighbor[i]].neighbor[(neighbors.which_vertex_not[i] + 1) % 3]; + if (other_face_idx != stl->stats.number_of_facets) { + printf("in remove_facet: neighbor = %d numfacets = %d this is wrong\n", other_face_idx, stl->stats.number_of_facets); + return; + } + other_face_idx = facet_number; + } + }; + + auto remove_degenerate = [stl, remove_facet](int facet) + { + // Update statistics on face connectivity. + auto stl_update_connects_remove_1 = [stl](int facet_num) { + assert(! stl->error); + //FIXME when decreasing 3_edge, should I increase 2_edge etc? + switch ((stl->neighbors_start[facet_num].neighbor[0] == -1) + (stl->neighbors_start[facet_num].neighbor[1] == -1) + (stl->neighbors_start[facet_num].neighbor[2] == -1)) { + case 0: // Facet has 3 neighbors + -- stl->stats.connected_facets_3_edge; break; + case 1: // Facet has 2 neighbors + -- stl->stats.connected_facets_2_edge; break; + case 2: // Facet has 1 neighbor + -- stl->stats.connected_facets_1_edge; break; + case 3: // Facet has 0 neighbors + break; + default: + assert(false); + } + }; + + int edge_to_collapse = 0; + if (stl->facet_start[facet].vertex[0] == stl->facet_start[facet].vertex[1]) { + if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { + // All 3 vertices are equal. Collapse the edge with no neighbor if it exists. + const int *nbr = stl->neighbors_start[facet].neighbor; + edge_to_collapse = (nbr[0] == -1) ? 0 : (nbr[1] == -1) ? 1 : 2; + } else { + edge_to_collapse = 0; + } + } else if (stl->facet_start[facet].vertex[1] == stl->facet_start[facet].vertex[2]) { + edge_to_collapse = 1; + } else if (stl->facet_start[facet].vertex[2] == stl->facet_start[facet].vertex[0]) { + edge_to_collapse = 2; + } else { + // No degenerate. Function shouldn't have been called. + return; + } + + int edge[3] = { (edge_to_collapse + 1) % 3, (edge_to_collapse + 2) % 3, edge_to_collapse }; + int neighbor[] = { + stl->neighbors_start[facet].neighbor[edge[0]], + stl->neighbors_start[facet].neighbor[edge[1]], + stl->neighbors_start[facet].neighbor[edge[2]] + }; + int vnot[] = { + stl->neighbors_start[facet].which_vertex_not[edge[0]], + stl->neighbors_start[facet].which_vertex_not[edge[1]], + stl->neighbors_start[facet].which_vertex_not[edge[2]] + }; + // Update statistics on edge connectivity. + if (neighbor[0] == -1) + stl_update_connects_remove_1(neighbor[1]); + if (neighbor[1] == -1) + stl_update_connects_remove_1(neighbor[0]); + + if (neighbor[0] >= 0) { + if (neighbor[1] >= 0) { + // Adjust the "flip" flag for the which_vertex_not values. + if (vnot[0] > 2) { + if (vnot[1] > 2) { + // The face to be removed has its normal flipped compared to the left & right neighbors, therefore after removing this face + // the two remaining neighbors will be oriented correctly. + vnot[0] -= 3; + vnot[1] -= 3; + } else + // One neighbor has its normal inverted compared to the face to be removed, the other is oriented equally. + // After removal, the two neighbors will have their normals flipped. + vnot[1] += 3; + } else if (vnot[1] > 2) + // One neighbor has its normal inverted compared to the face to be removed, the other is oriented equally. + // After removal, the two neighbors will have their normals flipped. + vnot[0] += 3; + } + stl->neighbors_start[neighbor[0]].neighbor[(vnot[0] + 1) % 3] = (neighbor[0] == neighbor[1]) ? -1 : neighbor[1]; + stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = vnot[1]; + } + if (neighbor[1] >= 0) { + stl->neighbors_start[neighbor[1]].neighbor[(vnot[1] + 1) % 3] = (neighbor[0] == neighbor[1]) ? -1 : neighbor[0]; + stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = vnot[0]; + } + if (neighbor[2] >= 0) { + stl_update_connects_remove_1(neighbor[2]); + stl->neighbors_start[neighbor[2]].neighbor[(vnot[2] + 1) % 3] = -1; + } + + remove_facet(facet); + }; + // remove degenerate facets for (uint32_t i = 0; i < stl->stats.number_of_facets;) if (stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[1] || stl->facet_start[i].vertex[0] == stl->facet_start[i].vertex[2] || stl->facet_start[i].vertex[1] == stl->facet_start[i].vertex[2]) { - remove_degenerate(stl, i); + remove_degenerate(i); // assert(stl_validate(stl)); } else ++ i; @@ -754,7 +721,7 @@ void stl_remove_unconnected_facets(stl_file *stl) stl->neighbors_start[i].neighbor[1] == -1 && stl->neighbors_start[i].neighbor[2] == -1) { // This facet is completely unconnected. Remove it. - remove_facet(stl, i); + remove_facet(i); assert(stl_validate(stl)); } else ++ i; @@ -850,8 +817,7 @@ stl_fill_holes(stl_file *stl) { } break; } else { - vnot = stl->neighbors_start[facet_num]. - which_vertex_not[next_edge]; + vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; facet_num = next_facet; } @@ -867,27 +833,14 @@ Try using a smaller tolerance or don't do a nearby check\n"); } } -void -stl_add_facet(stl_file *stl, stl_facet *new_facet) { - if (stl->error) return; - - stl->stats.facets_added += 1; - if(stl->stats.facets_malloced < (int)stl->stats.number_of_facets + 1) { - stl->facet_start = (stl_facet*)realloc(stl->facet_start, - (sizeof(stl_facet) * (stl->stats.facets_malloced + 256))); - if(stl->facet_start == NULL) perror("stl_add_facet"); - stl->neighbors_start = (stl_neighbors*)realloc(stl->neighbors_start, - (sizeof(stl_neighbors) * (stl->stats.facets_malloced + 256))); - if(stl->neighbors_start == NULL) perror("stl_add_facet"); - stl->stats.facets_malloced += 256; - } - stl->facet_start[stl->stats.number_of_facets] = *new_facet; - - /* note that the normal vector is not set here, just initialized to 0 */ - stl->facet_start[stl->stats.number_of_facets].normal = stl_normal::Zero(); - - stl->neighbors_start[stl->stats.number_of_facets].neighbor[0] = -1; - stl->neighbors_start[stl->stats.number_of_facets].neighbor[1] = -1; - stl->neighbors_start[stl->stats.number_of_facets].neighbor[2] = -1; - stl->stats.number_of_facets += 1; +void stl_add_facet(stl_file *stl, const stl_facet *new_facet) +{ + if (stl->error) + return; + ++ stl->stats.facets_added; + ++ stl->stats.number_of_facets; + stl->facet_start.emplace_back(*new_facet); + // note that the normal vector is not set here, just initialized to 0. + stl->facet_start[stl->stats.number_of_facets].normal = stl_normal::Zero(); + stl->neighbors_start.emplace_back(); } diff --git a/src/admesh/normals.cpp b/src/admesh/normals.cpp index ecf08b59c..e11f1a3c1 100644 --- a/src/admesh/normals.cpp +++ b/src/admesh/normals.cpp @@ -84,7 +84,6 @@ stl_reverse_facet(stl_file *stl, int facet_num) { void stl_fix_normal_directions(stl_file *stl) { - char *norm_sw; /* int edge_num;*/ /* int vnot;*/ int checked = 0; @@ -101,7 +100,6 @@ stl_fix_normal_directions(stl_file *stl) { struct stl_normal *newn; struct stl_normal *temp; - int* reversed_ids; int reversed_count = 0; int id; int force_exit = 0; @@ -112,20 +110,15 @@ stl_fix_normal_directions(stl_file *stl) { if (stl->stats.number_of_facets == 0) return; /* Initialize linked list. */ - head = (struct stl_normal*)malloc(sizeof(struct stl_normal)); - if(head == NULL) perror("stl_fix_normal_directions"); - tail = (struct stl_normal*)malloc(sizeof(struct stl_normal)); - if(tail == NULL) perror("stl_fix_normal_directions"); + head = new stl_normal; + tail = new stl_normal; head->next = tail; tail->next = tail; /* Initialize list that keeps track of already fixed facets. */ - norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char)); - if(norm_sw == NULL) perror("stl_fix_normal_directions"); - + std::vector norm_sw(stl->stats.number_of_facets, 0); /* Initialize list that keeps track of reversed facets. */ - reversed_ids = (int*)calloc(stl->stats.number_of_facets, sizeof(int)); - if (reversed_ids == NULL) perror("stl_fix_normal_directions reversed_ids"); + std::vector reversed_ids(stl->stats.number_of_facets, 0); facet_num = 0; /* If normal vector is not within tolerance and backwards: @@ -166,8 +159,7 @@ stl_fix_normal_directions(stl_file *stl) { /* If we haven't fixed this facet yet, add it to the list: */ if(norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) { /* Add node to beginning of list. */ - newn = (struct stl_normal*)malloc(sizeof(struct stl_normal)); - if(newn == NULL) perror("stl_fix_normal_directions"); + newn = new stl_normal; newn->facet_num = stl->neighbors_start[facet_num].neighbor[j]; newn->next = head->next; head->next = newn; @@ -187,7 +179,7 @@ stl_fix_normal_directions(stl_file *stl) { } temp = head->next; /* Delete this facet from the list. */ head->next = head->next->next; - free(temp); + delete temp; } else { /* if we ran out of facets to fix: */ /* All of the facets in this part have been fixed. */ stl->stats.number_of_parts += 1; @@ -213,10 +205,8 @@ stl_fix_normal_directions(stl_file *stl) { } } } - free(head); - free(tail); - free(reversed_ids); - free(norm_sw); + delete head; + delete tail; } static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) { diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index 2ad270903..8162c6a8d 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -31,17 +31,8 @@ void stl_invalidate_shared_vertices(stl_file *stl) { - if (stl->error) - return; - - if (stl->v_indices != nullptr) { - free(stl->v_indices); - stl->v_indices = nullptr; - } - if (stl->v_shared != nullptr) { - free(stl->v_shared); - stl->v_shared = nullptr; - } + stl->v_indices.clear(); + stl->v_shared.clear(); } void stl_generate_shared_vertices(stl_file *stl) @@ -53,23 +44,11 @@ void stl_generate_shared_vertices(stl_file *stl) stl_invalidate_shared_vertices(stl); // 3 indices to vertex per face - stl->v_indices = (v_indices_struct*)calloc(stl->stats.number_of_facets, sizeof(v_indices_struct)); - if (stl->v_indices == nullptr) - perror("stl_generate_shared_vertices"); + stl->v_indices.assign(stl->stats.number_of_facets, v_indices_struct()); // Shared vertices (3D coordinates) - stl->v_shared = (stl_vertex*)calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex)); - if (stl->v_shared == nullptr) - perror("stl_generate_shared_vertices"); - stl->stats.shared_malloced = stl->stats.number_of_facets / 2; + stl->v_shared.assign(stl->stats.number_of_facets / 2, stl_vertex()); stl->stats.shared_vertices = 0; - for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { - // vertex index -1 means no shared vertex was assigned yet. - stl->v_indices[i].vertex[0] = -1; - stl->v_indices[i].vertex[1] = -1; - stl->v_indices[i].vertex[2] = -1; - } - // A degenerate mesh may contain loops: Traversing a fan will end up in an endless loop // while never reaching the starting face. To avoid these endless loops, traversed faces at each fan traversal // are marked with a unique fan_traversal_stamp. @@ -82,13 +61,7 @@ void stl_generate_shared_vertices(stl_file *stl) // Shared vertex was already assigned. continue; // Create a new shared vertex. - if (stl->stats.shared_vertices == stl->stats.shared_malloced) { - stl->stats.shared_malloced += 1024; - stl->v_shared = (stl_vertex*)realloc(stl->v_shared, stl->stats.shared_malloced * sizeof(stl_vertex)); - if(stl->v_shared == nullptr) - perror("stl_generate_shared_vertices"); - } - stl->v_shared[stl->stats.shared_vertices] = stl->facet_start[facet_idx].vertex[j]; + stl->v_shared.emplace_back(stl->facet_start[facet_idx].vertex[j]); // Traverse the fan around the j-th vertex of the i-th face, assign the newly created shared vertex index to all the neighboring triangles in the triangle fan. int facet_in_fan_idx = facet_idx; bool edge_direction = false; diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 5ecd94bd1..345951121 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -27,6 +27,7 @@ #include #include +#include #include // Size of the binary STL header, free form. @@ -44,18 +45,18 @@ static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); struct stl_facet { - stl_normal normal; - stl_vertex vertex[3]; - char extra[2]; + stl_normal normal; + stl_vertex vertex[3]; + char extra[2]; - stl_facet rotated(const Eigen::Quaternion &rot) { - stl_facet out; - out.normal = rot * this->normal; - out.vertex[0] = rot * this->vertex[0]; - out.vertex[1] = rot * this->vertex[1]; - out.vertex[2] = rot * this->vertex[2]; - return out; - } + stl_facet rotated(const Eigen::Quaternion &rot) const { + stl_facet out; + out.normal = rot * this->normal; + out.vertex[0] = rot * this->vertex[0]; + out.vertex[1] = rot * this->vertex[1]; + out.vertex[2] = rot * this->vertex[2]; + return out; + } }; #define SIZEOF_STL_FACET 50 @@ -67,86 +68,100 @@ static_assert(sizeof(stl_facet) >= SIZEOF_STL_FACET, "size of stl_facet incorrec typedef enum {binary, ascii, inmemory} stl_type; -typedef struct { - stl_vertex p1; - stl_vertex p2; - int facet_number; -} stl_edge; +struct stl_edge { + stl_vertex p1; + stl_vertex p2; + int facet_number; +}; -typedef struct stl_hash_edge { - // Key of a hash edge: sorted vertices of the edge. - uint32_t key[6]; - // Compare two keys. - bool operator==(const stl_hash_edge &rhs) { return memcmp(key, rhs.key, sizeof(key)) == 0; } - bool operator!=(const stl_hash_edge &rhs) { return ! (*this == rhs); } - int hash(int M) const { return ((key[0] / 11 + key[1] / 7 + key[2] / 3) ^ (key[3] / 11 + key[4] / 7 + key[5] / 3)) % M; } - // Index of a facet owning this edge. - int facet_number; - // Index of this edge inside the facet with an index of facet_number. - // If this edge is stored backwards, which_edge is increased by 3. - int which_edge; - struct stl_hash_edge *next; -} stl_hash_edge; +struct stl_hash_edge { + // Key of a hash edge: sorted vertices of the edge. + uint32_t key[6]; + // Compare two keys. + bool operator==(const stl_hash_edge &rhs) { return memcmp(key, rhs.key, sizeof(key)) == 0; } + bool operator!=(const stl_hash_edge &rhs) { return ! (*this == rhs); } + int hash(int M) const { return ((key[0] / 11 + key[1] / 7 + key[2] / 3) ^ (key[3] / 11 + key[4] / 7 + key[5] / 3)) % M; } + // Index of a facet owning this edge. + int facet_number; + // Index of this edge inside the facet with an index of facet_number. + // If this edge is stored backwards, which_edge is increased by 3. + int which_edge; + struct stl_hash_edge *next; +}; -typedef struct { - // Index of a neighbor facet. - int neighbor[3]; - // Index of an opposite vertex at the neighbor face. - char which_vertex_not[3]; -} stl_neighbors; +struct stl_neighbors { + stl_neighbors() { reset(); } + void reset() { + neighbor[0] = -1; + neighbor[1] = -1; + neighbor[2] = -1; + which_vertex_not[0] = -1; + which_vertex_not[1] = -1; + which_vertex_not[2] = -1; + } -typedef struct { - int vertex[3]; -} v_indices_struct; + // Index of a neighbor facet. + int neighbor[3]; + // Index of an opposite vertex at the neighbor face. + char which_vertex_not[3]; +}; -typedef struct { - char header[81]; - stl_type type; - uint32_t number_of_facets; - stl_vertex max; - stl_vertex min; - stl_vertex size; - float bounding_diameter; - float shortest_edge; - float volume; - unsigned number_of_blocks; - int connected_edges; - int connected_facets_1_edge; - int connected_facets_2_edge; - int connected_facets_3_edge; - int facets_w_1_bad_edge; - int facets_w_2_bad_edge; - int facets_w_3_bad_edge; - int original_num_facets; - int edges_fixed; - int degenerate_facets; - int facets_removed; - int facets_added; - int facets_reversed; - int backwards_edges; - int normals_fixed; - int number_of_parts; - int malloced; - int freed; - int facets_malloced; - int collisions; - int shared_vertices; - int shared_malloced; -} stl_stats; +struct v_indices_struct { + // -1 means no vertex index has been assigned yet + v_indices_struct() { vertex[0] = -1; vertex[1] = -1; vertex[2] = -1; } + int vertex[3]; +}; -typedef struct { - FILE *fp; - stl_facet *facet_start; - stl_hash_edge **heads; - stl_hash_edge *tail; - int M; - stl_neighbors *neighbors_start; - v_indices_struct *v_indices; - stl_vertex *v_shared; - stl_stats stats; - char error; -} stl_file; +struct stl_stats { + char header[81]; + stl_type type; + uint32_t number_of_facets; + stl_vertex max; + stl_vertex min; + stl_vertex size; + float bounding_diameter; + float shortest_edge; + float volume; + unsigned number_of_blocks; + int connected_edges; + int connected_facets_1_edge; + int connected_facets_2_edge; + int connected_facets_3_edge; + int facets_w_1_bad_edge; + int facets_w_2_bad_edge; + int facets_w_3_bad_edge; + int original_num_facets; + int edges_fixed; + int degenerate_facets; + int facets_removed; + int facets_added; + int facets_reversed; + int backwards_edges; + int normals_fixed; + int number_of_parts; + int shared_vertices; + // hash table statistics + int malloced; + int freed; + int collisions; +}; + +struct stl_file { + FILE *fp; + std::vector facet_start; + std::vector neighbors_start; + // Hash table on edges + std::vector heads; + stl_hash_edge* tail; + int M; + // Indexed face set + std::vector v_indices; + std::vector v_shared; + // Statistics + stl_stats stats; + char error; +}; extern void stl_open(stl_file *stl, const char *file); extern void stl_close(stl_file *stl); @@ -272,7 +287,7 @@ extern void stl_allocate(stl_file *stl); extern void stl_read(stl_file *stl, int first_facet, bool first); extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first); extern void stl_reallocate(stl_file *stl); -extern void stl_add_facet(stl_file *stl, stl_facet *new_facet); +extern void stl_add_facet(stl_file *stl, const stl_facet *new_facet); extern void stl_clear_error(stl_file *stl); extern int stl_get_error(stl_file *stl); diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp index 85f66785b..81060c0a3 100644 --- a/src/admesh/stl_io.cpp +++ b/src/admesh/stl_io.cpp @@ -109,7 +109,6 @@ Normals fixed : %5d\n", stl->stats.normals_fixed); void stl_write_ascii(stl_file *stl, const char *file, const char *label) { - int i; char *error_msg; if (stl->error) return; @@ -129,7 +128,7 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) { fprintf(fp, "solid %s\n", label); - for(i = 0; i < stl->stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { fprintf(fp, " facet normal % .8E % .8E % .8E\n", stl->facet_start[i].normal(0), stl->facet_start[i].normal(1), stl->facet_start[i].normal(2)); @@ -154,7 +153,6 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) { void stl_print_neighbors(stl_file *stl, char *file) { - int i; FILE *fp; char *error_msg; @@ -173,7 +171,7 @@ stl_print_neighbors(stl_file *stl, char *file) { return; } - for(i = 0; i < stl->stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n", i, stl->neighbors_start[i].neighbor[0], @@ -200,7 +198,6 @@ void stl_internal_reverse_quads(char *buf, size_t cnt) void stl_write_binary(stl_file *stl, const char *file, const char *label) { FILE *fp; - int i; char *error_msg; if (stl->error) return; @@ -219,13 +216,13 @@ stl_write_binary(stl_file *stl, const char *file, const char *label) { } fprintf(fp, "%s", label); - for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp); + for(size_t i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp); fseek(fp, LABEL_SIZE, SEEK_SET); #ifdef BOOST_LITTLE_ENDIAN fwrite(&stl->stats.number_of_facets, 4, 1, fp); - for (i = 0; i < stl->stats.number_of_facets; ++ i) - fwrite(stl->facet_start + i, SIZEOF_STL_FACET, 1, fp); + for (const stl_facet &facet : stl->facet_start) + fwrite(&facet, SIZEOF_STL_FACET, 1, fp); #else /* BOOST_LITTLE_ENDIAN */ char buffer[50]; // Convert the number of facets to little endian. @@ -288,8 +285,6 @@ stl_write_neighbor(stl_file *stl, int facet) { void stl_write_quad_object(stl_file *stl, char *file) { FILE *fp; - int i; - int j; char *error_msg; stl_vertex connect_color = stl_vertex::Zero(); stl_vertex uncon_1_color = stl_vertex::Zero(); @@ -313,10 +308,10 @@ stl_write_quad_object(stl_file *stl, char *file) { } fprintf(fp, "CQUAD\n"); - for(i = 0; i < stl->stats.number_of_facets; i++) { - j = ((stl->neighbors_start[i].neighbor[0] == -1) + - (stl->neighbors_start[i].neighbor[1] == -1) + - (stl->neighbors_start[i].neighbor[2] == -1)); + for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { + int j = ((stl->neighbors_start[i].neighbor[0] == -1) + + (stl->neighbors_start[i].neighbor[1] == -1) + + (stl->neighbors_start[i].neighbor[2] == -1)); if(j == 0) { color = connect_color; } else if(j == 1) { @@ -346,9 +341,8 @@ stl_write_quad_object(stl_file *stl, char *file) { fclose(fp); } -void -stl_write_dxf(stl_file *stl, const char *file, char *label) { - int i; +void stl_write_dxf(stl_file *stl, const char *file, char *label) +{ FILE *fp; char *error_msg; @@ -375,7 +369,7 @@ stl_write_dxf(stl_file *stl, const char *file, char *label) { fprintf(fp, "0\nSECTION\n2\nENTITIES\n"); - for(i = 0; i < stl->stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { fprintf(fp, "0\n3DFACE\n8\n0\n"); fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index 911f4f5e8..81b7914c3 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -35,22 +35,38 @@ #error "SEEK_SET not defined" #endif -void -stl_open(stl_file *stl, const char *file) { - stl_initialize(stl); - stl_count_facets(stl, file); - stl_allocate(stl); - stl_read(stl, 0, true); - if (stl->fp != nullptr) { - fclose(stl->fp); - stl->fp = nullptr; - } +void stl_open(stl_file *stl, const char *file) +{ + stl_initialize(stl); + stl_count_facets(stl, file); + stl_allocate(stl); + stl_read(stl, 0, true); + if (stl->fp != nullptr) { + fclose(stl->fp); + stl->fp = nullptr; + } } -void -stl_initialize(stl_file *stl) { - memset(stl, 0, sizeof(stl_file)); - stl->stats.volume = -1.0; +void stl_initialize(stl_file *stl) +{ + stl->fp = nullptr; + stl->tail = nullptr; + stl->M = 0; + stl->error = 0; + stl->facet_start.clear(); + stl->neighbors_start.clear(); + stl->v_indices.clear(); + stl->v_shared.clear(); + memset(&stl->stats, 0, sizeof(stl_stats)); + stl->stats.volume = -1.0; +} + +void stl_close(stl_file *stl) +{ + assert(stl->fp == nullptr); + assert(stl->heads.empty()); + assert(stl->tail == nullptr); + stl_initialize(stl); } #ifndef BOOST_LITTLE_ENDIAN @@ -175,20 +191,14 @@ stl_count_facets(stl_file *stl, const char *file) { stl->stats.original_num_facets = stl->stats.number_of_facets; } -void -stl_allocate(stl_file *stl) { - if (stl->error) return; - - /* Allocate memory for the entire .STL file */ - stl->facet_start = (stl_facet*)calloc(stl->stats.number_of_facets, - sizeof(stl_facet)); - if(stl->facet_start == NULL) perror("stl_initialize"); - stl->stats.facets_malloced = stl->stats.number_of_facets; - - /* Allocate memory for the neighbors list */ - stl->neighbors_start = (stl_neighbors*) - calloc(stl->stats.number_of_facets, sizeof(stl_neighbors)); - if(stl->facet_start == NULL) perror("stl_initialize"); +void stl_allocate(stl_file *stl) +{ + if (stl->error) + return; + // Allocate memory for the entire .STL file. + stl->facet_start.assign(stl->stats.number_of_facets, stl_facet()); + // Allocate memory for the neighbors list. + stl->neighbors_start.assign(stl->stats.number_of_facets, stl_neighbors()); } void @@ -237,23 +247,14 @@ stl_open_merge(stl_file *stl, char *file_to_merge) { stl->fp=origFp; } -extern void -stl_reallocate(stl_file *stl) { - if (stl->error) return; - /* Reallocate more memory for the .STL file(s) */ - stl->facet_start = (stl_facet*)realloc(stl->facet_start, stl->stats.number_of_facets * - sizeof(stl_facet)); - if(stl->facet_start == NULL) perror("stl_initialize"); - stl->stats.facets_malloced = stl->stats.number_of_facets; - - /* Reallocate more memory for the neighbors list */ - stl->neighbors_start = (stl_neighbors*) - realloc(stl->neighbors_start, stl->stats.number_of_facets * - sizeof(stl_neighbors)); - if(stl->facet_start == NULL) perror("stl_initialize"); +void stl_reallocate(stl_file *stl) +{ + if (stl->error) + return; + stl->facet_start.resize(stl->stats.number_of_facets); + stl->neighbors_start.resize(stl->stats.number_of_facets); } - /* Reads the contents of the file pointed to by stl->fp into the stl structure, starting at facet first_facet. The second argument says if it's our first time running this for the stl and therefore we should reset our max and min stats. */ @@ -366,20 +367,3 @@ void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first) stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]); } } - -void stl_close(stl_file *stl) -{ - assert(stl->fp == nullptr); - assert(stl->heads == nullptr); - assert(stl->tail == nullptr); - - if (stl->facet_start != NULL) - free(stl->facet_start); - if (stl->neighbors_start != NULL) - free(stl->neighbors_start); - if (stl->v_indices != NULL) - free(stl->v_indices); - if (stl->v_shared != NULL) - free(stl->v_shared); - memset(stl, 0, sizeof(stl_file)); -} diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index c2d2c2726..61e0d11e7 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -32,45 +32,39 @@ static float get_area(stl_facet *facet); static float get_volume(stl_file *stl); -void -stl_verify_neighbors(stl_file *stl) { - int i; - int j; - stl_edge edge_a; - stl_edge edge_b; - int neighbor; - int vnot; +void stl_verify_neighbors(stl_file *stl) +{ + if (stl->error) + return; - if (stl->error) return; + stl->stats.backwards_edges = 0; - stl->stats.backwards_edges = 0; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - edge_a.p1 = stl->facet_start[i].vertex[j]; - edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3]; - neighbor = stl->neighbors_start[i].neighbor[j]; - vnot = stl->neighbors_start[i].which_vertex_not[j]; - - if(neighbor == -1) - continue; /* this edge has no neighbor... Continue. */ - if(vnot < 3) { - edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; - edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; - } else { - stl->stats.backwards_edges += 1; - edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; - edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; - } - if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) { - /* These edges should match but they don't. Print results. */ - printf("edge %d of facet %d doesn't match edge %d of facet %d\n", - j, i, vnot + 1, neighbor); - stl_write_facet(stl, (char*)"first facet", i); - stl_write_facet(stl, (char*)"second facet", neighbor); - } - } - } + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + for (int j = 0; j < 3; ++ j) { + stl_edge edge_a; + edge_a.p1 = stl->facet_start[i].vertex[j]; + edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3]; + int neighbor = stl->neighbors_start[i].neighbor[j]; + if (neighbor == -1) + continue; // this edge has no neighbor... Continue. + int vnot = stl->neighbors_start[i].which_vertex_not[j]; + stl_edge edge_b; + if (vnot < 3) { + edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; + edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; + } else { + stl->stats.backwards_edges += 1; + edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3]; + edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3]; + } + if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) { + // These edges should match but they don't. Print results. + printf("edge %d of facet %d doesn't match edge %d of facet %d\n", j, i, vnot + 1, neighbor); + stl_write_facet(stl, (char*)"first facet", i); + stl_write_facet(stl, (char*)"second facet", neighbor); + } + } + } } void stl_translate(stl_file *stl, float x, float y, float z) @@ -263,21 +257,19 @@ void stl_mirror_yz(stl_file *stl) void stl_mirror_xz(stl_file *stl) { - if (stl->error) - return; + if (stl->error) + return; - for (int i = 0; i < stl->stats.number_of_facets; i++) { - for (int j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j](1) *= -1.0; - } - } - float temp_size = stl->stats.min(1); - stl->stats.min(1) = stl->stats.max(1); - stl->stats.max(1) = temp_size; - stl->stats.min(1) *= -1.0; - stl->stats.max(1) *= -1.0; - stl_reverse_all_facets(stl); - stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + stl->facet_start[i].vertex[j](1) *= -1.0; + float temp_size = stl->stats.min(1); + stl->stats.min(1) = stl->stats.max(1); + stl->stats.max(1) = temp_size; + stl->stats.min(1) *= -1.0; + stl->stats.max(1) *= -1.0; + stl_reverse_all_facets(stl); + stl->stats.facets_reversed -= stl->stats.number_of_facets; // for not altering stats } static float get_volume(stl_file *stl) @@ -463,18 +455,18 @@ bool stl_validate(stl_file *stl) { assert(! stl->error); assert(stl->fp == nullptr); - assert(stl->facet_start != nullptr); - assert(stl->heads == nullptr); + assert(! stl->facet_start.empty()); + assert(stl->heads.empty()); assert(stl->tail == nullptr); - assert(stl->neighbors_start != nullptr); - assert((stl->v_indices == nullptr) == (stl->v_shared == nullptr)); + assert(! stl->neighbors_start.empty()); + assert((stl->v_indices.empty()) == (stl->v_shared.empty())); assert(stl->stats.number_of_facets > 0); #ifdef _DEBUG // Verify validity of neighborship data. for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) { const stl_neighbors &nbr = stl->neighbors_start[facet_idx]; - const int *vertices = (stl->v_indices == nullptr) ? nullptr : stl->v_indices[facet_idx].vertex; + const int *vertices = (stl->v_indices.empty()) ? nullptr : stl->v_indices[facet_idx].vertex; for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) { int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx]; assert(nbr_face < (int)stl->stats.number_of_facets); diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 38b34c462..c3916a14e 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1885,7 +1885,7 @@ namespace Slic3r { volume->mesh.repair(); stl_file& stl = volume->mesh.stl; - if (stl.v_shared == nullptr) + if (stl.v_shared.empty()) stl_generate_shared_vertices(&stl); if (stl.stats.shared_vertices == 0) diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index d26b5f3ed..f48b5b58c 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -926,7 +926,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) if (! volume->mesh.repaired) throw std::runtime_error("store_amf() requires repair()"); auto &stl = volume->mesh.stl; - if (stl.v_shared == nullptr) + if (stl.v_shared.empty()) stl_generate_shared_vertices(&stl); const Transform3d& matrix = volume->get_matrix(); for (size_t i = 0; i < stl.stats.shared_vertices; ++i) { diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index 502cac6e9..d983b1098 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -167,10 +167,10 @@ static void extract_model_from_archive( stl.stats.original_num_facets = header.nTriangles; stl_allocate(&stl); if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) { - memcpy((char*)stl.facet_start, data.data() + sizeof(StlHeader), 50 * header.nTriangles); + memcpy((char*)stl.facet_start.data(), data.data() + sizeof(StlHeader), 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; + unsigned char *data = (unsigned char*)stl.facet_start.data(); for (size_t i = header.nTriangles - 1; i > 0; -- i) memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET); } @@ -257,7 +257,7 @@ static void extract_model_from_archive( stl.stats.number_of_facets = (uint32_t)facets.size(); stl.stats.original_num_facets = (int)facets.size(); stl_allocate(&stl); - memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50); + memcpy((void*)stl.facet_start.data(), facets.data(), facets.size() * 50); stl_get_size(&stl); mesh.repair(); // Add a mesh to a model. diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 3b1bd5df2..64fbb9a2a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -910,18 +910,16 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const if (v->is_model_part()) { const stl_file &stl = v->mesh.stl; Transform3d trafo = trafo_instance * v->get_matrix(); - if (stl.v_shared == nullptr) { + if (stl.v_shared.empty()) { // Using the STL faces. - for (unsigned int i = 0; i < stl.stats.number_of_facets; ++ i) { - const stl_facet &facet = stl.facet_start[i]; + for (const stl_facet &facet : stl.facet_start) for (size_t j = 0; j < 3; ++ j) { Vec3d p = trafo * facet.vertex[j].cast(); pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); } - } } else { // Using the shared vertices should be a bit quicker than using the STL faces. - for (int i = 0; i < stl.stats.shared_vertices; ++ i) { + for (int i = 0; i < stl.stats.shared_vertices; ++ i) { Vec3d p = trafo * stl.v_shared[i].cast(); pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); } @@ -1347,13 +1345,9 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const Transform3d mv = mi * v->get_matrix(); const TriangleMesh& hull = v->get_convex_hull(); - for (uint32_t f = 0; f < hull.stl.stats.number_of_facets; ++f) - { - const stl_facet* facet = hull.stl.facet_start + f; - min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[0].cast())); - min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast())); - min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast())); - } + for (const stl_facet &facet : hull.stl.facet_start) + for (int i = 0; i < 3; ++ i) + min_z = std::min(min_z, (mv * facet.vertex[i].cast()).z()); } return min_z + inst->get_offset(Z); diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index c368b8604..1609b9ac4 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -121,19 +121,10 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { V.resize(3*stl.stats.number_of_facets, 3); F.resize(stl.stats.number_of_facets, 3); for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) { - const stl_facet* facet = stl.facet_start+i; - V(3*i+0, 0) = double(facet->vertex[0](0)); - V(3*i+0, 1) = double(facet->vertex[0](1)); - V(3*i+0, 2) = double(facet->vertex[0](2)); - - V(3*i+1, 0) = double(facet->vertex[1](0)); - V(3*i+1, 1) = double(facet->vertex[1](1)); - V(3*i+1, 2) = double(facet->vertex[1](2)); - - V(3*i+2, 0) = double(facet->vertex[2](0)); - V(3*i+2, 1) = double(facet->vertex[2](1)); - V(3*i+2, 2) = double(facet->vertex[2](2)); - + const stl_facet &facet = stl.facet_start[i]; + V.block<1, 3>(3 * i + 0, 0) = facet.vertex[0].cast(); + V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1].cast(); + V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2].cast(); F(i, 0) = int(3*i+0); F(i, 1) = int(3*i+1); F(i, 2) = int(3*i+2); diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp index 2ef4aec8c..ad03b550b 100644 --- a/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -27,8 +27,8 @@ void SlicingAdaptive::prepare() nfaces_total += (*it_mesh)->stl.stats.number_of_facets; m_faces.reserve(nfaces_total); for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) - for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i) - m_faces.push_back((*it_mesh)->stl.facet_start + i); + for (const stl_facet &face : (*it_mesh)->stl.facet_start) + m_faces.emplace_back(&face); // 2) Sort faces lexicographically by their Z span. std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index b7c6c07a7..ac23abd26 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -51,7 +51,7 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f stl.stats.type = inmemory; // count facets and allocate memory - stl.stats.number_of_facets = facets.size(); + stl.stats.number_of_facets = (uint32_t)facets.size(); stl.stats.original_num_facets = stl.stats.number_of_facets; stl_allocate(&stl); @@ -78,25 +78,14 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other) stl_close(&this->stl); this->stl = other.stl; this->repaired = other.repaired; - this->stl.heads = nullptr; + this->stl.heads.clear(); this->stl.tail = nullptr; this->stl.error = other.stl.error; - if (other.stl.facet_start != nullptr) { - this->stl.facet_start = (stl_facet*)calloc(other.stl.stats.number_of_facets, sizeof(stl_facet)); - std::copy(other.stl.facet_start, other.stl.facet_start + other.stl.stats.number_of_facets, this->stl.facet_start); - } - if (other.stl.neighbors_start != nullptr) { - this->stl.neighbors_start = (stl_neighbors*)calloc(other.stl.stats.number_of_facets, sizeof(stl_neighbors)); - std::copy(other.stl.neighbors_start, other.stl.neighbors_start + other.stl.stats.number_of_facets, this->stl.neighbors_start); - } - if (other.stl.v_indices != nullptr) { - this->stl.v_indices = (v_indices_struct*)calloc(other.stl.stats.number_of_facets, sizeof(v_indices_struct)); - std::copy(other.stl.v_indices, other.stl.v_indices + other.stl.stats.number_of_facets, this->stl.v_indices); - } - if (other.stl.v_shared != nullptr) { - this->stl.v_shared = (stl_vertex*)calloc(other.stl.stats.shared_vertices, sizeof(stl_vertex)); - std::copy(other.stl.v_shared, other.stl.v_shared + other.stl.stats.shared_vertices, this->stl.v_shared); - } + this->stl.facet_start = other.stl.facet_start; + this->stl.neighbors_start = other.stl.neighbors_start; + this->stl.v_indices = other.stl.v_indices; + this->stl.v_shared = other.stl.v_shared; + this->stl.stats = other.stl.stats; return *this; } @@ -125,8 +114,8 @@ void TriangleMesh::repair() // checking nearby //int last_edges_fixed = 0; - float tolerance = stl.stats.shortest_edge; - float increment = stl.stats.bounding_diameter / 10000.0; + float tolerance = (float)stl.stats.shortest_edge; + float increment = (float)stl.stats.bounding_diameter / 10000.0f; int iterations = 2; if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) { for (int i = 0; i < iterations; i++) { @@ -444,7 +433,7 @@ TriangleMeshPtrs TriangleMesh::split() const TriangleMesh* mesh = new TriangleMesh; meshes.emplace_back(mesh); mesh->stl.stats.type = inmemory; - mesh->stl.stats.number_of_facets = facets.size(); + mesh->stl.stats.number_of_facets = (uint32_t)facets.size(); mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets; stl_clear_error(&mesh->stl); stl_allocate(&mesh->stl); @@ -486,13 +475,12 @@ ExPolygons TriangleMesh::horizontal_projection() const { Polygons pp; pp.reserve(this->stl.stats.number_of_facets); - for (uint32_t i = 0; i < this->stl.stats.number_of_facets; ++ i) { - stl_facet* facet = &this->stl.facet_start[i]; + for (const stl_facet &facet : this->stl.facet_start) { Polygon p; p.points.resize(3); - p.points[0] = Point::new_scale(facet->vertex[0](0), facet->vertex[0](1)); - p.points[1] = Point::new_scale(facet->vertex[1](0), facet->vertex[1](1)); - p.points[2] = Point::new_scale(facet->vertex[2](0), facet->vertex[2](1)); + p.points[0] = Point::new_scale(facet.vertex[0](0), facet.vertex[0](1)); + p.points[1] = Point::new_scale(facet.vertex[1](0), facet.vertex[1](1)); + p.points[2] = Point::new_scale(facet.vertex[2](0), facet.vertex[2](1)); p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that pp.emplace_back(p); } @@ -526,17 +514,15 @@ BoundingBoxf3 TriangleMesh::bounding_box() const BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) const { BoundingBoxf3 bbox; - if (stl.v_shared == nullptr) { + if (stl.v_shared.empty()) { // Using the STL faces. - for (size_t i = 0; i < this->facets_count(); ++ i) { - const stl_facet &facet = this->stl.facet_start[i]; + for (const stl_facet &facet : this->stl.facet_start) for (size_t j = 0; j < 3; ++ j) bbox.merge(trafo * facet.vertex[j].cast()); - } } else { // Using the shared vertices should be a bit quicker than using the STL faces. - for (int i = 0; i < stl.stats.shared_vertices; ++ i) - bbox.merge(trafo * this->stl.v_shared[i].cast()); + for (const stl_vertex &v : this->stl.v_shared) + bbox.merge(trafo * v.cast()); } return bbox; } @@ -551,18 +537,12 @@ TriangleMesh TriangleMesh::convex_hull_3d() const std::vector src_vertices; // We will now fill the vector with input points for computation: - stl_facet* facet_ptr = stl.facet_start; - while (facet_ptr < stl.facet_start + stl.stats.number_of_facets) - { - for (int i = 0; i < 3; ++i) - { - const stl_vertex& v = facet_ptr->vertex[i]; + for (const stl_facet &facet : stl.facet_start) + for (int i = 0; i < 3; ++ i) { + const stl_vertex& v = facet.vertex[i]; src_vertices.emplace_back(v(0), v(1), v(2)); } - facet_ptr += 1; - } - // The qhull call: orgQhull::Qhull qhull; qhull.disableOutputStream(); // we want qhull to be quiet @@ -606,7 +586,7 @@ void TriangleMesh::require_shared_vertices() assert(stl_validate(&this->stl)); if (! this->repaired) this->repair(); - if (this->stl.v_shared == nullptr) { + if (this->stl.v_shared.empty()) { BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices"; stl_generate_shared_vertices(&(this->stl)); } @@ -622,10 +602,9 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac throw_on_cancel(); facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1); - v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices); - // Scale the copied vertices. - for (int i = 0; i < this->mesh->stl.stats.shared_vertices; ++ i) - this->v_scaled_shared[i] *= float(1. / SCALING_FACTOR); + v_scaled_shared.assign(_mesh->stl.v_shared.size(), stl_vertex()); + for (size_t i = 0; i < v_scaled_shared.size(); ++ i) + this->v_scaled_shared[i] = _mesh->stl.v_shared[i] / float(SCALING_FACTOR); // Create a mapping from triangle edge into face. struct EdgeToFace { @@ -814,7 +793,7 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const { - const stl_facet &facet = m_use_quaternion ? this->mesh->stl.facet_start[facet_idx].rotated(m_quaternion) : this->mesh->stl.facet_start[facet_idx]; + const stl_facet &facet = m_use_quaternion ? (this->mesh->stl.facet_start.data() + facet_idx)->rotated(m_quaternion) : *(this->mesh->stl.facet_start.data() + facet_idx); // find facet extents const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2))); @@ -1710,7 +1689,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - slicing object"; float scaled_z = scale_(z); for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) { - stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; + const stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; // find facet extents float min_z = std::min(facet->vertex[0](2), std::min(facet->vertex[1](2), facet->vertex[2](2))); @@ -1901,10 +1880,10 @@ TriangleMesh make_cylinder(double r, double h, double fa) //FIXME better to discretize an Icosahedron recursively http://www.songho.ca/opengl/gl_sphere.html TriangleMesh make_sphere(double radius, double fa) { - int sectorCount = ceil(2. * M_PI / fa); - int stackCount = ceil(M_PI / fa); - float sectorStep = 2. * M_PI / sectorCount; - float stackStep = M_PI / stackCount; + int sectorCount = int(ceil(2. * M_PI / fa)); + int stackCount = int(ceil(M_PI / fa)); + float sectorStep = float(2. * M_PI / sectorCount); + float stackStep = float(M_PI / stackCount); Pointf3s vertices; vertices.reserve((stackCount - 1) * sectorCount + 2); diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index c284f6482..4de1f5989 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -58,7 +58,7 @@ public: TriangleMeshPtrs split() const; void merge(const TriangleMesh &mesh); ExPolygons horizontal_projection() const; - const float* first_vertex() const { return this->stl.facet_start ? &this->stl.facet_start->vertex[0](0) : nullptr; } + const float* first_vertex() const { return this->stl.facet_start.empty() ? nullptr : &this->stl.facet_start.front().vertex[0](0); } // 2D convex hull of a 3D mesh projected into the Z=0 plane. Polygon convex_hull(); BoundingBoxf3 bounding_box() const; @@ -69,7 +69,7 @@ public: void reset_repair_stats(); bool needed_repair() const; void require_shared_vertices(); - bool has_shared_vertices() const { return stl.v_shared != NULL; } + bool has_shared_vertices() const { return ! stl.v_shared.empty(); } size_t facets_count() const { return this->stl.stats.number_of_facets; } bool empty() const { return this->facets_count() == 0; } bool is_splittable() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index d118a6877..ae017f7d1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -396,10 +396,10 @@ void GLGizmoSlaSupports::update_mesh() V.resize(3 * stl.stats.number_of_facets, 3); F.resize(stl.stats.number_of_facets, 3); for (unsigned int i=0; ivertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2); - V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2); - V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2); + const stl_facet &facet = stl.facet_start[i]; + V.block<1, 3>(3 * i + 0, 0) = facet.vertex[0]; + V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1]; + V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2]; F(i, 0) = 3*i+0; F(i, 1) = 3*i+1; F(i, 2) = 3*i+2; From c7ba48a4735cb2f6e98b56999a1b5d7476bde0fb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 5 Jun 2019 09:54:52 +0200 Subject: [PATCH 09/44] Fix of perl bindings --- xs/xsp/TriangleMesh.xsp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index e519f9210..a188e0b8d 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -100,7 +100,7 @@ TriangleMesh::vertices() CODE: if (!THIS->repaired) CONFESS("vertices() requires repair()"); - if (THIS->stl.v_shared == NULL) + if (THIS->stl.v_shared.empty()) stl_generate_shared_vertices(&(THIS->stl)); // vertices @@ -124,7 +124,7 @@ TriangleMesh::facets() CODE: if (!THIS->repaired) CONFESS("facets() requires repair()"); - if (THIS->stl.v_shared == NULL) + if (THIS->stl.v_shared.empty()) stl_generate_shared_vertices(&(THIS->stl)); // facets From 12797f2aa8a3d663b2c07a98d2194603140daade Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 5 Jun 2019 15:50:27 +0200 Subject: [PATCH 10/44] Re-adding missing file --- src/libslic3r/SLA/bicubic.h | 186 ++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/libslic3r/SLA/bicubic.h diff --git a/src/libslic3r/SLA/bicubic.h b/src/libslic3r/SLA/bicubic.h new file mode 100644 index 000000000..870d00dbd --- /dev/null +++ b/src/libslic3r/SLA/bicubic.h @@ -0,0 +1,186 @@ +#ifndef BICUBIC_HPP +#define BICUBIC_HPP + +#include +#include +#include + +#include + +namespace Slic3r { + +namespace BicubicInternal { + // Linear kernel, to be able to test cubic methods with hat kernels. + template + struct LinearKernel + { + typedef T FloatType; + + static T a00() { return T(0.); } + static T a01() { return T(0.); } + static T a02() { return T(0.); } + static T a03() { return T(0.); } + static T a10() { return T(1.); } + static T a11() { return T(-1.); } + static T a12() { return T(0.); } + static T a13() { return T(0.); } + static T a20() { return T(0.); } + static T a21() { return T(1.); } + static T a22() { return T(0.); } + static T a23() { return T(0.); } + static T a30() { return T(0.); } + static T a31() { return T(0.); } + static T a32() { return T(0.); } + static T a33() { return T(0.); } + }; + + // Interpolation kernel aka Catmul-Rom aka Keyes kernel. + template + struct CubicCatmulRomKernel + { + typedef T FloatType; + + static T a00() { return 0; } + static T a01() { return (T)-0.5; } + static T a02() { return (T) 1.; } + static T a03() { return (T)-0.5; } + static T a10() { return (T) 1.; } + static T a11() { return 0; } + static T a12() { return (T)-5./2.; } + static T a13() { return (T) 3./2.; } + static T a20() { return 0; } + static T a21() { return (T) 0.5; } + static T a22() { return (T) 2.; } + static T a23() { return (T)-3./2.; } + static T a30() { return 0; } + static T a31() { return 0; } + static T a32() { return (T)-0.5; } + static T a33() { return (T) 0.5; } + }; + + // B-spline kernel + template + struct CubicBSplineKernel + { + typedef T FloatType; + + static T a00() { return (T) 1./6.; } + static T a01() { return (T) -3./6.; } + static T a02() { return (T) 3./6.; } + static T a03() { return (T) -1./6.; } + static T a10() { return (T) 4./6.; } + static T a11() { return 0; } + static T a12() { return (T) -6./6.; } + static T a13() { return (T) 3./6.; } + static T a20() { return (T) 1./6.; } + static T a21() { return (T) 3./6.; } + static T a22() { return (T) 3./6.; } + static T a23() { return (T)- 3./6.; } + static T a30() { return 0; } + static T a31() { return 0; } + static T a32() { return 0; } + static T a33() { return (T) 1./6.; } + }; + + template + inline T clamp(T a, T lower, T upper) + { + return (a < lower) ? lower : + (a > upper) ? upper : a; + } +} + +template +struct CubicKernel +{ + typedef typename KERNEL KernelInternal; + typedef typename KERNEL::FloatType FloatType; + + static FloatType kernel(FloatType x) + { + x = fabs(x); + if (x >= (FloatType)2.) + return 0.0f; + if (x <= (FloatType)1.) { + FloatType x2 = x * x; + FloatType x3 = x2 * x; + return KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3; + } + assert(x > (FloatType)1. && x < (FloatType)2.); + x -= (FloatType)1.; + FloatType x2 = x * x; + FloatType x3 = x2 * x; + return KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3; + } + + static FloatType interpolate(FloatType f0, FloatType f1, FloatType f2, FloatType f3, FloatType x) + { + const FloatType x2 = x*x; + const FloatType x3 = x*x*x; + return f0*(KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3) + + f1*(KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3) + + f2*(KERNEL::a20() + KERNEL::a21() * x + KERNEL::a22() * x2 + KERNEL::a23() * x3) + + f3*(KERNEL::a30() + KERNEL::a31() * x + KERNEL::a32() * x2 + KERNEL::a33() * x3); + } +}; + +// Linear splines +typedef CubicKernel> LinearKernelf; +typedef CubicKernel> LinearKerneld; +// Catmul-Rom splines +typedef CubicKernel> CubicCatmulRomKernelf; +typedef CubicKernel> CubicCatmulRomKerneld; +typedef CubicKernel> CubicInterpolationKernelf; +typedef CubicKernel> CubicInterpolationKerneld; +// Cubic B-splines +typedef CubicKernel> CubicBSplineKernelf; +typedef CubicKernel> CubicBSplineKerneld; + +template +static float cubic_interpolate(const Eigen::ArrayBase &F, const typename KERNEL::FloatType pt, const typename KERNEL::FloatType dx) +{ + typedef typename KERNEL::FloatType T; + const int w = int(F.size()); + const int ix = (int)floor(pt); + const T s = pt - (T)ix; + + if (ix > 1 && ix + 2 < w) { + // Inside the fully interpolated region. + return KERNEL::interpolate(F[ix - 1], F[ix], F[ix + 1], F[ix + 2], s); + } + // Transition region. Extend with a constant function. + auto f = [&F, w](x) { return F[BicubicInternal::clamp(x, 0, w - 1)]; } + return KERNEL::interpolate(f(ix - 1), f(ix), f(ix + 1), f(ix + 2), s); +} + +template +static float bicubic_interpolate(const Eigen::MatrixBase &F, const Eigen::Matrix &pt, const typename KERNEL::FloatType dx) +{ + typedef typename KERNEL::FloatType T; + const int w = F.cols(); + const int h = F.rows(); + const int ix = (int)floor(pt[0]); + const int iy = (int)floor(pt[1]); + const T s = pt[0] - (T)ix; + const T t = pt[1] - (T)iy; + + if (ix > 1 && ix + 2 < w && iy > 1 && iy + 2 < h) { + // Inside the fully interpolated region. + return KERNEL::interpolate( + KERNEL::interpolate(F(ix-1,iy-1),F(ix ,iy-1),F(ix+1,iy-1),F(ix+2,iy-1),s), + KERNEL::interpolate(F(ix-1,iy ),F(ix ,iy ),F(ix+1,iy ),F(ix+2,iy ),s), + KERNEL::interpolate(F(ix-1,iy+1),F(ix ,iy+1),F(ix+1,iy+1),F(ix+2,iy+1),s), + KERNEL::interpolate(F(ix-1,iy+2),F(ix ,iy+2),F(ix+1,iy+2),F(ix+2,iy+2),s),t); + } + // Transition region. Extend with a constant function. + auto f = [&f, w, h](int x, int y) { return F(BicubicInternal::clamp(x,0,w-1),BicubicInternal::clamp(y,0,h-1)); } + return KERNEL::interpolate( + KERNEL::interpolate(f(ix-1,iy-1),f(ix ,iy-1),f(ix+1,iy-1),f(ix+2,iy-1),s), + KERNEL::interpolate(f(ix-1,iy ),f(ix ,iy ),f(ix+1,iy ),f(ix+2,iy ),s), + KERNEL::interpolate(f(ix-1,iy+1),f(ix ,iy+1),f(ix+1,iy+1),f(ix+2,iy+1),s), + KERNEL::interpolate(f(ix-1,iy+2),f(ix ,iy+2),f(ix+1,iy+2),f(ix+2,iy+2),s),t); +} + +} // namespace Slic3r + +#endif /* BICUBIC_HPP */ From d4fe7b5a962b0aff416e8b8929535034fe9ba69b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 6 Jun 2019 14:27:07 +0200 Subject: [PATCH 11/44] Adding rotating calipers algorithm for minimum are bounding box rotation. Cleanup, fix build on windows and add test for rotcalipers. Try to fix compilation on windows With updates from libnest2d Another build fix. Clean up and add comments. adding rotcalipers test and some cleanup Trying to fix on OSX Fix rotcalipers array indexing Get rid of boost convex hull. Adding helper function 'remove_collinear_points' Importing new libnest2d upgrades. Disable using __int128 in NFP on OSX --- src/libnest2d/CMakeLists.txt | 16 +- src/libnest2d/include/libnest2d.h | 125 ++--- .../libnest2d/backends/clipper/CMakeLists.txt | 13 +- .../backends/clipper/clipper_polygon.hpp | 8 +- .../libnest2d/backends/clipper/geometries.hpp | 117 +--- src/libnest2d/include/libnest2d/common.hpp | 28 + .../include/libnest2d/geometry_traits.hpp | 408 ++++++++++---- .../include/libnest2d/geometry_traits_nfp.hpp | 527 +++--------------- src/libnest2d/include/libnest2d/libnest2d.hpp | 15 +- src/libnest2d/include/libnest2d/optimizer.hpp | 4 +- .../libnest2d/optimizers/nlopt/CMakeLists.txt | 6 +- .../optimizers/optimlib/CMakeLists.txt | 5 - .../libnest2d/placers/bottomleftplacer.hpp | 8 +- .../include/libnest2d/placers/nfpplacer.hpp | 13 +- .../include/libnest2d/utils/boost_alg.hpp | 37 +- .../include/libnest2d/utils/rotcalipers.hpp | 268 +++++++++ src/libnest2d/src/libnest2d.cpp | 23 + src/libnest2d/tests/CMakeLists.txt | 9 +- src/libnest2d/tests/test.cpp | 205 ++++++- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Int128.hpp | 6 + src/libslic3r/MinAreaBoundingBox.cpp | 142 +++++ src/libslic3r/MinAreaBoundingBox.hpp | 59 ++ src/libslic3r/ModelArrange.cpp | 31 +- src/slic3r/GUI/Plater.cpp | 53 +- 25 files changed, 1272 insertions(+), 856 deletions(-) delete mode 100644 src/libnest2d/include/libnest2d/optimizers/optimlib/CMakeLists.txt create mode 100644 src/libnest2d/include/libnest2d/utils/rotcalipers.hpp create mode 100644 src/libnest2d/src/libnest2d.cpp create mode 100644 src/libslic3r/MinAreaBoundingBox.cpp create mode 100644 src/libslic3r/MinAreaBoundingBox.hpp diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt index 2508c984a..d50c78b17 100644 --- a/src/libnest2d/CMakeLists.txt +++ b/src/libnest2d/CMakeLists.txt @@ -48,6 +48,9 @@ set(LIBNEST2D_SRCFILES ${SRC_DIR}/libnest2d/optimizer.hpp ${SRC_DIR}/libnest2d/utils/metaloop.hpp ${SRC_DIR}/libnest2d/utils/rotfinder.hpp + ${SRC_DIR}/libnest2d/utils/rotcalipers.hpp + ${SRC_DIR}/libnest2d/utils/bigint.hpp + ${SRC_DIR}/libnest2d/utils/rational.hpp ${SRC_DIR}/libnest2d/placers/placer_boilerplate.hpp ${SRC_DIR}/libnest2d/placers/bottomleftplacer.hpp ${SRC_DIR}/libnest2d/placers/nfpplacer.hpp @@ -70,12 +73,10 @@ if(TBB_FOUND) # The Intel TBB library will use the std::exception_ptr feature of C++11. target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0) - target_link_libraries(libnest2d INTERFACE tbb) - # The following breaks compilation on Visual Studio in Debug mode. - #find_package(Threads REQUIRED) - #target_link_libraries(libnest2d INTERFACE ${TBB_LIBRARIES} ${CMAKE_DL_LIBS} - # Threads::Threads - # ) + find_package(Threads REQUIRED) + target_link_libraries(libnest2d INTERFACE ${TBB_LIBRARIES} ${CMAKE_DL_LIBS} + Threads::Threads + ) else() find_package(OpenMP QUIET) @@ -92,10 +93,11 @@ endif() add_subdirectory(${SRC_DIR}/libnest2d/backends/${LIBNEST2D_GEOMETRIES}) target_link_libraries(libnest2d INTERFACE ${LIBNEST2D_GEOMETRIES}Backend) + add_subdirectory(${SRC_DIR}/libnest2d/optimizers/${LIBNEST2D_OPTIMIZER}) target_link_libraries(libnest2d INTERFACE ${LIBNEST2D_OPTIMIZER}Optimizer) -#target_sources(libnest2d INTERFACE ${LIBNEST2D_SRCFILES}) +# target_sources(libnest2d INTERFACE ${LIBNEST2D_SRCFILES}) target_include_directories(libnest2d INTERFACE ${SRC_DIR}) if(NOT LIBNEST2D_HEADER_ONLY) diff --git a/src/libnest2d/include/libnest2d.h b/src/libnest2d/include/libnest2d.h index 4ad752421..a6eb36a4b 100644 --- a/src/libnest2d/include/libnest2d.h +++ b/src/libnest2d/include/libnest2d.h @@ -47,6 +47,17 @@ using NfpPlacer = _NfpPlacer; // This supports only box shaped bins using BottomLeftPlacer = placers::_BottomLeftPlacer; +#ifdef LIBNEST2D_STATIC + +extern template class Nester; +extern template class Nester; +extern template PackGroup Nester::execute( + std::vector::iterator, std::vector::iterator); +extern template PackGroup Nester::execute( + std::vector::iterator, std::vector::iterator); + +#endif + template::iterator> @@ -60,19 +71,6 @@ PackGroup nest(Iterator from, Iterator to, return nester.execute(from, to); } -template> -PackGroup nest(Container&& cont, - const typename Placer::BinType& bin, - Coord dist = 0, - const typename Placer::Config& pconf = {}, - const typename Selector::Config& sconf = {}) -{ - return nest(cont.begin(), cont.end(), - bin, dist, pconf, sconf); -} - template::iterator> @@ -90,6 +88,42 @@ PackGroup nest(Iterator from, Iterator to, return nester.execute(from, to); } +#ifdef LIBNEST2D_STATIC + +extern template class Nester; +extern template class Nester; + +extern template PackGroup nest(std::vector::iterator from, + std::vector::iterator to, + const Box& bin, + Coord dist = 0, + const NfpPlacer::Config& pconf, + const FirstFitSelection::Config& sconf); + +extern template PackGroup nest(std::vector::iterator from, + std::vector::iterator to, + const Box& bin, + ProgressFunction prg, + StopCondition scond, + Coord dist = 0, + const NfpPlacer::Config& pconf, + const FirstFitSelection::Config& sconf); + +#endif + +template> +PackGroup nest(Container&& cont, + const typename Placer::BinType& bin, + Coord dist = 0, + const typename Placer::Config& pconf = {}, + const typename Selector::Config& sconf = {}) +{ + return nest(cont.begin(), cont.end(), + bin, dist, pconf, sconf); +} + template> @@ -105,71 +139,6 @@ PackGroup nest(Container&& cont, bin, prg, scond, dist, pconf, sconf); } -#ifdef LIBNEST2D_STATIC -extern template -PackGroup nest&>( - std::vector& cont, - const Box& bin, - Coord dist, - const NfpPlacer::Config& pcfg, - const FirstFitSelection::Config& scfg -); - -extern template -PackGroup nest&>( - std::vector& cont, - const Box& bin, - ProgressFunction prg, - StopCondition scond, - Coord dist, - const NfpPlacer::Config& pcfg, - const FirstFitSelection::Config& scfg -); - -extern template -PackGroup nest>( - std::vector&& cont, - const Box& bin, - Coord dist, - const NfpPlacer::Config& pcfg, - const FirstFitSelection::Config& scfg -); - -extern template -PackGroup nest>( - std::vector&& cont, - const Box& bin, - ProgressFunction prg, - StopCondition scond, - Coord dist, - const NfpPlacer::Config& pcfg, - const FirstFitSelection::Config& scfg -); - -extern template -PackGroup nest::iterator>( - std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - Coord dist, - const NfpPlacer::Config& pcfg, - const FirstFitSelection::Config& scfg -); - -extern template -PackGroup nest::iterator>( - std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - ProgressFunction prg, - StopCondition scond, - Coord dist, - const NfpPlacer::Config& pcfg, - const FirstFitSelection::Config& scfg -); - -#endif - } #endif // LIBNEST2D_H diff --git a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt index e20cbc70d..e207ecde4 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt +++ b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt @@ -33,19 +33,18 @@ if(NOT TARGET clipper) # If there is a clipper target in the parent project we a # ${clipper_library_BINARY_DIR} # ) - add_library(ClipperBackend STATIC + add_library(clipperBackend STATIC ${clipper_library_SOURCE_DIR}/clipper.cpp ${clipper_library_SOURCE_DIR}/clipper.hpp) - target_include_directories(ClipperBackend INTERFACE - ${clipper_library_SOURCE_DIR}) + target_include_directories(clipperBackend INTERFACE ${clipper_library_SOURCE_DIR}) else() message(FATAL_ERROR "Can't find clipper library and no SVN client found to download. You can download the clipper sources and define a clipper target in your project, that will work for libnest2d.") endif() else() - add_library(ClipperBackend INTERFACE) - target_link_libraries(ClipperBackend INTERFACE Clipper::Clipper) + add_library(clipperBackend INTERFACE) + target_link_libraries(clipperBackend INTERFACE Clipper::Clipper) endif() else() # set(CLIPPER_INCLUDE_DIRS "" PARENT_SCOPE) @@ -69,6 +68,6 @@ target_include_directories(clipperBackend SYSTEM INTERFACE ${Boost_INCLUDE_DIRS} target_compile_definitions(clipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER) -# And finally plug the ClipperBackend into libnest2d -#target_link_libraries(libnest2d INTERFACE ClipperBackend) +# And finally plug the clipperBackend into libnest2d +# target_link_libraries(libnest2d INTERFACE clipperBackend) diff --git a/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp b/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp index e9fbfbd18..6511fbb72 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp @@ -12,13 +12,13 @@ struct Polygon { inline Polygon() = default; inline explicit Polygon(const Path& cont): Contour(cont) {} - inline explicit Polygon(const Paths& holes): - Holes(holes) {} +// inline explicit Polygon(const Paths& holes): +// Holes(holes) {} inline Polygon(const Path& cont, const Paths& holes): Contour(cont), Holes(holes) {} inline explicit Polygon(Path&& cont): Contour(std::move(cont)) {} - inline explicit Polygon(Paths&& holes): Holes(std::move(holes)) {} +// inline explicit Polygon(Paths&& holes): Holes(std::move(holes)) {} inline Polygon(Path&& cont, Paths&& holes): Contour(std::move(cont)), Holes(std::move(holes)) {} }; @@ -42,7 +42,7 @@ inline IntPoint& operator -=(IntPoint& p, const IntPoint& pa ) { return p; } -inline IntPoint operator -(IntPoint& p ) { +inline IntPoint operator -(const IntPoint& p ) { IntPoint ret = p; ret.X = -ret.X; ret.Y = -ret.Y; diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index 232668f61..e9fad405b 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -20,43 +20,23 @@ using PathImpl = ClipperLib::Path; using HoleStore = ClipperLib::Paths; using PolygonImpl = ClipperLib::Polygon; -// Type of coordinate units used by Clipper -template<> struct CoordType { - using Type = ClipperLib::cInt; -}; - -// Type of point used by Clipper -template<> struct PointType { - using Type = PointImpl; -}; - -template<> struct PointType { - using Type = PointImpl; -}; - -template<> struct PointType { - using Type = PointImpl; -}; - -template<> struct CountourType { - using Type = PathImpl; -}; - template<> struct ShapeTag { using Type = PolygonTag; }; -template<> struct ShapeTag { using Type = PathTag; }; -template<> struct ShapeTag { using Type = PointTag; }; +template<> struct ShapeTag { using Type = PathTag; }; +template<> struct ShapeTag { using Type = PointTag; }; -template<> struct ShapeTag> { - using Type = MultiPolygonTag; -}; +// Type of coordinate units used by Clipper. Enough to specialize for point, +// the rest of the types will work (Path, Polygon) +template<> struct CoordType { using Type = ClipperLib::cInt; }; -template<> struct PointType> { - using Type = PointImpl; -}; +// Enough to specialize for path, it will work for multishape and Polygon +template<> struct PointType { using Type = PointImpl; }; -template<> struct HolesContainer { - using Type = ClipperLib::Paths; -}; +// This is crucial. CountourType refers to itself by default, so we don't have +// to secialize for clipper Path. ContourType::Type is PathImpl. +template<> struct ContourType { using Type = PathImpl; }; + +// The holes are contained in Clipper::Paths +template<> struct HolesContainer { using Type = ClipperLib::Paths; }; namespace pointlike { @@ -86,39 +66,11 @@ template<> inline TCoord& y(PointImpl& p) } +// Using the libnest2d default area implementation #define DISABLE_BOOST_AREA -namespace _smartarea { - -template -inline double area(const PolygonImpl& /*sh*/) { - return std::nan(""); -} - -template<> -inline double area(const PolygonImpl& sh) { - return std::accumulate(sh.Holes.begin(), sh.Holes.end(), - ClipperLib::Area(sh.Contour), - [](double a, const ClipperLib::Path& pt){ - return a + ClipperLib::Area(pt); - }); -} - -template<> -inline double area(const PolygonImpl& sh) { - return -area(sh); -} - -} - namespace shapelike { -// Tell libnest2d how to make string out of a ClipperPolygon object -template<> inline double area(const PolygonImpl& sh, const PolygonTag&) -{ - return _smartarea::area::Value>(sh); -} - template<> inline void offset(PolygonImpl& sh, TCoord distance) { #define DISABLE_BOOST_OFFSET @@ -200,43 +152,16 @@ inline PolygonImpl create(const PathImpl& path, const HoleStore& holes) { PolygonImpl p; p.Contour = path; - - // Expecting that the coordinate system Y axis is positive in upwards - // direction - if(ClipperLib::Orientation(p.Contour)) { - // Not clockwise then reverse the b*tch - ClipperLib::ReversePath(p.Contour); - } - p.Holes = holes; - for(auto& h : p.Holes) { - if(!ClipperLib::Orientation(h)) { - ClipperLib::ReversePath(h); - } - } - + return p; } template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) { PolygonImpl p; p.Contour.swap(path); - - // Expecting that the coordinate system Y axis is positive in upwards - // direction - if(ClipperLib::Orientation(p.Contour)) { - // Not clockwise then reverse the b*tch - ClipperLib::ReversePath(p.Contour); - } - p.Holes.swap(holes); - - for(auto& h : p.Holes) { - if(!ClipperLib::Orientation(h)) { - ClipperLib::ReversePath(h); - } - } - + return p; } @@ -314,13 +239,13 @@ inline void rotate(PolygonImpl& sh, const Radians& rads) } // namespace shapelike #define DISABLE_BOOST_NFP_MERGE -inline std::vector clipper_execute( +inline TMultiShape clipper_execute( ClipperLib::Clipper& clipper, ClipperLib::ClipType clipType, ClipperLib::PolyFillType subjFillType = ClipperLib::pftEvenOdd, ClipperLib::PolyFillType clipFillType = ClipperLib::pftEvenOdd) { - shapelike::Shapes retv; + TMultiShape retv; ClipperLib::PolyTree result; clipper.Execute(clipType, result, subjFillType, clipFillType); @@ -370,8 +295,8 @@ inline std::vector clipper_execute( namespace nfp { -template<> inline std::vector -merge(const std::vector& shapes) +template<> inline TMultiShape +merge(const TMultiShape& shapes) { ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution); @@ -394,6 +319,8 @@ merge(const std::vector& shapes) } +#define DISABLE_BOOST_CONVEX_HULL + //#define DISABLE_BOOST_SERIALIZE //#define DISABLE_BOOST_UNSERIALIZE diff --git a/src/libnest2d/include/libnest2d/common.hpp b/src/libnest2d/include/libnest2d/common.hpp index 6867f76f3..66e095ae2 100644 --- a/src/libnest2d/include/libnest2d/common.hpp +++ b/src/libnest2d/include/libnest2d/common.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L #define BP2D_NOEXCEPT @@ -197,6 +198,33 @@ public: } }; +struct ScalarTag {}; +struct BigIntTag {}; +struct RationalTag {}; + +template struct _NumTag { + using Type = + enable_if_t::value, ScalarTag>; +}; + +template using NumTag = typename _NumTag>::Type; + +/// A local version for abs that is garanteed to work with libnest2d types +template inline T abs(const T& v, ScalarTag) +{ + return std::abs(v); +} + +template inline T abs(const T& v) { return abs(v, NumTag()); } + +template inline T2 cast(const T1& v, ScalarTag, ScalarTag) +{ + return static_cast(v); +} + +template inline T2 cast(const T1& v) { + return cast(v, NumTag(), NumTag()); +} } #endif // LIBNEST2D_CONFIG_HPP diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 917f5280d..6c55d0e3f 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -7,45 +7,125 @@ #include #include #include -#include #include #include +#include -#include "common.hpp" +#include namespace libnest2d { +// Meta tags for different geometry concepts. +struct PointTag {}; +struct PolygonTag {}; +struct PathTag {}; +struct MultiPolygonTag {}; +struct BoxTag {}; +struct CircleTag {}; + +/// Meta-function to derive the tag of a shape type. +template struct ShapeTag { using Type = typename Shape::Tag; }; + +/// Tag will be used instead of `typename ShapeTag::Type` +template using Tag = typename ShapeTag>::Type; + +/// Meta function to derive the contour type for a polygon which could be itself +template struct ContourType { using Type = RawShape; }; + +/// TContour instead of `typename ContourType::type` +template +using TContour = typename ContourType>::Type; + +/// Getting the type of point structure used by a shape. +template struct PointType { + using Type = typename PointType>::Type; +}; + +/// TPoint as shorthand for `typename PointType::Type`. +template +using TPoint = typename PointType>::Type; + /// Getting the coordinate data type for a geometry class. -template struct CoordType { using Type = long; }; +template struct CoordType { + using Type = typename CoordType>::Type; +}; /// TCoord as shorthand for typename `CoordType::Type`. template using TCoord = typename CoordType>::Type; -/// Getting the type of point structure used by a shape. -template struct PointType { using Type = typename Sh::PointType; }; +/// Getting the computation type for a certain geometry type. +/// It is the coordinate type by default but it is advised that a type with +/// larger precision and (or) range is specified. +template::value> struct ComputeType {}; -/// TPoint as shorthand for `typename PointType::Type`. -template -using TPoint = typename PointType>::Type; +/// A compute type is introduced to hold the results of computations on +/// coordinates and points. It should be larger in range than the coordinate +/// type or the range of coordinates should be limited to not loose precision. +template struct ComputeType { + using Type = typename ComputeType>::Type; +}; +/// libnest2d will choose a default compute type for various coordinate types +/// if the backend has not specified anything. +template struct DoublePrecision { using Type = T; }; +template<> struct DoublePrecision { using Type = int16_t; }; +template<> struct DoublePrecision { using Type = int32_t; }; +template<> struct DoublePrecision { using Type = int64_t; }; +template<> struct DoublePrecision { using Type = double; }; +template<> struct DoublePrecision { using Type = long double; }; +template struct ComputeType { + using Type = typename DoublePrecision::Type; +}; -template struct CountourType { using Type = RawShape; }; - -template -using TContour = typename CountourType>::Type; - +/// TCompute shorthand for `typename ComputeType::Type` +template using TCompute = typename ComputeType>::Type; +/// A meta function to derive a container type for holes in a polygon template struct HolesContainer { using Type = std::vector>; }; +/// Shorthand for `typename HolesContainer::Type` template using THolesContainer = typename HolesContainer>::Type; +/* + * TContour, TPoint, TCoord and TCompute should be usable for any type for which + * it makes sense. For example, the point type could be derived from the contour, + * the polygon and (or) the multishape as well. The coordinate type also and + * including the point type. TCoord, TCoord, TCoord are + * all valid types and derives the coordinate type of template argument Polygon, + * Path and Point. This is also true for TCompute, but it can also take the + * coordinate type as argument. + */ -template -struct LastPointIsFirst { static const bool Value = true; }; +/* + * A Multi shape concept is also introduced. A multi shape is something that + * can contain the result of an operation where the input is one polygon and + * the result could be many polygons or path -> paths. The MultiShape should be + * a container type. If the backend does not specialize the MultiShape template, + * a default multi shape container will be used. + */ + +/// The default multi shape container. +template struct DefaultMultiShape: public std::vector { + using Tag = MultiPolygonTag; + template DefaultMultiShape(Args&&...args): + std::vector(std::forward(args)...) {} +}; + +/// The MultiShape Type trait which gets the container type for a geometry type. +template struct MultiShape { using Type = DefaultMultiShape; }; + +/// use TMultiShape instead of `typename MultiShape::Type` +template +using TMultiShape = typename MultiShape>::Type; + +// A specialization of ContourType to work with the default multishape type +template struct ContourType> { + using Type = typename ContourType::Type; +}; enum class Orientation { CLOCKWISE, @@ -59,6 +139,11 @@ struct OrientationType { static const Orientation Value = Orientation::CLOCKWISE; }; +template inline /*constexpr*/ bool is_clockwise() { + return OrientationType>::Value == Orientation::CLOCKWISE; +} + + /** * \brief A point pair base class for other point pairs (segment, box, ...). * \tparam RawPoint The actual point type to use. @@ -69,21 +154,6 @@ struct PointPair { RawPoint p2; }; -struct PointTag {}; -struct PolygonTag {}; -struct PathTag {}; -struct MultiPolygonTag {}; -struct BoxTag {}; -struct CircleTag {}; - -/// Meta-functions to derive the tags -template struct ShapeTag { using Type = typename Shape::Tag; }; -template using Tag = typename ShapeTag>::Type; - -template struct MultiShape { using Type = std::vector; }; -template -using TMultiShape =typename MultiShape>::Type; - /** * \brief An abstraction of a box; */ @@ -114,11 +184,16 @@ public: inline RawPoint center() const BP2D_NOEXCEPT; - inline double area() const BP2D_NOEXCEPT { - return double(width()*height()); + template> + inline Unit area() const BP2D_NOEXCEPT { + return Unit(width())*height(); } }; +template struct PointType<_Box> { + using Type = typename _Box::PointType; +}; + template class _Circle { RawPoint center_; @@ -129,7 +204,6 @@ public: using PointType = RawPoint; _Circle() = default; - _Circle(const RawPoint& center, double r): center_(center), radius_(r) {} inline const RawPoint& center() const BP2D_NOEXCEPT { return center_; } @@ -137,12 +211,16 @@ public: inline double radius() const BP2D_NOEXCEPT { return radius_; } inline void radius(double r) { radius_ = r; } - + inline double area() const BP2D_NOEXCEPT { - return 2.0*Pi*radius_*radius_; + return Pi_2 * radius_ * radius_; } }; +template struct PointType<_Circle> { + using Type = typename _Circle::PointType; +}; + /** * \brief An abstraction of a directed line segment with two points. */ @@ -185,7 +263,12 @@ public: inline Radians angleToXaxis() const; /// The length of the segment in the measure of the coordinate system. - inline double length(); + template> inline Unit sqlength() const; + +}; + +template struct PointType<_Segment> { + using Type = typename _Circle::PointType; }; // This struct serves almost as a namespace. The only difference is that is can @@ -216,33 +299,56 @@ inline TCoord& y(RawPoint& p) return p.y(); } -template -inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/) +template> +inline Unit squaredDistance(const RawPoint& p1, const RawPoint& p2) { - static_assert(always_false::value, - "PointLike::distance(point, point) unimplemented!"); - return 0; + auto x1 = Unit(x(p1)), y1 = Unit(y(p1)), x2 = Unit(x(p2)), y2 = Unit(y(p2)); + Unit a = (x2 - x1), b = (y2 - y1); + return a * a + b * b; } template -inline double distance(const RawPoint& /*p1*/, - const _Segment& /*s*/) +inline double distance(const RawPoint& p1, const RawPoint& p2) { - static_assert(always_false::value, - "PointLike::distance(point, segment) unimplemented!"); - return 0; + return std::sqrt(squaredDistance(p1, p2)); } -template -inline std::pair, bool> horizontalDistance( +// create perpendicular vector +template inline Pt perp(const Pt& p) +{ + return Pt(y(p), -x(p)); +} + +template> +inline Unit dotperp(const Pt& a, const Pt& b) +{ + return Unit(x(a)) * Unit(y(b)) - Unit(y(a)) * Unit(x(b)); +} + +// dot product +template> +inline Unit dot(const Pt& a, const Pt& b) +{ + return Unit(x(a)) * x(b) + Unit(y(a)) * y(b); +} + +// squared vector magnitude +template> +inline Unit magnsq(const Pt& p) +{ + return Unit(x(p)) * x(p) + Unit(y(p)) * y(p); +} + +template> +inline std::pair horizontalDistance( const RawPoint& p, const _Segment& s) { - using Unit = TCoord; - auto x = pointlike::x(p), y = pointlike::y(p); - auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); - auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); + namespace pl = pointlike; + auto x = Unit(pl::x(p)), y = Unit(pl::y(p)); + auto x1 = Unit(pl::x(s.first())), y1 = Unit(pl::y(s.first())); + auto x2 = Unit(pl::x(s.second())), y2 = Unit(pl::y(s.second())); - TCoord ret; + Unit ret; if( (y < y1 && y < y2) || (y > y1 && y > y2) ) return {0, false}; @@ -250,8 +356,7 @@ inline std::pair, bool> horizontalDistance( ret = std::min( x-x1, x -x2); else if( (y == y1 && y == y2) && (x < x1 && x < x2)) ret = -std::min(x1 - x, x2 - x); - else if(std::abs(y - y1) <= std::numeric_limits::epsilon() && - std::abs(y - y2) <= std::numeric_limits::epsilon()) + else if(y == y1 && y == y2) ret = 0; else ret = x - x1 + (x1 - x2)*(y1 - y)/(y1 - y2); @@ -259,16 +364,16 @@ inline std::pair, bool> horizontalDistance( return {ret, true}; } -template -inline std::pair, bool> verticalDistance( +template> +inline std::pair verticalDistance( const RawPoint& p, const _Segment& s) { - using Unit = TCoord; - auto x = pointlike::x(p), y = pointlike::y(p); - auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first()); - auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second()); + namespace pl = pointlike; + auto x = Unit(pl::x(p)), y = Unit(pl::y(p)); + auto x1 = Unit(pl::x(s.first())), y1 = Unit(pl::y(s.first())); + auto x2 = Unit(pl::x(s.second())), y2 = Unit(pl::y(s.second())); - TCoord ret; + Unit ret; if( (x < x1 && x < x2) || (x > x1 && x > x2) ) return {0, false}; @@ -276,8 +381,7 @@ inline std::pair, bool> verticalDistance( ret = std::min( y-y1, y -y2); else if( (x == x1 && x == x2) && (y < y1 && y < y2)) ret = -std::min(y1 - y, y2 - y); - else if(std::abs(x - x1) <= std::numeric_limits::epsilon() && - std::abs(x - x2) <= std::numeric_limits::epsilon()) + else if(x == x1 && x == x2) ret = 0; else ret = y - y1 + (y1 - y2)*(x1 - x)/(x1 - x2); @@ -333,9 +437,10 @@ inline Radians _Segment::angleToXaxis() const } template -inline double _Segment::length() +template +inline Unit _Segment::sqlength() const { - return pointlike::distance(first(), second()); + return pointlike::squaredDistance(first(), second()); } template @@ -346,8 +451,8 @@ inline RawPoint _Box::center() const BP2D_NOEXCEPT { using Coord = TCoord; RawPoint ret = { // No rounding here, we dont know if these are int coords - static_cast( (getX(minc) + getX(maxc))/2.0 ), - static_cast( (getY(minc) + getY(maxc))/2.0 ) + Coord( (getX(minc) + getX(maxc)) / Coord(2) ), + Coord( (getY(minc) + getY(maxc)) / Coord(2) ) }; return ret; @@ -362,9 +467,6 @@ enum class Formats { // used in friend declarations and can be aliased at class scope. namespace shapelike { -template -using Shapes = TMultiShape; - template inline RawShape create(const TContour& contour, const THolesContainer& holes) @@ -449,7 +551,7 @@ inline void reserve(RawPath& p, size_t vertex_capacity, const PathTag&) template inline void addVertex(RawShape& sh, const PathTag&, Args...args) { - return sh.emplace_back(std::forward(args)...); + sh.emplace_back(std::forward(args)...); } template @@ -504,13 +606,8 @@ inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/) "shapelike::unserialize() unimplemented!"); } -template -inline double area(const RawShape& /*sh*/, const PolygonTag&) -{ - static_assert(always_false::value, - "shapelike::area() unimplemented!"); - return 0; -} +template +inline Unit area(const Cntr& poly, const PathTag& ); template inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/) @@ -556,14 +653,14 @@ inline bool touches( const TPoint& /*point*/, template inline _Box> boundingBox(const RawShape& /*sh*/, - const PolygonTag&) + const PathTag&) { static_assert(always_false::value, "shapelike::boundingBox(shape) unimplemented!"); } template -inline _Box> +inline _Box> boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&) { static_assert(always_false::value, @@ -571,21 +668,10 @@ boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&) } template -inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&) -{ - static_assert(always_false::value, - "shapelike::convexHull(shape) unimplemented!"); - return RawShape(); -} +inline RawShape convexHull(const RawShape& sh, const PathTag&); -template -inline typename RawShapes::value_type -convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&) -{ - static_assert(always_false::value, - "shapelike::convexHull(shapes) unimplemented!"); - return typename RawShapes::value_type(); -} +template +inline S convexHull(const RawShapes& sh, const MultiPolygonTag&); template inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/) @@ -745,13 +831,19 @@ inline void reserve(T& sh, size_t vertex_capacity) { template inline void addVertex(RawShape& sh, const PolygonTag&, Args...args) { - return addVertex(contour(sh), PathTag(), std::forward(args)...); + addVertex(contour(sh), PathTag(), std::forward(args)...); } template // Tag dispatcher inline void addVertex(RawShape& sh, Args...args) { - return addVertex(sh, Tag(), std::forward(args)...); + addVertex(sh, Tag(), std::forward(args)...); +} + +template +inline _Box> boundingBox(const RawShape& poly, const PolygonTag&) +{ + return boundingBox(contour(poly), PathTag()); } template @@ -786,7 +878,7 @@ inline _Box> boundingBox(const S& sh) template inline double area(const Box& box, const BoxTag& ) { - return box.area(); + return box.template area(); } template @@ -795,6 +887,35 @@ inline double area(const Circle& circ, const CircleTag& ) return circ.area(); } +template +inline Unit area(const Cntr& poly, const PathTag& ) +{ + namespace sl = shapelike; + if (sl::cend(poly) - sl::cbegin(poly) < 3) return 0.0; + + Unit a = 0; + for (auto i = sl::cbegin(poly), j = std::prev(sl::cend(poly)); + i < sl::cend(poly); ++i) + { + auto xj = Unit(getX(*j)), yj = Unit(getY(*j)); + auto xi = Unit(getX(*i)), yi = Unit(getY(*i)); + a += (xj + xi) * (yj - yi); + j = i; + } + a /= 2; + return is_clockwise() ? a : -a; +} + +template inline double area(const S& poly, const PolygonTag& ) +{ + auto hls = holes(poly); + return std::accumulate(hls.begin(), hls.end(), + area(contour(poly), PathTag()), + [](double a, const TContour &h){ + return a + area(h, PathTag()); + }); +} + template // Dispatching function inline double area(const RawShape& sh) { @@ -811,6 +932,12 @@ inline double area(const RawShapes& shapes, const MultiPolygonTag&) }); } +template +inline RawShape convexHull(const RawShape& sh, const PolygonTag&) +{ + return create(convexHull(contour(sh), PathTag())); +} + template inline auto convexHull(const RawShape& sh) -> decltype(convexHull(sh, Tag())) // TODO: C++14 could deduce @@ -818,11 +945,91 @@ inline auto convexHull(const RawShape& sh) return convexHull(sh, Tag()); } +template +inline RawShape convexHull(const RawShape& sh, const PathTag&) +{ + using Unit = TCompute; + using Point = TPoint; + namespace sl = shapelike; + + size_t edges = sl::cend(sh) - sl::cbegin(sh); + if(edges <= 3) return {}; + + bool closed = false; + std::vector U, L; + U.reserve(1 + edges / 2); L.reserve(1 + edges / 2); + + std::vector pts; pts.reserve(edges); + std::copy(sl::cbegin(sh), sl::cend(sh), std::back_inserter(pts)); + + auto fpt = pts.front(), lpt = pts.back(); + if(getX(fpt) == getX(lpt) && getY(fpt) == getY(lpt)) { + closed = true; pts.pop_back(); + } + + std::sort(pts.begin(), pts.end(), + [](const Point& v1, const Point& v2) + { + Unit x1 = getX(v1), x2 = getX(v2), y1 = getY(v1), y2 = getY(v2); + return x1 == x2 ? y1 < y2 : x1 < x2; + }); + + auto dir = [](const Point& p, const Point& q, const Point& r) { + return (Unit(getY(q)) - getY(p)) * (Unit(getX(r)) - getX(p)) - + (Unit(getX(q)) - getX(p)) * (Unit(getY(r)) - getY(p)); + }; + + auto ik = pts.begin(); + + while(ik != pts.end()) { + + while(U.size() > 1 && dir(U[U.size() - 2], U.back(), *ik) <= 0) + U.pop_back(); + while(L.size() > 1 && dir(L[L.size() - 2], L.back(), *ik) >= 0) + L.pop_back(); + + U.emplace_back(*ik); + L.emplace_back(*ik); + + ++ik; + } + + RawShape ret; reserve(ret, U.size() + L.size()); + if(is_clockwise()) { + for(auto it = U.begin(); it != std::prev(U.end()); ++it) + addVertex(ret, *it); + for(auto it = L.rbegin(); it != std::prev(L.rend()); ++it) + addVertex(ret, *it); + if(closed) addVertex(ret, *std::prev(L.rend())); + } else { + for(auto it = L.begin(); it != std::prev(L.end()); ++it) + addVertex(ret, *it); + for(auto it = U.rbegin(); it != std::prev(U.rend()); ++it) + addVertex(ret, *it); + if(closed) addVertex(ret, *std::prev(U.rend())); + } + + return ret; +} + +template +inline S convexHull(const RawShapes& sh, const MultiPolygonTag&) +{ + namespace sl = shapelike; + S cntr; + for(auto& poly : sh) + for(auto it = sl::cbegin(poly); it != sl::cend(poly); ++it) + addVertex(cntr, *it); + + return convexHull(cntr, Tag()); +} + template inline bool isInside(const TP& point, const TC& circ, const PointTag&, const CircleTag&) { - return pointlike::distance(point, circ.center()) < circ.radius(); + auto r = circ.radius(); + return pointlike::squaredDistance(point, circ.center()) < r * r; } template @@ -972,6 +1179,9 @@ template inline bool isConvex(const RawShape& sh) // dispatch using Segment = _Segment; \ using Polygons = TMultiShape +namespace sl = shapelike; +namespace pl = pointlike; + } #endif // GEOMETRY_TRAITS_HPP diff --git a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp index cb0580ef4..4a2c69bca 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp @@ -1,26 +1,22 @@ #ifndef GEOMETRIES_NOFITPOLYGON_HPP #define GEOMETRIES_NOFITPOLYGON_HPP -#include "geometry_traits.hpp" #include #include #include #include +#include + namespace libnest2d { namespace __nfp { // Do not specialize this... -template +template> inline bool _vsort(const TPoint& v1, const TPoint& v2) { - using Coord = TCoord>; - Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2); - auto diff = y1 - y2; - if(std::abs(diff) <= std::numeric_limits::epsilon()) - return x1 < x2; - - return diff < 0; + Unit x1 = getX(v1), x2 = getX(v2), y1 = getY(v1), y2 = getY(v2); + return y1 == y2 ? x1 < x2 : y1 < y2; } template> @@ -202,7 +198,7 @@ inline TPoint referenceVertex(const RawShape& sh) * convex as well in this case. * */ -template +template inline NfpResult nfpConvexOnly(const RawShape& sh, const RawShape& other) { @@ -238,12 +234,62 @@ inline NfpResult nfpConvexOnly(const RawShape& sh, ++first; ++next; } } - - // Sort the edges by angle to X axis. - std::sort(edgelist.begin(), edgelist.end(), - [](const Edge& e1, const Edge& e2) + + std::sort(edgelist.begin(), edgelist.end(), + [](const Edge& e1, const Edge& e2) { - return e1.angleToXaxis() > e2.angleToXaxis(); + Vertex ax(1, 0); // Unit vector for the X axis + + // get cectors from the edges + Vertex p1 = e1.second() - e1.first(); + Vertex p2 = e2.second() - e2.first(); + + // Quadrant mapping array. The quadrant of a vector can be determined + // from the dot product of the vector and its perpendicular pair + // with the unit vector X axis. The products will carry the values + // lcos = dot(p, ax) = l * cos(phi) and + // lsin = -dotperp(p, ax) = l * sin(phi) where + // l is the length of vector p. From the signs of these values we can + // construct an index which has the sign of lcos as MSB and the + // sign of lsin as LSB. This index can be used to retrieve the actual + // quadrant where vector p resides using the following map: + // (+ is 0, - is 1) + // cos | sin | decimal | quadrant + // + | + | 0 | 0 + // + | - | 1 | 3 + // - | + | 2 | 1 + // - | - | 3 | 2 + std::array quadrants {0, 3, 1, 2 }; + + std::array q {0, 0}; // Quadrant indices for p1 and p2 + + using TDots = std::array, 2>; + TDots lcos { pl::dot(p1, ax), pl::dot(p2, ax) }; + TDots lsin { -pl::dotperp(p1, ax), -pl::dotperp(p2, ax) }; + + // Construct the quadrant indices for p1 and p2 + for(size_t i = 0; i < 2; ++i) + if(lcos[i] == 0) q[i] = lsin[i] > 0 ? 1 : 3; + else if(lsin[i] == 0) q[i] = lcos[i] > 0 ? 0 : 2; + else q[i] = quadrants[((lcos[i] < 0) << 1) + (lsin[i] < 0)]; + + if(q[0] == q[1]) { // only bother if p1 and p2 are in the same quadrant + auto lsq1 = pl::magnsq(p1); // squared magnitudes, avoid sqrt + auto lsq2 = pl::magnsq(p2); // squared magnitudes, avoid sqrt + + // We will actually compare l^2 * cos^2(phi) which saturates the + // cos function. But with the quadrant info we can get the sign back + int sign = q[0] == 1 || q[0] == 2 ? -1 : 1; + + // If Ratio is an actual rational type, there is no precision loss + auto pcos1 = Ratio(lcos[0]) / lsq1 * sign * lcos[0]; + auto pcos2 = Ratio(lcos[1]) / lsq2 * sign * lcos[1]; + + return q[0] < 2 ? pcos1 < pcos2 : pcos1 > pcos2; + } + + // If in different quadrants, compare the quadrant indices only. + return q[0] > q[1]; }); __nfp::buildPolygon(edgelist, rsh, top_nfp); @@ -253,456 +299,9 @@ inline NfpResult nfpConvexOnly(const RawShape& sh, template NfpResult nfpSimpleSimple(const RawShape& cstationary, - const RawShape& cother) + const RawShape& cother) { - - // Algorithms are from the original algorithm proposed in paper: - // https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf - - // ///////////////////////////////////////////////////////////////////////// - // Algorithm 1: Obtaining the minkowski sum - // ///////////////////////////////////////////////////////////////////////// - - // I guess this is not a full minkowski sum of the two input polygons by - // definition. This yields a subset that is compatible with the next 2 - // algorithms. - - using Result = NfpResult; - using Vertex = TPoint; - using Coord = TCoord; - using Edge = _Segment; - namespace sl = shapelike; - using std::signbit; - using std::sort; - using std::vector; - using std::ref; - using std::reference_wrapper; - - // TODO The original algorithms expects the stationary polygon in - // counter clockwise and the orbiter in clockwise order. - // So for preventing any further complication, I will make the input - // the way it should be, than make my way around the orientations. - - // Reverse the stationary contour to counter clockwise - auto stcont = sl::contour(cstationary); - { - std::reverse(sl::begin(stcont), sl::end(stcont)); - stcont.pop_back(); - auto it = std::min_element(sl::begin(stcont), sl::end(stcont), - [](const Vertex& v1, const Vertex& v2) { - return getY(v1) < getY(v2); - }); - std::rotate(sl::begin(stcont), it, sl::end(stcont)); - sl::addVertex(stcont, sl::front(stcont)); - } - RawShape stationary; - sl::contour(stationary) = stcont; - - // Reverse the orbiter contour to counter clockwise - auto orbcont = sl::contour(cother); - { - std::reverse(orbcont.begin(), orbcont.end()); - - // Step 1: Make the orbiter reverse oriented - - orbcont.pop_back(); - auto it = std::min_element(orbcont.begin(), orbcont.end(), - [](const Vertex& v1, const Vertex& v2) { - return getY(v1) < getY(v2); - }); - - std::rotate(orbcont.begin(), it, orbcont.end()); - orbcont.emplace_back(orbcont.front()); - - for(auto &v : orbcont) v = -v; - - } - - // Copy the orbiter (contour only), we will have to work on it - RawShape orbiter; - sl::contour(orbiter) = orbcont; - - // An edge with additional data for marking it - struct MarkedEdge { - Edge e; Radians turn_angle = 0; bool is_turning_point = false; - MarkedEdge() = default; - MarkedEdge(const Edge& ed, Radians ta, bool tp): - e(ed), turn_angle(ta), is_turning_point(tp) {} - - // debug - std::string label; - }; - - // Container for marked edges - using EdgeList = vector; - - EdgeList A, B; - - // This is how an edge list is created from the polygons - auto fillEdgeList = [](EdgeList& L, const RawShape& ppoly, int dir) { - auto& poly = sl::contour(ppoly); - - L.reserve(sl::contourVertexCount(poly)); - - if(dir > 0) { - auto it = poly.begin(); - auto nextit = std::next(it); - - double turn_angle = 0; - bool is_turn_point = false; - - while(nextit != poly.end()) { - L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point); - it++; nextit++; - } - } else { - auto it = sl::rbegin(poly); - auto nextit = std::next(it); - - double turn_angle = 0; - bool is_turn_point = false; - - while(nextit != sl::rend(poly)) { - L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point); - it++; nextit++; - } - } - - auto getTurnAngle = [](const Edge& e1, const Edge& e2) { - auto phi = e1.angleToXaxis(); - auto phi_prev = e2.angleToXaxis(); - auto turn_angle = phi-phi_prev; - if(turn_angle > Pi) turn_angle -= TwoPi; - if(turn_angle < -Pi) turn_angle += TwoPi; - return turn_angle; - }; - - auto eit = L.begin(); - auto enext = std::next(eit); - - eit->turn_angle = getTurnAngle(L.front().e, L.back().e); - - while(enext != L.end()) { - enext->turn_angle = getTurnAngle( enext->e, eit->e); - eit->is_turning_point = - signbit(enext->turn_angle) != signbit(eit->turn_angle); - ++eit; ++enext; - } - - L.back().is_turning_point = signbit(L.back().turn_angle) != - signbit(L.front().turn_angle); - - }; - - // Step 2: Fill the edgelists - fillEdgeList(A, stationary, 1); - fillEdgeList(B, orbiter, 1); - - int i = 1; - for(MarkedEdge& me : A) { - std::cout << "a" << i << ":\n\t" - << getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t" - << getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t" - << "Turning point: " << (me.is_turning_point ? "yes" : "no") - << std::endl; - - me.label = "a"; me.label += std::to_string(i); - i++; - } - - i = 1; - for(MarkedEdge& me : B) { - std::cout << "b" << i << ":\n\t" - << getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t" - << getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t" - << "Turning point: " << (me.is_turning_point ? "yes" : "no") - << std::endl; - me.label = "b"; me.label += std::to_string(i); - i++; - } - - // A reference to a marked edge that also knows its container - struct MarkedEdgeRef { - reference_wrapper eref; - reference_wrapper> container; - Coord dir = 1; // Direction modifier - - inline Radians angleX() const { return eref.get().e.angleToXaxis(); } - inline const Edge& edge() const { return eref.get().e; } - inline Edge& edge() { return eref.get().e; } - inline bool isTurningPoint() const { - return eref.get().is_turning_point; - } - inline bool isFrom(const vector& cont ) { - return &(container.get()) == &cont; - } - inline bool eq(const MarkedEdgeRef& mr) { - return &(eref.get()) == &(mr.eref.get()); - } - - MarkedEdgeRef(reference_wrapper er, - reference_wrapper> ec): - eref(er), container(ec), dir(1) {} - - MarkedEdgeRef(reference_wrapper er, - reference_wrapper> ec, - Coord d): - eref(er), container(ec), dir(d) {} - }; - - using EdgeRefList = vector; - - // Comparing two marked edges - auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) { - return e1.angleX() < e2.angleX(); - }; - - EdgeRefList Aref, Bref; // We create containers for the references - Aref.reserve(A.size()); Bref.reserve(B.size()); - - // Fill reference container for the stationary polygon - std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) { - Aref.emplace_back( ref(me), ref(Aref) ); - }); - - // Fill reference container for the orbiting polygon - std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) { - Bref.emplace_back( ref(me), ref(Bref) ); - }); - - auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure - (const EdgeRefList& Q, const EdgeRefList& R, bool positive) - { - - // Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)" - // Sort the containers of edge references and merge them. - // Q could be sorted only once and be reused here but we would still - // need to merge it with sorted(R). - - EdgeRefList merged; - EdgeRefList S, seq; - merged.reserve(Q.size() + R.size()); - - merged.insert(merged.end(), R.begin(), R.end()); - std::stable_sort(merged.begin(), merged.end(), sortfn); - merged.insert(merged.end(), Q.begin(), Q.end()); - std::stable_sort(merged.begin(), merged.end(), sortfn); - - // Step 2 "set i = 1, k = 1, direction = 1, s1 = q1" - // we don't use i, instead, q is an iterator into Q. k would be an index - // into the merged sequence but we use "it" as an iterator for that - - // here we obtain references for the containers for later comparisons - const auto& Rcont = R.begin()->container.get(); - const auto& Qcont = Q.begin()->container.get(); - - // Set the initial direction - Coord dir = 1; - - // roughly i = 1 (so q = Q.begin()) and s1 = q1 so S[0] = q; - if(positive) { - auto q = Q.begin(); - S.emplace_back(*q); - - // Roughly step 3 - - std::cout << "merged size: " << merged.size() << std::endl; - auto mit = merged.begin(); - for(bool finish = false; !finish && q != Q.end();) { - ++q; // "Set i = i + 1" - - while(!finish && mit != merged.end()) { - if(mit->isFrom(Rcont)) { - auto s = *mit; - s.dir = dir; - S.emplace_back(s); - } - - if(mit->eq(*q)) { - S.emplace_back(*q); - if(mit->isTurningPoint()) dir = -dir; - if(q == Q.begin()) finish = true; - break; - } - - mit += dir; - // __nfp::advance(mit, merged, dir > 0); - } - } - } else { - auto q = Q.rbegin(); - S.emplace_back(*q); - - // Roughly step 3 - - std::cout << "merged size: " << merged.size() << std::endl; - auto mit = merged.begin(); - for(bool finish = false; !finish && q != Q.rend();) { - ++q; // "Set i = i + 1" - - while(!finish && mit != merged.end()) { - if(mit->isFrom(Rcont)) { - auto s = *mit; - s.dir = dir; - S.emplace_back(s); - } - - if(mit->eq(*q)) { - S.emplace_back(*q); - S.back().dir = -1; - if(mit->isTurningPoint()) dir = -dir; - if(q == Q.rbegin()) finish = true; - break; - } - - mit += dir; - // __nfp::advance(mit, merged, dir > 0); - } - } - } - - - // Step 4: - - // "Let starting edge r1 be in position si in sequence" - // whaaat? I guess this means the following: - auto it = S.begin(); - while(!it->eq(*R.begin())) ++it; - - // "Set j = 1, next = 2, direction = 1, seq1 = si" - // we don't use j, seq is expanded dynamically. - dir = 1; - auto next = std::next(R.begin()); seq.emplace_back(*it); - - // Step 5: - // "If all si edges have been allocated to seqj" should mean that - // we loop until seq has equal size with S - auto send = it; //it == S.begin() ? it : std::prev(it); - while(it != S.end()) { - ++it; if(it == S.end()) it = S.begin(); - if(it == send) break; - - if(it->isFrom(Qcont)) { - seq.emplace_back(*it); // "If si is from Q, j = j + 1, seqj = si" - - // "If si is a turning point in Q, - // direction = - direction, next = next + direction" - if(it->isTurningPoint()) { - dir = -dir; - next += dir; -// __nfp::advance(next, R, dir > 0); - } - } - - if(it->eq(*next) /*&& dir == next->dir*/) { // "If si = direction.rnext" - // "j = j + 1, seqj = si, next = next + direction" - seq.emplace_back(*it); - next += dir; -// __nfp::advance(next, R, dir > 0); - } - } - - return seq; - }; - - std::vector seqlist; - seqlist.reserve(Bref.size()); - - EdgeRefList Bslope = Bref; // copy Bref, we will make a slope diagram - - // make the slope diagram of B - std::sort(Bslope.begin(), Bslope.end(), sortfn); - - auto slopeit = Bslope.begin(); // search for the first turning point - while(!slopeit->isTurningPoint() && slopeit != Bslope.end()) slopeit++; - - if(slopeit == Bslope.end()) { - // no turning point means convex polygon. - seqlist.emplace_back(mink(Aref, Bref, true)); - } else { - int dir = 1; - - auto firstturn = Bref.begin(); - while(!firstturn->eq(*slopeit)) ++firstturn; - - assert(firstturn != Bref.end()); - - EdgeRefList bgroup; bgroup.reserve(Bref.size()); - bgroup.emplace_back(*slopeit); - - auto b_it = std::next(firstturn); - while(b_it != firstturn) { - if(b_it == Bref.end()) b_it = Bref.begin(); - - while(!slopeit->eq(*b_it)) { - __nfp::advance(slopeit, Bslope, dir > 0); - } - - if(!slopeit->isTurningPoint()) { - bgroup.emplace_back(*slopeit); - } else { - if(!bgroup.empty()) { - if(dir > 0) bgroup.emplace_back(*slopeit); - for(auto& me : bgroup) { - std::cout << me.eref.get().label << ", "; - } - std::cout << std::endl; - seqlist.emplace_back(mink(Aref, bgroup, dir == 1 ? true : false)); - bgroup.clear(); - if(dir < 0) bgroup.emplace_back(*slopeit); - } else { - bgroup.emplace_back(*slopeit); - } - - dir *= -1; - } - ++b_it; - } - } - -// while(it != Bref.end()) // This is step 3 and step 4 in one loop -// if(it->isTurningPoint()) { -// R = {R.last, it++}; -// auto seq = mink(Q, R, orientation); - -// // TODO step 6 (should be 5 shouldn't it?): linking edges from A -// // I don't get this step - -// seqlist.insert(seqlist.end(), seq.begin(), seq.end()); -// orientation = !orientation; -// } else ++it; - -// if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true); - - // ///////////////////////////////////////////////////////////////////////// - // Algorithm 2: breaking Minkowski sums into track line trips - // ///////////////////////////////////////////////////////////////////////// - - - // ///////////////////////////////////////////////////////////////////////// - // Algorithm 3: finding the boundary of the NFP from track line trips - // ///////////////////////////////////////////////////////////////////////// - - - for(auto& seq : seqlist) { - std::cout << "seqlist size: " << seq.size() << std::endl; - for(auto& s : seq) { - std::cout << (s.dir > 0 ? "" : "-") << s.eref.get().label << ", "; - } - std::cout << std::endl; - } - - auto& seq = seqlist.front(); - RawShape rsh; - Vertex top_nfp; - std::vector edgelist; edgelist.reserve(seq.size()); - for(auto& s : seq) { - edgelist.emplace_back(s.eref.get().e); - } - - __nfp::buildPolygon(edgelist, rsh, top_nfp); - - return Result(rsh, top_nfp); + return {}; } // Specializable NFP implementation class. Specialize it if you have a faster diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index c7b252e5d..5d74aa3d9 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -8,13 +8,10 @@ #include #include -#include "geometry_traits.hpp" +#include namespace libnest2d { -namespace sl = shapelike; -namespace pl = pointlike; - /** * \brief An item to be placed on a bin. * @@ -422,13 +419,9 @@ private: static inline bool vsort(const Vertex& v1, const Vertex& v2) { - Coord &&x1 = getX(v1), &&x2 = getX(v2); - Coord &&y1 = getY(v1), &&y2 = getY(v2); - auto diff = y1 - y2; - if(std::abs(diff) <= std::numeric_limits::epsilon()) - return x1 < x2; - - return diff < 0; + TCompute x1 = getX(v1), x2 = getX(v2); + TCompute y1 = getY(v1), y2 = getY(v2); + return y1 == y2 ? x1 < x2 : y1 < y2; } }; diff --git a/src/libnest2d/include/libnest2d/optimizer.hpp b/src/libnest2d/include/libnest2d/optimizer.hpp index 962a47392..e4c149f22 100644 --- a/src/libnest2d/include/libnest2d/optimizer.hpp +++ b/src/libnest2d/include/libnest2d/optimizer.hpp @@ -4,7 +4,8 @@ #include #include #include -#include "common.hpp" + +#include namespace libnest2d { namespace opt { @@ -60,6 +61,7 @@ enum class Method { L_SIMPLEX, L_SUBPLEX, G_GENETIC, + G_PARTICLE_SWARM //... }; diff --git a/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt b/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt index 4e16d4fc5..6f51718d8 100644 --- a/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt +++ b/src/libnest2d/include/libnest2d/optimizers/nlopt/CMakeLists.txt @@ -48,7 +48,7 @@ else() target_link_libraries(nloptOptimizer INTERFACE Nlopt::Nlopt) endif() -#target_sources( NloptOptimizer INTERFACE +#target_sources( nloptOptimizer INTERFACE #${CMAKE_CURRENT_SOURCE_DIR}/simplex.hpp #${CMAKE_CURRENT_SOURCE_DIR}/subplex.hpp #${CMAKE_CURRENT_SOURCE_DIR}/genetic.hpp @@ -57,5 +57,5 @@ endif() target_compile_definitions(nloptOptimizer INTERFACE LIBNEST2D_OPTIMIZER_NLOPT) -# And finally plug the NloptOptimizer into libnest2d -#target_link_libraries(libnest2d INTERFACE NloptOptimizer) +# And finally plug the nloptOptimizer into libnest2d +#target_link_libraries(libnest2d INTERFACE nloptOptimizer) diff --git a/src/libnest2d/include/libnest2d/optimizers/optimlib/CMakeLists.txt b/src/libnest2d/include/libnest2d/optimizers/optimlib/CMakeLists.txt deleted file mode 100644 index efbbd9cfb..000000000 --- a/src/libnest2d/include/libnest2d/optimizers/optimlib/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -find_package(Armadillo REQUIRED) - -add_library(OptimlibOptimizer INTERFACE) -target_include_directories(OptimlibOptimizer INTERFACE ${ARMADILLO_INCLUDE_DIRS}) -target_link_libraries(OptimlibOptimizer INTERFACE ${ARMADILLO_LIBRARIES}) \ No newline at end of file diff --git a/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp b/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp index 7f10be7d7..28aaad5ce 100644 --- a/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/bottomleftplacer.hpp @@ -7,15 +7,15 @@ namespace libnest2d { namespace placers { -template struct Epsilon {}; +template struct DefaultEpsilon {}; template -struct Epsilon::value, T> > { +struct DefaultEpsilon::value, T> > { static const T Value = 1; }; template -struct Epsilon::value, T> > { +struct DefaultEpsilon::value, T> > { static const T Value = 1e-3; }; @@ -24,7 +24,7 @@ struct BLConfig { DECLARE_MAIN_TYPES(RawShape); Coord min_obj_distance = 0; - Coord epsilon = Epsilon::Value; + Coord epsilon = DefaultEpsilon::Value; bool allow_rotations = false; }; diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 91affe978..c1f15fe61 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -103,14 +103,14 @@ Key hash(const _Item& item) { while(deg > N) { ms++; deg -= N; } ls += int(deg); ret.push_back(char(ms)); ret.push_back(char(ls)); - circ += seg.length(); + circ += std::sqrt(seg.template sqlength()); } it = ctr.begin(); nx = std::next(it); while(nx != ctr.end()) { Segment seg(*it++, *nx++); - auto l = int(M * seg.length() / circ); + auto l = int(M * std::sqrt(seg.template sqlength()) / circ); int ms = 'A', ls = 'A'; while(l > N) { ms++; l -= N; } ls += l; @@ -249,6 +249,11 @@ template class EdgeCache { std::vector holes_; double accuracy_ = 1.0; + + static double length(const Edge &e) + { + return std::sqrt(e.template sqlength()); + } void createCache(const RawShape& sh) { { // For the contour @@ -260,7 +265,7 @@ template class EdgeCache { while(next != endit) { contour_.emap.emplace_back(*(first++), *(next++)); - contour_.full_distance += contour_.emap.back().length(); + contour_.full_distance += length(contour_.emap.back()); contour_.distances.emplace_back(contour_.full_distance); } } @@ -275,7 +280,7 @@ template class EdgeCache { while(next != endit) { hc.emap.emplace_back(*(first++), *(next++)); - hc.full_distance += hc.emap.back().length(); + hc.full_distance += length(hc.emap.back()); hc.distances.emplace_back(hc.full_distance); } diff --git a/src/libnest2d/include/libnest2d/utils/boost_alg.hpp b/src/libnest2d/include/libnest2d/utils/boost_alg.hpp index baf1c6a10..16dee513b 100644 --- a/src/libnest2d/include/libnest2d/utils/boost_alg.hpp +++ b/src/libnest2d/include/libnest2d/utils/boost_alg.hpp @@ -311,19 +311,19 @@ struct range_value { namespace libnest2d { // Now the algorithms that boost can provide... -namespace pointlike { -template<> -inline double distance(const PointImpl& p1, const PointImpl& p2 ) -{ - return boost::geometry::distance(p1, p2); -} +//namespace pointlike { +//template<> +//inline double distance(const PointImpl& p1, const PointImpl& p2 ) +//{ +// return boost::geometry::distance(p1, p2); +//} -template<> -inline double distance(const PointImpl& p, const bp2d::Segment& seg ) -{ - return boost::geometry::distance(p, seg); -} -} +//template<> +//inline double distance(const PointImpl& p, const bp2d::Segment& seg ) +//{ +// return boost::geometry::distance(p, seg); +//} +//} namespace shapelike { // Tell libnest2d how to make string out of a ClipperPolygon object @@ -382,16 +382,9 @@ inline bool touches( const PointImpl& point, const PolygonImpl& shape) } #ifndef DISABLE_BOOST_BOUNDING_BOX -template<> -inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&) -{ - bp2d::Box b; - boost::geometry::envelope(sh, b); - return b; -} template<> -inline bp2d::Box boundingBox(const PathImpl& sh, const PolygonTag&) +inline bp2d::Box boundingBox(const PathImpl& sh, const PathTag&) { bp2d::Box b; boost::geometry::envelope(sh, b); @@ -410,9 +403,9 @@ inline bp2d::Box boundingBox(const bp2d::Shapes& shapes, #ifndef DISABLE_BOOST_CONVEX_HULL template<> -inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&) +inline PathImpl convexHull(const PathImpl& sh, const PathTag&) { - PolygonImpl ret; + PathImpl ret; boost::geometry::convex_hull(sh, ret); return ret; } diff --git a/src/libnest2d/include/libnest2d/utils/rotcalipers.hpp b/src/libnest2d/include/libnest2d/utils/rotcalipers.hpp new file mode 100644 index 000000000..76f91ba0c --- /dev/null +++ b/src/libnest2d/include/libnest2d/utils/rotcalipers.hpp @@ -0,0 +1,268 @@ +#ifndef ROTCALIPERS_HPP +#define ROTCALIPERS_HPP + +#include +#include +#include +#include + +#include + +namespace libnest2d { + +template> class RotatedBox { + Pt axis_; + Unit bottom_ = Unit(0), right_ = Unit(0); +public: + + RotatedBox() = default; + RotatedBox(const Pt& axis, Unit b, Unit r): + axis_(axis), bottom_(b), right_(r) {} + + inline long double area() const { + long double asq = pl::magnsq(axis_); + return cast(bottom_) * cast(right_) / asq; + } + + inline long double width() const { + return abs(bottom_) / std::sqrt(pl::magnsq(axis_)); + } + + inline long double height() const { + return abs(right_) / std::sqrt(pl::magnsq(axis_)); + } + + inline Unit bottom_extent() const { return bottom_; } + inline Unit right_extent() const { return right_; } + inline const Pt& axis() const { return axis_; } + + inline Radians angleToX() const { + double ret = std::atan2(getY(axis_), getX(axis_)); + auto s = std::signbit(ret); + if(s) ret += Pi_2; + return -ret; + } +}; + +template , class Unit = TCompute> +Poly removeCollinearPoints(const Poly& sh, Unit eps = Unit(0)) +{ + Poly ret; sl::reserve(ret, sl::contourVertexCount(sh)); + + Pt eprev = *sl::cbegin(sh) - *std::prev(sl::cend(sh)); + + auto it = sl::cbegin(sh); + auto itx = std::next(it); + if(itx != sl::cend(sh)) while (it != sl::cend(sh)) + { + Pt enext = *itx - *it; + + auto dp = pl::dotperp(eprev, enext); + if(abs(dp) > eps) sl::addVertex(ret, *it); + + eprev = enext; + if (++itx == sl::cend(sh)) itx = sl::cbegin(sh); + ++it; + } + + return ret; +} + +// The area of the bounding rectangle with the axis dir and support vertices +template, class R = TCompute> +inline R rectarea(const Pt& w, // the axis + const Pt& vb, const Pt& vr, + const Pt& vt, const Pt& vl) +{ + Unit a = pl::dot(w, vr - vl); + Unit b = pl::dot(-pl::perp(w), vt - vb); + R m = R(a) / pl::magnsq(w); + m = m * b; + return m; +}; + +template, + class R = TCompute, + class It = typename std::vector::const_iterator> +inline R rectarea(const Pt& w, const std::array& rect) +{ + return rectarea(w, *rect[0], *rect[1], *rect[2], *rect[3]); +} + +// This function is only applicable to counter-clockwise oriented convex +// polygons where only two points can be collinear witch each other. +template , + class Ratio = TCompute> +RotatedBox, Unit> minAreaBoundingBox(const RawShape& sh) +{ + using Point = TPoint; + using Iterator = typename TContour::const_iterator; + using pointlike::dot; using pointlike::magnsq; using pointlike::perp; + + // Get the first and the last vertex iterator + auto first = sl::cbegin(sh); + auto last = std::prev(sl::cend(sh)); + + // Check conditions and return undefined box if input is not sane. + if(last == first) return {}; + if(getX(*first) == getX(*last) && getY(*first) == getY(*last)) --last; + if(last - first < 2) return {}; + + RawShape shcpy; // empty at this point + { + Point p = *first, q = *std::next(first), r = *last; + + // Determine orientation from first 3 vertex (should be consistent) + Unit d = (Unit(getY(q)) - getY(p)) * (Unit(getX(r)) - getX(p)) - + (Unit(getX(q)) - getX(p)) * (Unit(getY(r)) - getY(p)); + + if(d > 0) { + // The polygon is clockwise. A flip is needed (for now) + sl::reserve(shcpy, last - first); + auto it = last; while(it != first) sl::addVertex(shcpy, *it--); + sl::addVertex(shcpy, *first); + first = sl::cbegin(shcpy); last = std::prev(sl::cend(shcpy)); + } + } + + // Cyclic iterator increment + auto inc = [&first, &last](Iterator& it) { + if(it == last) it = first; else ++it; + }; + + // Cyclic previous iterator + auto prev = [&first, &last](Iterator it) { + return it == first ? last : std::prev(it); + }; + + // Cyclic next iterator + auto next = [&first, &last](Iterator it) { + return it == last ? first : std::next(it); + }; + + // Establish initial (axis aligned) rectangle support verices by determining + // polygon extremes: + + auto it = first; + Iterator minX = it, maxX = it, minY = it, maxY = it; + + do { // Linear walk through the vertices and save the extreme positions + + Point v = *it, d = v - *minX; + if(getX(d) < 0 || (getX(d) == 0 && getY(d) < 0)) minX = it; + + d = v - *maxX; + if(getX(d) > 0 || (getX(d) == 0 && getY(d) > 0)) maxX = it; + + d = v - *minY; + if(getY(d) < 0 || (getY(d) == 0 && getX(d) > 0)) minY = it; + + d = v - *maxY; + if(getY(d) > 0 || (getY(d) == 0 && getX(d) < 0)) maxY = it; + + } while(++it != std::next(last)); + + // Update the vertices defining the bounding rectangle. The rectangle with + // the smallest rotation is selected and the supporting vertices are + // returned in the 'rect' argument. + auto update = [&next, &inc] + (const Point& w, std::array& rect) + { + Iterator B = rect[0], Bn = next(B); + Iterator R = rect[1], Rn = next(R); + Iterator T = rect[2], Tn = next(T); + Iterator L = rect[3], Ln = next(L); + + Point b = *Bn - *B, r = *Rn - *R, t = *Tn - *T, l = *Ln - *L; + Point pw = perp(w); + using Pt = Point; + + Unit dotwpb = dot( w, b), dotwpr = dot(-pw, r); + Unit dotwpt = dot(-w, t), dotwpl = dot( pw, l); + Unit dw = magnsq(w); + + std::array angles; + angles[0] = (Ratio(dotwpb) / magnsq(b)) * dotwpb; + angles[1] = (Ratio(dotwpr) / magnsq(r)) * dotwpr; + angles[2] = (Ratio(dotwpt) / magnsq(t)) * dotwpt; + angles[3] = (Ratio(dotwpl) / magnsq(l)) * dotwpl; + + using AngleIndex = std::pair; + std::vector A; A.reserve(4); + + for (size_t i = 3, j = 0; j < 4; i = j++) { + if(rect[i] != rect[j] && angles[i] < dw) { + auto iv = std::make_pair(angles[i], i); + auto it = std::lower_bound(A.begin(), A.end(), iv, + [](const AngleIndex& ai, + const AngleIndex& aj) + { + return ai.first > aj.first; + }); + + A.insert(it, iv); + } + } + + // The polygon is supposed to be a rectangle. + if(A.empty()) return false; + + auto amin = A.front().first; + auto imin = A.front().second; + for(auto& a : A) if(a.first == amin) inc(rect[a.second]); + + std::rotate(rect.begin(), rect.begin() + imin, rect.end()); + + return true; + }; + + Point w(1, 0); + Point w_min = w; + Ratio minarea((Unit(getX(*maxX)) - getX(*minX)) * + (Unit(getY(*maxY)) - getY(*minY))); + + std::array rect = {minY, maxX, maxY, minX}; + std::array minrect = rect; + + // An edge might be examined twice in which case the algorithm terminates. + size_t c = 0, count = last - first + 1; + std::vector edgemask(count, false); + + while(c++ < count) + { + // Update the support vertices, if cannot be updated, break the cycle. + if(! update(w, rect)) break; + + size_t eidx = size_t(rect[0] - first); + + if(edgemask[eidx]) break; + edgemask[eidx] = true; + + // get the unnormalized direction vector + w = *rect[0] - *prev(rect[0]); + + // get the area of the rotated rectangle + Ratio rarea = rectarea(w, rect); + + // Update min area and the direction of the min bounding box; + if(rarea <= minarea) { w_min = w; minarea = rarea; minrect = rect; } + } + + Unit a = dot(w_min, *minrect[1] - *minrect[3]); + Unit b = dot(-perp(w_min), *minrect[2] - *minrect[0]); + RotatedBox bb(w_min, a, b); + + return bb; +} + +template Radians minAreaBoundingBoxRotation(const RawShape& sh) +{ + return minAreaBoundingBox(sh).angleToX(); +} + + +} + +#endif // ROTCALIPERS_HPP diff --git a/src/libnest2d/src/libnest2d.cpp b/src/libnest2d/src/libnest2d.cpp new file mode 100644 index 000000000..021458787 --- /dev/null +++ b/src/libnest2d/src/libnest2d.cpp @@ -0,0 +1,23 @@ +#include + +namespace libnest2d { + +template class Nester; +template class Nester; + +template PackGroup nest(std::vector::iterator from, + std::vector::iterator to, + const Box& bin, + Coord dist = 0, + const NfpPlacer::Config& pconf, + const FirstFitSelection::Config& sconf); + +template PackGroup nest(std::vector::iterator from, + std::vector::iterator to, + const Box& bin, + ProgressFunction prg, + StopCondition scond, + Coord dist = 0, + const NfpPlacer::Config& pconf, + const FirstFitSelection::Config& sconf); +} diff --git a/src/libnest2d/tests/CMakeLists.txt b/src/libnest2d/tests/CMakeLists.txt index fc3cd309d..1b7d8e3ae 100644 --- a/src/libnest2d/tests/CMakeLists.txt +++ b/src/libnest2d/tests/CMakeLists.txt @@ -49,7 +49,12 @@ add_executable(tests_clipper_nlopt target_link_libraries(tests_clipper_nlopt libnest2d ${GTEST_LIBS_TO_LINK} ) -target_include_directories(tests_clipper_nlopt PRIVATE BEFORE - ${GTEST_INCLUDE_DIRS}) +target_include_directories(tests_clipper_nlopt PRIVATE BEFORE ${GTEST_INCLUDE_DIRS}) + +if(NOT LIBNEST2D_HEADER_ONLY) + target_link_libraries(tests_clipper_nlopt ${LIBNAME}) +else() + target_link_libraries(tests_clipper_nlopt libnest2d) +endif() add_test(libnest2d_tests tests_clipper_nlopt) diff --git a/src/libnest2d/tests/test.cpp b/src/libnest2d/tests/test.cpp index 3b0eae161..5741e87b4 100644 --- a/src/libnest2d/tests/test.cpp +++ b/src/libnest2d/tests/test.cpp @@ -3,11 +3,43 @@ #include #include "printer_parts.h" -#include +//#include #include "../tools/svgtools.hpp" +#include + +#include "boost/multiprecision/integer.hpp" +#include "boost/rational.hpp" + +//#include "../tools/Int128.hpp" + +//#include "gte/Mathematics/GteMinimumAreaBox2.h" + //#include "../tools/libnfpglue.hpp" //#include "../tools/nfp_svgnest_glue.hpp" +namespace libnest2d { +#if !defined(_MSC_VER) && defined(__SIZEOF_INT128__) && !defined(__APPLE__) +using LargeInt = __int128; +#else +using LargeInt = boost::multiprecision::int128_t; +template<> struct _NumTag { using Type = ScalarTag; }; +#endif +template struct _NumTag> { using Type = RationalTag; }; + +namespace nfp { + +template +struct NfpImpl +{ + NfpResult operator()(const S &sh, const S &other) + { + return nfpConvexOnly>(sh, other); + } +}; + +} +} + std::vector& prusaParts() { static std::vector ret; @@ -31,8 +63,8 @@ TEST(BasicFunctionality, Angles) ASSERT_DOUBLE_EQ(rad, Pi); ASSERT_DOUBLE_EQ(deg, 180); ASSERT_DOUBLE_EQ(deg2, 180); - ASSERT_DOUBLE_EQ(rad, (Radians) deg); - ASSERT_DOUBLE_EQ( (Degrees) rad, deg); + ASSERT_DOUBLE_EQ(rad, Radians(deg)); + ASSERT_DOUBLE_EQ( Degrees(rad), deg); ASSERT_TRUE(rad == deg); @@ -151,12 +183,12 @@ TEST(GeometryAlgorithms, Distance) { Segment seg(p1, p3); - ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755); +// ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755); auto result = pointlike::horizontalDistance(p2, seg); - auto check = [](Coord val, Coord expected) { - if(std::is_floating_point::value) + auto check = [](TCompute val, TCompute expected) { + if(std::is_floating_point>::value) ASSERT_DOUBLE_EQ(static_cast(val), static_cast(expected)); else @@ -415,7 +447,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose) namespace { using namespace libnest2d; -template +template void exportSVG(std::vector>& result, const Bin& bin, int idx = 0) { @@ -500,6 +532,41 @@ TEST(GeometryAlgorithms, BottomLeftStressTest) { } } +TEST(GeometryAlgorithms, convexHull) { + using namespace libnest2d; + + ClipperLib::Path poly = PRINTER_PART_POLYGONS[0]; + + auto chull = sl::convexHull(poly); + + ASSERT_EQ(chull.size(), poly.size()); +} + + +TEST(GeometryAlgorithms, NestTest) { + std::vector input = prusaParts(); + + PackGroup result = libnest2d::nest(input, + Box(250000000, 210000000), + [](unsigned cnt) { + std::cout + << "parts left: " << cnt + << std::endl; + }); + + ASSERT_LE(result.size(), 2); + + int partsum = std::accumulate(result.begin(), + result.end(), + 0, + [](int s, + const decltype(result)::value_type &bin) { + return s += bin.size(); + }); + + ASSERT_EQ(input.size(), partsum); +} + namespace { struct ItemPair { @@ -713,7 +780,7 @@ void testNfp(const std::vector& testdata) { auto& exportfun = exportSVG; - auto onetest = [&](Item& orbiter, Item& stationary, unsigned testidx){ + auto onetest = [&](Item& orbiter, Item& stationary, unsigned /*testidx*/){ testcase++; orbiter.translate({210*SCALE, 0}); @@ -820,7 +887,7 @@ TEST(GeometryAlgorithms, mergePileWithPolygon) { rect2.translate({10, 0}); rect3.translate({25, 0}); - shapelike::Shapes pile; + TMultiShape pile; pile.push_back(rect1.transformedShape()); pile.push_back(rect2.transformedShape()); @@ -833,6 +900,126 @@ TEST(GeometryAlgorithms, mergePileWithPolygon) { ASSERT_EQ(shapelike::area(result.front()), ref.area()); } +namespace { + +long double refMinAreaBox(const PolygonImpl& p) { + + auto it = sl::cbegin(p), itx = std::next(it); + + long double min_area = std::numeric_limits::max(); + + + auto update_min = [&min_area, &it, &itx, &p]() { + Segment s(*it, *itx); + + PolygonImpl rotated = p; + sl::rotate(rotated, -s.angleToXaxis()); + auto bb = sl::boundingBox(rotated); + auto area = cast(sl::area(bb)); + if(min_area > area) min_area = area; + }; + + while(itx != sl::cend(p)) { + update_min(); + ++it; ++itx; + } + + it = std::prev(sl::cend(p)); itx = sl::cbegin(p); + update_min(); + + return min_area; +} + +template struct BoostGCD { + T operator()(const T &a, const T &b) { return boost::gcd(a, b); } +}; + +using Unit = int64_t; +using Ratio = boost::rational;// Rational; + +//double gteMinAreaBox(const PolygonImpl& p) { + +// using GteCoord = ClipperLib::cInt; +// using GtePoint = gte::Vector2; + +// gte::MinimumAreaBox2 mb; + +// std::vector points; +// points.reserve(p.Contour.size()); + +// for(auto& pt : p.Contour) points.emplace_back(GtePoint{GteCoord(pt.X), GteCoord(pt.Y)}); + +// mb(int(points.size()), points.data(), 0, nullptr, true); + +// auto min_area = double(mb.GetArea()); + +// return min_area; +//} + +} + +TEST(RotatingCalipers, MinAreaBBCClk) { +// PolygonImpl poly({{-50, 30}, {-50, -50}, {50, -50}, {50, 50}, {-40, 50}}); + +// PolygonImpl poly({{-50, 0}, {50, 0}, {0, 100}}); + + auto u = [](ClipperLib::cInt n) { return n*1000000; }; + PolygonImpl poly({ {u(0), u(0)}, {u(4), u(1)}, {u(2), u(4)}}); + + + long double arearef = refMinAreaBox(poly); + long double area = minAreaBoundingBox(poly).area(); +// double gtearea = gteMinAreaBox(poly); + + ASSERT_LE(std::abs(area - arearef), 500e6 ); +// ASSERT_LE(std::abs(gtearea - arearef), 500 ); +// ASSERT_DOUBLE_EQ(gtearea, arearef); +} + +TEST(RotatingCalipers, AllPrusaMinBB) { + size_t idx = 0; + long double err_epsilon = 500e6l; + + for(ClipperLib::Path rinput : PRINTER_PART_POLYGONS) { +// ClipperLib::Path rinput = PRINTER_PART_POLYGONS[idx]; +// rinput.pop_back(); +// std::reverse(rinput.begin(), rinput.end()); + +// PolygonImpl poly(removeCollinearPoints(rinput, 1000000)); + PolygonImpl poly(rinput); + + long double arearef = refMinAreaBox(poly); + auto bb = minAreaBoundingBox(rinput); + long double area = cast(bb.area()); +// double area = gteMinAreaBox(poly); + + bool succ = std::abs(arearef - area) < err_epsilon; + std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: " + << arearef << " actual: " << area << std::endl; + + ASSERT_TRUE(succ); + } + + for(ClipperLib::Path rinput : STEGOSAUR_POLYGONS) { + rinput.pop_back(); + std::reverse(rinput.begin(), rinput.end()); + + PolygonImpl poly(removeCollinearPoints(rinput, 1000000)); + + + long double arearef = refMinAreaBox(poly); + auto bb = minAreaBoundingBox(poly); + long double area = cast(bb.area()); +// double area = gteMinAreaBox(poly); + + bool succ = std::abs(arearef - area) < err_epsilon; + std::cout << idx++ << " " << (succ? "ok" : "failed") << " ref: " + << arearef << " actual: " << area << std::endl; + + ASSERT_TRUE(succ); + } +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 833b1ae62..e06a5f52a 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -163,6 +163,8 @@ add_library(libslic3r STATIC MTUtils.hpp Zipper.hpp Zipper.cpp + MinAreaBoundingBox.hpp + MinAreaBoundingBox.cpp miniz_extension.hpp miniz_extension.cpp SLA/SLABoilerPlate.hpp diff --git a/src/libslic3r/Int128.hpp b/src/libslic3r/Int128.hpp index 56dc5f461..8dc9e012d 100644 --- a/src/libslic3r/Int128.hpp +++ b/src/libslic3r/Int128.hpp @@ -37,6 +37,8 @@ * * *******************************************************************************/ +#ifndef SLIC3R_INT128_HPP +#define SLIC3R_INT128_HPP // #define SLIC3R_DEBUG // Make assert active if SLIC3R_DEBUG @@ -48,6 +50,8 @@ #endif #include +#include +#include #if ! defined(_MSC_VER) && defined(__SIZEOF_INT128__) #define HAS_INTRINSIC_128_TYPE @@ -293,3 +297,5 @@ public: return sign_determinant_2x2(p1, q1, p2, q2) * invert; } }; + +#endif // SLIC3R_INT128_HPP diff --git a/src/libslic3r/MinAreaBoundingBox.cpp b/src/libslic3r/MinAreaBoundingBox.cpp new file mode 100644 index 000000000..6fc1b3327 --- /dev/null +++ b/src/libslic3r/MinAreaBoundingBox.cpp @@ -0,0 +1,142 @@ +#include "MinAreaBoundingBox.hpp" + +#include +#include + +#include + +#if !defined(HAS_INTRINSIC_128_TYPE) || defined(__APPLE__) +#include +#endif + +#include +#include + +namespace libnest2d { + +template<> struct PointType { using Type = Slic3r::Point; }; +template<> struct CoordType { using Type = coord_t; }; +template<> struct ShapeTag { using Type = PolygonTag; }; +template<> struct ShapeTag { using Type = PolygonTag; }; +template<> struct ShapeTag { using Type = PathTag; }; +template<> struct ShapeTag { using Type = PointTag; }; +template<> struct ContourType { using Type = Slic3r::Points; }; +template<> struct ContourType { using Type = Slic3r::Points; }; + +namespace pointlike { + +template<> inline coord_t x(const Slic3r::Point& p) { return p.x(); } +template<> inline coord_t y(const Slic3r::Point& p) { return p.y(); } +template<> inline coord_t& x(Slic3r::Point& p) { return p.x(); } +template<> inline coord_t& y(Slic3r::Point& p) { return p.y(); } + +} // pointlike + +namespace shapelike { +template<> inline Slic3r::Points& contour(Slic3r::ExPolygon& sh) { return sh.contour.points; } +template<> inline const Slic3r::Points& contour(const Slic3r::ExPolygon& sh) { return sh.contour.points; } +template<> inline Slic3r::Points& contour(Slic3r::Polygon& sh) { return sh.points; } +template<> inline const Slic3r::Points& contour(const Slic3r::Polygon& sh) { return sh.points; } + +template<> Slic3r::Points::iterator begin(Slic3r::Points& pts, const PathTag&) { return pts.begin();} +template<> Slic3r::Points::const_iterator cbegin(const Slic3r::Points& pts, const PathTag&) { return pts.begin(); } +template<> Slic3r::Points::iterator end(Slic3r::Points& pts, const PathTag&) { return pts.end();} +template<> Slic3r::Points::const_iterator cend(const Slic3r::Points& pts, const PathTag&) { return pts.cend(); } + +template<> inline Slic3r::ExPolygon create(Slic3r::Points&& contour) +{ + Slic3r::ExPolygon expoly; expoly.contour.points.swap(contour); + return expoly; +} + +template<> inline Slic3r::Polygon create(Slic3r::Points&& contour) +{ + Slic3r::Polygon poly; poly.points.swap(contour); + return poly; +} + +} // shapelike +} // libnest2d + +namespace Slic3r { + +// Used as compute type. +using Unit = int64_t; + +#if !defined(HAS_INTRINSIC_128_TYPE) || defined(__APPLE__) +using Rational = boost::rational; +#else +using Rational = boost::rational<__int128>; +#endif + +MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc) +{ + const Polygon& chull = pc == pcConvex ? p : libnest2d::sl::convexHull(p); + + libnest2d::RotatedBox box = + libnest2d::minAreaBoundingBox(chull); + + m_right = box.right_extent(); + m_bottom = box.bottom_extent(); + m_axis = box.axis(); +} + +MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc) +{ + const ExPolygon& chull = pc == pcConvex ? p : libnest2d::sl::convexHull(p); + + libnest2d::RotatedBox box = + libnest2d::minAreaBoundingBox(chull); + + m_right = box.right_extent(); + m_bottom = box.bottom_extent(); + m_axis = box.axis(); +} + +MinAreaBoundigBox::MinAreaBoundigBox(const Points &pts, PolygonLevel pc) +{ + const Points& chull = pc == pcConvex ? pts : libnest2d::sl::convexHull(pts); + + libnest2d::RotatedBox box = + libnest2d::minAreaBoundingBox(chull); + + m_right = box.right_extent(); + m_bottom = box.bottom_extent(); + m_axis = box.axis(); +} + +double MinAreaBoundigBox::angle_to_X() const +{ + double ret = std::atan2(m_axis.y(), m_axis.x()); + auto s = std::signbit(ret); + if(s) ret += 2 * PI; + return -ret; +} + +long double MinAreaBoundigBox::width() const +{ + return std::abs(m_bottom) / std::sqrt(libnest2d::pl::magnsq(m_axis)); +} + +long double MinAreaBoundigBox::height() const +{ + return std::abs(m_right) / std::sqrt(libnest2d::pl::magnsq(m_axis)); +} + +long double MinAreaBoundigBox::area() const +{ + long double asq = libnest2d::pl::magnsq(m_axis); + return m_bottom * m_right / asq; +} + +void remove_collinear_points(Polygon &p) +{ + p = libnest2d::removeCollinearPoints(p, Unit(0)); +} + +void remove_collinear_points(ExPolygon &p) +{ + p = libnest2d::removeCollinearPoints(p, Unit(0)); +} + +} diff --git a/src/libslic3r/MinAreaBoundingBox.hpp b/src/libslic3r/MinAreaBoundingBox.hpp new file mode 100644 index 000000000..30d0e9799 --- /dev/null +++ b/src/libslic3r/MinAreaBoundingBox.hpp @@ -0,0 +1,59 @@ +#ifndef MINAREABOUNDINGBOX_HPP +#define MINAREABOUNDINGBOX_HPP + +#include "libslic3r/Point.hpp" + +namespace Slic3r { + +class Polygon; +class ExPolygon; + +void remove_collinear_points(Polygon& p); +void remove_collinear_points(ExPolygon& p); + +/// A class that holds a rotated bounding box. If instantiated with a polygon +/// type it will hold the minimum area bounding box for the given polygon. +/// If the input polygon is convex, the complexity is linear to the number of +/// points. Otherwise a convex hull of O(n*log(n)) has to be performed. +class MinAreaBoundigBox { + Point m_axis; + long double m_bottom = 0.0l, m_right = 0.0l; +public: + + // Polygons can be convex or simple (convex or concave with possible holes) + enum PolygonLevel { + pcConvex, pcSimple + }; + + // Constructors with various types of geometry data used in Slic3r. + // If the convexity is known apriory, pcConvex can be used to skip + // convex hull calculation. It is very important that the input polygons + // do NOT have any collinear points (except for the first and the last + // vertex being the same -- meaning a closed polygon for boost) + // To make sure this constraint is satisfied, you can call + // remove_collinear_points on the input polygon before handing over here) + explicit MinAreaBoundigBox(const Polygon&, PolygonLevel = pcSimple); + explicit MinAreaBoundigBox(const ExPolygon&, PolygonLevel = pcSimple); + explicit MinAreaBoundigBox(const Points&, PolygonLevel = pcSimple); + + // Returns the angle in radians needed for the box to be aligned with the + // X axis. Rotate the polygon by this angle and it will be aligned. + double angle_to_X() const; + + // The box width + long double width() const; + + // The box height + long double height() const; + + // The box area + long double area() const; + + // The axis of the rotated box. If the angle_to_X is not sufficiently + // precise, use this unnormalized direction vector. + const Point& axis() const { return m_axis; } +}; + +} + +#endif // MINAREABOUNDINGBOX_HPP diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index b088a1f17..b323285cc 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -9,6 +9,31 @@ #include #include +#include +#include + +namespace libnest2d { +#if !defined(_MSC_VER) && defined(__SIZEOF_INT128__) && !defined(__APPLE__) +using LargeInt = __int128; +#else +using LargeInt = boost::multiprecision::int128_t; +template<> struct _NumTag { using Type = ScalarTag; }; +#endif +template struct _NumTag> { using Type = RationalTag; }; + +namespace nfp { + +template +struct NfpImpl +{ + NfpResult operator()(const S &sh, const S &other) + { + return nfpConvexOnly>(sh, other); + } +}; + +} +} namespace Slic3r { @@ -130,7 +155,7 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) { // at the same time, it has to provide reasonable results. std::tuple objfunc(const PointImpl& bincenter, - const shapelike::Shapes& merged_pile, + const TMultiShape& merged_pile, const Box& pilebb, const ItemGroup& items, const Item &item, @@ -301,7 +326,7 @@ protected: using Packer = Nester; using PConfig = typename Packer::PlacementConfig; using Distance = TCoord; - using Pile = sl::Shapes; + using Pile = TMultiShape; Packer m_pck; PConfig m_pconf; // Placement configuration @@ -589,7 +614,7 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model, const WipeTowerInfo& // Invalid geometries would throw exceptions when arranging if(item.vertexCount() > 3) { - item.rotation(float(Geometry::rotation_diff_z(rotation0, objinst->get_rotation()))), + item.rotation(Geometry::rotation_diff_z(rotation0, objinst->get_rotation())); item.translation({ ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR), ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 44f77b3f7..193abba06 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -37,7 +37,12 @@ #include "libslic3r/SLA/SLARotfinder.hpp" #include "libslic3r/Utils.hpp" -#include "libnest2d/optimizers/nlopt/genetic.hpp" +//#include "libslic3r/ClipperUtils.hpp" + +// #include "libnest2d/optimizers/nlopt/genetic.hpp" +// #include "libnest2d/backends/clipper/geometries.hpp" +// #include "libnest2d/utils/rotcalipers.hpp" +#include "libslic3r/MinAreaBoundingBox.hpp" #include "GUI.hpp" #include "GUI_App.hpp" @@ -2261,46 +2266,18 @@ void Plater::priv::sla_optimize_rotation() { 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) // wasn't canceled 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); - + + Polygon trchull = o->convex_hull_2d(oi->get_transformation().get_matrix()); + MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex); + double r = rotbb.angle_to_X(); + + // The box should be landscape + if(rotbb.width() < rotbb.height()) r += PI / 2; + Vec3d rt = oi->get_rotation(); rt(Z) += r; oi->set_rotation(rt); } From d809b4894a910ebcefcd89d8776697a7c38c86f4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 6 Jun 2019 12:21:38 +0200 Subject: [PATCH 12/44] Small addition to qhull dep handling. --- src/qhull/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/qhull/CMakeLists.txt b/src/qhull/CMakeLists.txt index 262214a5c..734bb8295 100644 --- a/src/qhull/CMakeLists.txt +++ b/src/qhull/CMakeLists.txt @@ -10,7 +10,7 @@ # see bug report: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=925540 -# find_package(Qhull 7.2 QUIET) +find_package(Qhull 7.2 QUIET) add_library(qhull INTERFACE) @@ -139,9 +139,7 @@ endif(UNIX) ################################################## # LIBDIR is defined in the main xs CMake file: -target_include_directories(${qhull_STATIC} PRIVATE ${LIBDIR}/qhull/src) - +target_include_directories(${qhull_STATIC} BEFORE PUBLIC ${LIBDIR}/qhull/src) target_link_libraries(qhull INTERFACE ${qhull_STATIC}) -target_include_directories(qhull INTERFACE ${LIBDIR}/qhull/src) endif() From 025f86ca3f14e35d617ddb6c76dc5d463d5bf11e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Jun 2019 11:04:09 +0200 Subject: [PATCH 13/44] Fix of the previous refactoring. --- src/admesh/connect.cpp | 8 +++++--- src/admesh/shared.cpp | 3 ++- src/admesh/util.cpp | 5 +++++ src/libslic3r/TriangleMesh.cpp | 35 +++++++++++++++------------------- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index b99f93f3d..d35c48aba 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -837,10 +837,12 @@ void stl_add_facet(stl_file *stl, const stl_facet *new_facet) { if (stl->error) return; - ++ stl->stats.facets_added; - ++ stl->stats.number_of_facets; - stl->facet_start.emplace_back(*new_facet); + assert(stl->facet_start.size() == stl->stats.number_of_facets); + assert(stl->neighbors_start.size() == stl->stats.number_of_facets); + stl->facet_start.emplace_back(*new_facet); // note that the normal vector is not set here, just initialized to 0. stl->facet_start[stl->stats.number_of_facets].normal = stl_normal::Zero(); stl->neighbors_start.emplace_back(); + ++ stl->stats.facets_added; + ++ stl->stats.number_of_facets; } diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index 8162c6a8d..bc264ee96 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -33,6 +33,7 @@ void stl_invalidate_shared_vertices(stl_file *stl) { stl->v_indices.clear(); stl->v_shared.clear(); + stl->stats.shared_vertices = 0; } void stl_generate_shared_vertices(stl_file *stl) @@ -46,7 +47,7 @@ void stl_generate_shared_vertices(stl_file *stl) // 3 indices to vertex per face stl->v_indices.assign(stl->stats.number_of_facets, v_indices_struct()); // Shared vertices (3D coordinates) - stl->v_shared.assign(stl->stats.number_of_facets / 2, stl_vertex()); + stl->v_shared.reserve(stl->stats.number_of_facets / 2); stl->stats.shared_vertices = 0; // A degenerate mesh may contain loops: Traversing a fan will end up in an endless loop diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index 61e0d11e7..d8640e575 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -456,11 +456,16 @@ bool stl_validate(stl_file *stl) assert(! stl->error); assert(stl->fp == nullptr); assert(! stl->facet_start.empty()); + assert(stl->facet_start.size() == stl->stats.number_of_facets); + assert(stl->neighbors_start.size() == stl->stats.number_of_facets); + assert(stl->facet_start.size() == stl->neighbors_start.size()); assert(stl->heads.empty()); assert(stl->tail == nullptr); assert(! stl->neighbors_start.empty()); assert((stl->v_indices.empty()) == (stl->v_shared.empty())); assert(stl->stats.number_of_facets > 0); + assert(stl->v_shared.size() == stl->stats.shared_vertices); + assert(stl->v_shared.empty() || stl->v_indices.size() == stl->stats.number_of_facets); #ifdef _DEBUG // Verify validity of neighborship data. diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index a974f5af3..ad12047d2 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -47,7 +47,6 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f { stl_initialize(&this->stl); stl_file &stl = this->stl; - stl.error = 0; stl.stats.type = inmemory; // count facets and allocate memory @@ -55,7 +54,7 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f stl.stats.original_num_facets = stl.stats.number_of_facets; stl_allocate(&stl); - for (uint32_t i = 0; i < stl.stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl.stats.number_of_facets; ++ i) { stl_facet facet; facet.vertex[0] = points[facets[i](0)].cast(); facet.vertex[1] = points[facets[i](1)].cast(); @@ -76,16 +75,8 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other) { stl_close(&this->stl); - this->stl = other.stl; - this->repaired = other.repaired; - this->stl.heads.clear(); - this->stl.tail = nullptr; - this->stl.error = other.stl.error; - this->stl.facet_start = other.stl.facet_start; - this->stl.neighbors_start = other.stl.neighbors_start; - this->stl.v_indices = other.stl.v_indices; - this->stl.v_shared = other.stl.v_shared; - this->stl.stats = other.stl.stats; + this->stl = other.stl; + this->repaired = other.repaired; return *this; } @@ -1711,10 +1702,12 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) if (min_z > z || (min_z == z && max_z > z)) { // facet is above the cut plane and does not belong to it - if (upper != NULL) stl_add_facet(&upper->stl, facet); + if (upper != nullptr) + stl_add_facet(&upper->stl, facet); } else if (max_z < z || (max_z == z && min_z < z)) { // facet is below the cut plane and does not belong to it - if (lower != NULL) stl_add_facet(&lower->stl, facet); + if (lower != nullptr) + stl_add_facet(&lower->stl, facet); } else if (min_z < z && max_z > z) { // Facet is cut by the slicing plane. @@ -1761,22 +1754,24 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) quadrilateral[1].vertex[2] = v0v1; if (v0(2) > z) { - if (upper != NULL) stl_add_facet(&upper->stl, &triangle); - if (lower != NULL) { + if (upper != nullptr) + stl_add_facet(&upper->stl, &triangle); + if (lower != nullptr) { stl_add_facet(&lower->stl, &quadrilateral[0]); stl_add_facet(&lower->stl, &quadrilateral[1]); } } else { - if (upper != NULL) { + if (upper != nullptr) { stl_add_facet(&upper->stl, &quadrilateral[0]); stl_add_facet(&upper->stl, &quadrilateral[1]); } - if (lower != NULL) stl_add_facet(&lower->stl, &triangle); + if (lower != nullptr) + stl_add_facet(&lower->stl, &triangle); } } } - if (upper != NULL) { + if (upper != nullptr) { BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating upper part"; ExPolygons section; this->make_expolygons_simple(upper_lines, §ion); @@ -1790,7 +1785,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) } } - if (lower != NULL) { + if (lower != nullptr) { BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating lower part"; ExPolygons section; this->make_expolygons_simple(lower_lines, §ion); From 40b27e8332f5590d3998a2ef6c89bce65a48da27 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Jun 2019 16:53:08 +0200 Subject: [PATCH 14/44] admesh refactoring: Move the hashing structure out of stl_file --- src/admesh/connect.cpp | 1025 ++++++++++------------- src/admesh/stl.h | 24 +- src/admesh/stl_io.cpp | 13 - src/admesh/stlinit.cpp | 4 - src/admesh/util.cpp | 2 - src/libslic3r/Fill/FillRectilinear3.cpp | 2 +- 6 files changed, 459 insertions(+), 611 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index d35c48aba..03b13343d 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -32,37 +32,363 @@ #include "stl.h" +struct HashEdge { + // Key of a hash edge: sorted vertices of the edge. + uint32_t key[6]; + // Compare two keys. + bool operator==(const HashEdge &rhs) const { return memcmp(key, rhs.key, sizeof(key)) == 0; } + bool operator!=(const HashEdge &rhs) const { return ! (*this == rhs); } + int hash(int M) const { return ((key[0] / 11 + key[1] / 7 + key[2] / 3) ^ (key[3] / 11 + key[4] / 7 + key[5] / 3)) % M; } -static void stl_match_neighbors_nearby(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b); -static void stl_record_neighbors(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b); -static void stl_initialize_facet_check_nearby(stl_file *stl); -static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b); -static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, - stl_vertex *a, stl_vertex *b, float tolerance); -static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, - void (*match_neighbors)(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b)); -static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b); -static void stl_free_edges(stl_file *stl); -static void stl_change_vertices(stl_file *stl, int facet_num, int vnot, - stl_vertex new_vertex); -static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, - stl_hash_edge *edge_b, int *facet1, int *vertex1, - int *facet2, int *vertex2, - stl_vertex *new_vertex1, stl_vertex *new_vertex2); -extern int stl_check_normal_vector(stl_file *stl, - int facet_num, int normal_fix_flag); + // Index of a facet owning this edge. + int facet_number; + // Index of this edge inside the facet with an index of facet_number. + // If this edge is stored backwards, which_edge is increased by 3. + int which_edge; + struct HashEdge *next; -static inline size_t hash_size_from_nr_faces(const size_t nr_faces) + void load_exact(stl_file *stl, const stl_vertex *a, const stl_vertex *b) + { + { + stl_vertex diff = (*a - *b).cwiseAbs(); + float max_diff = std::max(diff(0), std::max(diff(1), diff(2))); + stl->stats.shortest_edge = std::min(max_diff, stl->stats.shortest_edge); + } + + // Ensure identical vertex ordering of equal edges. + // This method is numerically robust. + if (stl_vertex_lower(*a, *b)) { + } else { + // This edge is loaded backwards. + std::swap(a, b); + this->which_edge += 3; + } + memcpy(&this->key[0], a->data(), sizeof(stl_vertex)); + memcpy(&this->key[3], b->data(), sizeof(stl_vertex)); + // Switch negative zeros to positive zeros, so memcmp will consider them to be equal. + for (size_t i = 0; i < 6; ++ i) { + unsigned char *p = (unsigned char*)(this->key + i); + #ifdef BOOST_LITTLE_ENDIAN + if (p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0x80) + // Negative zero, switch to positive zero. + p[3] = 0; + #else /* BOOST_LITTLE_ENDIAN */ + if (p[0] == 0x80 && p[1] == 0 && p[2] == 0 && p[3] == 0) + // Negative zero, switch to positive zero. + p[0] = 0; + #endif /* BOOST_LITTLE_ENDIAN */ + } + } + + bool load_nearby(const stl_file *stl, const stl_vertex &a, const stl_vertex &b, float tolerance) + { + // Index of a grid cell spaced by tolerance. + typedef Eigen::Matrix Vec3i; + Vec3i vertex1 = ((a - stl->stats.min) / tolerance).cast(); + Vec3i vertex2 = ((b - stl->stats.min) / tolerance).cast(); + static_assert(sizeof(Vec3i) == 12, "size of Vec3i incorrect"); + + if (vertex1 == vertex2) + // Both vertices hash to the same value + return false; + + // Ensure identical vertex ordering of edges, which vertices land into equal grid cells. + // This method is numerically robust. + if ((vertex1[0] != vertex2[0]) ? + (vertex1[0] < vertex2[0]) : + ((vertex1[1] != vertex2[1]) ? + (vertex1[1] < vertex2[1]) : + (vertex1[2] < vertex2[2]))) { + memcpy(&this->key[0], vertex1.data(), sizeof(stl_vertex)); + memcpy(&this->key[3], vertex2.data(), sizeof(stl_vertex)); + } else { + memcpy(&this->key[0], vertex2.data(), sizeof(stl_vertex)); + memcpy(&this->key[3], vertex1.data(), sizeof(stl_vertex)); + this->which_edge += 3; /* this edge is loaded backwards */ + } + return true; + } +}; + +struct HashTableEdges { + HashTableEdges(size_t number_of_faces) { + this->M = (int)hash_size_from_nr_faces(number_of_faces); + this->heads.assign(this->M, nullptr); + this->tail = new HashEdge; + this->tail->next = this->tail; + for (int i = 0; i < this->M; ++ i) + this->heads[i] = this->tail; + } + ~HashTableEdges() { + for (int i = 0; i < this->M; ++ i) { + for (HashEdge *temp = this->heads[i]; this->heads[i] != this->tail; temp = this->heads[i]) { + this->heads[i] = this->heads[i]->next; + delete temp; + ++ this->freed; + } + } + this->heads.clear(); + delete this->tail; + this->tail = nullptr; + } + + void insert_edge(stl_file *stl, const HashEdge &edge, void (*match_neighbors)(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b)) + { + int chain_number = edge.hash(this->M); + HashEdge *link = this->heads[chain_number]; + if (link == this->tail) { + // This list doesn't have any edges currently in it. Add this one. + HashEdge *new_edge = new HashEdge(edge); + ++ this->malloced; + new_edge->next = this->tail; + this->heads[chain_number] = new_edge; + } else if (edges_equal(edge, *link)) { + // This is a match. Record result in neighbors list. + match_neighbors(stl, edge, *link); + // Delete the matched edge from the list. + this->heads[chain_number] = link->next; + delete link; + ++ this->freed; + } else { + // Continue through the rest of the list. + for (;;) { + if (link->next == this->tail) { + // This is the last item in the list. Insert a new edge. + HashEdge *new_edge = new HashEdge; + ++ this->malloced; + *new_edge = edge; + new_edge->next = this->tail; + link->next = new_edge; + ++ this->collisions; + break; + } + if (edges_equal(edge, *link->next)) { + // This is a match. Record result in neighbors list. + match_neighbors(stl, edge, *link->next); + // Delete the matched edge from the list. + HashEdge *temp = link->next; + link->next = link->next->next; + delete temp; + ++ this->freed; + break; + } + // This is not a match. Go to the next link. + link = link->next; + ++ this->collisions; + } + } + } + + // Hash table on edges + std::vector heads; + HashEdge* tail; + int M; + + size_t malloced = 0; + size_t freed = 0; + size_t collisions = 0; + +private: + static inline size_t hash_size_from_nr_faces(const size_t nr_faces) + { + // Good primes for addressing a cca. 30 bit space. + // https://planetmath.org/goodhashtableprimes + static std::vector primes{ 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 }; + // Find a prime number for 50% filling of the shared triangle edges in the mesh. + auto it = std::upper_bound(primes.begin(), primes.end(), nr_faces * 3 * 2 - 1); + return (it == primes.end()) ? primes.back() : *it; + } + + // Edges equal for hashing. Edgesof different facet are allowed to be matched. + static inline bool edges_equal(const HashEdge &edge_a, const HashEdge &edge_b) + { + return edge_a.facet_number != edge_b.facet_number && edge_a == edge_b; + } +}; + +static void record_neighbors(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b) { - // Good primes for addressing a cca. 30 bit space. - // https://planetmath.org/goodhashtableprimes - static std::vector primes{ 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741 }; - // Find a prime number for 50% filling of the shared triangle edges in the mesh. - auto it = std::upper_bound(primes.begin(), primes.end(), nr_faces * 3 * 2 - 1); - return (it == primes.end()) ? primes.back() : *it; + // Facet a's neighbor is facet b + stl->neighbors_start[edge_a.facet_number].neighbor[edge_a.which_edge % 3] = edge_b.facet_number; /* sets the .neighbor part */ + stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] = (edge_b.which_edge + 2) % 3; /* sets the .which_vertex_not part */ + + // Facet b's neighbor is facet a + stl->neighbors_start[edge_b.facet_number].neighbor[edge_b.which_edge % 3] = edge_a.facet_number; /* sets the .neighbor part */ + stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] = (edge_a.which_edge + 2) % 3; /* sets the .which_vertex_not part */ + + if (((edge_a.which_edge < 3) && (edge_b.which_edge < 3)) || ((edge_a.which_edge > 2) && (edge_b.which_edge > 2))) { + // These facets are oriented in opposite directions, their normals are probably messed up. + stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] += 3; + stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] += 3; + } + + // Count successful connects: + // Total connects: + stl->stats.connected_edges += 2; + // Count individual connects: + switch (stl->neighbors_start[edge_a.facet_number].num_neighbors()) { + case 1: ++ stl->stats.connected_facets_1_edge; break; + case 2: ++ stl->stats.connected_facets_2_edge; break; + case 3: ++ stl->stats.connected_facets_3_edge; break; + default: assert(false); + } + switch (stl->neighbors_start[edge_b.facet_number].num_neighbors()) { + case 1: ++ stl->stats.connected_facets_1_edge; break; + case 2: ++ stl->stats.connected_facets_2_edge; break; + case 3: ++ stl->stats.connected_facets_3_edge; break; + default: assert(false); + } +} + +static void match_neighbors_nearby(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b) +{ + record_neighbors(stl, edge_a, edge_b); + + // Which vertices to change + int facet1 = -1; + int facet2 = -1; + int vertex1, vertex2; + stl_vertex new_vertex1, new_vertex2; + { + int v1a; // pair 1, facet a + int v1b; // pair 1, facet b + int v2a; // pair 2, facet a + int v2b; // pair 2, facet b + // Find first pair. + if (edge_a.which_edge < 3) { + v1a = edge_a.which_edge; + v2a = (edge_a.which_edge + 1) % 3; + } else { + v2a = edge_a.which_edge % 3; + v1a = (edge_a.which_edge + 1) % 3; + } + if (edge_b.which_edge < 3) { + v1b = edge_b.which_edge; + v2b = (edge_b.which_edge + 1) % 3; + } else { + v2b = edge_b.which_edge % 3; + v1b = (edge_b.which_edge + 1) % 3; + } + + // Of the first pair, which vertex, if any, should be changed + if (stl->facet_start[edge_a.facet_number].vertex[v1a] != stl->facet_start[edge_b.facet_number].vertex[v1b]) { + // These facets are different. + if ( (stl->neighbors_start[edge_a.facet_number].neighbor[v1a] == -1) + && (stl->neighbors_start[edge_a.facet_number].neighbor[(v1a + 2) % 3] == -1)) { + // This vertex has no neighbors. This is a good one to change. + facet1 = edge_a.facet_number; + vertex1 = v1a; + new_vertex1 = stl->facet_start[edge_b.facet_number].vertex[v1b]; + } else { + facet1 = edge_b.facet_number; + vertex1 = v1b; + new_vertex1 = stl->facet_start[edge_a.facet_number].vertex[v1a]; + } + } + + // Of the second pair, which vertex, if any, should be changed. + if (stl->facet_start[edge_a.facet_number].vertex[v2a] == stl->facet_start[edge_b.facet_number].vertex[v2b]) { + // These facets are different. + if ( (stl->neighbors_start[edge_a.facet_number].neighbor[v2a] == -1) + && (stl->neighbors_start[edge_a.facet_number].neighbor[(v2a + 2) % 3] == -1)) { + // This vertex has no neighbors. This is a good one to change. + facet2 = edge_a.facet_number; + vertex2 = v2a; + new_vertex2 = stl->facet_start[edge_b.facet_number].vertex[v2b]; + } else { + facet2 = edge_b.facet_number; + vertex2 = v2b; + new_vertex2 = stl->facet_start[edge_a.facet_number].vertex[v2a]; + } + } + } + + auto change_vertices = [stl](int facet_num, int vnot, stl_vertex new_vertex) + { + int first_facet = facet_num; + bool direction = false; + + for (;;) { + int pivot_vertex; + int next_edge; + if (vnot > 2) { + if (direction) { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot % 3; + } + else { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + direction = !direction; + } + else { + if (direction) { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + else { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot; + } + } +#if 0 + if (stl->facet_start[facet_num].vertex[pivot_vertex](0) == new_vertex(0) && + stl->facet_start[facet_num].vertex[pivot_vertex](1) == new_vertex(1) && + stl->facet_start[facet_num].vertex[pivot_vertex](2) == new_vertex(2)) + printf("Changing vertex %f,%f,%f: Same !!!\r\n", new_vertex(0), new_vertex(1), new_vertex(2)); + else { + if (stl->facet_start[facet_num].vertex[pivot_vertex](0) != new_vertex(0)) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex](0), + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](0)), + new_vertex(0), + *reinterpret_cast(&new_vertex(0))); + if (stl->facet_start[facet_num].vertex[pivot_vertex](1) != new_vertex(1)) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex](1), + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](1)), + new_vertex(1), + *reinterpret_cast(&new_vertex(1))); + if (stl->facet_start[facet_num].vertex[pivot_vertex](2) != new_vertex(2)) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex](2), + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](2)), + new_vertex(2), + *reinterpret_cast(&new_vertex(2))); + } +#endif + stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; + vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; + facet_num = stl->neighbors_start[facet_num].neighbor[next_edge]; + if (facet_num == -1) + break; + + if (facet_num == first_facet) { + // back to the beginning + printf("Back to the first facet changing vertices: probably a mobius part.\nTry using a smaller tolerance or don't do a nearby check\n"); + return; + } + } + }; + + if (facet1 != -1) { + int vnot1 = (facet1 == edge_a.facet_number) ? + (edge_a.which_edge + 2) % 3 : + (edge_b.which_edge + 2) % 3; + if (((vnot1 + 2) % 3) == vertex1) + vnot1 += 3; + change_vertices(facet1, vnot1, new_vertex1); + } + if (facet2 != -1) { + int vnot2 = (facet2 == edge_a.facet_number) ? + (edge_a.which_edge + 2) % 3 : + (edge_b.which_edge + 2) % 3; + if (((vnot2 + 2) % 3) == vertex2) + vnot2 += 3; + change_vertices(facet2, vnot2, new_vertex2); + } + stl->stats.edges_fixed += 2; } // This function builds the neighbors list. No modifications are made @@ -93,30 +419,21 @@ void stl_check_facets_exact(stl_file *stl) } // Initialize hash table. - stl->stats.malloced = 0; - stl->stats.freed = 0; - stl->stats.collisions = 0; - stl->M = (int)hash_size_from_nr_faces(stl->stats.number_of_facets); + HashTableEdges hash_table(stl->stats.number_of_facets); for (auto &neighbor : stl->neighbors_start) neighbor.reset(); - stl->heads.assign(stl->M, nullptr); - stl->tail = new stl_hash_edge; - stl->tail->next = stl->tail; - for (int i = 0; i < stl->M; ++ i) - stl->heads[i] = stl->tail; // Connect neighbor edges. - for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { const stl_facet &facet = stl->facet_start[i]; for (int j = 0; j < 3; ++ j) { - stl_hash_edge edge; + HashEdge edge; edge.facet_number = i; edge.which_edge = j; - stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); - insert_hash_edge(stl, edge, stl_record_neighbors); + edge.load_exact(stl, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); + hash_table.insert_edge(stl, edge, record_neighbors); } } - stl_free_edges(stl); #if 0 printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n", @@ -125,444 +442,33 @@ void stl_check_facets_exact(stl_file *stl) #endif } -static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b) { - - if (stl->error) return; - - { - stl_vertex diff = (*a - *b).cwiseAbs(); - float max_diff = std::max(diff(0), std::max(diff(1), diff(2))); - stl->stats.shortest_edge = std::min(max_diff, stl->stats.shortest_edge); - } - - // Ensure identical vertex ordering of equal edges. - // This method is numerically robust. - if (stl_vertex_lower(*a, *b)) { - } else { - std::swap(a, b); - edge->which_edge += 3; /* this edge is loaded backwards */ - } - memcpy(&edge->key[0], a->data(), sizeof(stl_vertex)); - memcpy(&edge->key[3], b->data(), sizeof(stl_vertex)); - // Switch negative zeros to positive zeros, so memcmp will consider them to be equal. - for (size_t i = 0; i < 6; ++ i) { - unsigned char *p = (unsigned char*)(edge->key + i); -#ifdef BOOST_LITTLE_ENDIAN - if (p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0x80) - // Negative zero, switch to positive zero. - p[3] = 0; -#else /* BOOST_LITTLE_ENDIAN */ - if (p[0] == 0x80 && p[1] == 0 && p[2] == 0 && p[3] == 0) - // Negative zero, switch to positive zero. - p[0] = 0; -#endif /* BOOST_LITTLE_ENDIAN */ - } -} - -static void insert_hash_edge(stl_file *stl, stl_hash_edge edge, - void (*match_neighbors)(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b)) -{ - if (stl->error) return; - - int chain_number = edge.hash(stl->M); - stl_hash_edge *link = stl->heads[chain_number]; - - stl_hash_edge *new_edge; - stl_hash_edge *temp; - if(link == stl->tail) { - /* This list doesn't have any edges currently in it. Add this one. */ - new_edge = new stl_hash_edge; - if(new_edge == NULL) perror("insert_hash_edge"); - stl->stats.malloced++; - *new_edge = edge; - new_edge->next = stl->tail; - stl->heads[chain_number] = new_edge; - return; - } else if(!stl_compare_function(&edge, link)) { - /* This is a match. Record result in neighbors list. */ - match_neighbors(stl, &edge, link); - /* Delete the matched edge from the list. */ - stl->heads[chain_number] = link->next; - delete link; - stl->stats.freed++; - return; - } else { - /* Continue through the rest of the list */ - for(;;) { - if(link->next == stl->tail) { - /* This is the last item in the list. Insert a new edge. */ - new_edge = new stl_hash_edge; - if(new_edge == NULL) perror("insert_hash_edge"); - stl->stats.malloced++; - *new_edge = edge; - new_edge->next = stl->tail; - link->next = new_edge; - stl->stats.collisions++; - return; - } else if(!stl_compare_function(&edge, link->next)) { - /* This is a match. Record result in neighbors list. */ - match_neighbors(stl, &edge, link->next); - - /* Delete the matched edge from the list. */ - temp = link->next; - link->next = link->next->next; - delete temp; - stl->stats.freed++; - return; - } else { - /* This is not a match. Go to the next link */ - link = link->next; - stl->stats.collisions++; - } - } - } -} - -// Return 1 if the edges are not matched. -static inline int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b) -{ - // Don't match edges of the same facet - return (edge_a->facet_number == edge_b->facet_number) || (*edge_a != *edge_b); -} - void stl_check_facets_nearby(stl_file *stl, float tolerance) -{ - if (stl->error) - return; - - if( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets) - && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets) - && (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) { - /* No need to check any further. All facets are connected */ - return; - } - - stl_initialize_facet_check_nearby(stl); - - for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { - //FIXME is the copy necessary? - stl_facet facet = stl->facet_start[i]; - for (int j = 0; j < 3; j++) { - if(stl->neighbors_start[i].neighbor[j] == -1) { - stl_hash_edge edge; - edge.facet_number = i; - edge.which_edge = j; - if(stl_load_edge_nearby(stl, &edge, &facet.vertex[j], - &facet.vertex[(j + 1) % 3], - tolerance)) { - /* only insert edges that have different keys */ - insert_hash_edge(stl, edge, stl_match_neighbors_nearby); - } - } - } - } - - stl_free_edges(stl); -} - -static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, stl_vertex *a, stl_vertex *b, float tolerance) -{ - // Index of a grid cell spaced by tolerance. - typedef Eigen::Matrix Vec3i; - Vec3i vertex1 = ((*a - stl->stats.min) / tolerance).cast(); - Vec3i vertex2 = ((*b - stl->stats.min) / tolerance).cast(); - static_assert(sizeof(Vec3i) == 12, "size of Vec3i incorrect"); - - if (vertex1 == vertex2) - // Both vertices hash to the same value - return 0; - - // Ensure identical vertex ordering of edges, which vertices land into equal grid cells. - // This method is numerically robust. - if ((vertex1[0] != vertex2[0]) ? - (vertex1[0] < vertex2[0]) : - ((vertex1[1] != vertex2[1]) ? - (vertex1[1] < vertex2[1]) : - (vertex1[2] < vertex2[2]))) { - memcpy(&edge->key[0], vertex1.data(), sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex2.data(), sizeof(stl_vertex)); - } else { - memcpy(&edge->key[0], vertex2.data(), sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex1.data(), sizeof(stl_vertex)); - edge->which_edge += 3; /* this edge is loaded backwards */ - } - return 1; -} - -static void stl_free_edges(stl_file *stl) -{ - if (stl->error) - return; - - if(stl->stats.malloced != stl->stats.freed) { - for (int i = 0; i < stl->M; i++) { - for (stl_hash_edge *temp = stl->heads[i]; stl->heads[i] != stl->tail; temp = stl->heads[i]) { - stl->heads[i] = stl->heads[i]->next; - delete temp; - ++ stl->stats.freed; - } - } - } - stl->heads.clear(); - delete stl->tail; - stl->tail = nullptr; -} - -static void stl_initialize_facet_check_nearby(stl_file *stl) { if (stl->error) return; - stl->stats.malloced = 0; - stl->stats.freed = 0; - stl->stats.collisions = 0; + if ( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets) + && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets) + && (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) { + // No need to check any further. All facets are connected. + return; + } - /* tolerance = STL_MAX(stl->stats.shortest_edge, tolerance);*/ - /* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/ - /* tolerance *= 0.5;*/ - stl->M = (int)hash_size_from_nr_faces(stl->stats.number_of_facets); - - stl->heads.assign(stl->M, nullptr); - stl->tail = new stl_hash_edge; - stl->tail->next = stl->tail; - - for (int i = 0; i < stl->M; ++ i) - stl->heads[i] = stl->tail; -} - -static void -stl_record_neighbors(stl_file *stl, - stl_hash_edge *edge_a, stl_hash_edge *edge_b) { - int i; - int j; - - if (stl->error) return; - - /* Facet a's neighbor is facet b */ - stl->neighbors_start[edge_a->facet_number].neighbor[edge_a->which_edge % 3] = edge_b->facet_number; /* sets the .neighbor part */ - stl->neighbors_start[edge_a->facet_number].which_vertex_not[edge_a->which_edge % 3] = (edge_b->which_edge + 2) % 3; /* sets the .which_vertex_not part */ - - /* Facet b's neighbor is facet a */ - stl->neighbors_start[edge_b->facet_number].neighbor[edge_b->which_edge % 3] = edge_a->facet_number; /* sets the .neighbor part */ - stl->neighbors_start[edge_b->facet_number].which_vertex_not[edge_b->which_edge % 3] = (edge_a->which_edge + 2) % 3; /* sets the .which_vertex_not part */ - - if( ((edge_a->which_edge < 3) && (edge_b->which_edge < 3)) - || ((edge_a->which_edge > 2) && (edge_b->which_edge > 2))) { - /* these facets are oriented in opposite directions. */ - /* their normals are probably messed up. */ - stl->neighbors_start[edge_a->facet_number].which_vertex_not[edge_a->which_edge % 3] += 3; - stl->neighbors_start[edge_b->facet_number].which_vertex_not[edge_b->which_edge % 3] += 3; - } - - - /* Count successful connects */ - /* Total connects */ - stl->stats.connected_edges += 2; - /* Count individual connects */ - i = ((stl->neighbors_start[edge_a->facet_number].neighbor[0] == -1) + - (stl->neighbors_start[edge_a->facet_number].neighbor[1] == -1) + - (stl->neighbors_start[edge_a->facet_number].neighbor[2] == -1)); - j = ((stl->neighbors_start[edge_b->facet_number].neighbor[0] == -1) + - (stl->neighbors_start[edge_b->facet_number].neighbor[1] == -1) + - (stl->neighbors_start[edge_b->facet_number].neighbor[2] == -1)); - if(i == 2) { - stl->stats.connected_facets_1_edge +=1; - } else if(i == 1) { - stl->stats.connected_facets_2_edge +=1; - } else { - stl->stats.connected_facets_3_edge +=1; - } - if(j == 2) { - stl->stats.connected_facets_1_edge +=1; - } else if(j == 1) { - stl->stats.connected_facets_2_edge +=1; - } else { - stl->stats.connected_facets_3_edge +=1; - } -} - -static void stl_match_neighbors_nearby(stl_file *stl, stl_hash_edge *edge_a, stl_hash_edge *edge_b) -{ - int facet1; - int facet2; - int vertex1; - int vertex2; - int vnot1; - int vnot2; - stl_vertex new_vertex1; - stl_vertex new_vertex2; - - if (stl->error) return; - - stl_record_neighbors(stl, edge_a, edge_b); - stl_which_vertices_to_change(stl, edge_a, edge_b, &facet1, &vertex1, - &facet2, &vertex2, &new_vertex1, &new_vertex2); - if(facet1 != -1) { - if(facet1 == edge_a->facet_number) { - vnot1 = (edge_a->which_edge + 2) % 3; - } else { - vnot1 = (edge_b->which_edge + 2) % 3; - } - if(((vnot1 + 2) % 3) == vertex1) { - vnot1 += 3; - } - stl_change_vertices(stl, facet1, vnot1, new_vertex1); - } - if(facet2 != -1) { - if(facet2 == edge_a->facet_number) { - vnot2 = (edge_a->which_edge + 2) % 3; - } else { - vnot2 = (edge_b->which_edge + 2) % 3; - } - if(((vnot2 + 2) % 3) == vertex2) { - vnot2 += 3; - } - stl_change_vertices(stl, facet2, vnot2, new_vertex2); - } - stl->stats.edges_fixed += 2; -} - - -static void stl_change_vertices(stl_file *stl, int facet_num, int vnot, stl_vertex new_vertex) { - int first_facet; - int direction; - int next_edge; - int pivot_vertex; - - if (stl->error) return; - - first_facet = facet_num; - direction = 0; - - for(;;) { - if(vnot > 2) { - if(direction == 0) { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - direction = 1; - } else { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot % 3; - direction = 0; - } - } else { - if(direction == 0) { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot; - } else { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - } - } -#if 0 - if (stl->facet_start[facet_num].vertex[pivot_vertex](0) == new_vertex(0) && - stl->facet_start[facet_num].vertex[pivot_vertex](1) == new_vertex(1) && - stl->facet_start[facet_num].vertex[pivot_vertex](2) == new_vertex(2)) - printf("Changing vertex %f,%f,%f: Same !!!\r\n", - new_vertex(0), new_vertex(1), new_vertex(2)); - else { - if (stl->facet_start[facet_num].vertex[pivot_vertex](0) != new_vertex(0)) - printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex](0), - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](0)), - new_vertex(0), - *reinterpret_cast(&new_vertex(0))); - if (stl->facet_start[facet_num].vertex[pivot_vertex](1) != new_vertex(1)) - printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex](1), - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](1)), - new_vertex(1), - *reinterpret_cast(&new_vertex(1))); - if (stl->facet_start[facet_num].vertex[pivot_vertex](2) != new_vertex(2)) - printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex](2), - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](2)), - new_vertex(2), - *reinterpret_cast(&new_vertex(2))); - } -#endif - stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; - vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; - facet_num = stl->neighbors_start[facet_num].neighbor[next_edge]; - - if(facet_num == -1) { - break; - } - - if(facet_num == first_facet) { - /* back to the beginning */ - printf("\ -Back to the first facet changing vertices: probably a mobius part.\n\ -Try using a smaller tolerance or don't do a nearby check\n"); - return; - } - } -} - -static void -stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a, - stl_hash_edge *edge_b, int *facet1, int *vertex1, - int *facet2, int *vertex2, - stl_vertex *new_vertex1, stl_vertex *new_vertex2) { - int v1a; /* pair 1, facet a */ - int v1b; /* pair 1, facet b */ - int v2a; /* pair 2, facet a */ - int v2b; /* pair 2, facet b */ - - /* Find first pair */ - if(edge_a->which_edge < 3) { - v1a = edge_a->which_edge; - v2a = (edge_a->which_edge + 1) % 3; - } else { - v2a = edge_a->which_edge % 3; - v1a = (edge_a->which_edge + 1) % 3; - } - if(edge_b->which_edge < 3) { - v1b = edge_b->which_edge; - v2b = (edge_b->which_edge + 1) % 3; - } else { - v2b = edge_b->which_edge % 3; - v1b = (edge_b->which_edge + 1) % 3; - } - - // Of the first pair, which vertex, if any, should be changed - if(stl->facet_start[edge_a->facet_number].vertex[v1a] == - stl->facet_start[edge_b->facet_number].vertex[v1b]) { - // These facets are already equal. No need to change. - *facet1 = -1; - } else { - if( (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1) - && (stl->neighbors_start[edge_a->facet_number].neighbor[(v1a + 2) % 3] == -1)) { - /* This vertex has no neighbors. This is a good one to change */ - *facet1 = edge_a->facet_number; - *vertex1 = v1a; - *new_vertex1 = stl->facet_start[edge_b->facet_number].vertex[v1b]; - } else { - *facet1 = edge_b->facet_number; - *vertex1 = v1b; - *new_vertex1 = stl->facet_start[edge_a->facet_number].vertex[v1a]; - } - } - - /* Of the second pair, which vertex, if any, should be changed */ - if(stl->facet_start[edge_a->facet_number].vertex[v2a] == - stl->facet_start[edge_b->facet_number].vertex[v2b]) { - // These facets are already equal. No need to change. - *facet2 = -1; - } else { - if( (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1) - && (stl->neighbors_start[edge_a->facet_number].neighbor[(v2a + 2) % 3] == -1)) { - /* This vertex has no neighbors. This is a good one to change */ - *facet2 = edge_a->facet_number; - *vertex2 = v2a; - *new_vertex2 = stl->facet_start[edge_b->facet_number].vertex[v2b]; - } else { - *facet2 = edge_b->facet_number; - *vertex2 = v2b; - *new_vertex2 = stl->facet_start[edge_a->facet_number].vertex[v2a]; - } - } + HashTableEdges hash_table(stl->stats.number_of_facets); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + //FIXME is the copy necessary? + stl_facet facet = stl->facet_start[i]; + for (int j = 0; j < 3; j++) { + if (stl->neighbors_start[i].neighbor[j] == -1) { + HashEdge edge; + edge.facet_number = i; + edge.which_edge = j; + if (edge.load_nearby(stl, facet.vertex[j], facet.vertex[(j + 1) % 3], tolerance)) + // Only insert edges that have different keys. + hash_table.insert_edge(stl, edge, match_neighbors_nearby); + } + } + } } void stl_remove_unconnected_facets(stl_file *stl) @@ -728,109 +634,88 @@ void stl_remove_unconnected_facets(stl_file *stl) } } -void -stl_fill_holes(stl_file *stl) { - stl_facet facet; - stl_facet new_facet; - int neighbors_initial[3]; - stl_hash_edge edge; - int first_facet; - int direction; - int facet_num; - int vnot; - int next_edge; - int pivot_vertex; - int next_facet; - int j; - int k; +void stl_fill_holes(stl_file *stl) +{ + if (stl->error) + return; - if (stl->error) return; + // Insert all unconnected edges into hash list. + HashTableEdges hash_table(stl->stats.number_of_facets); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + stl_facet facet = stl->facet_start[i]; + for (int j = 0; j < 3; ++ j) { + if(stl->neighbors_start[i].neighbor[j] != -1) + continue; + HashEdge edge; + edge.facet_number = i; + edge.which_edge = j; + edge.load_exact(stl, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); + hash_table.insert_edge(stl, edge, record_neighbors); + } + } - /* Insert all unconnected edges into hash list */ - stl_initialize_facet_check_nearby(stl); - for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { - facet = stl->facet_start[i]; - for(j = 0; j < 3; j++) { - if(stl->neighbors_start[i].neighbor[j] != -1) continue; - edge.facet_number = i; - edge.which_edge = j; - stl_load_edge_exact(stl, &edge, &facet.vertex[j], - &facet.vertex[(j + 1) % 3]); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + stl_facet facet = stl->facet_start[i]; + int neighbors_initial[3] = { stl->neighbors_start[i].neighbor[0], stl->neighbors_start[i].neighbor[1], stl->neighbors_start[i].neighbor[2] }; + int first_facet = i; + for (int j = 0; j < 3; ++ j) { + if (stl->neighbors_start[i].neighbor[j] != -1) + continue; - insert_hash_edge(stl, edge, stl_record_neighbors); - } - } + stl_facet new_facet; + new_facet.vertex[0] = facet.vertex[j]; + new_facet.vertex[1] = facet.vertex[(j + 1) % 3]; + bool direction = neighbors_initial[(j + 2) % 3] == -1; + int facet_num = i; + int vnot = (j + 2) % 3; - for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { - facet = stl->facet_start[i]; - neighbors_initial[0] = stl->neighbors_start[i].neighbor[0]; - neighbors_initial[1] = stl->neighbors_start[i].neighbor[1]; - neighbors_initial[2] = stl->neighbors_start[i].neighbor[2]; - first_facet = i; - for(j = 0; j < 3; j++) { - if(stl->neighbors_start[i].neighbor[j] != -1) continue; + for (;;) { + int pivot_vertex = 0; + int next_edge = 0; + if (vnot > 2) { + if (direction) { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot % 3; + } else { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + direction = ! direction; + } else { + if(direction == 0) { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot; + } else { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + } - new_facet.vertex[0] = facet.vertex[j]; - new_facet.vertex[1] = facet.vertex[(j + 1) % 3]; - if(neighbors_initial[(j + 2) % 3] == -1) { - direction = 1; - } else { - direction = 0; - } + int next_facet = stl->neighbors_start[facet_num].neighbor[next_edge]; + if (next_facet == -1) { + new_facet.vertex[2] = stl->facet_start[facet_num].vertex[vnot % 3]; + stl_add_facet(stl, &new_facet); + for (int k = 0; k < 3; ++ k) { + HashEdge edge; + edge.facet_number = stl->stats.number_of_facets - 1; + edge.which_edge = k; + edge.load_exact(stl, &new_facet.vertex[k], &new_facet.vertex[(k + 1) % 3]); + hash_table.insert_edge(stl, edge, record_neighbors); + } + break; + } - facet_num = i; - vnot = (j + 2) % 3; + vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; + facet_num = next_facet; - for(;;) { - if(vnot > 2) { - if(direction == 0) { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - direction = 1; - } else { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot % 3; - direction = 0; - } - } else { - if(direction == 0) { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot; - } else { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - } - } - next_facet = stl->neighbors_start[facet_num].neighbor[next_edge]; - - if(next_facet == -1) { - new_facet.vertex[2] = stl->facet_start[facet_num]. - vertex[vnot % 3]; - stl_add_facet(stl, &new_facet); - for(k = 0; k < 3; k++) { - edge.facet_number = stl->stats.number_of_facets - 1; - edge.which_edge = k; - stl_load_edge_exact(stl, &edge, &new_facet.vertex[k], - &new_facet.vertex[(k + 1) % 3]); - - insert_hash_edge(stl, edge, stl_record_neighbors); - } - break; - } else { - vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; - facet_num = next_facet; - } - - if(facet_num == first_facet) { - /* back to the beginning */ - printf("\ -Back to the first facet filling holes: probably a mobius part.\n\ -Try using a smaller tolerance or don't do a nearby check\n"); - return; - } - } - } - } + if (facet_num == first_facet) { + // back to the beginning + printf("Back to the first facet filling holes: probably a mobius part.\nTry using a smaller tolerance or don't do a nearby check\n"); + return; + } + } + } + } } void stl_add_facet(stl_file *stl, const stl_facet *new_facet) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 345951121..bd49ad59f 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -74,21 +74,6 @@ struct stl_edge { int facet_number; }; -struct stl_hash_edge { - // Key of a hash edge: sorted vertices of the edge. - uint32_t key[6]; - // Compare two keys. - bool operator==(const stl_hash_edge &rhs) { return memcmp(key, rhs.key, sizeof(key)) == 0; } - bool operator!=(const stl_hash_edge &rhs) { return ! (*this == rhs); } - int hash(int M) const { return ((key[0] / 11 + key[1] / 7 + key[2] / 3) ^ (key[3] / 11 + key[4] / 7 + key[5] / 3)) % M; } - // Index of a facet owning this edge. - int facet_number; - // Index of this edge inside the facet with an index of facet_number. - // If this edge is stored backwards, which_edge is increased by 3. - int which_edge; - struct stl_hash_edge *next; -}; - struct stl_neighbors { stl_neighbors() { reset(); } void reset() { @@ -99,8 +84,10 @@ struct stl_neighbors { which_vertex_not[1] = -1; which_vertex_not[2] = -1; } + int num_neighbors_missing() const { return (this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1); } + int num_neighbors() const { return 3 - this->num_neighbors_missing(); } - // Index of a neighbor facet. + // Index of a neighbor facet. int neighbor[3]; // Index of an opposite vertex at the neighbor face. char which_vertex_not[3]; @@ -151,10 +138,6 @@ struct stl_file { FILE *fp; std::vector facet_start; std::vector neighbors_start; - // Hash table on edges - std::vector heads; - stl_hash_edge* tail; - int M; // Indexed face set std::vector v_indices; std::vector v_shared; @@ -177,7 +160,6 @@ extern void stl_check_facets_nearby(stl_file *stl, float tolerance); extern void stl_remove_unconnected_facets(stl_file *stl); extern void stl_write_vertex(stl_file *stl, int facet, int vertex); extern void stl_write_facet(stl_file *stl, char *label, int facet); -extern void stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge); extern void stl_write_neighbor(stl_file *stl, int facet); extern void stl_write_quad_object(stl_file *stl, char *file); extern void stl_verify_neighbors(stl_file *stl); diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp index 81060c0a3..1e2e1479e 100644 --- a/src/admesh/stl_io.cpp +++ b/src/admesh/stl_io.cpp @@ -257,19 +257,6 @@ stl_write_facet(stl_file *stl, char *label, int facet) { stl_write_vertex(stl, facet, 2); } -void -stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge) { - if (stl->error) return; - printf("edge (%d)/(%d) %s\n", edge.facet_number, edge.which_edge, label); - if(edge.which_edge < 3) { - stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3); - stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3); - } else { - stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3); - stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3); - } -} - void stl_write_neighbor(stl_file *stl, int facet) { if (stl->error) return; diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index 81b7914c3..b7ba21b77 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -50,8 +50,6 @@ void stl_open(stl_file *stl, const char *file) void stl_initialize(stl_file *stl) { stl->fp = nullptr; - stl->tail = nullptr; - stl->M = 0; stl->error = 0; stl->facet_start.clear(); stl->neighbors_start.clear(); @@ -64,8 +62,6 @@ void stl_initialize(stl_file *stl) void stl_close(stl_file *stl) { assert(stl->fp == nullptr); - assert(stl->heads.empty()); - assert(stl->tail == nullptr); stl_initialize(stl); } diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index d8640e575..91293d048 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -459,8 +459,6 @@ bool stl_validate(stl_file *stl) assert(stl->facet_start.size() == stl->stats.number_of_facets); assert(stl->neighbors_start.size() == stl->stats.number_of_facets); assert(stl->facet_start.size() == stl->neighbors_start.size()); - assert(stl->heads.empty()); - assert(stl->tail == nullptr); assert(! stl->neighbors_start.empty()); assert((stl->v_indices.empty()) == (stl->v_shared.empty())); assert(stl->stats.number_of_facets > 0); diff --git a/src/libslic3r/Fill/FillRectilinear3.cpp b/src/libslic3r/Fill/FillRectilinear3.cpp index 8a0b90ead..dab584298 100644 --- a/src/libslic3r/Fill/FillRectilinear3.cpp +++ b/src/libslic3r/Fill/FillRectilinear3.cpp @@ -15,7 +15,7 @@ #include "FillRectilinear3.hpp" - #define SLIC3R_DEBUG +// #define SLIC3R_DEBUG // Make assert active if SLIC3R_DEBUG #ifdef SLIC3R_DEBUG From a1c38794fbc12c0d88c59ac85230f058d0779b64 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Jun 2019 17:17:36 +0200 Subject: [PATCH 15/44] Refactored admesh to get rid of the error and fp members of stl_file. --- src/admesh/connect.cpp | 15 --- src/admesh/normals.cpp | 118 +++++++---------- src/admesh/shared.cpp | 33 ++--- src/admesh/stl.h | 74 ++++------- src/admesh/stl_io.cpp | 111 ++++++---------- src/admesh/stlinit.cpp | 228 ++++++++++++--------------------- src/admesh/util.cpp | 39 +----- src/libslic3r/Format/PRUS.cpp | 1 - src/libslic3r/Format/STL.cpp | 3 +- src/libslic3r/TriangleMesh.cpp | 5 +- src/libslic3r/TriangleMesh.hpp | 14 +- xs/xsp/TriangleMesh.xsp | 1 - 12 files changed, 213 insertions(+), 429 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index 03b13343d..9553e7c4d 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -396,9 +396,6 @@ static void match_neighbors_nearby(stl_file *stl, const HashEdge &edge_a, const // floats of the first edge matches all six floats of the second edge. void stl_check_facets_exact(stl_file *stl) { - if (stl->error) - return; - stl->stats.connected_edges = 0; stl->stats.connected_facets_1_edge = 0; stl->stats.connected_facets_2_edge = 0; @@ -444,9 +441,6 @@ void stl_check_facets_exact(stl_file *stl) void stl_check_facets_nearby(stl_file *stl, float tolerance) { - if (stl->error) - return; - if ( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets) && (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets) && (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) { @@ -476,9 +470,6 @@ void stl_remove_unconnected_facets(stl_file *stl) // A couple of things need to be done here. One is to remove any completely unconnected facets (0 edges connected) since these are // useless and could be completely wrong. The second thing that needs to be done is to remove any degenerate facets that were created during // stl_check_facets_nearby(). - if (stl->error) - return; - auto remove_facet = [stl](int facet_number) { ++ stl->stats.facets_removed; @@ -526,7 +517,6 @@ void stl_remove_unconnected_facets(stl_file *stl) { // Update statistics on face connectivity. auto stl_update_connects_remove_1 = [stl](int facet_num) { - assert(! stl->error); //FIXME when decreasing 3_edge, should I increase 2_edge etc? switch ((stl->neighbors_start[facet_num].neighbor[0] == -1) + (stl->neighbors_start[facet_num].neighbor[1] == -1) + (stl->neighbors_start[facet_num].neighbor[2] == -1)) { case 0: // Facet has 3 neighbors @@ -636,9 +626,6 @@ void stl_remove_unconnected_facets(stl_file *stl) void stl_fill_holes(stl_file *stl) { - if (stl->error) - return; - // Insert all unconnected edges into hash list. HashTableEdges hash_table(stl->stats.number_of_facets); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { @@ -720,8 +707,6 @@ void stl_fill_holes(stl_file *stl) void stl_add_facet(stl_file *stl, const stl_facet *new_facet) { - if (stl->error) - return; assert(stl->facet_start.size() == stl->stats.number_of_facets); assert(stl->neighbors_start.size() == stl->stats.number_of_facets); stl->facet_start.emplace_back(*new_facet); diff --git a/src/admesh/normals.cpp b/src/admesh/normals.cpp index e11f1a3c1..f20d9e68e 100644 --- a/src/admesh/normals.cpp +++ b/src/admesh/normals.cpp @@ -29,61 +29,43 @@ static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag); -static void -stl_reverse_facet(stl_file *stl, int facet_num) { - stl_vertex tmp_vertex; - /* int tmp_neighbor;*/ - int neighbor[3]; - int vnot[3]; +static void reverse_facet(stl_file *stl, int facet_num) +{ + stl->stats.facets_reversed += 1; - stl->stats.facets_reversed += 1; + int neighbor[3] = { stl->neighbors_start[facet_num].neighbor[0], stl->neighbors_start[facet_num].neighbor[1], stl->neighbors_start[facet_num].neighbor[2] }; + int vnot[3] = { stl->neighbors_start[facet_num].which_vertex_not[0], stl->neighbors_start[facet_num].which_vertex_not[1], stl->neighbors_start[facet_num].which_vertex_not[2] }; - neighbor[0] = stl->neighbors_start[facet_num].neighbor[0]; - neighbor[1] = stl->neighbors_start[facet_num].neighbor[1]; - neighbor[2] = stl->neighbors_start[facet_num].neighbor[2]; - vnot[0] = stl->neighbors_start[facet_num].which_vertex_not[0]; - vnot[1] = stl->neighbors_start[facet_num].which_vertex_not[1]; - vnot[2] = stl->neighbors_start[facet_num].which_vertex_not[2]; + // reverse the facet + stl_vertex tmp_vertex = stl->facet_start[facet_num].vertex[0]; + stl->facet_start[facet_num].vertex[0] = + stl->facet_start[facet_num].vertex[1]; + stl->facet_start[facet_num].vertex[1] = tmp_vertex; - /* reverse the facet */ - tmp_vertex = stl->facet_start[facet_num].vertex[0]; - stl->facet_start[facet_num].vertex[0] = - stl->facet_start[facet_num].vertex[1]; - stl->facet_start[facet_num].vertex[1] = tmp_vertex; + // fix the vnots of the neighboring facets + if (neighbor[0] != -1) + stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = (stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6; + if (neighbor[1] != -1) + stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = (stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6; + if (neighbor[2] != -1) + stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] = (stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6; - /* fix the vnots of the neighboring facets */ - if(neighbor[0] != -1) - stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = - (stl->neighbors_start[neighbor[0]]. - which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6; - if(neighbor[1] != -1) - stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = - (stl->neighbors_start[neighbor[1]]. - which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6; - if(neighbor[2] != -1) - stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] = - (stl->neighbors_start[neighbor[2]]. - which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6; + // swap the neighbors of the facet that is being reversed + stl->neighbors_start[facet_num].neighbor[1] = neighbor[2]; + stl->neighbors_start[facet_num].neighbor[2] = neighbor[1]; - /* swap the neighbors of the facet that is being reversed */ - stl->neighbors_start[facet_num].neighbor[1] = neighbor[2]; - stl->neighbors_start[facet_num].neighbor[2] = neighbor[1]; + // swap the vnots of the facet that is being reversed + stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2]; + stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1]; - /* swap the vnots of the facet that is being reversed */ - stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2]; - stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1]; - - /* reverse the values of the vnots of the facet that is being reversed */ - stl->neighbors_start[facet_num].which_vertex_not[0] = - (stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6; - stl->neighbors_start[facet_num].which_vertex_not[1] = - (stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6; - stl->neighbors_start[facet_num].which_vertex_not[2] = - (stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6; + // reverse the values of the vnots of the facet that is being reversed + stl->neighbors_start[facet_num].which_vertex_not[0] = (stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6; + stl->neighbors_start[facet_num].which_vertex_not[1] = (stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6; + stl->neighbors_start[facet_num].which_vertex_not[2] = (stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6; } -void -stl_fix_normal_directions(stl_file *stl) { +void stl_fix_normal_directions(stl_file *stl) +{ /* int edge_num;*/ /* int vnot;*/ int checked = 0; @@ -104,8 +86,6 @@ stl_fix_normal_directions(stl_file *stl) { int id; int force_exit = 0; - if (stl->error) return; - // this may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209 if (stl->stats.number_of_facets == 0) return; @@ -125,7 +105,7 @@ stl_fix_normal_directions(stl_file *stl) { Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances of it being wrong randomly are low if most of the triangles are right: */ if (stl_check_normal_vector(stl, 0, 0) == 2) { - stl_reverse_facet(stl, 0); + reverse_facet(stl, 0); reversed_ids[reversed_count++] = 0; } @@ -144,12 +124,12 @@ stl_fix_normal_directions(stl_file *stl) { if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] == 1) { /* trying to modify a facet already marked as fixed, revert all changes made until now and exit (fixes: #716, #574, #413, #269, #262, #259, #230, #228, #206) */ for (id = reversed_count - 1; id >= 0; --id) { - stl_reverse_facet(stl, reversed_ids[id]); + reverse_facet(stl, reversed_ids[id]); } force_exit = 1; break; } else { - stl_reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]); + reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]); reversed_ids[reversed_count++] = stl->neighbors_start[facet_num].neighbor[j]; } } @@ -193,7 +173,7 @@ stl_fix_normal_directions(stl_file *stl) { /* This is the first facet of the next part. */ facet_num = i; if(stl_check_normal_vector(stl, i, 0) == 2) { - stl_reverse_facet(stl, i); + reverse_facet(stl, i); reversed_ids[reversed_count++] = i; } @@ -209,7 +189,8 @@ stl_fix_normal_directions(stl_file *stl) { delete tail; } -static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) { +static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) +{ /* Returns 0 if the normal is within tolerance */ /* Returns 1 if the normal is not within tolerance, but direction is OK */ /* Returns 2 if the normal is not within tolerance and backwards */ @@ -260,26 +241,19 @@ static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_ return 4; } -void stl_fix_normal_values(stl_file *stl) { - int i; - - if (stl->error) return; - - for(i = 0; i < stl->stats.number_of_facets; i++) { - stl_check_normal_vector(stl, i, 1); - } +void stl_fix_normal_values(stl_file *stl) +{ + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + stl_check_normal_vector(stl, i, 1); } void stl_reverse_all_facets(stl_file *stl) { - if (stl->error) - return; - - stl_normal normal; - for(int i = 0; i < stl->stats.number_of_facets; i++) { - stl_reverse_facet(stl, i); - stl_calculate_normal(normal, &stl->facet_start[i]); - stl_normalize_vector(normal); - stl->facet_start[i].normal = normal; - } + stl_normal normal; + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + reverse_facet(stl, i); + stl_calculate_normal(normal, &stl->facet_start[i]); + stl_normalize_vector(normal); + stl->facet_start[i].normal = normal; + } } diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index bc264ee96..a757d8816 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -38,15 +38,10 @@ void stl_invalidate_shared_vertices(stl_file *stl) void stl_generate_shared_vertices(stl_file *stl) { - if (stl->error) - return; - - /* make sure this function is idempotent and does not leak memory */ - stl_invalidate_shared_vertices(stl); - // 3 indices to vertex per face stl->v_indices.assign(stl->stats.number_of_facets, v_indices_struct()); // Shared vertices (3D coordinates) + stl->v_shared.clear(); stl->v_shared.reserve(stl->stats.number_of_facets / 2); stl->stats.shared_vertices = 0; @@ -139,11 +134,8 @@ void stl_generate_shared_vertices(stl_file *stl) } } -void stl_write_off(stl_file *stl, const char *file) +bool stl_write_off(stl_file *stl, const char *file) { - if (stl->error) - return; - /* Open the file */ FILE *fp = boost::nowide::fopen(file, "w"); if (fp == nullptr) { @@ -151,8 +143,7 @@ void stl_write_off(stl_file *stl, const char *file) sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + return false; } fprintf(fp, "OFF\n"); @@ -162,13 +153,11 @@ void stl_write_off(stl_file *stl, const char *file) for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); fclose(fp); + return true; } -void stl_write_vrml(stl_file *stl, const char *file) +bool stl_write_vrml(stl_file *stl, const char *file) { - if (stl->error) - return; - /* Open the file */ FILE *fp = boost::nowide::fopen(file, "w"); if (fp == nullptr) { @@ -176,8 +165,7 @@ void stl_write_vrml(stl_file *stl, const char *file) sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + return false; } fprintf(fp, "#VRML V1.0 ascii\n\n"); @@ -210,12 +198,11 @@ void stl_write_vrml(stl_file *stl, const char *file) fprintf(fp, "\t}\n"); fprintf(fp, "}\n"); fclose(fp); + return true; } -void stl_write_obj (stl_file *stl, const char *file) +bool stl_write_obj(stl_file *stl, const char *file) { - if (stl->error) - return; FILE *fp = boost::nowide::fopen(file, "w"); if (fp == nullptr) { @@ -223,8 +210,7 @@ void stl_write_obj (stl_file *stl, const char *file) sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + return false; } for (int i = 0; i < stl->stats.shared_vertices; ++ i) @@ -232,4 +218,5 @@ void stl_write_obj (stl_file *stl, const char *file) for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1); fclose(fp); + return true; } diff --git a/src/admesh/stl.h b/src/admesh/stl.h index bd49ad59f..51153ede7 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -127,15 +127,9 @@ struct stl_stats { int normals_fixed; int number_of_parts; int shared_vertices; - - // hash table statistics - int malloced; - int freed; - int collisions; }; struct stl_file { - FILE *fp; std::vector facet_start; std::vector neighbors_start; // Indexed face set @@ -143,17 +137,15 @@ struct stl_file { std::vector v_shared; // Statistics stl_stats stats; - char error; }; -extern void stl_open(stl_file *stl, const char *file); -extern void stl_close(stl_file *stl); +extern bool stl_open(stl_file *stl, const char *file); extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file); -extern void stl_print_neighbors(stl_file *stl, char *file); +extern bool stl_print_neighbors(stl_file *stl, char *file); extern void stl_put_little_int(FILE *fp, int value_in); extern void stl_put_little_float(FILE *fp, float value_in); -extern void stl_write_ascii(stl_file *stl, const char *file, const char *label); -extern void stl_write_binary(stl_file *stl, const char *file, const char *label); +extern bool stl_write_ascii(stl_file *stl, const char *file, const char *label); +extern bool stl_write_binary(stl_file *stl, const char *file, const char *label); extern void stl_write_binary_block(stl_file *stl, FILE *fp); extern void stl_check_facets_exact(stl_file *stl); extern void stl_check_facets_nearby(stl_file *stl, float tolerance); @@ -161,7 +153,7 @@ extern void stl_remove_unconnected_facets(stl_file *stl); extern void stl_write_vertex(stl_file *stl, int facet, int vertex); extern void stl_write_facet(stl_file *stl, char *label, int facet); extern void stl_write_neighbor(stl_file *stl, int facet); -extern void stl_write_quad_object(stl_file *stl, char *file); +extern bool stl_write_quad_object(stl_file *stl, char *file); extern void stl_verify_neighbors(stl_file *stl); extern void stl_fill_holes(stl_file *stl); extern void stl_fix_normal_directions(stl_file *stl); @@ -183,34 +175,28 @@ extern void stl_get_size(stl_file *stl); template extern void stl_transform(stl_file *stl, T *trafo3x4) { - if (stl->error) - return; + for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { + stl_facet &face = stl->facet_start[i_face]; + for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) { + stl_vertex &v_dst = face.vertex[i_vertex]; + stl_vertex v_src = v_dst; + v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]); + v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]); + v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]); + } + stl_vertex &v_dst = face.normal; + stl_vertex v_src = v_dst; + v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2)); + v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2)); + v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2)); + } - for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { - stl_facet &face = stl->facet_start[i_face]; - for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) { - stl_vertex &v_dst = face.vertex[i_vertex]; - stl_vertex v_src = v_dst; - v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]); - v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]); - v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]); - } - stl_vertex &v_dst = face.normal; - stl_vertex v_src = v_dst; - v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2)); - v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2)); - v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2)); - } - - stl_get_size(stl); + stl_get_size(stl); } template inline void stl_transform(stl_file *stl, const Eigen::Transform& t) { - if (stl->error) - return; - const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0); for (size_t i = 0; i < stl->stats.number_of_facets; ++i) { stl_facet &f = stl->facet_start[i]; @@ -225,9 +211,6 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform inline void stl_transform(stl_file *stl, const Eigen::Matrix& m) { - if (stl->error) - return; - for (size_t i = 0; i < stl->stats.number_of_facets; ++i) { stl_facet &f = stl->facet_start[i]; for (size_t j = 0; j < 3; ++j) @@ -238,13 +221,12 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrixvertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]); } @@ -263,17 +245,13 @@ extern void stl_calculate_volume(stl_file *stl); extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag); -extern void stl_initialize(stl_file *stl); -extern void stl_count_facets(stl_file *stl, const char *file); +extern void stl_reset(stl_file *stl); extern void stl_allocate(stl_file *stl); extern void stl_read(stl_file *stl, int first_facet, bool first); extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first); extern void stl_reallocate(stl_file *stl); extern void stl_add_facet(stl_file *stl, const stl_facet *new_facet); -extern void stl_clear_error(stl_file *stl); -extern int stl_get_error(stl_file *stl); -extern void stl_exit_on_error(stl_file *stl); // Validate the mesh, assert on error. extern bool stl_validate(stl_file *stl); diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp index 1e2e1479e..8f809d379 100644 --- a/src/admesh/stl_io.cpp +++ b/src/admesh/stl_io.cpp @@ -33,10 +33,8 @@ #define SEEK_END 2 #endif -void -stl_stats_out(stl_file *stl, FILE *file, char *input_file) { - if (stl->error) return; - +void stl_stats_out(stl_file *stl, FILE *file, char *input_file) +{ /* this is here for Slic3r, without our config.h it won't use this part of the code anyway */ #ifndef VERSION @@ -107,12 +105,10 @@ Backwards edges : %5d\n", stl->stats.backwards_edges); Normals fixed : %5d\n", stl->stats.normals_fixed); } -void -stl_write_ascii(stl_file *stl, const char *file, const char *label) { +bool stl_write_ascii(stl_file *stl, const char *file, const char *label) +{ char *error_msg; - if (stl->error) return; - /* Open the file */ FILE *fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { @@ -122,8 +118,7 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) { file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + return false; } fprintf(fp, "solid %s\n", label); @@ -149,15 +144,14 @@ stl_write_ascii(stl_file *stl, const char *file, const char *label) { fprintf(fp, "endsolid %s\n", label); fclose(fp); + return true; } -void -stl_print_neighbors(stl_file *stl, char *file) { +bool stl_print_neighbors(stl_file *stl, char *file) +{ FILE *fp; char *error_msg; - if (stl->error) return; - /* Open the file */ fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { @@ -167,8 +161,7 @@ stl_print_neighbors(stl_file *stl, char *file) { file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + return false; } for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { @@ -182,6 +175,7 @@ stl_print_neighbors(stl_file *stl, char *file) { (int)stl->neighbors_start[i].which_vertex_not[2]); } fclose(fp); + return true; } #ifndef BOOST_LITTLE_ENDIAN @@ -195,13 +189,11 @@ void stl_internal_reverse_quads(char *buf, size_t cnt) } #endif -void -stl_write_binary(stl_file *stl, const char *file, const char *label) { +bool stl_write_binary(stl_file *stl, const char *file, const char *label) +{ FILE *fp; char *error_msg; - if (stl->error) return; - /* Open the file */ fp = boost::nowide::fopen(file, "wb"); if(fp == NULL) { @@ -211,8 +203,7 @@ stl_write_binary(stl_file *stl, const char *file, const char *label) { file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + return false; } fprintf(fp, "%s", label); @@ -237,40 +228,38 @@ stl_write_binary(stl_file *stl, const char *file, const char *label) { } #endif /* BOOST_LITTLE_ENDIAN */ fclose(fp); + return true; } -void -stl_write_vertex(stl_file *stl, int facet, int vertex) { - if (stl->error) return; - printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet, +void stl_write_vertex(stl_file *stl, int facet, int vertex) +{ + printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet, stl->facet_start[facet].vertex[vertex](0), stl->facet_start[facet].vertex[vertex](1), stl->facet_start[facet].vertex[vertex](2)); } -void -stl_write_facet(stl_file *stl, char *label, int facet) { - if (stl->error) return; - printf("facet (%d)/ %s\n", facet, label); - stl_write_vertex(stl, facet, 0); - stl_write_vertex(stl, facet, 1); - stl_write_vertex(stl, facet, 2); +void stl_write_facet(stl_file *stl, char *label, int facet) +{ + printf("facet (%d)/ %s\n", facet, label); + stl_write_vertex(stl, facet, 0); + stl_write_vertex(stl, facet, 1); + stl_write_vertex(stl, facet, 2); } -void -stl_write_neighbor(stl_file *stl, int facet) { - if (stl->error) return; - printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet, - stl->neighbors_start[facet].neighbor[0], - stl->neighbors_start[facet].neighbor[1], - stl->neighbors_start[facet].neighbor[2], - stl->neighbors_start[facet].which_vertex_not[0], - stl->neighbors_start[facet].which_vertex_not[1], - stl->neighbors_start[facet].which_vertex_not[2]); +void stl_write_neighbor(stl_file *stl, int facet) +{ + printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet, + stl->neighbors_start[facet].neighbor[0], + stl->neighbors_start[facet].neighbor[1], + stl->neighbors_start[facet].neighbor[2], + stl->neighbors_start[facet].which_vertex_not[0], + stl->neighbors_start[facet].which_vertex_not[1], + stl->neighbors_start[facet].which_vertex_not[2]); } -void -stl_write_quad_object(stl_file *stl, char *file) { +bool stl_write_quad_object(stl_file *stl, char *file) +{ FILE *fp; char *error_msg; stl_vertex connect_color = stl_vertex::Zero(); @@ -279,8 +268,6 @@ stl_write_quad_object(stl_file *stl, char *file) { stl_vertex uncon_3_color = stl_vertex::Zero(); stl_vertex color; - if (stl->error) return; - /* Open the file */ fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { @@ -290,8 +277,7 @@ stl_write_quad_object(stl_file *stl, char *file) { file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + return false; } fprintf(fp, "CQUAD\n"); @@ -326,15 +312,14 @@ stl_write_quad_object(stl_file *stl, char *file) { stl->facet_start[i].vertex[2](2), color(0), color(1), color(2)); } fclose(fp); + return true; } -void stl_write_dxf(stl_file *stl, const char *file, char *label) +bool stl_write_dxf(stl_file *stl, const char *file, char *label) { FILE *fp; char *error_msg; - if (stl->error) return; - /* Open the file */ fp = boost::nowide::fopen(file, "w"); if(fp == NULL) { @@ -344,8 +329,7 @@ void stl_write_dxf(stl_file *stl, const char *file, char *label) file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + return false; } fprintf(fp, "999\n%s\n", label); @@ -375,22 +359,5 @@ void stl_write_dxf(stl_file *stl, const char *file, char *label) fprintf(fp, "0\nENDSEC\n0\nEOF\n"); fclose(fp); -} - -void -stl_clear_error(stl_file *stl) { - stl->error = 0; -} - -void -stl_exit_on_error(stl_file *stl) { - if (!stl->error) return; - stl->error = 0; - stl_close(stl); - exit(1); -} - -int -stl_get_error(stl_file *stl) { - return stl->error; + return true; } diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index b7ba21b77..088842d51 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -35,42 +35,8 @@ #error "SEEK_SET not defined" #endif -void stl_open(stl_file *stl, const char *file) +static FILE* stl_open_count_facets(stl_file *stl, const char *file) { - stl_initialize(stl); - stl_count_facets(stl, file); - stl_allocate(stl); - stl_read(stl, 0, true); - if (stl->fp != nullptr) { - fclose(stl->fp); - stl->fp = nullptr; - } -} - -void stl_initialize(stl_file *stl) -{ - stl->fp = nullptr; - stl->error = 0; - stl->facet_start.clear(); - stl->neighbors_start.clear(); - stl->v_indices.clear(); - stl->v_shared.clear(); - memset(&stl->stats, 0, sizeof(stl_stats)); - stl->stats.volume = -1.0; -} - -void stl_close(stl_file *stl) -{ - assert(stl->fp == nullptr); - stl_initialize(stl); -} - -#ifndef BOOST_LITTLE_ENDIAN -extern void stl_internal_reverse_quads(char *buf, size_t cnt); -#endif /* BOOST_LITTLE_ENDIAN */ - -void -stl_count_facets(stl_file *stl, const char *file) { long file_size; uint32_t header_num_facets; uint32_t num_facets; @@ -80,30 +46,27 @@ stl_count_facets(stl_file *stl, const char *file) { int num_lines = 1; char *error_msg; - if (stl->error) return; - /* Open the file in binary mode first */ - stl->fp = boost::nowide::fopen(file, "rb"); - if(stl->fp == NULL) { + FILE *fp = boost::nowide::fopen(file, "rb"); + if (fp == nullptr) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ sprintf(error_msg, "stl_initialize: Couldn't open %s for reading", file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + return nullptr; } /* Find size of file */ - fseek(stl->fp, 0, SEEK_END); - file_size = ftell(stl->fp); + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); /* Check for binary or ASCII file */ - fseek(stl->fp, HEADER_SIZE, SEEK_SET); - if (!fread(chtest, sizeof(chtest), 1, stl->fp)) { + fseek(fp, HEADER_SIZE, SEEK_SET); + if (!fread(chtest, sizeof(chtest), 1, fp)) { perror("The input is an empty file"); - stl->error = 1; - return; + fclose(fp); + return nullptr; } stl->stats.type = ascii; for(s = 0; s < sizeof(chtest); s++) { @@ -112,7 +75,7 @@ stl_count_facets(stl_file *stl, const char *file) { break; } } - rewind(stl->fp); + rewind(fp); /* Get the header and the number of facets in the .STL file */ /* If the .STL file is binary, then do the following */ @@ -121,18 +84,18 @@ stl_count_facets(stl_file *stl, const char *file) { if(((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0) || (file_size < STL_MIN_FILE_SIZE)) { fprintf(stderr, "The file %s has the wrong size.\n", file); - stl->error = 1; - return; + fclose(fp); + return nullptr; } num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET; /* Read the header */ - if (fread(stl->stats.header, LABEL_SIZE, 1, stl->fp) > 79) { + if (fread(stl->stats.header, LABEL_SIZE, 1, fp) > 79) { stl->stats.header[80] = '\0'; } /* Read the int following the header. This should contain # of facets */ - bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp) != 0; + bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, fp) != 0; #ifndef BOOST_LITTLE_ENDIAN // Convert from little endian to big endian. stl_internal_reverse_quads((char*)&header_num_facets, 4); @@ -147,23 +110,23 @@ stl_count_facets(stl_file *stl, const char *file) { /* Reopen the file in text mode (for getting correct newlines on Windows) */ // fix to silence a warning about unused return value. // obviously if it fails we have problems.... - stl->fp = boost::nowide::freopen(file, "r", stl->fp); + fp = boost::nowide::freopen(file, "r", fp); // do another null check to be safe - if(stl->fp == NULL) { + if(fp == nullptr) { error_msg = (char*) malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ sprintf(error_msg, "stl_initialize: Couldn't open %s for reading", file); perror(error_msg); free(error_msg); - stl->error = 1; - return; + fclose(fp); + return nullptr; } /* Find the number of facets */ char linebuf[100]; - while (fgets(linebuf, 100, stl->fp) != NULL) { + while (fgets(linebuf, 100, fp) != nullptr) { /* don't count short lines */ if (strlen(linebuf) <= 4) continue; @@ -173,11 +136,11 @@ stl_count_facets(stl_file *stl, const char *file) { ++num_lines; } - rewind(stl->fp); + rewind(fp); /* Get the header */ for(i = 0; - (i < 80) && (stl->stats.header[i] = getc(stl->fp)) != '\n'; i++); + (i < 80) && (stl->stats.header[i] = getc(fp)) != '\n'; i++); stl->stats.header[i] = '\0'; /* Lose the '\n' */ stl->stats.header[80] = '\0'; @@ -185,84 +148,20 @@ stl_count_facets(stl_file *stl, const char *file) { } stl->stats.number_of_facets += num_facets; stl->stats.original_num_facets = stl->stats.number_of_facets; + return fp; } -void stl_allocate(stl_file *stl) -{ - if (stl->error) - return; - // Allocate memory for the entire .STL file. - stl->facet_start.assign(stl->stats.number_of_facets, stl_facet()); - // Allocate memory for the neighbors list. - stl->neighbors_start.assign(stl->stats.number_of_facets, stl_neighbors()); -} - -void -stl_open_merge(stl_file *stl, char *file_to_merge) { - int num_facets_so_far; - stl_type origStlType; - FILE *origFp; - stl_file stl_to_merge; - - if (stl->error) return; - - /* Record how many facets we have so far from the first file. We will start putting - facets in the next position. Since we're 0-indexed, it'l be the same position. */ - num_facets_so_far = stl->stats.number_of_facets; - - /* Record the file type we started with: */ - origStlType=stl->stats.type; - /* Record the file pointer too: */ - origFp=stl->fp; - - /* Initialize the sturucture with zero stats, header info and sizes: */ - stl_initialize(&stl_to_merge); - stl_count_facets(&stl_to_merge, file_to_merge); - - /* Copy what we need to into stl so that we can read the file_to_merge directly into it - using stl_read: Save the rest of the valuable info: */ - stl->stats.type=stl_to_merge.stats.type; - stl->fp=stl_to_merge.fp; - - /* Add the number of facets we already have in stl with what we we found in stl_to_merge but - haven't read yet. */ - stl->stats.number_of_facets=num_facets_so_far+stl_to_merge.stats.number_of_facets; - - /* Allocate enough room for stl->stats.number_of_facets facets and neighbors: */ - stl_reallocate(stl); - - /* Read the file to merge directly into stl, adding it to what we have already. - Start at num_facets_so_far, the index to the first unused facet. Also say - that this isn't our first time so we should augment stats like min and max - instead of erasing them. */ - stl_read(stl, num_facets_so_far, false); - - /* Restore the stl information we overwrote (for stl_read) so that it still accurately - reflects the subject part: */ - stl->stats.type=origStlType; - stl->fp=origFp; -} - -void stl_reallocate(stl_file *stl) -{ - if (stl->error) - return; - stl->facet_start.resize(stl->stats.number_of_facets); - stl->neighbors_start.resize(stl->stats.number_of_facets); -} - -/* Reads the contents of the file pointed to by stl->fp into the stl structure, +/* Reads the contents of the file pointed to by fp into the stl structure, starting at facet first_facet. The second argument says if it's our first time running this for the stl and therefore we should reset our max and min stats. */ -void stl_read(stl_file *stl, int first_facet, bool first) { +static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first) +{ stl_facet facet; - if (stl->error) return; - if(stl->stats.type == binary) { - fseek(stl->fp, HEADER_SIZE, SEEK_SET); + fseek(fp, HEADER_SIZE, SEEK_SET); } else { - rewind(stl->fp); + rewind(fp); } char normal_buf[3][32]; @@ -271,10 +170,8 @@ void stl_read(stl_file *stl, int first_facet, bool first) { /* Read a single facet from a binary .STL file */ { /* we assume little-endian architecture! */ - if (fread(&facet, 1, SIZEOF_STL_FACET, stl->fp) != SIZEOF_STL_FACET) { - stl->error = 1; - return; - } + if (fread(&facet, 1, SIZEOF_STL_FACET, fp) != SIZEOF_STL_FACET) + return false; #ifndef BOOST_LITTLE_ENDIAN // Convert the loaded little endian data to big endian. stl_internal_reverse_quads((char*)&facet, 48); @@ -284,27 +181,26 @@ void stl_read(stl_file *stl, int first_facet, bool first) { { // skip solid/endsolid // (in this order, otherwise it won't work when they are paired in the middle of a file) - fscanf(stl->fp, "endsolid%*[^\n]\n"); - fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid") + fscanf(fp, "endsolid%*[^\n]\n"); + fscanf(fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid") // Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs. - int res_normal = fscanf(stl->fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]); + int res_normal = fscanf(fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]); assert(res_normal == 3); - int res_outer_loop = fscanf(stl->fp, " outer loop"); + int res_outer_loop = fscanf(fp, " outer loop"); assert(res_outer_loop == 0); - int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2)); + int res_vertex1 = fscanf(fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2)); assert(res_vertex1 == 3); - int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2)); + int res_vertex2 = fscanf(fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2)); assert(res_vertex2 == 3); - int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2)); + int res_vertex3 = fscanf(fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2)); assert(res_vertex3 == 3); - int res_endloop = fscanf(stl->fp, " endloop"); + int res_endloop = fscanf(fp, " endloop"); assert(res_endloop == 0); // There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines. - int res_endfacet = fscanf(stl->fp, " endfacet "); + int res_endfacet = fscanf(fp, " endfacet "); if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) { perror("Something is syntactically very wrong with this ASCII STL!"); - stl->error = 1; - return; + return false; } // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. @@ -338,13 +234,51 @@ void stl_read(stl_file *stl, int first_facet, bool first) { } stl->stats.size = stl->stats.max - stl->stats.min; stl->stats.bounding_diameter = stl->stats.size.norm(); + return true; +} + +bool stl_open(stl_file *stl, const char *file) +{ + stl_reset(stl); + FILE *fp = stl_open_count_facets(stl, file); + if (fp == nullptr) + return false; + stl_allocate(stl); + bool result = stl_read(stl, fp, 0, true); + fclose(fp); + return result; +} + +void stl_reset(stl_file *stl) +{ + stl->facet_start.clear(); + stl->neighbors_start.clear(); + stl->v_indices.clear(); + stl->v_shared.clear(); + memset(&stl->stats, 0, sizeof(stl_stats)); + stl->stats.volume = -1.0; +} + +#ifndef BOOST_LITTLE_ENDIAN +extern void stl_internal_reverse_quads(char *buf, size_t cnt); +#endif /* BOOST_LITTLE_ENDIAN */ + +void stl_allocate(stl_file *stl) +{ + // Allocate memory for the entire .STL file. + stl->facet_start.assign(stl->stats.number_of_facets, stl_facet()); + // Allocate memory for the neighbors list. + stl->neighbors_start.assign(stl->stats.number_of_facets, stl_neighbors()); +} + +void stl_reallocate(stl_file *stl) +{ + stl->facet_start.resize(stl->stats.number_of_facets); + stl->neighbors_start.resize(stl->stats.number_of_facets); } void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first) { - if (stl->error) - return; - // While we are going through all of the facets, let's find the // maximum and minimum values for x, y, and z diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index 91293d048..db05cbc09 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -34,9 +34,6 @@ static float get_volume(stl_file *stl); void stl_verify_neighbors(stl_file *stl) { - if (stl->error) - return; - stl->stats.backwards_edges = 0; for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { @@ -69,9 +66,6 @@ void stl_verify_neighbors(stl_file *stl) void stl_translate(stl_file *stl, float x, float y, float z) { - if (stl->error) - return; - stl_vertex new_min(x, y, z); stl_vertex shift = new_min - stl->stats.min; for (int i = 0; i < stl->stats.number_of_facets; ++ i) @@ -85,9 +79,6 @@ void stl_translate(stl_file *stl, float x, float y, float z) /* Translates the stl by x,y,z, relatively from wherever it is currently */ void stl_translate_relative(stl_file *stl, float x, float y, float z) { - if (stl->error) - return; - stl_vertex shift(x, y, z); for (int i = 0; i < stl->stats.number_of_facets; ++ i) for (int j = 0; j < 3; ++ j) @@ -99,9 +90,6 @@ void stl_translate_relative(stl_file *stl, float x, float y, float z) void stl_scale_versor(stl_file *stl, const stl_vertex &versor) { - if (stl->error) - return; - // Scale extents. auto s = versor.array(); stl->stats.min.array() *= s; @@ -120,9 +108,6 @@ void stl_scale_versor(stl_file *stl, const stl_vertex &versor) static void calculate_normals(stl_file *stl) { - if (stl->error) - return; - stl_normal normal; for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) { stl_calculate_normal(normal, &stl->facet_start[i]); @@ -139,8 +124,6 @@ stl_rotate_x(stl_file *stl, float angle) { double c = cos(radian_angle); double s = sin(radian_angle); - if (stl->error) return; - for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { stl_rotate(&stl->facet_start[i].vertex[j](1), @@ -159,8 +142,6 @@ stl_rotate_y(stl_file *stl, float angle) { double c = cos(radian_angle); double s = sin(radian_angle); - if (stl->error) return; - for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { stl_rotate(&stl->facet_start[i].vertex[j](2), @@ -179,8 +160,6 @@ stl_rotate_z(stl_file *stl, float angle) { double c = cos(radian_angle); double s = sin(radian_angle); - if (stl->error) return; - for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { stl_rotate(&stl->facet_start[i].vertex[j](0), @@ -203,7 +182,7 @@ stl_rotate(float *x, float *y, const double c, const double s) { void stl_get_size(stl_file *stl) { - if (stl->error || stl->stats.number_of_facets == 0) + if (stl->stats.number_of_facets == 0) return; stl->stats.min = stl->facet_start[0].vertex[0]; stl->stats.max = stl->stats.min; @@ -220,9 +199,6 @@ void stl_get_size(stl_file *stl) void stl_mirror_xy(stl_file *stl) { - if (stl->error) - return; - for(int i = 0; i < stl->stats.number_of_facets; i++) { for(int j = 0; j < 3; j++) { stl->facet_start[i].vertex[j](2) *= -1.0; @@ -239,8 +215,6 @@ void stl_mirror_xy(stl_file *stl) void stl_mirror_yz(stl_file *stl) { - if (stl->error) return; - for (int i = 0; i < stl->stats.number_of_facets; i++) { for (int j = 0; j < 3; j++) { stl->facet_start[i].vertex[j](0) *= -1.0; @@ -257,9 +231,6 @@ void stl_mirror_yz(stl_file *stl) void stl_mirror_xz(stl_file *stl) { - if (stl->error) - return; - for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) for (int j = 0; j < 3; ++ j) stl->facet_start[i].vertex[j](1) *= -1.0; @@ -274,9 +245,6 @@ void stl_mirror_xz(stl_file *stl) static float get_volume(stl_file *stl) { - if (stl->error) - return 0; - // Choose a point, any point as the reference. stl_vertex p0 = stl->facet_start[0].vertex[0]; float volume = 0.f; @@ -291,7 +259,6 @@ static float get_volume(stl_file *stl) void stl_calculate_volume(stl_file *stl) { - if (stl->error) return; stl->stats.volume = get_volume(stl); if(stl->stats.volume < 0.0) { stl_reverse_all_facets(stl); @@ -346,8 +313,6 @@ void stl_repair(stl_file *stl, int i; int last_edges_fixed = 0; - if (stl->error) return; - if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag || fill_holes_flag || normal_directions_flag) { if (verbose_flag) @@ -453,8 +418,6 @@ All facets connected. No further nearby check necessary.\n"); // Check validity of the mesh, assert on error. bool stl_validate(stl_file *stl) { - assert(! stl->error); - assert(stl->fp == nullptr); assert(! stl->facet_start.empty()); assert(stl->facet_start.size() == stl->stats.number_of_facets); assert(stl->neighbors_start.size() == stl->stats.number_of_facets); diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index d983b1098..03ea71a83 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -161,7 +161,6 @@ static void extract_model_from_archive( else { // Header has been extracted. Now read the faces. stl_file &stl = mesh.stl; - stl.error = 0; stl.stats.type = inmemory; stl.stats.number_of_facets = header.nTriangles; stl.stats.original_num_facets = header.nTriangles; diff --git a/src/libslic3r/Format/STL.cpp b/src/libslic3r/Format/STL.cpp index b00623d1d..932906fe0 100644 --- a/src/libslic3r/Format/STL.cpp +++ b/src/libslic3r/Format/STL.cpp @@ -17,8 +17,7 @@ namespace Slic3r { bool load_stl(const char *path, Model *model, const char *object_name_in) { TriangleMesh mesh; - mesh.ReadSTLFile(path); - if (mesh.stl.error) { + if (! mesh.ReadSTLFile(path)) { // die "Failed to open $file\n" if !-e $path; return false; } diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index ad12047d2..27163eb57 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -45,7 +45,7 @@ namespace Slic3r { TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& facets) : repaired(false) { - stl_initialize(&this->stl); + stl_reset(&this->stl); stl_file &stl = this->stl; stl.stats.type = inmemory; @@ -74,7 +74,7 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other) { - stl_close(&this->stl); + stl_reset(&this->stl); this->stl = other.stl; this->repaired = other.repaired; return *this; @@ -426,7 +426,6 @@ TriangleMeshPtrs TriangleMesh::split() const mesh->stl.stats.type = inmemory; mesh->stl.stats.number_of_facets = (uint32_t)facets.size(); mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets; - stl_clear_error(&mesh->stl); stl_allocate(&mesh->stl); // Assign the facets to the new mesh. diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 4de1f5989..c16f4a664 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -21,18 +21,18 @@ typedef std::vector TriangleMeshPtrs; class TriangleMesh { public: - TriangleMesh() : repaired(false) { stl_initialize(&this->stl); } + TriangleMesh() : repaired(false) { stl_reset(&this->stl); } TriangleMesh(const Pointf3s &points, const std::vector &facets); - TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_initialize(&this->stl); *this = other; } - TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_initialize(&this->stl); this->swap(other); } + TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_reset(&this->stl); *this = other; } + TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_reset(&this->stl); this->swap(other); } ~TriangleMesh() { clear(); } TriangleMesh& operator=(const TriangleMesh &other); TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; } - void clear() { stl_close(&this->stl); this->repaired = false; } + void clear() { stl_reset(&this->stl); this->repaired = false; } void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); } - void ReadSTLFile(const char* input_file) { stl_open(&stl, input_file); } - void write_ascii(const char* output_file) { stl_write_ascii(&this->stl, output_file, ""); } - void write_binary(const char* output_file) { stl_write_binary(&this->stl, output_file, ""); } + bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); } + bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); } + bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); } void repair(); float volume(); void check_topology(); diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index a188e0b8d..3cfbaa54d 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -46,7 +46,6 @@ TriangleMesh::ReadFromPerl(vertices, facets) SV* facets CODE: stl_file &stl = THIS->stl; - stl.error = 0; stl.stats.type = inmemory; // count facets and allocate memory From 65238a89b10fb18e7f9b647f5026da9276d32d58 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Jun 2019 17:36:15 +0200 Subject: [PATCH 16/44] admesh refactoring: Removed the shared_vertices counter as it is now contained inside v_shared std::vector --- src/admesh/shared.cpp | 14 +++++--------- src/admesh/stl.h | 2 -- src/admesh/util.cpp | 1 - src/libslic3r/Format/3mf.cpp | 6 +++--- src/libslic3r/Format/AMF.cpp | 4 ++-- src/libslic3r/Model.cpp | 2 +- src/libslic3r/TriangleMesh.cpp | 4 ++-- xs/xsp/TriangleMesh.xsp | 4 ++-- 8 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index a757d8816..e9f075498 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -33,7 +33,6 @@ void stl_invalidate_shared_vertices(stl_file *stl) { stl->v_indices.clear(); stl->v_shared.clear(); - stl->stats.shared_vertices = 0; } void stl_generate_shared_vertices(stl_file *stl) @@ -43,7 +42,6 @@ void stl_generate_shared_vertices(stl_file *stl) // Shared vertices (3D coordinates) stl->v_shared.clear(); stl->v_shared.reserve(stl->stats.number_of_facets / 2); - stl->stats.shared_vertices = 0; // A degenerate mesh may contain loops: Traversing a fan will end up in an endless loop // while never reaching the starting face. To avoid these endless loops, traversed faces at each fan traversal @@ -91,7 +89,7 @@ void stl_generate_shared_vertices(stl_file *stl) next_edge = pivot_vertex; } } - stl->v_indices[facet_in_fan_idx].vertex[pivot_vertex] = stl->stats.shared_vertices; + stl->v_indices[facet_in_fan_idx].vertex[pivot_vertex] = stl->v_shared.size() - 1; fan_traversal_facet_visited[facet_in_fan_idx] = fan_traversal_stamp; // next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge! @@ -128,8 +126,6 @@ void stl_generate_shared_vertices(stl_file *stl) facet_in_fan_idx = next_facet; } } - - ++ stl->stats.shared_vertices; } } } @@ -147,8 +143,8 @@ bool stl_write_off(stl_file *stl, const char *file) } fprintf(fp, "OFF\n"); - fprintf(fp, "%d %d 0\n", stl->stats.shared_vertices, stl->stats.number_of_facets); - for (int i = 0; i < stl->stats.shared_vertices; ++ i) + fprintf(fp, "%d %d 0\n", stl->v_shared.size(), stl->stats.number_of_facets); + for (int i = 0; i < stl->v_shared.size(); ++ i) fprintf(fp, "\t%f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); @@ -184,7 +180,7 @@ bool stl_write_vrml(stl_file *stl, const char *file) fprintf(fp, "\t\t\tpoint [\n"); int i = 0; - for (; i < (stl->stats.shared_vertices - 1); i++) + for (; i + 1 < stl->v_shared.size(); ++ i) fprintf(fp, "\t\t\t\t%f %f %f,\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); fprintf(fp, "\t\t\t\t%f %f %f]\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); fprintf(fp, "\t\t}\n"); @@ -213,7 +209,7 @@ bool stl_write_obj(stl_file *stl, const char *file) return false; } - for (int i = 0; i < stl->stats.shared_vertices; ++ i) + for (size_t i = 0; i < stl->v_shared.size(); ++ i) fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1); diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 51153ede7..4aee6048f 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -109,7 +109,6 @@ struct stl_stats { float bounding_diameter; float shortest_edge; float volume; - unsigned number_of_blocks; int connected_edges; int connected_facets_1_edge; int connected_facets_2_edge; @@ -126,7 +125,6 @@ struct stl_stats { int backwards_edges; int normals_fixed; int number_of_parts; - int shared_vertices; }; struct stl_file { diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index db05cbc09..685b641b4 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -425,7 +425,6 @@ bool stl_validate(stl_file *stl) assert(! stl->neighbors_start.empty()); assert((stl->v_indices.empty()) == (stl->v_shared.empty())); assert(stl->stats.number_of_facets > 0); - assert(stl->v_shared.size() == stl->stats.shared_vertices); assert(stl->v_shared.empty() || stl->v_indices.size() == stl->stats.number_of_facets); #ifdef _DEBUG diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index c3916a14e..d67106656 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1888,17 +1888,17 @@ namespace Slic3r { if (stl.v_shared.empty()) stl_generate_shared_vertices(&stl); - if (stl.stats.shared_vertices == 0) + if (stl.v_shared.empty()) { add_error("Found invalid mesh"); return false; } - vertices_count += stl.stats.shared_vertices; + vertices_count += stl.v_shared.size(); const Transform3d& matrix = volume->get_matrix(); - for (int i = 0; i < stl.stats.shared_vertices; ++i) + for (size_t i = 0; i < stl.v_shared.size(); ++i) { stream << " <" << VERTEX_TAG << " "; Vec3f v = (matrix * stl.v_shared[i].cast()).cast(); diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index f48b5b58c..e81ced3ad 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -929,7 +929,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) if (stl.v_shared.empty()) stl_generate_shared_vertices(&stl); const Transform3d& matrix = volume->get_matrix(); - for (size_t i = 0; i < stl.stats.shared_vertices; ++i) { + for (size_t i = 0; i < stl.v_shared.size(); ++i) { stream << " \n"; stream << " \n"; Vec3f v = (matrix * stl.v_shared[i].cast()).cast(); @@ -939,7 +939,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << " \n"; stream << " \n"; } - num_vertices += stl.stats.shared_vertices; + num_vertices += stl.v_shared.size(); } stream << " \n"; for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) { diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 64fbb9a2a..6da7fc0c3 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -919,7 +919,7 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const } } else { // Using the shared vertices should be a bit quicker than using the STL faces. - for (int i = 0; i < stl.stats.shared_vertices; ++ i) { + for (size_t i = 0; i < stl.v_shared.size(); ++ i) { Vec3d p = trafo * stl.v_shared[i].cast(); pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); } diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 27163eb57..003affc27 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -484,8 +484,8 @@ Polygon TriangleMesh::convex_hull() { this->require_shared_vertices(); Points pp; - pp.reserve(this->stl.stats.shared_vertices); - for (int i = 0; i < this->stl.stats.shared_vertices; ++ i) { + pp.reserve(this->stl.v_shared.size()); + for (size_t i = 0; i < this->stl.v_shared.size(); ++ i) { const stl_vertex &v = this->stl.v_shared[i]; pp.emplace_back(Point::new_scale(v(0), v(1))); } diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 3cfbaa54d..53b052027 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -104,8 +104,8 @@ TriangleMesh::vertices() // vertices AV* vertices = newAV(); - av_extend(vertices, THIS->stl.stats.shared_vertices); - for (int i = 0; i < THIS->stl.stats.shared_vertices; i++) { + av_extend(vertices, THIS->stl.v_shared.size()); + for (size_t i = 0; i < THIS->stl.v_shared.size(); i++) { AV* vertex = newAV(); av_store(vertices, i, newRV_noinc((SV*)vertex)); av_extend(vertex, 2); From 6defabea537ba871070fc405a14c2173cb92fc89 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Jun 2019 18:30:54 +0200 Subject: [PATCH 17/44] admesh refactoring: separation of the shared vertices / indices into an indexed_triangle_set structure --- src/admesh/shared.cpp | 111 +++++++++++++++++++++++--------- src/admesh/stl.h | 26 ++++---- src/admesh/stlinit.cpp | 2 - src/admesh/util.cpp | 50 -------------- src/libslic3r/Format/3mf.cpp | 24 +++---- src/libslic3r/Format/AMF.cpp | 17 +++-- src/libslic3r/Model.cpp | 9 +-- src/libslic3r/TriangleMesh.cpp | 46 ++++++------- src/libslic3r/TriangleMesh.hpp | 3 +- src/slic3r/GUI/PresetBundle.cpp | 4 +- xs/xsp/TriangleMesh.xsp | 24 +++---- 11 files changed, 154 insertions(+), 162 deletions(-) diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index e9f075498..7da2841b0 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -29,19 +29,13 @@ #include "stl.h" -void stl_invalidate_shared_vertices(stl_file *stl) -{ - stl->v_indices.clear(); - stl->v_shared.clear(); -} - -void stl_generate_shared_vertices(stl_file *stl) +void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its) { // 3 indices to vertex per face - stl->v_indices.assign(stl->stats.number_of_facets, v_indices_struct()); + its.indices.assign(stl->stats.number_of_facets, v_indices_struct()); // Shared vertices (3D coordinates) - stl->v_shared.clear(); - stl->v_shared.reserve(stl->stats.number_of_facets / 2); + its.vertices.clear(); + its.vertices.reserve(stl->stats.number_of_facets / 2); // A degenerate mesh may contain loops: Traversing a fan will end up in an endless loop // while never reaching the starting face. To avoid these endless loops, traversed faces at each fan traversal @@ -51,11 +45,11 @@ void stl_generate_shared_vertices(stl_file *stl) for (uint32_t facet_idx = 0; facet_idx < stl->stats.number_of_facets; ++ facet_idx) { for (int j = 0; j < 3; ++ j) { - if (stl->v_indices[facet_idx].vertex[j] != -1) + if (its.indices[facet_idx].vertex[j] != -1) // Shared vertex was already assigned. continue; // Create a new shared vertex. - stl->v_shared.emplace_back(stl->facet_start[facet_idx].vertex[j]); + its.vertices.emplace_back(stl->facet_start[facet_idx].vertex[j]); // Traverse the fan around the j-th vertex of the i-th face, assign the newly created shared vertex index to all the neighboring triangles in the triangle fan. int facet_in_fan_idx = facet_idx; bool edge_direction = false; @@ -89,7 +83,7 @@ void stl_generate_shared_vertices(stl_file *stl) next_edge = pivot_vertex; } } - stl->v_indices[facet_in_fan_idx].vertex[pivot_vertex] = stl->v_shared.size() - 1; + its.indices[facet_in_fan_idx].vertex[pivot_vertex] = its.vertices.size() - 1; fan_traversal_facet_visited[facet_in_fan_idx] = fan_traversal_stamp; // next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge! @@ -130,7 +124,7 @@ void stl_generate_shared_vertices(stl_file *stl) } } -bool stl_write_off(stl_file *stl, const char *file) +bool its_write_off(const indexed_triangle_set &its, const char *file) { /* Open the file */ FILE *fp = boost::nowide::fopen(file, "w"); @@ -143,16 +137,16 @@ bool stl_write_off(stl_file *stl, const char *file) } fprintf(fp, "OFF\n"); - fprintf(fp, "%d %d 0\n", stl->v_shared.size(), stl->stats.number_of_facets); - for (int i = 0; i < stl->v_shared.size(); ++ i) - fprintf(fp, "\t%f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); - for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) - fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); + fprintf(fp, "%d %d 0\n", (int)its.vertices.size(), (int)its.indices.size()); + for (int i = 0; i < its.vertices.size(); ++ i) + fprintf(fp, "\t%f %f %f\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2)); + for (uint32_t i = 0; i < its.indices.size(); ++ i) + fprintf(fp, "\t3 %d %d %d\n", its.indices[i].vertex[0], its.indices[i].vertex[1], its.indices[i].vertex[2]); fclose(fp); return true; } -bool stl_write_vrml(stl_file *stl, const char *file) +bool its_write_vrml(const indexed_triangle_set &its, const char *file) { /* Open the file */ FILE *fp = boost::nowide::fopen(file, "w"); @@ -180,16 +174,16 @@ bool stl_write_vrml(stl_file *stl, const char *file) fprintf(fp, "\t\t\tpoint [\n"); int i = 0; - for (; i + 1 < stl->v_shared.size(); ++ i) - fprintf(fp, "\t\t\t\t%f %f %f,\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); - fprintf(fp, "\t\t\t\t%f %f %f]\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); + for (; i + 1 < its.vertices.size(); ++ i) + fprintf(fp, "\t\t\t\t%f %f %f,\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2)); + fprintf(fp, "\t\t\t\t%f %f %f]\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2)); fprintf(fp, "\t\t}\n"); fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n"); fprintf(fp, "\t\t\tcoordIndex [\n"); - for (int i = 0; i + 1 < (int)stl->stats.number_of_facets; ++ i) - fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); - fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0], stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]); + for (size_t i = 0; i + 1 < its.indices.size(); ++ i) + fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", its.indices[i].vertex[0], its.indices[i].vertex[1], its.indices[i].vertex[2]); + fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", its.indices[i].vertex[0], its.indices[i].vertex[1], its.indices[i].vertex[2]); fprintf(fp, "\t\t}\n"); fprintf(fp, "\t}\n"); fprintf(fp, "}\n"); @@ -197,7 +191,7 @@ bool stl_write_vrml(stl_file *stl, const char *file) return true; } -bool stl_write_obj(stl_file *stl, const char *file) +bool its_write_obj(const indexed_triangle_set &its, const char *file) { FILE *fp = boost::nowide::fopen(file, "w"); @@ -209,10 +203,65 @@ bool stl_write_obj(stl_file *stl, const char *file) return false; } - for (size_t i = 0; i < stl->v_shared.size(); ++ i) - fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2)); - for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) - fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1); + for (size_t i = 0; i < its.vertices.size(); ++ i) + fprintf(fp, "v %f %f %f\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2)); + for (size_t i = 0; i < its.indices.size(); ++ i) + fprintf(fp, "f %d %d %d\n", its.indices[i].vertex[0]+1, its.indices[i].vertex[1]+1, its.indices[i].vertex[2]+1); fclose(fp); return true; } + + +// Check validity of the mesh, assert on error. +bool stl_validate(const stl_file *stl, const indexed_triangle_set &its) +{ + assert(! stl->facet_start.empty()); + assert(stl->facet_start.size() == stl->stats.number_of_facets); + assert(stl->neighbors_start.size() == stl->stats.number_of_facets); + assert(stl->facet_start.size() == stl->neighbors_start.size()); + assert(! stl->neighbors_start.empty()); + assert((its.indices.empty()) == (its.vertices.empty())); + assert(stl->stats.number_of_facets > 0); + assert(its.vertices.empty() || its.indices.size() == stl->stats.number_of_facets); + +#ifdef _DEBUG + // Verify validity of neighborship data. + for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) { + const stl_neighbors &nbr = stl->neighbors_start[facet_idx]; + const int *vertices = (its.indices.empty()) ? nullptr : its.indices[facet_idx].vertex; + for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) { + int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx]; + assert(nbr_face < (int)stl->stats.number_of_facets); + if (nbr_face != -1) { + int nbr_vnot = nbr.which_vertex_not[nbr_idx]; + assert(nbr_vnot >= 0 && nbr_vnot < 6); + // Neighbor of the neighbor is the original face. + assert(stl->neighbors_start[nbr_face].neighbor[(nbr_vnot + 1) % 3] == facet_idx); + int vnot_back = stl->neighbors_start[nbr_face].which_vertex_not[(nbr_vnot + 1) % 3]; + assert(vnot_back >= 0 && vnot_back < 6); + assert((nbr_vnot < 3) == (vnot_back < 3)); + assert(vnot_back % 3 == (nbr_idx + 2) % 3); + if (vertices != nullptr) { + // Has shared vertices. + if (nbr_vnot < 3) { + // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are correctly oriented. + assert((its.indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[nbr_idx])); + } else { + // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped. + assert((its.indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[nbr_idx])); + } + } + } + } + } +#endif /* _DEBUG */ + + return true; +} + +// Check validity of the mesh, assert on error. +bool stl_validate(const stl_file *stl) +{ + indexed_triangle_set its; + return stl_validate(stl, its); +} diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 4aee6048f..bb5d25296 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -130,21 +130,22 @@ struct stl_stats { struct stl_file { std::vector facet_start; std::vector neighbors_start; - // Indexed face set - std::vector v_indices; - std::vector v_shared; // Statistics stl_stats stats; }; +struct indexed_triangle_set +{ + void clear() { indices.clear(); vertices.clear(); } + std::vector indices; + std::vector vertices; +}; + extern bool stl_open(stl_file *stl, const char *file); extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file); extern bool stl_print_neighbors(stl_file *stl, char *file); -extern void stl_put_little_int(FILE *fp, int value_in); -extern void stl_put_little_float(FILE *fp, float value_in); extern bool stl_write_ascii(stl_file *stl, const char *file, const char *label); extern bool stl_write_binary(stl_file *stl, const char *file, const char *label); -extern void stl_write_binary_block(stl_file *stl, FILE *fp); extern void stl_check_facets_exact(stl_file *stl); extern void stl_check_facets_nearby(stl_file *stl, float tolerance); extern void stl_remove_unconnected_facets(stl_file *stl); @@ -219,12 +220,12 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrixvertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]); } @@ -251,6 +252,7 @@ extern void stl_reallocate(stl_file *stl); extern void stl_add_facet(stl_file *stl, const stl_facet *new_facet); // Validate the mesh, assert on error. -extern bool stl_validate(stl_file *stl); +extern bool stl_validate(const stl_file *stl); +extern bool stl_validate(const stl_file *stl, const indexed_triangle_set &its); #endif diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index 088842d51..44477511f 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -253,8 +253,6 @@ void stl_reset(stl_file *stl) { stl->facet_start.clear(); stl->neighbors_start.clear(); - stl->v_indices.clear(); - stl->v_shared.clear(); memset(&stl->stats, 0, sizeof(stl_stats)); stl->stats.volume = -1.0; } diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index 685b641b4..f4e4dbf0a 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -73,7 +73,6 @@ void stl_translate(stl_file *stl, float x, float y, float z) stl->facet_start[i].vertex[j] += shift; stl->stats.min = new_min; stl->stats.max += shift; - stl_invalidate_shared_vertices(stl); } /* Translates the stl by x,y,z, relatively from wherever it is currently */ @@ -85,7 +84,6 @@ void stl_translate_relative(stl_file *stl, float x, float y, float z) stl->facet_start[i].vertex[j] += shift; stl->stats.min += shift; stl->stats.max += shift; - stl_invalidate_shared_vertices(stl); } void stl_scale_versor(stl_file *stl, const stl_vertex &versor) @@ -103,7 +101,6 @@ void stl_scale_versor(stl_file *stl, const stl_vertex &versor) for (int i = 0; i < stl->stats.number_of_facets; ++ i) for (int j = 0; j < 3; ++ j) stl->facet_start[i].vertex[j].array() *= s; - stl_invalidate_shared_vertices(stl); } static void calculate_normals(stl_file *stl) @@ -414,50 +411,3 @@ All facets connected. No further nearby check necessary.\n"); stl_verify_neighbors(stl); } } - -// Check validity of the mesh, assert on error. -bool stl_validate(stl_file *stl) -{ - assert(! stl->facet_start.empty()); - assert(stl->facet_start.size() == stl->stats.number_of_facets); - assert(stl->neighbors_start.size() == stl->stats.number_of_facets); - assert(stl->facet_start.size() == stl->neighbors_start.size()); - assert(! stl->neighbors_start.empty()); - assert((stl->v_indices.empty()) == (stl->v_shared.empty())); - assert(stl->stats.number_of_facets > 0); - assert(stl->v_shared.empty() || stl->v_indices.size() == stl->stats.number_of_facets); - -#ifdef _DEBUG - // Verify validity of neighborship data. - for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) { - const stl_neighbors &nbr = stl->neighbors_start[facet_idx]; - const int *vertices = (stl->v_indices.empty()) ? nullptr : stl->v_indices[facet_idx].vertex; - for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) { - int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx]; - assert(nbr_face < (int)stl->stats.number_of_facets); - if (nbr_face != -1) { - int nbr_vnot = nbr.which_vertex_not[nbr_idx]; - assert(nbr_vnot >= 0 && nbr_vnot < 6); - // Neighbor of the neighbor is the original face. - assert(stl->neighbors_start[nbr_face].neighbor[(nbr_vnot + 1) % 3] == facet_idx); - int vnot_back = stl->neighbors_start[nbr_face].which_vertex_not[(nbr_vnot + 1) % 3]; - assert(vnot_back >= 0 && vnot_back < 6); - assert((nbr_vnot < 3) == (vnot_back < 3)); - assert(vnot_back % 3 == (nbr_idx + 2) % 3); - if (vertices != nullptr) { - // Has shared vertices. - if (nbr_vnot < 3) { - // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are correctly oriented. - assert((stl->v_indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[(nbr_idx + 1) % 3] && stl->v_indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[nbr_idx])); - } else { - // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped. - assert((stl->v_indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[(nbr_idx + 1) % 3] && stl->v_indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[nbr_idx])); - } - } - } - } - } -#endif /* _DEBUG */ - - return true; -} diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index d67106656..8298fe222 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1881,27 +1881,23 @@ namespace Slic3r { volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first; - if (!volume->mesh.repaired) - volume->mesh.repair(); + volume->mesh.require_shared_vertices(); - stl_file& stl = volume->mesh.stl; - if (stl.v_shared.empty()) - stl_generate_shared_vertices(&stl); - - if (stl.v_shared.empty()) + const indexed_triangle_set &its = volume->mesh.its; + if (its.vertices.empty()) { add_error("Found invalid mesh"); return false; } - vertices_count += stl.v_shared.size(); + vertices_count += its.vertices.size(); const Transform3d& matrix = volume->get_matrix(); - for (size_t i = 0; i < stl.v_shared.size(); ++i) + for (size_t i = 0; i < its.vertices.size(); ++i) { stream << " <" << VERTEX_TAG << " "; - Vec3f v = (matrix * stl.v_shared[i].cast()).cast(); + Vec3f v = (matrix * its.vertices[i].cast()).cast(); stream << "x=\"" << v(0) << "\" "; stream << "y=\"" << v(1) << "\" "; stream << "z=\"" << v(2) << "\" />\n"; @@ -1920,19 +1916,19 @@ namespace Slic3r { VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume); assert(volume_it != volumes_offsets.end()); - stl_file& stl = volume->mesh.stl; + const indexed_triangle_set &its = volume->mesh.its; // updates triangle offsets volume_it->second.first_triangle_id = triangles_count; - triangles_count += stl.stats.number_of_facets; + triangles_count += its.indices.size(); volume_it->second.last_triangle_id = triangles_count - 1; - for (uint32_t i = 0; i < stl.stats.number_of_facets; ++i) + for (size_t i = 0; i < its.indices.size(); ++ i) { stream << " <" << TRIANGLE_TAG << " "; for (int j = 0; j < 3; ++j) { - stream << "v" << j + 1 << "=\"" << stl.v_indices[i].vertex[j] + volume_it->second.first_vertex_id << "\" "; + stream << "v" << j + 1 << "=\"" << its.indices[i].vertex[j] + volume_it->second.first_vertex_id << "\" "; } stream << "/>\n"; } diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index e81ced3ad..dcd913864 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -923,23 +923,22 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) int num_vertices = 0; for (ModelVolume *volume : object->volumes) { vertices_offsets.push_back(num_vertices); - if (! volume->mesh.repaired) + if (! volume->mesh.repaired) throw std::runtime_error("store_amf() requires repair()"); - auto &stl = volume->mesh.stl; - if (stl.v_shared.empty()) - stl_generate_shared_vertices(&stl); + volume->mesh.require_shared_vertices(); + const indexed_triangle_set &its = volume->mesh.its; const Transform3d& matrix = volume->get_matrix(); - for (size_t i = 0; i < stl.v_shared.size(); ++i) { + for (size_t i = 0; i < its.vertices.size(); ++i) { stream << " \n"; stream << " \n"; - Vec3f v = (matrix * stl.v_shared[i].cast()).cast(); + Vec3f v = (matrix * its.vertices[i].cast()).cast(); stream << " " << v(0) << "\n"; stream << " " << v(1) << "\n"; stream << " " << v(2) << "\n"; stream << " \n"; stream << " \n"; } - num_vertices += stl.v_shared.size(); + num_vertices += its.vertices.size(); } stream << " \n"; for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) { @@ -956,10 +955,10 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) if (volume->is_modifier()) stream << " 1\n"; stream << " " << ModelVolume::type_to_string(volume->type()) << "\n"; - for (int i = 0; i < (int)volume->mesh.stl.stats.number_of_facets; ++i) { + for (size_t i = 0; i < (int)volume->mesh.its.indices.size(); ++i) { stream << " \n"; for (int j = 0; j < 3; ++j) - stream << " " << volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset << "\n"; + stream << " " << volume->mesh.its.indices[i].vertex[j] + vertices_offset << "\n"; stream << " \n"; } stream << " \n"; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 6da7fc0c3..770581c03 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -908,10 +908,11 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const Points pts; for (const ModelVolume *v : this->volumes) if (v->is_model_part()) { - const stl_file &stl = v->mesh.stl; Transform3d trafo = trafo_instance * v->get_matrix(); - if (stl.v_shared.empty()) { + const indexed_triangle_set &its = v->mesh.its; + if (its.vertices.empty()) { // Using the STL faces. + const stl_file& stl = v->mesh.stl; for (const stl_facet &facet : stl.facet_start) for (size_t j = 0; j < 3; ++ j) { Vec3d p = trafo * facet.vertex[j].cast(); @@ -919,8 +920,8 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const } } else { // Using the shared vertices should be a bit quicker than using the STL faces. - for (size_t i = 0; i < stl.v_shared.size(); ++ i) { - Vec3d p = trafo * stl.v_shared[i].cast(); + for (size_t i = 0; i < its.vertices.size(); ++ i) { + Vec3d p = trafo * its.vertices[i].cast(); pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y()))); } } diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 003affc27..ae35c8a5b 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -238,20 +238,20 @@ bool TriangleMesh::needed_repair() const void TriangleMesh::WriteOBJFile(const char* output_file) { - stl_generate_shared_vertices(&stl); - stl_write_obj(&stl, output_file); + stl_generate_shared_vertices(&stl, its); + its_write_obj(its, output_file); } void TriangleMesh::scale(float factor) { stl_scale(&(this->stl), factor); - stl_invalidate_shared_vertices(&this->stl); + this->its.clear(); } void TriangleMesh::scale(const Vec3d &versor) { stl_scale_versor(&this->stl, versor.cast()); - stl_invalidate_shared_vertices(&this->stl); + this->its.clear(); } void TriangleMesh::translate(float x, float y, float z) @@ -259,7 +259,7 @@ void TriangleMesh::translate(float x, float y, float z) if (x == 0.f && y == 0.f && z == 0.f) return; stl_translate_relative(&(this->stl), x, y, z); - stl_invalidate_shared_vertices(&this->stl); + this->its.clear(); } void TriangleMesh::translate(const Vec3f &displacement) @@ -282,7 +282,7 @@ void TriangleMesh::rotate(float angle, const Axis &axis) } else if (axis == Z) { stl_rotate_z(&(this->stl), angle); } - stl_invalidate_shared_vertices(&this->stl); + this->its.clear(); } void TriangleMesh::rotate(float angle, const Vec3d& axis) @@ -305,13 +305,13 @@ void TriangleMesh::mirror(const Axis &axis) } else if (axis == Z) { stl_mirror_xy(&this->stl); } - stl_invalidate_shared_vertices(&this->stl); + this->its.clear(); } void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed) { stl_transform(&stl, t); - stl_invalidate_shared_vertices(&stl); + this->its.clear(); if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) { // Left handed transformation is being applied. It is a good idea to flip the faces and their normals. this->repair(); @@ -322,7 +322,7 @@ void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed) void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed) { stl_transform(&stl, m); - stl_invalidate_shared_vertices(&stl); + this->its.clear(); if (fix_left_handed && m.determinant() < 0.) { // Left handed transformation is being applied. It is a good idea to flip the faces and their normals. this->repair(); @@ -443,7 +443,7 @@ void TriangleMesh::merge(const TriangleMesh &mesh) { // reset stats and metadata int number_of_facets = this->stl.stats.number_of_facets; - stl_invalidate_shared_vertices(&this->stl); + this->its.clear(); this->repaired = false; // update facet count and allocate more memory @@ -484,9 +484,9 @@ Polygon TriangleMesh::convex_hull() { this->require_shared_vertices(); Points pp; - pp.reserve(this->stl.v_shared.size()); - for (size_t i = 0; i < this->stl.v_shared.size(); ++ i) { - const stl_vertex &v = this->stl.v_shared[i]; + pp.reserve(this->its.vertices.size()); + for (size_t i = 0; i < this->its.vertices.size(); ++ i) { + const stl_vertex &v = this->its.vertices[i]; pp.emplace_back(Point::new_scale(v(0), v(1))); } return Slic3r::Geometry::convex_hull(pp); @@ -504,14 +504,14 @@ BoundingBoxf3 TriangleMesh::bounding_box() const BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) const { BoundingBoxf3 bbox; - if (stl.v_shared.empty()) { + if (this->its.vertices.empty()) { // Using the STL faces. for (const stl_facet &facet : this->stl.facet_start) for (size_t j = 0; j < 3; ++ j) bbox.merge(trafo * facet.vertex[j].cast()); } else { // Using the shared vertices should be a bit quicker than using the STL faces. - for (const stl_vertex &v : this->stl.v_shared) + for (const stl_vertex &v : this->its.vertices) bbox.merge(trafo * v.cast()); } return bbox; @@ -576,11 +576,11 @@ void TriangleMesh::require_shared_vertices() assert(stl_validate(&this->stl)); if (! this->repaired) this->repair(); - if (this->stl.v_shared.empty()) { + if (this->its.vertices.empty()) { BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices"; - stl_generate_shared_vertices(&(this->stl)); + stl_generate_shared_vertices(&this->stl, this->its); } - assert(stl_validate(&this->stl)); + assert(stl_validate(&this->stl, this->its)); BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; } @@ -592,9 +592,9 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac throw_on_cancel(); facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1); - v_scaled_shared.assign(_mesh->stl.v_shared.size(), stl_vertex()); + v_scaled_shared.assign(_mesh->its.vertices.size(), stl_vertex()); for (size_t i = 0; i < v_scaled_shared.size(); ++ i) - this->v_scaled_shared[i] = _mesh->stl.v_shared[i] / float(SCALING_FACTOR); + this->v_scaled_shared[i] = _mesh->its.vertices[i] / float(SCALING_FACTOR); // Create a mapping from triangle edge into face. struct EdgeToFace { @@ -614,8 +614,8 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) for (int i = 0; i < 3; ++ i) { EdgeToFace &e2f = edges_map[facet_idx*3+i]; - e2f.vertex_low = this->mesh->stl.v_indices[facet_idx].vertex[i]; - e2f.vertex_high = this->mesh->stl.v_indices[facet_idx].vertex[(i + 1) % 3]; + e2f.vertex_low = this->mesh->its.indices[facet_idx].vertex[i]; + e2f.vertex_high = this->mesh->its.indices[facet_idx].vertex[(i + 1) % 3]; e2f.face = facet_idx; // 1 based indexing, to be always strictly positive. e2f.face_edge = i + 1; @@ -852,7 +852,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( // Reorder vertices so that the first one is the one with lowest Z. // This is needed to get all intersection lines in a consistent order // (external on the right of the line) - const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; + const int *vertices = this->mesh->its.indices[facet_idx].vertex; int i = (facet.vertex[1].z() == min_z) ? 1 : ((facet.vertex[2].z() == min_z) ? 2 : 0); // These are used only if the cut plane is tilted: diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index c16f4a664..75082cfdb 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -69,12 +69,13 @@ public: void reset_repair_stats(); bool needed_repair() const; void require_shared_vertices(); - bool has_shared_vertices() const { return ! stl.v_shared.empty(); } + bool has_shared_vertices() const { return ! this->its.vertices.empty(); } size_t facets_count() const { return this->stl.stats.number_of_facets; } bool empty() const { return this->facets_count() == 0; } bool is_splittable() const; stl_file stl; + indexed_triangle_set its; bool repaired; private: diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index fb3b6f7a4..45092f257 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -781,7 +781,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool if (i == 0) suffix[0] = 0; else - sprintf(suffix, "%d", i); + sprintf(suffix, "%d", (int)i); std::string new_name = name + suffix; loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name), new_name, std::move(cfg), i == 0); @@ -1379,7 +1379,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst for (size_t i = 0; i < this->filament_presets.size(); ++ i) { char suffix[64]; if (i > 0) - sprintf(suffix, "_%d", i); + sprintf(suffix, "_%d", (int)i); else suffix[0] = 0; c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 53b052027..3e90bfefd 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -98,20 +98,18 @@ SV* TriangleMesh::vertices() CODE: if (!THIS->repaired) CONFESS("vertices() requires repair()"); - - if (THIS->stl.v_shared.empty()) - stl_generate_shared_vertices(&(THIS->stl)); + THIS->require_shared_vertices(); // vertices AV* vertices = newAV(); - av_extend(vertices, THIS->stl.v_shared.size()); - for (size_t i = 0; i < THIS->stl.v_shared.size(); i++) { + av_extend(vertices, THIS->its.vertices.size()); + for (size_t i = 0; i < THIS->its.vertices.size(); i++) { AV* vertex = newAV(); av_store(vertices, i, newRV_noinc((SV*)vertex)); av_extend(vertex, 2); - av_store(vertex, 0, newSVnv(THIS->stl.v_shared[i](0))); - av_store(vertex, 1, newSVnv(THIS->stl.v_shared[i](1))); - av_store(vertex, 2, newSVnv(THIS->stl.v_shared[i](2))); + av_store(vertex, 0, newSVnv(THIS->its.vertices[i](0))); + av_store(vertex, 1, newSVnv(THIS->its.vertices[i](1))); + av_store(vertex, 2, newSVnv(THIS->its.vertices[i](2))); } RETVAL = newRV_noinc((SV*)vertices); @@ -122,9 +120,7 @@ SV* TriangleMesh::facets() CODE: if (!THIS->repaired) CONFESS("facets() requires repair()"); - - if (THIS->stl.v_shared.empty()) - stl_generate_shared_vertices(&(THIS->stl)); + THIS->require_shared_vertices(); // facets AV* facets = newAV(); @@ -133,9 +129,9 @@ TriangleMesh::facets() AV* facet = newAV(); av_store(facets, i, newRV_noinc((SV*)facet)); av_extend(facet, 2); - av_store(facet, 0, newSVnv(THIS->stl.v_indices[i].vertex[0])); - av_store(facet, 1, newSVnv(THIS->stl.v_indices[i].vertex[1])); - av_store(facet, 2, newSVnv(THIS->stl.v_indices[i].vertex[2])); + av_store(facet, 0, newSVnv(THIS->its.indices[i].vertex[0])); + av_store(facet, 1, newSVnv(THIS->its.indices[i].vertex[1])); + av_store(facet, 2, newSVnv(THIS->its.indices[i].vertex[2])); } RETVAL = newRV_noinc((SV*)facets); From 313ec7424a9714ba6de64a94b7d4b89ebae05f91 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Jun 2019 19:45:38 +0200 Subject: [PATCH 18/44] admesh refactoring: replaced various diagnostics outputs with boost::log --- src/admesh/connect.cpp | 23 ++- src/admesh/shared.cpp | 16 +- src/admesh/stl.h | 2 +- src/admesh/stl_io.cpp | 283 +++++++++++-------------- src/admesh/stlinit.cpp | 356 +++++++++++++++----------------- src/admesh/util.cpp | 204 +++++++++--------- src/slic3r/GUI/PresetBundle.cpp | 2 +- 7 files changed, 405 insertions(+), 481 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index 9553e7c4d..5ae03597e 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include "stl.h" @@ -124,7 +125,9 @@ struct HashTableEdges { for (HashEdge *temp = this->heads[i]; this->heads[i] != this->tail; temp = this->heads[i]) { this->heads[i] = this->heads[i]->next; delete temp; +#ifndef NDEBUG ++ this->freed; +#endif /* NDEBUG */ } } this->heads.clear(); @@ -139,7 +142,9 @@ struct HashTableEdges { if (link == this->tail) { // This list doesn't have any edges currently in it. Add this one. HashEdge *new_edge = new HashEdge(edge); +#ifndef NDEBUG ++ this->malloced; +#endif /* NDEBUG */ new_edge->next = this->tail; this->heads[chain_number] = new_edge; } else if (edges_equal(edge, *link)) { @@ -148,18 +153,24 @@ struct HashTableEdges { // Delete the matched edge from the list. this->heads[chain_number] = link->next; delete link; +#ifndef NDEBUG ++ this->freed; +#endif /* NDEBUG */ } else { // Continue through the rest of the list. for (;;) { if (link->next == this->tail) { // This is the last item in the list. Insert a new edge. HashEdge *new_edge = new HashEdge; +#ifndef NDEBUG ++ this->malloced; +#endif /* NDEBUG */ *new_edge = edge; new_edge->next = this->tail; link->next = new_edge; +#ifndef NDEBUG ++ this->collisions; +#endif /* NDEBUG */ break; } if (edges_equal(edge, *link->next)) { @@ -169,12 +180,16 @@ struct HashTableEdges { HashEdge *temp = link->next; link->next = link->next->next; delete temp; +#ifndef NDEBUG ++ this->freed; +#endif /* NDEBUG */ break; } // This is not a match. Go to the next link. link = link->next; +#ifndef NDEBUG ++ this->collisions; +#endif /* NDEBUG */ } } } @@ -184,9 +199,11 @@ struct HashTableEdges { HashEdge* tail; int M; +#ifndef NDEBUG size_t malloced = 0; size_t freed = 0; size_t collisions = 0; +#endif /* NDEBUG */ private: static inline size_t hash_size_from_nr_faces(const size_t nr_faces) @@ -366,7 +383,7 @@ static void match_neighbors_nearby(stl_file *stl, const HashEdge &edge_a, const if (facet_num == first_facet) { // back to the beginning - printf("Back to the first facet changing vertices: probably a mobius part.\nTry using a smaller tolerance or don't do a nearby check\n"); + BOOST_LOG_TRIVIAL(info) << "Back to the first facet changing vertices: probably a mobius part. Try using a smaller tolerance or don't do a nearby check."; return; } } @@ -506,7 +523,7 @@ void stl_remove_unconnected_facets(stl_file *stl) if (neighbors.neighbor[i] != -1) { int &other_face_idx = stl->neighbors_start[neighbors.neighbor[i]].neighbor[(neighbors.which_vertex_not[i] + 1) % 3]; if (other_face_idx != stl->stats.number_of_facets) { - printf("in remove_facet: neighbor = %d numfacets = %d this is wrong\n", other_face_idx, stl->stats.number_of_facets); + BOOST_LOG_TRIVIAL(info) << "in remove_facet: neighbor = " << other_face_idx << " numfacets = " << stl->stats.number_of_facets << " this is wrong"; return; } other_face_idx = facet_number; @@ -697,7 +714,7 @@ void stl_fill_holes(stl_file *stl) if (facet_num == first_facet) { // back to the beginning - printf("Back to the first facet filling holes: probably a mobius part.\nTry using a smaller tolerance or don't do a nearby check\n"); + BOOST_LOG_TRIVIAL(info) << "Back to the first facet filling holes: probably a mobius part. Try using a smaller tolerance or don't do a nearby check."; return; } } diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index 7da2841b0..fe6d5e656 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -25,6 +25,7 @@ #include +#include #include #include "stl.h" @@ -129,10 +130,7 @@ bool its_write_off(const indexed_triangle_set &its, const char *file) /* Open the file */ FILE *fp = boost::nowide::fopen(file, "w"); if (fp == nullptr) { - char *error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); - perror(error_msg); - free(error_msg); + BOOST_LOG_TRIVIAL(error) << "stl_write_ascii: Couldn't open " << file << " for writing"; return false; } @@ -151,10 +149,7 @@ bool its_write_vrml(const indexed_triangle_set &its, const char *file) /* Open the file */ FILE *fp = boost::nowide::fopen(file, "w"); if (fp == nullptr) { - char *error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); - perror(error_msg); - free(error_msg); + BOOST_LOG_TRIVIAL(error) << "stl_write_vrml: Couldn't open " << file << " for writing"; return false; } @@ -196,10 +191,7 @@ bool its_write_obj(const indexed_triangle_set &its, const char *file) FILE *fp = boost::nowide::fopen(file, "w"); if (fp == nullptr) { - char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); - perror(error_msg); - free(error_msg); + BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing"; return false; } diff --git a/src/admesh/stl.h b/src/admesh/stl.h index bb5d25296..500d6bfdb 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -242,7 +242,7 @@ inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) { } extern void stl_calculate_volume(stl_file *stl); -extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag); +extern void stl_repair(stl_file *stl, bool fixall_flag, bool exact_flag, bool tolerance_flag, float tolerance, bool increment_flag, float increment, bool nearby_flag, int iterations, bool remove_unconnected_flag, bool fill_holes_flag, bool normal_directions_flag, bool normal_values_flag, bool reverse_all_flag, bool verbose_flag); extern void stl_reset(stl_file *stl); extern void stl_allocate(stl_file *stl); diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp index 8f809d379..dc4e4a7db 100644 --- a/src/admesh/stl_io.cpp +++ b/src/admesh/stl_io.cpp @@ -24,6 +24,7 @@ #include #include "stl.h" +#include #include #include @@ -107,65 +108,47 @@ Normals fixed : %5d\n", stl->stats.normals_fixed); bool stl_write_ascii(stl_file *stl, const char *file, const char *label) { - char *error_msg; + FILE *fp = boost::nowide::fopen(file, "w"); + if (fp == NULL) { + BOOST_LOG_TRIVIAL(error) << "stl_write_ascii: Couldn't open " << file << " for writing"; + return false; + } - /* Open the file */ - FILE *fp = boost::nowide::fopen(file, "w"); - if(fp == NULL) { - error_msg = (char*) - malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", - file); - perror(error_msg); - free(error_msg); - return false; - } + fprintf(fp, "solid %s\n", label); - fprintf(fp, "solid %s\n", label); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + fprintf(fp, " facet normal % .8E % .8E % .8E\n", + stl->facet_start[i].normal(0), stl->facet_start[i].normal(1), + stl->facet_start[i].normal(2)); + fprintf(fp, " outer loop\n"); + fprintf(fp, " vertex % .8E % .8E % .8E\n", + stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), + stl->facet_start[i].vertex[0](2)); + fprintf(fp, " vertex % .8E % .8E % .8E\n", + stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), + stl->facet_start[i].vertex[1](2)); + fprintf(fp, " vertex % .8E % .8E % .8E\n", + stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), + stl->facet_start[i].vertex[2](2)); + fprintf(fp, " endloop\n"); + fprintf(fp, " endfacet\n"); + } - for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { - fprintf(fp, " facet normal % .8E % .8E % .8E\n", - stl->facet_start[i].normal(0), stl->facet_start[i].normal(1), - stl->facet_start[i].normal(2)); - fprintf(fp, " outer loop\n"); - fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), - stl->facet_start[i].vertex[0](2)); - fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), - stl->facet_start[i].vertex[1](2)); - fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), - stl->facet_start[i].vertex[2](2)); - fprintf(fp, " endloop\n"); - fprintf(fp, " endfacet\n"); - } - - fprintf(fp, "endsolid %s\n", label); - - fclose(fp); - return true; + fprintf(fp, "endsolid %s\n", label); + fclose(fp); + return true; } bool stl_print_neighbors(stl_file *stl, char *file) { - FILE *fp; - char *error_msg; + FILE *fp = boost::nowide::fopen(file, "w"); + if (fp == NULL) { + BOOST_LOG_TRIVIAL(error) << "stl_print_neighbors: Couldn't open " << file << " for writing"; + return false; + } - /* Open the file */ - fp = boost::nowide::fopen(file, "w"); - if(fp == NULL) { - error_msg = (char*) - malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_print_neighbors: Couldn't open %s for writing", - file); - perror(error_msg); - free(error_msg); - return false; - } - - for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { - fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n", + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n", i, stl->neighbors_start[i].neighbor[0], (int)stl->neighbors_start[i].which_vertex_not[0], @@ -173,62 +156,54 @@ bool stl_print_neighbors(stl_file *stl, char *file) (int)stl->neighbors_start[i].which_vertex_not[1], stl->neighbors_start[i].neighbor[2], (int)stl->neighbors_start[i].which_vertex_not[2]); - } - fclose(fp); - return true; + } + fclose(fp); + return true; } #ifndef BOOST_LITTLE_ENDIAN // Swap a buffer of 32bit data from little endian to big endian and vice versa. void stl_internal_reverse_quads(char *buf, size_t cnt) { - for (size_t i = 0; i < cnt; i += 4) { - std::swap(buf[i], buf[i+3]); - std::swap(buf[i+1], buf[i+2]); - } + for (size_t i = 0; i < cnt; i += 4) { + std::swap(buf[i], buf[i+3]); + std::swap(buf[i+1], buf[i+2]); + } } #endif bool stl_write_binary(stl_file *stl, const char *file, const char *label) { - FILE *fp; - char *error_msg; + FILE *fp = boost::nowide::fopen(file, "wb"); + if (fp == NULL) { + BOOST_LOG_TRIVIAL(error) << "stl_write_binary: Couldn't open " << file << " for writing"; + return false; + } - /* Open the file */ - fp = boost::nowide::fopen(file, "wb"); - if(fp == NULL) { - error_msg = (char*) - malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_binary: Couldn't open %s for writing", - file); - perror(error_msg); - free(error_msg); - return false; - } + fprintf(fp, "%s", label); + for (size_t i = strlen(label); i < LABEL_SIZE; ++ i) + putc(0, fp); - fprintf(fp, "%s", label); - for(size_t i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp); - - fseek(fp, LABEL_SIZE, SEEK_SET); + fseek(fp, LABEL_SIZE, SEEK_SET); #ifdef BOOST_LITTLE_ENDIAN - fwrite(&stl->stats.number_of_facets, 4, 1, fp); - for (const stl_facet &facet : stl->facet_start) - fwrite(&facet, SIZEOF_STL_FACET, 1, fp); + fwrite(&stl->stats.number_of_facets, 4, 1, fp); + for (const stl_facet &facet : stl->facet_start) + fwrite(&facet, SIZEOF_STL_FACET, 1, fp); #else /* BOOST_LITTLE_ENDIAN */ - char buffer[50]; - // Convert the number of facets to little endian. - memcpy(buffer, &stl->stats.number_of_facets, 4); - stl_internal_reverse_quads(buffer, 4); - fwrite(buffer, 4, 1, fp); - for (i = 0; i < stl->stats.number_of_facets; ++ i) { - memcpy(buffer, stl->facet_start + i, 50); - // Convert to little endian. - stl_internal_reverse_quads(buffer, 48); - fwrite(buffer, SIZEOF_STL_FACET, 1, fp); - } + char buffer[50]; + // Convert the number of facets to little endian. + memcpy(buffer, &stl->stats.number_of_facets, 4); + stl_internal_reverse_quads(buffer, 4); + fwrite(buffer, 4, 1, fp); + for (i = 0; i < stl->stats.number_of_facets; ++ i) { + memcpy(buffer, stl->facet_start + i, 50); + // Convert to little endian. + stl_internal_reverse_quads(buffer, 48); + fwrite(buffer, SIZEOF_STL_FACET, 1, fp); + } #endif /* BOOST_LITTLE_ENDIAN */ - fclose(fp); - return true; + fclose(fp); + return true; } void stl_write_vertex(stl_file *stl, int facet, int vertex) @@ -260,53 +235,39 @@ void stl_write_neighbor(stl_file *stl, int facet) bool stl_write_quad_object(stl_file *stl, char *file) { - FILE *fp; - char *error_msg; - stl_vertex connect_color = stl_vertex::Zero(); - stl_vertex uncon_1_color = stl_vertex::Zero(); - stl_vertex uncon_2_color = stl_vertex::Zero(); - stl_vertex uncon_3_color = stl_vertex::Zero(); - stl_vertex color; + stl_vertex connect_color = stl_vertex::Zero(); + stl_vertex uncon_1_color = stl_vertex::Zero(); + stl_vertex uncon_2_color = stl_vertex::Zero(); + stl_vertex uncon_3_color = stl_vertex::Zero(); + stl_vertex color; - /* Open the file */ - fp = boost::nowide::fopen(file, "w"); - if(fp == NULL) { - error_msg = (char*) - malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_quad_object: Couldn't open %s for writing", - file); - perror(error_msg); - free(error_msg); - return false; - } + FILE *fp = boost::nowide::fopen(file, "w"); + if (fp == NULL) { + BOOST_LOG_TRIVIAL(error) << "stl_write_quad_object: Couldn't open " << file << " for writing"; + return false; + } - fprintf(fp, "CQUAD\n"); - for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { - int j = ((stl->neighbors_start[i].neighbor[0] == -1) + - (stl->neighbors_start[i].neighbor[1] == -1) + - (stl->neighbors_start[i].neighbor[2] == -1)); - if(j == 0) { - color = connect_color; - } else if(j == 1) { - color = uncon_1_color; - } else if(j == 2) { - color = uncon_2_color; - } else { - color = uncon_3_color; - } - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", + fprintf(fp, "CQUAD\n"); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + switch (stl->neighbors_start[i].num_neighbors_missing()) { + case 0: color = connect_color; break; + case 1: color = uncon_1_color; break; + case 2: color = uncon_2_color; break; + default: color = uncon_3_color; + } + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2), color(0), color(1), color(2)); - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2), color(0), color(1), color(2)); - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2), color(0), color(1), color(2)); - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2), color(0), color(1), color(2)); @@ -317,47 +278,37 @@ bool stl_write_quad_object(stl_file *stl, char *file) bool stl_write_dxf(stl_file *stl, const char *file, char *label) { - FILE *fp; - char *error_msg; + FILE *fp = boost::nowide::fopen(file, "w"); + if (fp == NULL) { + BOOST_LOG_TRIVIAL(error) << "stl_write_quad_object: Couldn't open " << file << " for writing"; + return false; + } - /* Open the file */ - fp = boost::nowide::fopen(file, "w"); - if(fp == NULL) { - error_msg = (char*) - malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", - file); - perror(error_msg); - free(error_msg); - return false; - } + fprintf(fp, "999\n%s\n", label); + fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n"); + fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\ + 0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n"); + fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n"); - fprintf(fp, "999\n%s\n", label); - fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n"); - fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\ -0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n"); - fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n"); + fprintf(fp, "0\nSECTION\n2\nENTITIES\n"); - fprintf(fp, "0\nSECTION\n2\nENTITIES\n"); + for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { + fprintf(fp, "0\n3DFACE\n8\n0\n"); + fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", + stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), + stl->facet_start[i].vertex[0](2)); + fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", + stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), + stl->facet_start[i].vertex[1](2)); + fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", + stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), + stl->facet_start[i].vertex[2](2)); + fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", + stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), + stl->facet_start[i].vertex[2](2)); + } - for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { - fprintf(fp, "0\n3DFACE\n8\n0\n"); - fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", - stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), - stl->facet_start[i].vertex[0](2)); - fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", - stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), - stl->facet_start[i].vertex[1](2)); - fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", - stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), - stl->facet_start[i].vertex[2](2)); - fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", - stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), - stl->facet_start[i].vertex[2](2)); - } - - fprintf(fp, "0\nENDSEC\n0\nEOF\n"); - - fclose(fp); - return true; + fprintf(fp, "0\nENDSEC\n0\nEOF\n"); + fclose(fp); + return true; } diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index 44477511f..24fbe9edc 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -37,118 +38,102 @@ static FILE* stl_open_count_facets(stl_file *stl, const char *file) { - long file_size; - uint32_t header_num_facets; - uint32_t num_facets; - int i; - size_t s; - unsigned char chtest[128]; - int num_lines = 1; - char *error_msg; + // Open the file in binary mode first. + FILE *fp = boost::nowide::fopen(file, "rb"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: Couldn't open " << file << " for reading"; + return nullptr; + } + // Find size of file. + fseek(fp, 0, SEEK_END); + long file_size = ftell(fp); - /* Open the file in binary mode first */ - FILE *fp = boost::nowide::fopen(file, "rb"); - if (fp == nullptr) { - error_msg = (char*) - malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_initialize: Couldn't open %s for reading", - file); - perror(error_msg); - free(error_msg); - return nullptr; - } - /* Find size of file */ - fseek(fp, 0, SEEK_END); - file_size = ftell(fp); + // Check for binary or ASCII file. + fseek(fp, HEADER_SIZE, SEEK_SET); + unsigned char chtest[128]; + if (! fread(chtest, sizeof(chtest), 1, fp)) { + BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: The input is an empty file: " << file; + fclose(fp); + return nullptr; + } + stl->stats.type = ascii; + for (size_t s = 0; s < sizeof(chtest); s++) { + if (chtest[s] > 127) { + stl->stats.type = binary; + break; + } + } + rewind(fp); - /* Check for binary or ASCII file */ - fseek(fp, HEADER_SIZE, SEEK_SET); - if (!fread(chtest, sizeof(chtest), 1, fp)) { - perror("The input is an empty file"); - fclose(fp); - return nullptr; - } - stl->stats.type = ascii; - for(s = 0; s < sizeof(chtest); s++) { - if(chtest[s] > 127) { - stl->stats.type = binary; - break; - } - } - rewind(fp); + uint32_t num_facets = 0; - /* Get the header and the number of facets in the .STL file */ - /* If the .STL file is binary, then do the following */ - if(stl->stats.type == binary) { - /* Test if the STL file has the right size */ - if(((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0) - || (file_size < STL_MIN_FILE_SIZE)) { - fprintf(stderr, "The file %s has the wrong size.\n", file); - fclose(fp); - return nullptr; - } - num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET; + // Get the header and the number of facets in the .STL file. + // If the .STL file is binary, then do the following: + if (stl->stats.type == binary) { + // Test if the STL file has the right size. + if (((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0) || (file_size < STL_MIN_FILE_SIZE)) { + BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: The file " << file << " has the wrong size."; + fclose(fp); + return nullptr; + } + num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET; - /* Read the header */ - if (fread(stl->stats.header, LABEL_SIZE, 1, fp) > 79) { - stl->stats.header[80] = '\0'; - } + // Read the header. + if (fread(stl->stats.header, LABEL_SIZE, 1, fp) > 79) + stl->stats.header[80] = '\0'; - /* Read the int following the header. This should contain # of facets */ - bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, fp) != 0; + // Read the int following the header. This should contain # of facets. + uint32_t header_num_facets; + bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, fp) != 0; #ifndef BOOST_LITTLE_ENDIAN - // Convert from little endian to big endian. - stl_internal_reverse_quads((char*)&header_num_facets, 4); + // Convert from little endian to big endian. + stl_internal_reverse_quads((char*)&header_num_facets, 4); #endif /* BOOST_LITTLE_ENDIAN */ - if (! header_num_faces_read || num_facets != header_num_facets) { - fprintf(stderr, - "Warning: File size doesn't match number of facets in the header\n"); - } - } - /* Otherwise, if the .STL file is ASCII, then do the following */ - else { - /* Reopen the file in text mode (for getting correct newlines on Windows) */ - // fix to silence a warning about unused return value. - // obviously if it fails we have problems.... - fp = boost::nowide::freopen(file, "r", fp); + if (! header_num_faces_read || num_facets != header_num_facets) + BOOST_LOG_TRIVIAL(info) << "stl_open_count_facets: Warning: File size doesn't match number of facets in the header: " << file; + } + // Otherwise, if the .STL file is ASCII, then do the following: + else + { + // Reopen the file in text mode (for getting correct newlines on Windows) + // fix to silence a warning about unused return value. + // obviously if it fails we have problems.... + fp = boost::nowide::freopen(file, "r", fp); - // do another null check to be safe - if(fp == nullptr) { - error_msg = (char*) - malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ - sprintf(error_msg, "stl_initialize: Couldn't open %s for reading", - file); - perror(error_msg); - free(error_msg); - fclose(fp); - return nullptr; - } + // do another null check to be safe + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: Couldn't open " << file << " for reading"; + fclose(fp); + return nullptr; + } - /* Find the number of facets */ - char linebuf[100]; - while (fgets(linebuf, 100, fp) != nullptr) { - /* don't count short lines */ - if (strlen(linebuf) <= 4) continue; - - /* skip solid/endsolid lines as broken STL file generators may put several of them */ - if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0) continue; - - ++num_lines; - } - - rewind(fp); - - /* Get the header */ - for(i = 0; - (i < 80) && (stl->stats.header[i] = getc(fp)) != '\n'; i++); - stl->stats.header[i] = '\0'; /* Lose the '\n' */ - stl->stats.header[80] = '\0'; + // Find the number of facets. + char linebuf[100]; + int num_lines = 1; + while (fgets(linebuf, 100, fp) != nullptr) { + // Don't count short lines. + if (strlen(linebuf) <= 4) + continue; + // Skip solid/endsolid lines as broken STL file generators may put several of them. + if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0) + continue; + ++ num_lines; + } - num_facets = num_lines / ASCII_LINES_PER_FACET; - } - stl->stats.number_of_facets += num_facets; - stl->stats.original_num_facets = stl->stats.number_of_facets; - return fp; + rewind(fp); + + // Get the header. + int i = 0; + for (; i < 80 && (stl->stats.header[i] = getc(fp)) != '\n'; ++ i) ; + stl->stats.header[i] = '\0'; // Lose the '\n' + stl->stats.header[80] = '\0'; + + num_facets = num_lines / ASCII_LINES_PER_FACET; + } + + stl->stats.number_of_facets += num_facets; + stl->stats.original_num_facets = stl->stats.number_of_facets; + return fp; } /* Reads the contents of the file pointed to by fp into the stl structure, @@ -156,85 +141,82 @@ static FILE* stl_open_count_facets(stl_file *stl, const char *file) time running this for the stl and therefore we should reset our max and min stats. */ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first) { - stl_facet facet; + if (stl->stats.type == binary) + fseek(fp, HEADER_SIZE, SEEK_SET); + else + rewind(fp); - if(stl->stats.type == binary) { - fseek(fp, HEADER_SIZE, SEEK_SET); - } else { - rewind(fp); - } + char normal_buf[3][32]; + for (uint32_t i = first_facet; i < stl->stats.number_of_facets; ++i) { + stl_facet facet; - char normal_buf[3][32]; - for(uint32_t i = first_facet; i < stl->stats.number_of_facets; i++) { - if(stl->stats.type == binary) - /* Read a single facet from a binary .STL file */ - { - /* we assume little-endian architecture! */ - if (fread(&facet, 1, SIZEOF_STL_FACET, fp) != SIZEOF_STL_FACET) - return false; + if (stl->stats.type == binary) { + // Read a single facet from a binary .STL file. We assume little-endian architecture! + if (fread(&facet, 1, SIZEOF_STL_FACET, fp) != SIZEOF_STL_FACET) + return false; #ifndef BOOST_LITTLE_ENDIAN - // Convert the loaded little endian data to big endian. - stl_internal_reverse_quads((char*)&facet, 48); + // Convert the loaded little endian data to big endian. + stl_internal_reverse_quads((char*)&facet, 48); #endif /* BOOST_LITTLE_ENDIAN */ - } else - /* Read a single facet from an ASCII .STL file */ - { - // skip solid/endsolid - // (in this order, otherwise it won't work when they are paired in the middle of a file) - fscanf(fp, "endsolid%*[^\n]\n"); - fscanf(fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid") - // Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs. - int res_normal = fscanf(fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]); - assert(res_normal == 3); - int res_outer_loop = fscanf(fp, " outer loop"); - assert(res_outer_loop == 0); - int res_vertex1 = fscanf(fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2)); - assert(res_vertex1 == 3); - int res_vertex2 = fscanf(fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2)); - assert(res_vertex2 == 3); - int res_vertex3 = fscanf(fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2)); - assert(res_vertex3 == 3); - int res_endloop = fscanf(fp, " endloop"); - assert(res_endloop == 0); - // There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines. - int res_endfacet = fscanf(fp, " endfacet "); - if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) { - perror("Something is syntactically very wrong with this ASCII STL!"); - return false; - } + } else { + // Read a single facet from an ASCII .STL file + // skip solid/endsolid + // (in this order, otherwise it won't work when they are paired in the middle of a file) + fscanf(fp, "endsolid%*[^\n]\n"); + fscanf(fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid") + // Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs. + int res_normal = fscanf(fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]); + assert(res_normal == 3); + int res_outer_loop = fscanf(fp, " outer loop"); + assert(res_outer_loop == 0); + int res_vertex1 = fscanf(fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2)); + assert(res_vertex1 == 3); + int res_vertex2 = fscanf(fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2)); + assert(res_vertex2 == 3); + int res_vertex3 = fscanf(fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2)); + assert(res_vertex3 == 3); + int res_endloop = fscanf(fp, " endloop"); + assert(res_endloop == 0); + // There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines. + int res_endfacet = fscanf(fp, " endfacet "); + if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) { + BOOST_LOG_TRIVIAL(error) << "Something is syntactically very wrong with this ASCII STL! "; + return false; + } - // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. - if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 || - sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 || - sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) { - // Normal was mangled. Maybe denormals or "not a number" were stored? - // Just reset the normal and silently ignore it. - memset(&facet.normal, 0, sizeof(facet.normal)); - } - } + // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition. + if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 || + sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 || + sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) { + // Normal was mangled. Maybe denormals or "not a number" were stored? + // Just reset the normal and silently ignore it. + memset(&facet.normal, 0, sizeof(facet.normal)); + } + } #if 0 - // Report close to zero vertex coordinates. Due to the nature of the floating point numbers, - // close to zero values may be represented with singificantly higher precision than the rest of the vertices. - // It may be worth to round these numbers to zero during loading to reduce the number of errors reported - // during the STL import. - for (size_t j = 0; j < 3; ++ j) { - if (facet.vertex[j](0) > -1e-12f && facet.vertex[j](0) < 1e-12f) - printf("stl_read: facet %d(0) = %e\r\n", j, facet.vertex[j](0)); - if (facet.vertex[j](1) > -1e-12f && facet.vertex[j](1) < 1e-12f) - printf("stl_read: facet %d(1) = %e\r\n", j, facet.vertex[j](1)); - if (facet.vertex[j](2) > -1e-12f && facet.vertex[j](2) < 1e-12f) - printf("stl_read: facet %d(2) = %e\r\n", j, facet.vertex[j](2)); - } + // Report close to zero vertex coordinates. Due to the nature of the floating point numbers, + // close to zero values may be represented with singificantly higher precision than the rest of the vertices. + // It may be worth to round these numbers to zero during loading to reduce the number of errors reported + // during the STL import. + for (size_t j = 0; j < 3; ++ j) { + if (facet.vertex[j](0) > -1e-12f && facet.vertex[j](0) < 1e-12f) + printf("stl_read: facet %d(0) = %e\r\n", j, facet.vertex[j](0)); + if (facet.vertex[j](1) > -1e-12f && facet.vertex[j](1) < 1e-12f) + printf("stl_read: facet %d(1) = %e\r\n", j, facet.vertex[j](1)); + if (facet.vertex[j](2) > -1e-12f && facet.vertex[j](2) < 1e-12f) + printf("stl_read: facet %d(2) = %e\r\n", j, facet.vertex[j](2)); + } #endif - /* Write the facet into memory. */ - stl->facet_start[i] = facet; - stl_facet_stats(stl, facet, first); - } - stl->stats.size = stl->stats.max - stl->stats.min; - stl->stats.bounding_diameter = stl->stats.size.norm(); - return true; + // Write the facet into memory. + stl->facet_start[i] = facet; + stl_facet_stats(stl, facet, first); + } + + stl->stats.size = stl->stats.max - stl->stats.min; + stl->stats.bounding_diameter = stl->stats.size.norm(); + return true; } bool stl_open(stl_file *stl, const char *file) @@ -277,21 +259,21 @@ void stl_reallocate(stl_file *stl) void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first) { - // While we are going through all of the facets, let's find the - // maximum and minimum values for x, y, and z + // While we are going through all of the facets, let's find the + // maximum and minimum values for x, y, and z - if (first) { - // Initialize the max and min values the first time through - stl->stats.min = facet.vertex[0]; - stl->stats.max = facet.vertex[0]; - stl_vertex diff = (facet.vertex[1] - facet.vertex[0]).cwiseAbs(); - stl->stats.shortest_edge = std::max(diff(0), std::max(diff(1), diff(2))); - first = false; - } + if (first) { + // Initialize the max and min values the first time through + stl->stats.min = facet.vertex[0]; + stl->stats.max = facet.vertex[0]; + stl_vertex diff = (facet.vertex[1] - facet.vertex[0]).cwiseAbs(); + stl->stats.shortest_edge = std::max(diff(0), std::max(diff(1), diff(2))); + first = false; + } - // Now find the max and min values. - for (size_t i = 0; i < 3; ++ i) { - stl->stats.min = stl->stats.min.cwiseMin(facet.vertex[i]); - stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]); - } + // Now find the max and min values. + for (size_t i = 0; i < 3; ++ i) { + stl->stats.min = stl->stats.min.cwiseMin(facet.vertex[i]); + stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]); + } } diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index f4e4dbf0a..bb135db95 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -25,13 +25,14 @@ #include #include +#include + #include "stl.h" static void stl_rotate(float *x, float *y, const double c, const double s); static float get_area(stl_facet *facet); static float get_volume(stl_file *stl); - void stl_verify_neighbors(stl_file *stl) { stl->stats.backwards_edges = 0; @@ -56,7 +57,7 @@ void stl_verify_neighbors(stl_file *stl) } if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) { // These edges should match but they don't. Print results. - printf("edge %d of facet %d doesn't match edge %d of facet %d\n", j, i, vnot + 1, neighbor); + BOOST_LOG_TRIVIAL(info) << "edge " << j << " of facet " << i << " doesn't match edge " << (vnot + 1) << " of facet " << neighbor; stl_write_facet(stl, (char*)"first facet", i); stl_write_facet(stl, (char*)"second facet", neighbor); } @@ -291,123 +292,104 @@ static float get_area(stl_facet *facet) return 0.5f * n.dot(sum); } -void stl_repair(stl_file *stl, - int fixall_flag, - int exact_flag, - int tolerance_flag, - float tolerance, - int increment_flag, - float increment, - int nearby_flag, - int iterations, - int remove_unconnected_flag, - int fill_holes_flag, - int normal_directions_flag, - int normal_values_flag, - int reverse_all_flag, - int verbose_flag) { - - int i; - int last_edges_fixed = 0; +void stl_repair( + stl_file *stl, + bool fixall_flag, + bool exact_flag, + bool tolerance_flag, + float tolerance, + bool increment_flag, + float increment, + bool nearby_flag, + int iterations, + bool remove_unconnected_flag, + bool fill_holes_flag, + bool normal_directions_flag, + bool normal_values_flag, + bool reverse_all_flag, + bool verbose_flag) +{ + if (exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag || fill_holes_flag || normal_directions_flag) { + if (verbose_flag) + printf("Checking exact...\n"); + exact_flag = true; + stl_check_facets_exact(stl); + stl->stats.facets_w_1_bad_edge = (stl->stats.connected_facets_2_edge - stl->stats.connected_facets_3_edge); + stl->stats.facets_w_2_bad_edge = (stl->stats.connected_facets_1_edge - stl->stats.connected_facets_2_edge); + stl->stats.facets_w_3_bad_edge = (stl->stats.number_of_facets - stl->stats.connected_facets_1_edge); + } - if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag - || fill_holes_flag || normal_directions_flag) { - if (verbose_flag) - printf("Checking exact...\n"); - exact_flag = 1; - stl_check_facets_exact(stl); - stl->stats.facets_w_1_bad_edge = - (stl->stats.connected_facets_2_edge - - stl->stats.connected_facets_3_edge); - stl->stats.facets_w_2_bad_edge = - (stl->stats.connected_facets_1_edge - - stl->stats.connected_facets_2_edge); - stl->stats.facets_w_3_bad_edge = - (stl->stats.number_of_facets - - stl->stats.connected_facets_1_edge); - } - - if(nearby_flag || fixall_flag) { - if(!tolerance_flag) { - tolerance = stl->stats.shortest_edge; - } - if(!increment_flag) { - increment = stl->stats.bounding_diameter / 10000.0; + if (nearby_flag || fixall_flag) { + if (! tolerance_flag) + tolerance = stl->stats.shortest_edge; + if (! increment_flag) + increment = stl->stats.bounding_diameter / 10000.0; } - if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { - for(i = 0; i < iterations; i++) { - if(stl->stats.connected_facets_3_edge < - stl->stats.number_of_facets) { - if (verbose_flag) - printf("\ -Checking nearby. Tolerance= %f Iteration=%d of %d...", - tolerance, i + 1, iterations); - stl_check_facets_nearby(stl, tolerance); - if (verbose_flag) - printf(" Fixed %d edges.\n", - stl->stats.edges_fixed - last_edges_fixed); - last_edges_fixed = stl->stats.edges_fixed; - tolerance += increment; - } else { - if (verbose_flag) - printf("\ -All facets connected. No further nearby check necessary.\n"); - break; - } - } - } else { - if (verbose_flag) - printf("All facets connected. No nearby check necessary.\n"); - } - } + if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { + int last_edges_fixed = 0; + for (int i = 0; i < iterations; ++ i) { + if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { + if (verbose_flag) + printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations); + stl_check_facets_nearby(stl, tolerance); + if (verbose_flag) + printf(" Fixed %d edges.\n", stl->stats.edges_fixed - last_edges_fixed); + last_edges_fixed = stl->stats.edges_fixed; + tolerance += increment; + } else { + if (verbose_flag) + printf("All facets connected. No further nearby check necessary.\n"); + break; + } + } + } else if (verbose_flag) + printf("All facets connected. No nearby check necessary.\n"); - if(remove_unconnected_flag || fixall_flag || fill_holes_flag) { - if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { - if (verbose_flag) - printf("Removing unconnected facets...\n"); - stl_remove_unconnected_facets(stl); - } else - if (verbose_flag) - printf("No unconnected need to be removed.\n"); - } + if (remove_unconnected_flag || fixall_flag || fill_holes_flag) { + if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { + if (verbose_flag) + printf("Removing unconnected facets...\n"); + stl_remove_unconnected_facets(stl); + } else if (verbose_flag) + printf("No unconnected need to be removed.\n"); + } - if(fill_holes_flag || fixall_flag) { - if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { - if (verbose_flag) - printf("Filling holes...\n"); - stl_fill_holes(stl); - } else - if (verbose_flag) - printf("No holes need to be filled.\n"); - } + if (fill_holes_flag || fixall_flag) { + if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) { + if (verbose_flag) + printf("Filling holes...\n"); + stl_fill_holes(stl); + } else if (verbose_flag) + printf("No holes need to be filled.\n"); + } - if(reverse_all_flag) { - if (verbose_flag) - printf("Reversing all facets...\n"); - stl_reverse_all_facets(stl); - } + if (reverse_all_flag) { + if (verbose_flag) + printf("Reversing all facets...\n"); + stl_reverse_all_facets(stl); + } - if(normal_directions_flag || fixall_flag) { - if (verbose_flag) - printf("Checking normal directions...\n"); - stl_fix_normal_directions(stl); - } + if (normal_directions_flag || fixall_flag) { + if (verbose_flag) + printf("Checking normal directions...\n"); + stl_fix_normal_directions(stl); + } - if(normal_values_flag || fixall_flag) { - if (verbose_flag) - printf("Checking normal values...\n"); - stl_fix_normal_values(stl); - } + if (normal_values_flag || fixall_flag) { + if (verbose_flag) + printf("Checking normal values...\n"); + stl_fix_normal_values(stl); + } - /* Always calculate the volume. It shouldn't take too long */ - if (verbose_flag) - printf("Calculating volume...\n"); - stl_calculate_volume(stl); + // Always calculate the volume. It shouldn't take too long. + if (verbose_flag) + printf("Calculating volume...\n"); + stl_calculate_volume(stl); - if(exact_flag) { - if (verbose_flag) - printf("Verifying neighbors...\n"); - stl_verify_neighbors(stl); - } + if (exact_flag) { + if (verbose_flag) + printf("Verifying neighbors...\n"); + stl_verify_neighbors(stl); + } } diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 45092f257..b28cb2eda 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -837,7 +837,7 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const return preset_name_dst; // Try to generate another name. char buf[64]; - sprintf(buf, " (%d)", i); + sprintf(buf, " (%d)", (int)i); preset_name_dst = preset_name_src + buf + bundle_name; } } From af5017c46c42305553e42f95c4f62c2cbe27d607 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Jun 2019 21:14:58 +0200 Subject: [PATCH 19/44] admesh refactoring: Use Eigen vec3i for indexed triangles. --- src/admesh/connect.cpp | 10 +- src/admesh/normals.cpp | 308 +++++++++++++++---------------- src/admesh/shared.cpp | 20 +- src/admesh/stl.h | 17 +- src/admesh/stl_io.cpp | 170 ++++++----------- src/admesh/util.cpp | 321 +++++++++++++++------------------ src/libslic3r/Format/3mf.cpp | 2 +- src/libslic3r/Format/AMF.cpp | 2 +- src/libslic3r/TriangleMesh.cpp | 6 +- xs/xsp/TriangleMesh.xsp | 6 +- 10 files changed, 369 insertions(+), 493 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index 5ae03597e..cbd4f1d33 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -196,13 +196,13 @@ struct HashTableEdges { // Hash table on edges std::vector heads; - HashEdge* tail; - int M; + HashEdge* tail; + int M; #ifndef NDEBUG - size_t malloced = 0; - size_t freed = 0; - size_t collisions = 0; + size_t malloced = 0; + size_t freed = 0; + size_t collisions = 0; #endif /* NDEBUG */ private: diff --git a/src/admesh/normals.cpp b/src/admesh/normals.cpp index f20d9e68e..464f9e6d8 100644 --- a/src/admesh/normals.cpp +++ b/src/admesh/normals.cpp @@ -27,19 +27,16 @@ #include "stl.h" -static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag); - static void reverse_facet(stl_file *stl, int facet_num) { - stl->stats.facets_reversed += 1; + ++ stl->stats.facets_reversed; int neighbor[3] = { stl->neighbors_start[facet_num].neighbor[0], stl->neighbors_start[facet_num].neighbor[1], stl->neighbors_start[facet_num].neighbor[2] }; int vnot[3] = { stl->neighbors_start[facet_num].which_vertex_not[0], stl->neighbors_start[facet_num].which_vertex_not[1], stl->neighbors_start[facet_num].which_vertex_not[2] }; // reverse the facet stl_vertex tmp_vertex = stl->facet_start[facet_num].vertex[0]; - stl->facet_start[facet_num].vertex[0] = - stl->facet_start[facet_num].vertex[1]; + stl->facet_start[facet_num].vertex[0] = stl->facet_start[facet_num].vertex[1]; stl->facet_start[facet_num].vertex[1] = tmp_vertex; // fix the vnots of the neighboring facets @@ -64,187 +61,164 @@ static void reverse_facet(stl_file *stl, int facet_num) stl->neighbors_start[facet_num].which_vertex_not[2] = (stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6; } -void stl_fix_normal_directions(stl_file *stl) +// Returns true if the normal was flipped. +static bool check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) { - /* int edge_num;*/ - /* int vnot;*/ - int checked = 0; - int facet_num; - /* int next_facet;*/ - int i; - int j; - struct stl_normal { - int facet_num; - struct stl_normal *next; - }; - struct stl_normal *head; - struct stl_normal *tail; - struct stl_normal *newn; - struct stl_normal *temp; + stl_facet *facet = &stl->facet_start[facet_num]; - int reversed_count = 0; - int id; - int force_exit = 0; + stl_normal normal; + stl_calculate_normal(normal, facet); + stl_normalize_vector(normal); + stl_normal normal_dif = (normal - facet->normal).cwiseAbs(); - // this may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209 - if (stl->stats.number_of_facets == 0) return; + const float eps = 0.001f; + if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { + // Normal is within tolerance. It is not really necessary to change the values here, but just for consistency, I will. + facet->normal = normal; + return false; + } - /* Initialize linked list. */ - head = new stl_normal; - tail = new stl_normal; - head->next = tail; - tail->next = tail; + stl_normal test_norm = facet->normal; + stl_normalize_vector(test_norm); + normal_dif = (normal - test_norm).cwiseAbs(); + if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { + // The normal is not within tolerance, but direction is OK. + if (normal_fix_flag) { + facet->normal = normal; + ++ stl->stats.normals_fixed; + } + return false; + } - /* Initialize list that keeps track of already fixed facets. */ - std::vector norm_sw(stl->stats.number_of_facets, 0); - /* Initialize list that keeps track of reversed facets. */ - std::vector reversed_ids(stl->stats.number_of_facets, 0); - - facet_num = 0; - /* If normal vector is not within tolerance and backwards: - Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances - of it being wrong randomly are low if most of the triangles are right: */ - if (stl_check_normal_vector(stl, 0, 0) == 2) { - reverse_facet(stl, 0); - reversed_ids[reversed_count++] = 0; - } - - /* Say that we've fixed this facet: */ - norm_sw[facet_num] = 1; - checked++; - - for(;;) { - /* Add neighbors_to_list. - Add unconnected neighbors to the list:a */ - for(j = 0; j < 3; j++) { - /* Reverse the neighboring facets if necessary. */ - if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) { - /* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */ - if(stl->neighbors_start[facet_num].neighbor[j] != -1) { - if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] == 1) { - /* trying to modify a facet already marked as fixed, revert all changes made until now and exit (fixes: #716, #574, #413, #269, #262, #259, #230, #228, #206) */ - for (id = reversed_count - 1; id >= 0; --id) { - reverse_facet(stl, reversed_ids[id]); - } - force_exit = 1; - break; - } else { - reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]); - reversed_ids[reversed_count++] = stl->neighbors_start[facet_num].neighbor[j]; - } - } - } - /* If this edge of the facet is connected: */ - if(stl->neighbors_start[facet_num].neighbor[j] != -1) { - /* If we haven't fixed this facet yet, add it to the list: */ - if(norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) { - /* Add node to beginning of list. */ - newn = new stl_normal; - newn->facet_num = stl->neighbors_start[facet_num].neighbor[j]; - newn->next = head->next; - head->next = newn; - } - } - } - - /* an error occourred, quit the for loop and exit */ - if (force_exit) break; - - /* Get next facet to fix from top of list. */ - if(head->next != tail) { - facet_num = head->next->facet_num; - if(norm_sw[facet_num] != 1) { /* If facet is in list mutiple times */ - norm_sw[facet_num] = 1; /* Record this one as being fixed. */ - checked++; - } - temp = head->next; /* Delete this facet from the list. */ - head->next = head->next->next; - delete temp; - } else { /* if we ran out of facets to fix: */ - /* All of the facets in this part have been fixed. */ - stl->stats.number_of_parts += 1; - if(checked >= stl->stats.number_of_facets) { - /* All of the facets have been checked. Bail out. */ - break; - } else { - /* There is another part here. Find it and continue. */ - for(i = 0; i < stl->stats.number_of_facets; i++) { - if(norm_sw[i] == 0) { - /* This is the first facet of the next part. */ - facet_num = i; - if(stl_check_normal_vector(stl, i, 0) == 2) { - reverse_facet(stl, i); - reversed_ids[reversed_count++] = i; - } - - norm_sw[facet_num] = 1; - checked++; - break; - } - } - } - } - } - delete head; - delete tail; + test_norm *= -1.f; + normal_dif = (normal - test_norm).cwiseAbs(); + if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { + // The normal is not within tolerance and backwards. + if (normal_fix_flag) { + facet->normal = normal; + ++ stl->stats.normals_fixed; + } + return true; + } + if (normal_fix_flag) { + facet->normal = normal; + ++ stl->stats.normals_fixed; + } + // Status is unknown. + return false; } -static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) +void stl_fix_normal_directions(stl_file *stl) { - /* Returns 0 if the normal is within tolerance */ - /* Returns 1 if the normal is not within tolerance, but direction is OK */ - /* Returns 2 if the normal is not within tolerance and backwards */ - /* Returns 4 if the status is unknown. */ + // This may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209 + if (stl->stats.number_of_facets == 0) + return; - stl_facet *facet; + struct stl_normal { + int facet_num; + stl_normal *next; + }; - facet = &stl->facet_start[facet_num]; + // Initialize linked list. + stl_normal *head = new stl_normal; + stl_normal *tail = new stl_normal; + head->next = tail; + tail->next = tail; - stl_normal normal; - stl_calculate_normal(normal, facet); - stl_normalize_vector(normal); - stl_normal normal_dif = (normal - facet->normal).cwiseAbs(); + // Initialize list that keeps track of already fixed facets. + std::vector norm_sw(stl->stats.number_of_facets, 0); + // Initialize list that keeps track of reversed facets. + std::vector reversed_ids(stl->stats.number_of_facets, 0); - const float eps = 0.001f; - if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { - /* It is not really necessary to change the values here */ - /* but just for consistency, I will. */ - facet->normal = normal; - return 0; - } + int facet_num = 0; + int reversed_count = 0; + // If normal vector is not within tolerance and backwards: + // Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances + // of it being wrong randomly are low if most of the triangles are right: + if (check_normal_vector(stl, 0, 0)) { + reverse_facet(stl, 0); + reversed_ids[reversed_count ++] = 0; + } - stl_normal test_norm = facet->normal; - stl_normalize_vector(test_norm); - normal_dif = (normal - test_norm).cwiseAbs(); - if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { - if(normal_fix_flag) { - facet->normal = normal; - stl->stats.normals_fixed += 1; - } - return 1; - } + // Say that we've fixed this facet: + norm_sw[facet_num] = 1; + int checked = 1; - test_norm *= -1.f; - normal_dif = (normal - test_norm).cwiseAbs(); - if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) { - // Facet is backwards. - if(normal_fix_flag) { - facet->normal = normal; - stl->stats.normals_fixed += 1; - } - return 2; - } - if(normal_fix_flag) { - facet->normal = normal; - stl->stats.normals_fixed += 1; - } - return 4; + for (;;) { + // Add neighbors_to_list. Add unconnected neighbors to the list. + bool force_exit = false; + for (int j = 0; j < 3; ++ j) { + // Reverse the neighboring facets if necessary. + if (stl->neighbors_start[facet_num].which_vertex_not[j] > 2) { + // If the facet has a neighbor that is -1, it means that edge isn't shared by another facet + if (stl->neighbors_start[facet_num].neighbor[j] != -1) { + if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] == 1) { + // trying to modify a facet already marked as fixed, revert all changes made until now and exit (fixes: #716, #574, #413, #269, #262, #259, #230, #228, #206) + for (int id = reversed_count - 1; id >= 0; -- id) + reverse_facet(stl, reversed_ids[id]); + force_exit = true; + break; + } + reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]); + reversed_ids[reversed_count ++] = stl->neighbors_start[facet_num].neighbor[j]; + } + } + // If this edge of the facet is connected: + if (stl->neighbors_start[facet_num].neighbor[j] != -1) { + // If we haven't fixed this facet yet, add it to the list: + if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) { + // Add node to beginning of list. + stl_normal *newn = new stl_normal; + newn->facet_num = stl->neighbors_start[facet_num].neighbor[j]; + newn->next = head->next; + head->next = newn; + } + } + } + + // an error occourred, quit the for loop and exit + if (force_exit) + break; + + // Get next facet to fix from top of list. + if (head->next != tail) { + facet_num = head->next->facet_num; + if (norm_sw[facet_num] != 1) { // If facet is in list mutiple times + norm_sw[facet_num] = 1; // Record this one as being fixed. + ++ checked; + } + stl_normal *temp = head->next; // Delete this facet from the list. + head->next = head->next->next; + delete temp; + } else { // If we ran out of facets to fix: All of the facets in this part have been fixed. + ++ stl->stats.number_of_parts; + if (checked >= stl->stats.number_of_facets) + // All of the facets have been checked. Bail out. + break; + // There is another part here. Find it and continue. + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + if (norm_sw[i] == 0) { + // This is the first facet of the next part. + facet_num = i; + if (check_normal_vector(stl, i, 0)) { + reverse_facet(stl, i); + reversed_ids[reversed_count++] = i; + } + norm_sw[facet_num] = 1; + ++ checked; + break; + } + } + } + + delete head; + delete tail; } void stl_fix_normal_values(stl_file *stl) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) - stl_check_normal_vector(stl, i, 1); + check_normal_vector(stl, i, 1); } void stl_reverse_all_facets(stl_file *stl) diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index fe6d5e656..78179e863 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -33,7 +33,7 @@ void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its) { // 3 indices to vertex per face - its.indices.assign(stl->stats.number_of_facets, v_indices_struct()); + its.indices.assign(stl->stats.number_of_facets, stl_triangle_vertex_indices(-1, -1, -1)); // Shared vertices (3D coordinates) its.vertices.clear(); its.vertices.reserve(stl->stats.number_of_facets / 2); @@ -46,7 +46,7 @@ void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its) for (uint32_t facet_idx = 0; facet_idx < stl->stats.number_of_facets; ++ facet_idx) { for (int j = 0; j < 3; ++ j) { - if (its.indices[facet_idx].vertex[j] != -1) + if (its.indices[facet_idx][j] != -1) // Shared vertex was already assigned. continue; // Create a new shared vertex. @@ -84,7 +84,7 @@ void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its) next_edge = pivot_vertex; } } - its.indices[facet_in_fan_idx].vertex[pivot_vertex] = its.vertices.size() - 1; + its.indices[facet_in_fan_idx][pivot_vertex] = its.vertices.size() - 1; fan_traversal_facet_visited[facet_in_fan_idx] = fan_traversal_stamp; // next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge! @@ -139,7 +139,7 @@ bool its_write_off(const indexed_triangle_set &its, const char *file) for (int i = 0; i < its.vertices.size(); ++ i) fprintf(fp, "\t%f %f %f\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2)); for (uint32_t i = 0; i < its.indices.size(); ++ i) - fprintf(fp, "\t3 %d %d %d\n", its.indices[i].vertex[0], its.indices[i].vertex[1], its.indices[i].vertex[2]); + fprintf(fp, "\t3 %d %d %d\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]); fclose(fp); return true; } @@ -177,8 +177,8 @@ bool its_write_vrml(const indexed_triangle_set &its, const char *file) fprintf(fp, "\t\t\tcoordIndex [\n"); for (size_t i = 0; i + 1 < its.indices.size(); ++ i) - fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", its.indices[i].vertex[0], its.indices[i].vertex[1], its.indices[i].vertex[2]); - fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", its.indices[i].vertex[0], its.indices[i].vertex[1], its.indices[i].vertex[2]); + fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]); + fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]); fprintf(fp, "\t\t}\n"); fprintf(fp, "\t}\n"); fprintf(fp, "}\n"); @@ -198,7 +198,7 @@ bool its_write_obj(const indexed_triangle_set &its, const char *file) for (size_t i = 0; i < its.vertices.size(); ++ i) fprintf(fp, "v %f %f %f\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2)); for (size_t i = 0; i < its.indices.size(); ++ i) - fprintf(fp, "f %d %d %d\n", its.indices[i].vertex[0]+1, its.indices[i].vertex[1]+1, its.indices[i].vertex[2]+1); + fprintf(fp, "f %d %d %d\n", its.indices[i][0]+1, its.indices[i][1]+1, its.indices[i][2]+1); fclose(fp); return true; } @@ -220,7 +220,7 @@ bool stl_validate(const stl_file *stl, const indexed_triangle_set &its) // Verify validity of neighborship data. for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) { const stl_neighbors &nbr = stl->neighbors_start[facet_idx]; - const int *vertices = (its.indices.empty()) ? nullptr : its.indices[facet_idx].vertex; + const int *vertices = (its.indices.empty()) ? nullptr : its.indices[facet_idx]; for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) { int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx]; assert(nbr_face < (int)stl->stats.number_of_facets); @@ -237,10 +237,10 @@ bool stl_validate(const stl_file *stl, const indexed_triangle_set &its) // Has shared vertices. if (nbr_vnot < 3) { // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are correctly oriented. - assert((its.indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[nbr_idx])); + assert((its.indices[nbr_face][(nbr_vnot + 1) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face][(nbr_vnot + 2) % 3] == vertices[nbr_idx])); } else { // Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped. - assert((its.indices[nbr_face].vertex[(nbr_vnot + 2) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face].vertex[(nbr_vnot + 1) % 3] == vertices[nbr_idx])); + assert((its.indices[nbr_face][(nbr_vnot + 2) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face][(nbr_vnot + 1) % 3] == vertices[nbr_idx])); } } } diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 500d6bfdb..fce23eb3f 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -41,6 +41,7 @@ typedef Eigen::Matrix stl_vertex; typedef Eigen::Matrix stl_normal; +typedef Eigen::Matrix stl_triangle_vertex_indices; static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); @@ -68,12 +69,6 @@ static_assert(sizeof(stl_facet) >= SIZEOF_STL_FACET, "size of stl_facet incorrec typedef enum {binary, ascii, inmemory} stl_type; -struct stl_edge { - stl_vertex p1; - stl_vertex p2; - int facet_number; -}; - struct stl_neighbors { stl_neighbors() { reset(); } void reset() { @@ -93,12 +88,6 @@ struct stl_neighbors { char which_vertex_not[3]; }; -struct v_indices_struct { - // -1 means no vertex index has been assigned yet - v_indices_struct() { vertex[0] = -1; vertex[1] = -1; vertex[2] = -1; } - int vertex[3]; -}; - struct stl_stats { char header[81]; stl_type type; @@ -137,8 +126,8 @@ struct stl_file { struct indexed_triangle_set { void clear() { indices.clear(); vertices.clear(); } - std::vector indices; - std::vector vertices; + std::vector indices; + std::vector vertices; }; extern bool stl_open(stl_file *stl, const char *file); diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp index dc4e4a7db..7e181716c 100644 --- a/src/admesh/stl_io.cpp +++ b/src/admesh/stl_io.cpp @@ -22,94 +22,55 @@ #include #include -#include "stl.h" #include #include #include -#if !defined(SEEK_SET) -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 -#endif +#include "stl.h" void stl_stats_out(stl_file *stl, FILE *file, char *input_file) { - /* this is here for Slic3r, without our config.h - it won't use this part of the code anyway */ + // This is here for Slic3r, without our config.h it won't use this part of the code anyway. #ifndef VERSION #define VERSION "unknown" #endif - fprintf(file, "\n\ -================= Results produced by ADMesh version " VERSION " ================\n"); - fprintf(file, "\ -Input file : %s\n", input_file); - if(stl->stats.type == binary) { - fprintf(file, "\ -File type : Binary STL file\n"); - } else { - fprintf(file, "\ -File type : ASCII STL file\n"); - } - fprintf(file, "\ -Header : %s\n", stl->stats.header); - fprintf(file, "============== Size ==============\n"); - fprintf(file, "Min X = % f, Max X = % f\n", - stl->stats.min(0), stl->stats.max(0)); - fprintf(file, "Min Y = % f, Max Y = % f\n", - stl->stats.min(1), stl->stats.max(1)); - fprintf(file, "Min Z = % f, Max Z = % f\n", - stl->stats.min(2), stl->stats.max(2)); - - fprintf(file, "\ -========= Facet Status ========== Original ============ Final ====\n"); - fprintf(file, "\ -Number of facets : %5d %5d\n", - stl->stats.original_num_facets, stl->stats.number_of_facets); - fprintf(file, "\ -Facets with 1 disconnected edge : %5d %5d\n", - stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge - - stl->stats.connected_facets_3_edge); - fprintf(file, "\ -Facets with 2 disconnected edges : %5d %5d\n", - stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge - - stl->stats.connected_facets_2_edge); - fprintf(file, "\ -Facets with 3 disconnected edges : %5d %5d\n", - stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - - stl->stats.connected_facets_1_edge); - fprintf(file, "\ -Total disconnected facets : %5d %5d\n", - stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge + - stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - - stl->stats.connected_facets_3_edge); - - fprintf(file, - "=== Processing Statistics === ===== Other Statistics =====\n"); - fprintf(file, "\ -Number of parts : %5d Volume : % f\n", - stl->stats.number_of_parts, stl->stats.volume); - fprintf(file, "\ -Degenerate facets : %5d\n", stl->stats.degenerate_facets); - fprintf(file, "\ -Edges fixed : %5d\n", stl->stats.edges_fixed); - fprintf(file, "\ -Facets removed : %5d\n", stl->stats.facets_removed); - fprintf(file, "\ -Facets added : %5d\n", stl->stats.facets_added); - fprintf(file, "\ -Facets reversed : %5d\n", stl->stats.facets_reversed); - fprintf(file, "\ -Backwards edges : %5d\n", stl->stats.backwards_edges); - fprintf(file, "\ -Normals fixed : %5d\n", stl->stats.normals_fixed); + fprintf(file, "\n================= Results produced by ADMesh version " VERSION " ================\n"); + fprintf(file, "Input file : %s\n", input_file); + if (stl->stats.type == binary) + fprintf(file, "File type : Binary STL file\n"); + else + fprintf(file, "File type : ASCII STL file\n"); + fprintf(file, "Header : %s\n", stl->stats.header); + fprintf(file, "============== Size ==============\n"); + fprintf(file, "Min X = % f, Max X = % f\n", stl->stats.min(0), stl->stats.max(0)); + fprintf(file, "Min Y = % f, Max Y = % f\n", stl->stats.min(1), stl->stats.max(1)); + fprintf(file, "Min Z = % f, Max Z = % f\n", stl->stats.min(2), stl->stats.max(2)); + fprintf(file, "========= Facet Status ========== Original ============ Final ====\n"); + fprintf(file, "Number of facets : %5d %5d\n", stl->stats.original_num_facets, stl->stats.number_of_facets); + fprintf(file, "Facets with 1 disconnected edge : %5d %5d\n", + stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge - stl->stats.connected_facets_3_edge); + fprintf(file, "Facets with 2 disconnected edges : %5d %5d\n", + stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge - stl->stats.connected_facets_2_edge); + fprintf(file, "Facets with 3 disconnected edges : %5d %5d\n", + stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - stl->stats.connected_facets_1_edge); + fprintf(file, "Total disconnected facets : %5d %5d\n", + stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge + stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - stl->stats.connected_facets_3_edge); + fprintf(file, "=== Processing Statistics === ===== Other Statistics =====\n"); + fprintf(file, "Number of parts : %5d Volume : %f\n", stl->stats.number_of_parts, stl->stats.volume); + fprintf(file, "Degenerate facets : %5d\n", stl->stats.degenerate_facets); + fprintf(file, "Edges fixed : %5d\n", stl->stats.edges_fixed); + fprintf(file, "Facets removed : %5d\n", stl->stats.facets_removed); + fprintf(file, "Facets added : %5d\n", stl->stats.facets_added); + fprintf(file, "Facets reversed : %5d\n", stl->stats.facets_reversed); + fprintf(file, "Backwards edges : %5d\n", stl->stats.backwards_edges); + fprintf(file, "Normals fixed : %5d\n", stl->stats.normals_fixed); } bool stl_write_ascii(stl_file *stl, const char *file, const char *label) { FILE *fp = boost::nowide::fopen(file, "w"); - if (fp == NULL) { + if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "stl_write_ascii: Couldn't open " << file << " for writing"; return false; } @@ -117,19 +78,11 @@ bool stl_write_ascii(stl_file *stl, const char *file, const char *label) fprintf(fp, "solid %s\n", label); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { - fprintf(fp, " facet normal % .8E % .8E % .8E\n", - stl->facet_start[i].normal(0), stl->facet_start[i].normal(1), - stl->facet_start[i].normal(2)); + fprintf(fp, " facet normal % .8E % .8E % .8E\n", stl->facet_start[i].normal(0), stl->facet_start[i].normal(1), stl->facet_start[i].normal(2)); fprintf(fp, " outer loop\n"); - fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), - stl->facet_start[i].vertex[0](2)); - fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), - stl->facet_start[i].vertex[1](2)); - fprintf(fp, " vertex % .8E % .8E % .8E\n", - stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), - stl->facet_start[i].vertex[2](2)); + fprintf(fp, " vertex % .8E % .8E % .8E\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2)); + fprintf(fp, " vertex % .8E % .8E % .8E\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2)); + fprintf(fp, " vertex % .8E % .8E % .8E\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2)); fprintf(fp, " endloop\n"); fprintf(fp, " endfacet\n"); } @@ -142,7 +95,7 @@ bool stl_write_ascii(stl_file *stl, const char *file, const char *label) bool stl_print_neighbors(stl_file *stl, char *file) { FILE *fp = boost::nowide::fopen(file, "w"); - if (fp == NULL) { + if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "stl_print_neighbors: Couldn't open " << file << " for writing"; return false; } @@ -175,7 +128,7 @@ void stl_internal_reverse_quads(char *buf, size_t cnt) bool stl_write_binary(stl_file *stl, const char *file, const char *label) { FILE *fp = boost::nowide::fopen(file, "wb"); - if (fp == NULL) { + if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "stl_write_binary: Couldn't open " << file << " for writing"; return false; } @@ -184,6 +137,9 @@ bool stl_write_binary(stl_file *stl, const char *file, const char *label) for (size_t i = strlen(label); i < LABEL_SIZE; ++ i) putc(0, fp); +#if !defined(SEEK_SET) + #define SEEK_SET 0 +#endif fseek(fp, LABEL_SIZE, SEEK_SET); #ifdef BOOST_LITTLE_ENDIAN fwrite(&stl->stats.number_of_facets, 4, 1, fp); @@ -242,7 +198,7 @@ bool stl_write_quad_object(stl_file *stl, char *file) stl_vertex color; FILE *fp = boost::nowide::fopen(file, "w"); - if (fp == NULL) { + if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "stl_write_quad_object: Couldn't open " << file << " for writing"; return false; } @@ -255,22 +211,10 @@ bool stl_write_quad_object(stl_file *stl, char *file) case 2: color = uncon_2_color; break; default: color = uncon_3_color; } - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[0](0), - stl->facet_start[i].vertex[0](1), - stl->facet_start[i].vertex[0](2), color(0), color(1), color(2)); - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[1](0), - stl->facet_start[i].vertex[1](1), - stl->facet_start[i].vertex[1](2), color(0), color(1), color(2)); - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[2](0), - stl->facet_start[i].vertex[2](1), - stl->facet_start[i].vertex[2](2), color(0), color(1), color(2)); - fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", - stl->facet_start[i].vertex[2](0), - stl->facet_start[i].vertex[2](1), - stl->facet_start[i].vertex[2](2), color(0), color(1), color(2)); + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2), color(0), color(1), color(2)); + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2), color(0), color(1), color(2)); + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2), color(0), color(1), color(2)); + fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2), color(0), color(1), color(2)); } fclose(fp); return true; @@ -279,7 +223,7 @@ bool stl_write_quad_object(stl_file *stl, char *file) bool stl_write_dxf(stl_file *stl, const char *file, char *label) { FILE *fp = boost::nowide::fopen(file, "w"); - if (fp == NULL) { + if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "stl_write_quad_object: Couldn't open " << file << " for writing"; return false; } @@ -292,20 +236,12 @@ bool stl_write_dxf(stl_file *stl, const char *file, char *label) fprintf(fp, "0\nSECTION\n2\nENTITIES\n"); - for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { fprintf(fp, "0\n3DFACE\n8\n0\n"); - fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", - stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), - stl->facet_start[i].vertex[0](2)); - fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", - stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), - stl->facet_start[i].vertex[1](2)); - fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", - stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), - stl->facet_start[i].vertex[2](2)); - fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", - stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), - stl->facet_start[i].vertex[2](2)); + fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2)); + fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2)); + fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2)); + fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2)); } fprintf(fp, "0\nENDSEC\n0\nEOF\n"); diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index bb135db95..6fff8a8ed 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -29,16 +29,17 @@ #include "stl.h" -static void stl_rotate(float *x, float *y, const double c, const double s); -static float get_area(stl_facet *facet); -static float get_volume(stl_file *stl); - void stl_verify_neighbors(stl_file *stl) { stl->stats.backwards_edges = 0; for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { for (int j = 0; j < 3; ++ j) { + struct stl_edge { + stl_vertex p1; + stl_vertex p2; + int facet_number; + }; stl_edge edge_a; edge_a.p1 = stl->facet_start[i].vertex[j]; edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3]; @@ -67,164 +68,140 @@ void stl_verify_neighbors(stl_file *stl) void stl_translate(stl_file *stl, float x, float y, float z) { - stl_vertex new_min(x, y, z); - stl_vertex shift = new_min - stl->stats.min; - for (int i = 0; i < stl->stats.number_of_facets; ++ i) - for (int j = 0; j < 3; ++ j) - stl->facet_start[i].vertex[j] += shift; - stl->stats.min = new_min; - stl->stats.max += shift; + stl_vertex new_min(x, y, z); + stl_vertex shift = new_min - stl->stats.min; + for (int i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + stl->facet_start[i].vertex[j] += shift; + stl->stats.min = new_min; + stl->stats.max += shift; } /* Translates the stl by x,y,z, relatively from wherever it is currently */ void stl_translate_relative(stl_file *stl, float x, float y, float z) { - stl_vertex shift(x, y, z); - for (int i = 0; i < stl->stats.number_of_facets; ++ i) - for (int j = 0; j < 3; ++ j) - stl->facet_start[i].vertex[j] += shift; - stl->stats.min += shift; - stl->stats.max += shift; + stl_vertex shift(x, y, z); + for (int i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + stl->facet_start[i].vertex[j] += shift; + stl->stats.min += shift; + stl->stats.max += shift; } void stl_scale_versor(stl_file *stl, const stl_vertex &versor) { - // Scale extents. - auto s = versor.array(); - stl->stats.min.array() *= s; - stl->stats.max.array() *= s; - // Scale size. - stl->stats.size.array() *= s; - // Scale volume. - if (stl->stats.volume > 0.0) - stl->stats.volume *= versor(0) * versor(1) * versor(2); - // Scale the mesh. - for (int i = 0; i < stl->stats.number_of_facets; ++ i) - for (int j = 0; j < 3; ++ j) - stl->facet_start[i].vertex[j].array() *= s; + // Scale extents. + auto s = versor.array(); + stl->stats.min.array() *= s; + stl->stats.max.array() *= s; + // Scale size. + stl->stats.size.array() *= s; + // Scale volume. + if (stl->stats.volume > 0.0) + stl->stats.volume *= versor(0) * versor(1) * versor(2); + // Scale the mesh. + for (int i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + stl->facet_start[i].vertex[j].array() *= s; } static void calculate_normals(stl_file *stl) { - stl_normal normal; - for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) { - stl_calculate_normal(normal, &stl->facet_start[i]); - stl_normalize_vector(normal); - stl->facet_start[i].normal = normal; - } + stl_normal normal; + for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { + stl_calculate_normal(normal, &stl->facet_start[i]); + stl_normalize_vector(normal); + stl->facet_start[i].normal = normal; + } } -void -stl_rotate_x(stl_file *stl, float angle) { - int i; - int j; - double radian_angle = (angle / 180.0) * M_PI; - double c = cos(radian_angle); - double s = sin(radian_angle); - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl_rotate(&stl->facet_start[i].vertex[j](1), - &stl->facet_start[i].vertex[j](2), c, s); - } - } - stl_get_size(stl); - calculate_normals(stl); +static void rotate_point_2d(float *x, float *y, const double c, const double s) +{ + double xold = *x; + double yold = *y; + *x = float(c * xold - s * yold); + *y = float(s * xold + c * yold); } -void -stl_rotate_y(stl_file *stl, float angle) { - int i; - int j; - double radian_angle = (angle / 180.0) * M_PI; - double c = cos(radian_angle); - double s = sin(radian_angle); - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl_rotate(&stl->facet_start[i].vertex[j](2), - &stl->facet_start[i].vertex[j](0), c, s); - } - } - stl_get_size(stl); - calculate_normals(stl); +void stl_rotate_x(stl_file *stl, float angle) +{ + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + rotate_point_2d(&stl->facet_start[i].vertex[j](1), &stl->facet_start[i].vertex[j](2), c, s); + stl_get_size(stl); + calculate_normals(stl); } -void -stl_rotate_z(stl_file *stl, float angle) { - int i; - int j; - double radian_angle = (angle / 180.0) * M_PI; - double c = cos(radian_angle); - double s = sin(radian_angle); - - for(i = 0; i < stl->stats.number_of_facets; i++) { - for(j = 0; j < 3; j++) { - stl_rotate(&stl->facet_start[i].vertex[j](0), - &stl->facet_start[i].vertex[j](1), c, s); - } - } - stl_get_size(stl); - calculate_normals(stl); +void stl_rotate_y(stl_file *stl, float angle) +{ + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + rotate_point_2d(&stl->facet_start[i].vertex[j](2), &stl->facet_start[i].vertex[j](0), c, s); + stl_get_size(stl); + calculate_normals(stl); } - - -static void -stl_rotate(float *x, float *y, const double c, const double s) { - double xold = *x; - double yold = *y; - *x = float(c * xold - s * yold); - *y = float(s * xold + c * yold); +void stl_rotate_z(stl_file *stl, float angle) +{ + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + rotate_point_2d(&stl->facet_start[i].vertex[j](0), &stl->facet_start[i].vertex[j](1), c, s); + stl_get_size(stl); + calculate_normals(stl); } void stl_get_size(stl_file *stl) { - if (stl->stats.number_of_facets == 0) - return; - stl->stats.min = stl->facet_start[0].vertex[0]; - stl->stats.max = stl->stats.min; - for (int i = 0; i < stl->stats.number_of_facets; ++ i) { - const stl_facet &face = stl->facet_start[i]; - for (int j = 0; j < 3; ++ j) { - stl->stats.min = stl->stats.min.cwiseMin(face.vertex[j]); - stl->stats.max = stl->stats.max.cwiseMax(face.vertex[j]); - } - } - stl->stats.size = stl->stats.max - stl->stats.min; - stl->stats.bounding_diameter = stl->stats.size.norm(); + if (stl->stats.number_of_facets == 0) + return; + stl->stats.min = stl->facet_start[0].vertex[0]; + stl->stats.max = stl->stats.min; + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + const stl_facet &face = stl->facet_start[i]; + for (int j = 0; j < 3; ++ j) { + stl->stats.min = stl->stats.min.cwiseMin(face.vertex[j]); + stl->stats.max = stl->stats.max.cwiseMax(face.vertex[j]); + } + } + stl->stats.size = stl->stats.max - stl->stats.min; + stl->stats.bounding_diameter = stl->stats.size.norm(); } void stl_mirror_xy(stl_file *stl) { - for(int i = 0; i < stl->stats.number_of_facets; i++) { - for(int j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j](2) *= -1.0; - } - } - float temp_size = stl->stats.min(2); - stl->stats.min(2) = stl->stats.max(2); - stl->stats.max(2) = temp_size; - stl->stats.min(2) *= -1.0; - stl->stats.max(2) *= -1.0; - stl_reverse_all_facets(stl); - stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; ++ j) + stl->facet_start[i].vertex[j](2) *= -1.0; + float temp_size = stl->stats.min(2); + stl->stats.min(2) = stl->stats.max(2); + stl->stats.max(2) = temp_size; + stl->stats.min(2) *= -1.0; + stl->stats.max(2) *= -1.0; + stl_reverse_all_facets(stl); + stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ } void stl_mirror_yz(stl_file *stl) { - for (int i = 0; i < stl->stats.number_of_facets; i++) { - for (int j = 0; j < 3; j++) { - stl->facet_start[i].vertex[j](0) *= -1.0; - } - } - float temp_size = stl->stats.min(0); - stl->stats.min(0) = stl->stats.max(0); - stl->stats.max(0) = temp_size; - stl->stats.min(0) *= -1.0; - stl->stats.max(0) *= -1.0; - stl_reverse_all_facets(stl); - stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) + for (int j = 0; j < 3; j++) + stl->facet_start[i].vertex[j](0) *= -1.0; + float temp_size = stl->stats.min(0); + stl->stats.min(0) = stl->stats.max(0); + stl->stats.max(0) = temp_size; + stl->stats.min(0) *= -1.0; + stl->stats.max(0) *= -1.0; + stl_reverse_all_facets(stl); + stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */ } void stl_mirror_xz(stl_file *stl) @@ -241,55 +218,55 @@ void stl_mirror_xz(stl_file *stl) stl->stats.facets_reversed -= stl->stats.number_of_facets; // for not altering stats } +static float get_area(stl_facet *facet) +{ + /* cast to double before calculating cross product because large coordinates + can result in overflowing product + (bad area is responsible for bad volume and bad facets reversal) */ + double cross[3][3]; + for (int i = 0; i < 3; i++) { + cross[i][0]=(((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](2)) - + ((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](1))); + cross[i][1]=(((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](0)) - + ((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](2))); + cross[i][2]=(((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](1)) - + ((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](0))); + } + + stl_normal sum; + sum(0) = cross[0][0] + cross[1][0] + cross[2][0]; + sum(1) = cross[0][1] + cross[1][1] + cross[2][1]; + sum(2) = cross[0][2] + cross[1][2] + cross[2][2]; + + // This should already be done. But just in case, let's do it again. + //FIXME this is questionable. the "sum" normal should be accurate, while the normal "n" may be calculated with a low accuracy. + stl_normal n; + stl_calculate_normal(n, facet); + stl_normalize_vector(n); + return 0.5f * n.dot(sum); +} + static float get_volume(stl_file *stl) { - // Choose a point, any point as the reference. - stl_vertex p0 = stl->facet_start[0].vertex[0]; - float volume = 0.f; - for(uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { - // Do dot product to get distance from point to plane. - float height = stl->facet_start[i].normal.dot(stl->facet_start[i].vertex[0] - p0); - float area = get_area(&stl->facet_start[i]); - volume += (area * height) / 3.0f; - } - return volume; + // Choose a point, any point as the reference. + stl_vertex p0 = stl->facet_start[0].vertex[0]; + float volume = 0.f; + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { + // Do dot product to get distance from point to plane. + float height = stl->facet_start[i].normal.dot(stl->facet_start[i].vertex[0] - p0); + float area = get_area(&stl->facet_start[i]); + volume += (area * height) / 3.0f; + } + return volume; } void stl_calculate_volume(stl_file *stl) { - stl->stats.volume = get_volume(stl); - if(stl->stats.volume < 0.0) { - stl_reverse_all_facets(stl); - stl->stats.volume = -stl->stats.volume; - } -} - -static float get_area(stl_facet *facet) -{ - /* cast to double before calculating cross product because large coordinates - can result in overflowing product - (bad area is responsible for bad volume and bad facets reversal) */ - double cross[3][3]; - for (int i = 0; i < 3; i++) { - cross[i][0]=(((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](2)) - - ((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](1))); - cross[i][1]=(((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](0)) - - ((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](2))); - cross[i][2]=(((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](1)) - - ((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](0))); - } - - stl_normal sum; - sum(0) = cross[0][0] + cross[1][0] + cross[2][0]; - sum(1) = cross[0][1] + cross[1][1] + cross[2][1]; - sum(2) = cross[0][2] + cross[1][2] + cross[2][2]; - - // This should already be done. But just in case, let's do it again. - //FIXME this is questionable. the "sum" normal should be accurate, while the normal "n" may be calculated with a low accuracy. - stl_normal n; - stl_calculate_normal(n, facet); - stl_normalize_vector(n); - return 0.5f * n.dot(sum); + stl->stats.volume = get_volume(stl); + if (stl->stats.volume < 0.0) { + stl_reverse_all_facets(stl); + stl->stats.volume = -stl->stats.volume; + } } void stl_repair( diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 8298fe222..654426528 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1928,7 +1928,7 @@ namespace Slic3r { stream << " <" << TRIANGLE_TAG << " "; for (int j = 0; j < 3; ++j) { - stream << "v" << j + 1 << "=\"" << its.indices[i].vertex[j] + volume_it->second.first_vertex_id << "\" "; + stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" "; } stream << "/>\n"; } diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index dcd913864..48887bc78 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -958,7 +958,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) for (size_t i = 0; i < (int)volume->mesh.its.indices.size(); ++i) { stream << " \n"; for (int j = 0; j < 3; ++j) - stream << " " << volume->mesh.its.indices[i].vertex[j] + vertices_offset << "\n"; + stream << " " << volume->mesh.its.indices[i][j] + vertices_offset << "\n"; stream << " \n"; } stream << " \n"; diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index ae35c8a5b..5b0ecbd00 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -614,8 +614,8 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) for (int i = 0; i < 3; ++ i) { EdgeToFace &e2f = edges_map[facet_idx*3+i]; - e2f.vertex_low = this->mesh->its.indices[facet_idx].vertex[i]; - e2f.vertex_high = this->mesh->its.indices[facet_idx].vertex[(i + 1) % 3]; + e2f.vertex_low = this->mesh->its.indices[facet_idx][i]; + e2f.vertex_high = this->mesh->its.indices[facet_idx][(i + 1) % 3]; e2f.face = facet_idx; // 1 based indexing, to be always strictly positive. e2f.face_edge = i + 1; @@ -852,7 +852,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( // Reorder vertices so that the first one is the one with lowest Z. // This is needed to get all intersection lines in a consistent order // (external on the right of the line) - const int *vertices = this->mesh->its.indices[facet_idx].vertex; + const stl_triangle_vertex_indices &vertices = this->mesh->its.indices[facet_idx]; int i = (facet.vertex[1].z() == min_z) ? 1 : ((facet.vertex[2].z() == min_z) ? 2 : 0); // These are used only if the cut plane is tilted: diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 3e90bfefd..f3153665c 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -129,9 +129,9 @@ TriangleMesh::facets() AV* facet = newAV(); av_store(facets, i, newRV_noinc((SV*)facet)); av_extend(facet, 2); - av_store(facet, 0, newSVnv(THIS->its.indices[i].vertex[0])); - av_store(facet, 1, newSVnv(THIS->its.indices[i].vertex[1])); - av_store(facet, 2, newSVnv(THIS->its.indices[i].vertex[2])); + av_store(facet, 0, newSVnv(THIS->its.indices[i][0])); + av_store(facet, 1, newSVnv(THIS->its.indices[i][1])); + av_store(facet, 2, newSVnv(THIS->its.indices[i][2])); } RETVAL = newRV_noinc((SV*)facets); From 0cb5b57c5cf6661f20ab0b3fca3a85ce3b9c790d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Jun 2019 22:43:21 +0200 Subject: [PATCH 20/44] SLA gimzmo: Sharing the Mesh's indexed triangle set with IGL AABB structure directly, without having to make a copy. --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 61 ++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 7 ++- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index ae017f7d1..89475a058 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -27,6 +27,7 @@ GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_i : GLGizmoBase(parent, sprite_id) #endif // ENABLE_SVG_ICONS , m_quadric(nullptr) + , m_its(nullptr) { m_quadric = ::gluNewQuadric(); if (m_quadric != nullptr) @@ -379,36 +380,24 @@ bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const bool GLGizmoSlaSupports::is_mesh_update_necessary() const { return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) - && ((m_model_object->id() != m_current_mesh_model_id) || m_V.size()==0); + && ((m_model_object->id() != m_current_mesh_model_id) || m_its == nullptr); } void GLGizmoSlaSupports::update_mesh() { wxBusyCursor wait; - Eigen::MatrixXf& V = m_V; - Eigen::MatrixXi& F = m_F; - // We rely on SLA model object having a single volume, // this way we can use that mesh directly. // This mesh does not account for the possible Z up SLA offset. m_mesh = &m_model_object->volumes.front()->mesh; const_cast(m_mesh)->require_shared_vertices(); // TriangleMeshSlicer needs this - const stl_file& stl = m_mesh->stl; - V.resize(3 * stl.stats.number_of_facets, 3); - F.resize(stl.stats.number_of_facets, 3); - for (unsigned int i=0; i(3 * i + 0, 0) = facet.vertex[0]; - V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1]; - V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2]; - F(i, 0) = 3*i+0; - F(i, 1) = 3*i+1; - F(i, 2) = 3*i+2; - } + m_its = &m_mesh->its; m_current_mesh_model_id = m_model_object->id(); m_editing_mode = false; - m_AABB = igl::AABB(); - m_AABB.init(m_V, m_F); + m_AABB.deinit(); + m_AABB.init( + MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3), + MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3)); } // Unprojects the mouse position on the mesh and return the hit point and normal of the facet. @@ -416,7 +405,7 @@ void GLGizmoSlaSupports::update_mesh() std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) { // if the gizmo doesn't have the V, F structures for igl, calculate them first: - if (m_V.size() == 0) + if (m_its == nullptr) update_mesh(); const Camera& camera = m_parent.get_camera(); @@ -442,7 +431,10 @@ std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse point1 = inv * point1; point2 = inv * point2; - if (!m_AABB.intersect_ray(m_V, m_F, point1.cast(), (point2-point1).cast(), hits)) + if (!m_AABB.intersect_ray( + MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3), + MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3), + point1.cast(), (point2-point1).cast(), hits)) throw std::invalid_argument("unproject_on_mesh(): No intersection found."); std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); @@ -457,9 +449,9 @@ std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse igl::Hit& hit = hits[i]; int fid = hit.id; // facet id bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit - a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0))); - b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0))); - result = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)); + a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]); + b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]); + result = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)]; if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast())) break; } @@ -564,15 +556,18 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Cast a ray in the direction of the camera and look for intersection with the mesh: std::vector hits; // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. - if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) { + if (m_AABB.intersect_ray( + MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3), + MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3), + support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) { std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); if (m_clipping_plane_distance != 0.f) { // If the closest hit facet normal points in the same direction as the ray, // we are looking through the mesh and should therefore discard the point: int fid = hits.front().id; // facet id - Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0))); - Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0))); + Vec3f a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]); + Vec3f b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]); if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f) is_obscured = true; @@ -582,7 +577,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous int fid = hit.id; // facet id Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit - Vec3f hit_pos = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)); + Vec3f hit_pos = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)]; if (is_point_clipped(hit_pos.cast())) { hits.erase(hits.begin()+j); --j; @@ -759,9 +754,12 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const int idx = 0; Eigen::Matrix pp = m_editing_mode_cache[i].support_point.pos; Eigen::Matrix cc; - m_AABB.squared_distance(m_V, m_F, pp, idx, cc); - Vec3f a = (m_V.row(m_F(idx, 1)) - m_V.row(m_F(idx, 0))); - Vec3f b = (m_V.row(m_F(idx, 2)) - m_V.row(m_F(idx, 0))); + m_AABB.squared_distance( + MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3), + MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3), + pp, idx, cc); + Vec3f a = (m_its->vertices[m_its->indices[idx](1)] - m_its->vertices[m_its->indices[idx](0)]); + Vec3f b = (m_its->vertices[m_its->indices[idx](2)] - m_its->vertices[m_its->indices[idx](0)]); m_editing_mode_cache[i].normal = a.cross(b); } @@ -1067,8 +1065,7 @@ void GLGizmoSlaSupports::on_set_state() m_clipping_plane_distance = 0.f; // Release triangle mesh slicer and the AABB spatial search structure. m_AABB.deinit(); - m_V = Eigen::MatrixXf(); - m_F = Eigen::MatrixXi(); + m_its = nullptr; m_tms.reset(); m_supports_tms.reset(); }); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index fb758d240..4946db12d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -35,10 +35,11 @@ private: const float RenderPointScale = 1.f; GLUquadricObj* m_quadric; - Eigen::MatrixXf m_V; // vertices - Eigen::MatrixXi m_F; // facets indices - igl::AABB m_AABB; + typedef Eigen::Map> MapMatrixXfUnaligned; + typedef Eigen::Map> MapMatrixXiUnaligned; + igl::AABB m_AABB; const TriangleMesh* m_mesh; + const indexed_triangle_set* m_its; mutable const TriangleMesh* m_supports_mesh; mutable std::vector m_triangles; mutable std::vector m_supports_triangles; From 590c290ede9ed9da7e608614fbe170d489d1aa06 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 10 Jun 2019 22:43:42 +0200 Subject: [PATCH 21/44] Fix of a typo. --- src/admesh/shared.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/admesh/shared.cpp b/src/admesh/shared.cpp index 78179e863..902bbfc9b 100644 --- a/src/admesh/shared.cpp +++ b/src/admesh/shared.cpp @@ -220,7 +220,7 @@ bool stl_validate(const stl_file *stl, const indexed_triangle_set &its) // Verify validity of neighborship data. for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) { const stl_neighbors &nbr = stl->neighbors_start[facet_idx]; - const int *vertices = (its.indices.empty()) ? nullptr : its.indices[facet_idx]; + const int *vertices = its.indices.empty() ? nullptr : its.indices[facet_idx].data(); for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) { int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx]; assert(nbr_face < (int)stl->stats.number_of_facets); From 5fc465b7e8e5398771e4b6db6043bc84770e5923 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 11 Jun 2019 09:29:32 +0200 Subject: [PATCH 22/44] admesh refactoring: Using boost::object_pool for linked list memory allocation. --- src/admesh/connect.cpp | 525 +++++++++++++++++++++-------------------- src/admesh/normals.cpp | 15 +- src/admesh/stl_io.cpp | 10 +- 3 files changed, 284 insertions(+), 266 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index cbd4f1d33..be782b1b7 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -28,8 +28,9 @@ #include #include +#include #include -#include +#include #include "stl.h" @@ -42,11 +43,11 @@ struct HashEdge { int hash(int M) const { return ((key[0] / 11 + key[1] / 7 + key[2] / 3) ^ (key[3] / 11 + key[4] / 7 + key[5] / 3)) % M; } // Index of a facet owning this edge. - int facet_number; + int facet_number; // Index of this edge inside the facet with an index of facet_number. // If this edge is stored backwards, which_edge is increased by 3. - int which_edge; - struct HashEdge *next; + int which_edge; + HashEdge *next; void load_exact(stl_file *stl, const stl_vertex *a, const stl_vertex *b) { @@ -69,15 +70,15 @@ struct HashEdge { // Switch negative zeros to positive zeros, so memcmp will consider them to be equal. for (size_t i = 0; i < 6; ++ i) { unsigned char *p = (unsigned char*)(this->key + i); - #ifdef BOOST_LITTLE_ENDIAN + #if BOOST_ENDIAN_LITTLE_BYTE if (p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0x80) // Negative zero, switch to positive zero. p[3] = 0; - #else /* BOOST_LITTLE_ENDIAN */ + #else /* BOOST_ENDIAN_LITTLE_BYTE */ if (p[0] == 0x80 && p[1] == 0 && p[2] == 0 && p[3] == 0) // Negative zero, switch to positive zero. p[0] = 0; - #endif /* BOOST_LITTLE_ENDIAN */ + #endif /* BOOST_ENDIAN_LITTLE_BYTE */ } } @@ -115,7 +116,7 @@ struct HashTableEdges { HashTableEdges(size_t number_of_faces) { this->M = (int)hash_size_from_nr_faces(number_of_faces); this->heads.assign(this->M, nullptr); - this->tail = new HashEdge; + this->tail = pool.construct(); this->tail->next = this->tail; for (int i = 0; i < this->M; ++ i) this->heads[i] = this->tail; @@ -124,80 +125,32 @@ struct HashTableEdges { for (int i = 0; i < this->M; ++ i) { for (HashEdge *temp = this->heads[i]; this->heads[i] != this->tail; temp = this->heads[i]) { this->heads[i] = this->heads[i]->next; - delete temp; + pool.destroy(temp); #ifndef NDEBUG ++ this->freed; #endif /* NDEBUG */ } } this->heads.clear(); - delete this->tail; + pool.destroy(this->tail); this->tail = nullptr; } - void insert_edge(stl_file *stl, const HashEdge &edge, void (*match_neighbors)(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b)) + void insert_edge_exact(stl_file *stl, const HashEdge &edge) { - int chain_number = edge.hash(this->M); - HashEdge *link = this->heads[chain_number]; - if (link == this->tail) { - // This list doesn't have any edges currently in it. Add this one. - HashEdge *new_edge = new HashEdge(edge); -#ifndef NDEBUG - ++ this->malloced; -#endif /* NDEBUG */ - new_edge->next = this->tail; - this->heads[chain_number] = new_edge; - } else if (edges_equal(edge, *link)) { - // This is a match. Record result in neighbors list. - match_neighbors(stl, edge, *link); - // Delete the matched edge from the list. - this->heads[chain_number] = link->next; - delete link; -#ifndef NDEBUG - ++ this->freed; -#endif /* NDEBUG */ - } else { - // Continue through the rest of the list. - for (;;) { - if (link->next == this->tail) { - // This is the last item in the list. Insert a new edge. - HashEdge *new_edge = new HashEdge; -#ifndef NDEBUG - ++ this->malloced; -#endif /* NDEBUG */ - *new_edge = edge; - new_edge->next = this->tail; - link->next = new_edge; -#ifndef NDEBUG - ++ this->collisions; -#endif /* NDEBUG */ - break; - } - if (edges_equal(edge, *link->next)) { - // This is a match. Record result in neighbors list. - match_neighbors(stl, edge, *link->next); - // Delete the matched edge from the list. - HashEdge *temp = link->next; - link->next = link->next->next; - delete temp; -#ifndef NDEBUG - ++ this->freed; -#endif /* NDEBUG */ - break; - } - // This is not a match. Go to the next link. - link = link->next; -#ifndef NDEBUG - ++ this->collisions; -#endif /* NDEBUG */ - } - } + this->insert_edge(stl, edge, [stl](const HashEdge& edge1, const HashEdge& edge2) { record_neighbors(stl, edge1, edge2); }); + } + + void insert_edge_nearby(stl_file *stl, const HashEdge &edge) + { + this->insert_edge(stl, edge, [stl](const HashEdge& edge1, const HashEdge& edge2) { match_neighbors_nearby(stl, edge1, edge2); }); } // Hash table on edges std::vector heads; HashEdge* tail; int M; + boost::object_pool pool; #ifndef NDEBUG size_t malloced = 0; @@ -216,198 +169,260 @@ private: return (it == primes.end()) ? primes.back() : *it; } + + // MatchNeighbors(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b) + template + void insert_edge(stl_file *stl, const HashEdge &edge, MatchNeighbors match_neighbors) + { + int chain_number = edge.hash(this->M); + HashEdge *link = this->heads[chain_number]; + if (link == this->tail) { + // This list doesn't have any edges currently in it. Add this one. + HashEdge *new_edge = pool.construct(edge); +#ifndef NDEBUG + ++ this->malloced; +#endif /* NDEBUG */ + new_edge->next = this->tail; + this->heads[chain_number] = new_edge; + } else if (edges_equal(edge, *link)) { + // This is a match. Record result in neighbors list. + match_neighbors(edge, *link); + // Delete the matched edge from the list. + this->heads[chain_number] = link->next; + pool.destroy(link); +#ifndef NDEBUG + ++ this->freed; +#endif /* NDEBUG */ + } else { + // Continue through the rest of the list. + for (;;) { + if (link->next == this->tail) { + // This is the last item in the list. Insert a new edge. + HashEdge *new_edge = pool.construct(); +#ifndef NDEBUG + ++ this->malloced; +#endif /* NDEBUG */ + *new_edge = edge; + new_edge->next = this->tail; + link->next = new_edge; +#ifndef NDEBUG + ++ this->collisions; +#endif /* NDEBUG */ + break; + } + if (edges_equal(edge, *link->next)) { + // This is a match. Record result in neighbors list. + match_neighbors(edge, *link->next); + // Delete the matched edge from the list. + HashEdge *temp = link->next; + link->next = link->next->next; + pool.destroy(temp); +#ifndef NDEBUG + ++ this->freed; +#endif /* NDEBUG */ + break; + } + // This is not a match. Go to the next link. + link = link->next; +#ifndef NDEBUG + ++ this->collisions; +#endif /* NDEBUG */ + } + } + } + // Edges equal for hashing. Edgesof different facet are allowed to be matched. static inline bool edges_equal(const HashEdge &edge_a, const HashEdge &edge_b) { return edge_a.facet_number != edge_b.facet_number && edge_a == edge_b; } + + static void record_neighbors(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b) + { + // Facet a's neighbor is facet b + stl->neighbors_start[edge_a.facet_number].neighbor[edge_a.which_edge % 3] = edge_b.facet_number; /* sets the .neighbor part */ + stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] = (edge_b.which_edge + 2) % 3; /* sets the .which_vertex_not part */ + + // Facet b's neighbor is facet a + stl->neighbors_start[edge_b.facet_number].neighbor[edge_b.which_edge % 3] = edge_a.facet_number; /* sets the .neighbor part */ + stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] = (edge_a.which_edge + 2) % 3; /* sets the .which_vertex_not part */ + + if (((edge_a.which_edge < 3) && (edge_b.which_edge < 3)) || ((edge_a.which_edge > 2) && (edge_b.which_edge > 2))) { + // These facets are oriented in opposite directions, their normals are probably messed up. + stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] += 3; + stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] += 3; + } + + // Count successful connects: + // Total connects: + stl->stats.connected_edges += 2; + // Count individual connects: + switch (stl->neighbors_start[edge_a.facet_number].num_neighbors()) { + case 1: ++ stl->stats.connected_facets_1_edge; break; + case 2: ++ stl->stats.connected_facets_2_edge; break; + case 3: ++ stl->stats.connected_facets_3_edge; break; + default: assert(false); + } + switch (stl->neighbors_start[edge_b.facet_number].num_neighbors()) { + case 1: ++ stl->stats.connected_facets_1_edge; break; + case 2: ++ stl->stats.connected_facets_2_edge; break; + case 3: ++ stl->stats.connected_facets_3_edge; break; + default: assert(false); + } + } + + static void match_neighbors_nearby(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b) + { + record_neighbors(stl, edge_a, edge_b); + + // Which vertices to change + int facet1 = -1; + int facet2 = -1; + int vertex1, vertex2; + stl_vertex new_vertex1, new_vertex2; + { + int v1a; // pair 1, facet a + int v1b; // pair 1, facet b + int v2a; // pair 2, facet a + int v2b; // pair 2, facet b + // Find first pair. + if (edge_a.which_edge < 3) { + v1a = edge_a.which_edge; + v2a = (edge_a.which_edge + 1) % 3; + } else { + v2a = edge_a.which_edge % 3; + v1a = (edge_a.which_edge + 1) % 3; + } + if (edge_b.which_edge < 3) { + v1b = edge_b.which_edge; + v2b = (edge_b.which_edge + 1) % 3; + } else { + v2b = edge_b.which_edge % 3; + v1b = (edge_b.which_edge + 1) % 3; + } + + // Of the first pair, which vertex, if any, should be changed + if (stl->facet_start[edge_a.facet_number].vertex[v1a] != stl->facet_start[edge_b.facet_number].vertex[v1b]) { + // These facets are different. + if ( (stl->neighbors_start[edge_a.facet_number].neighbor[v1a] == -1) + && (stl->neighbors_start[edge_a.facet_number].neighbor[(v1a + 2) % 3] == -1)) { + // This vertex has no neighbors. This is a good one to change. + facet1 = edge_a.facet_number; + vertex1 = v1a; + new_vertex1 = stl->facet_start[edge_b.facet_number].vertex[v1b]; + } else { + facet1 = edge_b.facet_number; + vertex1 = v1b; + new_vertex1 = stl->facet_start[edge_a.facet_number].vertex[v1a]; + } + } + + // Of the second pair, which vertex, if any, should be changed. + if (stl->facet_start[edge_a.facet_number].vertex[v2a] == stl->facet_start[edge_b.facet_number].vertex[v2b]) { + // These facets are different. + if ( (stl->neighbors_start[edge_a.facet_number].neighbor[v2a] == -1) + && (stl->neighbors_start[edge_a.facet_number].neighbor[(v2a + 2) % 3] == -1)) { + // This vertex has no neighbors. This is a good one to change. + facet2 = edge_a.facet_number; + vertex2 = v2a; + new_vertex2 = stl->facet_start[edge_b.facet_number].vertex[v2b]; + } else { + facet2 = edge_b.facet_number; + vertex2 = v2b; + new_vertex2 = stl->facet_start[edge_a.facet_number].vertex[v2a]; + } + } + } + + auto change_vertices = [stl](int facet_num, int vnot, stl_vertex new_vertex) + { + int first_facet = facet_num; + bool direction = false; + + for (;;) { + int pivot_vertex; + int next_edge; + if (vnot > 2) { + if (direction) { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot % 3; + } + else { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + direction = !direction; + } + else { + if (direction) { + pivot_vertex = (vnot + 2) % 3; + next_edge = pivot_vertex; + } + else { + pivot_vertex = (vnot + 1) % 3; + next_edge = vnot; + } + } + #if 0 + if (stl->facet_start[facet_num].vertex[pivot_vertex](0) == new_vertex(0) && + stl->facet_start[facet_num].vertex[pivot_vertex](1) == new_vertex(1) && + stl->facet_start[facet_num].vertex[pivot_vertex](2) == new_vertex(2)) + printf("Changing vertex %f,%f,%f: Same !!!\r\n", new_vertex(0), new_vertex(1), new_vertex(2)); + else { + if (stl->facet_start[facet_num].vertex[pivot_vertex](0) != new_vertex(0)) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex](0), + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](0)), + new_vertex(0), + *reinterpret_cast(&new_vertex(0))); + if (stl->facet_start[facet_num].vertex[pivot_vertex](1) != new_vertex(1)) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex](1), + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](1)), + new_vertex(1), + *reinterpret_cast(&new_vertex(1))); + if (stl->facet_start[facet_num].vertex[pivot_vertex](2) != new_vertex(2)) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex](2), + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](2)), + new_vertex(2), + *reinterpret_cast(&new_vertex(2))); + } + #endif + stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; + vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; + facet_num = stl->neighbors_start[facet_num].neighbor[next_edge]; + if (facet_num == -1) + break; + + if (facet_num == first_facet) { + // back to the beginning + BOOST_LOG_TRIVIAL(info) << "Back to the first facet changing vertices: probably a mobius part. Try using a smaller tolerance or don't do a nearby check."; + return; + } + } + }; + + if (facet1 != -1) { + int vnot1 = (facet1 == edge_a.facet_number) ? + (edge_a.which_edge + 2) % 3 : + (edge_b.which_edge + 2) % 3; + if (((vnot1 + 2) % 3) == vertex1) + vnot1 += 3; + change_vertices(facet1, vnot1, new_vertex1); + } + if (facet2 != -1) { + int vnot2 = (facet2 == edge_a.facet_number) ? + (edge_a.which_edge + 2) % 3 : + (edge_b.which_edge + 2) % 3; + if (((vnot2 + 2) % 3) == vertex2) + vnot2 += 3; + change_vertices(facet2, vnot2, new_vertex2); + } + stl->stats.edges_fixed += 2; + } }; -static void record_neighbors(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b) -{ - // Facet a's neighbor is facet b - stl->neighbors_start[edge_a.facet_number].neighbor[edge_a.which_edge % 3] = edge_b.facet_number; /* sets the .neighbor part */ - stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] = (edge_b.which_edge + 2) % 3; /* sets the .which_vertex_not part */ - - // Facet b's neighbor is facet a - stl->neighbors_start[edge_b.facet_number].neighbor[edge_b.which_edge % 3] = edge_a.facet_number; /* sets the .neighbor part */ - stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] = (edge_a.which_edge + 2) % 3; /* sets the .which_vertex_not part */ - - if (((edge_a.which_edge < 3) && (edge_b.which_edge < 3)) || ((edge_a.which_edge > 2) && (edge_b.which_edge > 2))) { - // These facets are oriented in opposite directions, their normals are probably messed up. - stl->neighbors_start[edge_a.facet_number].which_vertex_not[edge_a.which_edge % 3] += 3; - stl->neighbors_start[edge_b.facet_number].which_vertex_not[edge_b.which_edge % 3] += 3; - } - - // Count successful connects: - // Total connects: - stl->stats.connected_edges += 2; - // Count individual connects: - switch (stl->neighbors_start[edge_a.facet_number].num_neighbors()) { - case 1: ++ stl->stats.connected_facets_1_edge; break; - case 2: ++ stl->stats.connected_facets_2_edge; break; - case 3: ++ stl->stats.connected_facets_3_edge; break; - default: assert(false); - } - switch (stl->neighbors_start[edge_b.facet_number].num_neighbors()) { - case 1: ++ stl->stats.connected_facets_1_edge; break; - case 2: ++ stl->stats.connected_facets_2_edge; break; - case 3: ++ stl->stats.connected_facets_3_edge; break; - default: assert(false); - } -} - -static void match_neighbors_nearby(stl_file *stl, const HashEdge &edge_a, const HashEdge &edge_b) -{ - record_neighbors(stl, edge_a, edge_b); - - // Which vertices to change - int facet1 = -1; - int facet2 = -1; - int vertex1, vertex2; - stl_vertex new_vertex1, new_vertex2; - { - int v1a; // pair 1, facet a - int v1b; // pair 1, facet b - int v2a; // pair 2, facet a - int v2b; // pair 2, facet b - // Find first pair. - if (edge_a.which_edge < 3) { - v1a = edge_a.which_edge; - v2a = (edge_a.which_edge + 1) % 3; - } else { - v2a = edge_a.which_edge % 3; - v1a = (edge_a.which_edge + 1) % 3; - } - if (edge_b.which_edge < 3) { - v1b = edge_b.which_edge; - v2b = (edge_b.which_edge + 1) % 3; - } else { - v2b = edge_b.which_edge % 3; - v1b = (edge_b.which_edge + 1) % 3; - } - - // Of the first pair, which vertex, if any, should be changed - if (stl->facet_start[edge_a.facet_number].vertex[v1a] != stl->facet_start[edge_b.facet_number].vertex[v1b]) { - // These facets are different. - if ( (stl->neighbors_start[edge_a.facet_number].neighbor[v1a] == -1) - && (stl->neighbors_start[edge_a.facet_number].neighbor[(v1a + 2) % 3] == -1)) { - // This vertex has no neighbors. This is a good one to change. - facet1 = edge_a.facet_number; - vertex1 = v1a; - new_vertex1 = stl->facet_start[edge_b.facet_number].vertex[v1b]; - } else { - facet1 = edge_b.facet_number; - vertex1 = v1b; - new_vertex1 = stl->facet_start[edge_a.facet_number].vertex[v1a]; - } - } - - // Of the second pair, which vertex, if any, should be changed. - if (stl->facet_start[edge_a.facet_number].vertex[v2a] == stl->facet_start[edge_b.facet_number].vertex[v2b]) { - // These facets are different. - if ( (stl->neighbors_start[edge_a.facet_number].neighbor[v2a] == -1) - && (stl->neighbors_start[edge_a.facet_number].neighbor[(v2a + 2) % 3] == -1)) { - // This vertex has no neighbors. This is a good one to change. - facet2 = edge_a.facet_number; - vertex2 = v2a; - new_vertex2 = stl->facet_start[edge_b.facet_number].vertex[v2b]; - } else { - facet2 = edge_b.facet_number; - vertex2 = v2b; - new_vertex2 = stl->facet_start[edge_a.facet_number].vertex[v2a]; - } - } - } - - auto change_vertices = [stl](int facet_num, int vnot, stl_vertex new_vertex) - { - int first_facet = facet_num; - bool direction = false; - - for (;;) { - int pivot_vertex; - int next_edge; - if (vnot > 2) { - if (direction) { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot % 3; - } - else { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - } - direction = !direction; - } - else { - if (direction) { - pivot_vertex = (vnot + 2) % 3; - next_edge = pivot_vertex; - } - else { - pivot_vertex = (vnot + 1) % 3; - next_edge = vnot; - } - } -#if 0 - if (stl->facet_start[facet_num].vertex[pivot_vertex](0) == new_vertex(0) && - stl->facet_start[facet_num].vertex[pivot_vertex](1) == new_vertex(1) && - stl->facet_start[facet_num].vertex[pivot_vertex](2) == new_vertex(2)) - printf("Changing vertex %f,%f,%f: Same !!!\r\n", new_vertex(0), new_vertex(1), new_vertex(2)); - else { - if (stl->facet_start[facet_num].vertex[pivot_vertex](0) != new_vertex(0)) - printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex](0), - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](0)), - new_vertex(0), - *reinterpret_cast(&new_vertex(0))); - if (stl->facet_start[facet_num].vertex[pivot_vertex](1) != new_vertex(1)) - printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex](1), - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](1)), - new_vertex(1), - *reinterpret_cast(&new_vertex(1))); - if (stl->facet_start[facet_num].vertex[pivot_vertex](2) != new_vertex(2)) - printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", - stl->facet_start[facet_num].vertex[pivot_vertex](2), - *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex](2)), - new_vertex(2), - *reinterpret_cast(&new_vertex(2))); - } -#endif - stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; - vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; - facet_num = stl->neighbors_start[facet_num].neighbor[next_edge]; - if (facet_num == -1) - break; - - if (facet_num == first_facet) { - // back to the beginning - BOOST_LOG_TRIVIAL(info) << "Back to the first facet changing vertices: probably a mobius part. Try using a smaller tolerance or don't do a nearby check."; - return; - } - } - }; - - if (facet1 != -1) { - int vnot1 = (facet1 == edge_a.facet_number) ? - (edge_a.which_edge + 2) % 3 : - (edge_b.which_edge + 2) % 3; - if (((vnot1 + 2) % 3) == vertex1) - vnot1 += 3; - change_vertices(facet1, vnot1, new_vertex1); - } - if (facet2 != -1) { - int vnot2 = (facet2 == edge_a.facet_number) ? - (edge_a.which_edge + 2) % 3 : - (edge_b.which_edge + 2) % 3; - if (((vnot2 + 2) % 3) == vertex2) - vnot2 += 3; - change_vertices(facet2, vnot2, new_vertex2); - } - stl->stats.edges_fixed += 2; -} - // This function builds the neighbors list. No modifications are made // to any of the facets. The edges are said to match only if all six // floats of the first edge matches all six floats of the second edge. @@ -445,7 +460,7 @@ void stl_check_facets_exact(stl_file *stl) edge.facet_number = i; edge.which_edge = j; edge.load_exact(stl, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); - hash_table.insert_edge(stl, edge, record_neighbors); + hash_table.insert_edge_exact(stl, edge); } } @@ -476,7 +491,7 @@ void stl_check_facets_nearby(stl_file *stl, float tolerance) edge.which_edge = j; if (edge.load_nearby(stl, facet.vertex[j], facet.vertex[(j + 1) % 3], tolerance)) // Only insert edges that have different keys. - hash_table.insert_edge(stl, edge, match_neighbors_nearby); + hash_table.insert_edge_nearby(stl, edge); } } } @@ -654,7 +669,7 @@ void stl_fill_holes(stl_file *stl) edge.facet_number = i; edge.which_edge = j; edge.load_exact(stl, &facet.vertex[j], &facet.vertex[(j + 1) % 3]); - hash_table.insert_edge(stl, edge, record_neighbors); + hash_table.insert_edge_exact(stl, edge); } } @@ -704,7 +719,7 @@ void stl_fill_holes(stl_file *stl) edge.facet_number = stl->stats.number_of_facets - 1; edge.which_edge = k; edge.load_exact(stl, &new_facet.vertex[k], &new_facet.vertex[(k + 1) % 3]); - hash_table.insert_edge(stl, edge, record_neighbors); + hash_table.insert_edge_exact(stl, edge); } break; } diff --git a/src/admesh/normals.cpp b/src/admesh/normals.cpp index 464f9e6d8..4d47573f6 100644 --- a/src/admesh/normals.cpp +++ b/src/admesh/normals.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include "stl.h" static void reverse_facet(stl_file *stl, int facet_num) @@ -120,8 +122,9 @@ void stl_fix_normal_directions(stl_file *stl) }; // Initialize linked list. - stl_normal *head = new stl_normal; - stl_normal *tail = new stl_normal; + boost::object_pool pool; + stl_normal *head = pool.construct(); + stl_normal *tail = pool.construct(); head->next = tail; tail->next = tail; @@ -168,7 +171,7 @@ void stl_fix_normal_directions(stl_file *stl) // If we haven't fixed this facet yet, add it to the list: if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) { // Add node to beginning of list. - stl_normal *newn = new stl_normal; + stl_normal *newn = pool.construct(); newn->facet_num = stl->neighbors_start[facet_num].neighbor[j]; newn->next = head->next; head->next = newn; @@ -189,7 +192,7 @@ void stl_fix_normal_directions(stl_file *stl) } stl_normal *temp = head->next; // Delete this facet from the list. head->next = head->next->next; - delete temp; + pool.destroy(temp); } else { // If we ran out of facets to fix: All of the facets in this part have been fixed. ++ stl->stats.number_of_parts; if (checked >= stl->stats.number_of_facets) @@ -211,8 +214,8 @@ void stl_fix_normal_directions(stl_file *stl) } } - delete head; - delete tail; + pool.destroy(head); + pool.destroy(tail); } void stl_fix_normal_values(stl_file *stl) diff --git a/src/admesh/stl_io.cpp b/src/admesh/stl_io.cpp index 7e181716c..464c98907 100644 --- a/src/admesh/stl_io.cpp +++ b/src/admesh/stl_io.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include "stl.h" @@ -114,7 +114,7 @@ bool stl_print_neighbors(stl_file *stl, char *file) return true; } -#ifndef BOOST_LITTLE_ENDIAN +#if BOOST_ENDIAN_BIG_BYTE // Swap a buffer of 32bit data from little endian to big endian and vice versa. void stl_internal_reverse_quads(char *buf, size_t cnt) { @@ -141,11 +141,11 @@ bool stl_write_binary(stl_file *stl, const char *file, const char *label) #define SEEK_SET 0 #endif fseek(fp, LABEL_SIZE, SEEK_SET); -#ifdef BOOST_LITTLE_ENDIAN +#if BOOST_ENDIAN_LITTLE_BYTE fwrite(&stl->stats.number_of_facets, 4, 1, fp); for (const stl_facet &facet : stl->facet_start) fwrite(&facet, SIZEOF_STL_FACET, 1, fp); -#else /* BOOST_LITTLE_ENDIAN */ +#else /* BOOST_ENDIAN_LITTLE_BYTE */ char buffer[50]; // Convert the number of facets to little endian. memcpy(buffer, &stl->stats.number_of_facets, 4); @@ -157,7 +157,7 @@ bool stl_write_binary(stl_file *stl, const char *file, const char *label) stl_internal_reverse_quads(buffer, 48); fwrite(buffer, SIZEOF_STL_FACET, 1, fp); } -#endif /* BOOST_LITTLE_ENDIAN */ +#endif /* BOOST_ENDIAN_LITTLE_BYTE */ fclose(fp); return true; } From 0bb8ee149e8702fb644a70ab1f3924ff8e6f3d71 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 11 Jun 2019 17:08:47 +0200 Subject: [PATCH 23/44] Sharing TriangleMesh objects between the front end (UI) and back end (background processing) --- src/PrusaSlicer.cpp | 8 +- src/admesh/connect.cpp | 46 +++++--- src/admesh/stl.h | 38 +++++- src/admesh/stlinit.cpp | 2 +- src/admesh/util.cpp | 2 +- src/libslic3r/Format/3mf.cpp | 27 +++-- src/libslic3r/Format/AMF.cpp | 20 ++-- src/libslic3r/Model.cpp | 118 +++++++++---------- src/libslic3r/Model.hpp | 46 +++++--- src/libslic3r/PrintObject.cpp | 14 ++- src/libslic3r/Slicing.cpp | 2 +- src/libslic3r/TriangleMesh.cpp | 72 ++++++----- src/libslic3r/TriangleMesh.hpp | 2 +- src/slic3r/GUI/3DScene.cpp | 25 ++-- src/slic3r/GUI/3DScene.hpp | 12 +- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/GUI_ObjectList.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 3 +- src/slic3r/GUI/Plater.cpp | 2 +- xs/xsp/Model.xsp | 2 +- 20 files changed, 254 insertions(+), 193 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index fef1f6e7f..a3247b929 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -241,8 +241,7 @@ int CLI::run(int argc, char **argv) } else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") { std::vector new_models; for (auto &model : m_models) { - model.repair(); - model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0 + model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0 size_t num_objects = model.objects.size(); for (size_t i = 0; i < num_objects; ++ i) { @@ -301,8 +300,9 @@ int CLI::run(int argc, char **argv) } } } else if (opt_key == "repair") { - for (auto &model : m_models) - model.repair(); + // Models are repaired by default. + //for (auto &model : m_models) + // model.repair(); } else { boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl; return 1; diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index be782b1b7..1a7b7965d 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -59,7 +59,7 @@ struct HashEdge { // Ensure identical vertex ordering of equal edges. // This method is numerically robust. - if (stl_vertex_lower(*a, *b)) { + if (vertex_lower(*a, *b)) { } else { // This edge is loaded backwards. std::swap(a, b); @@ -110,6 +110,12 @@ struct HashEdge { } return true; } + +private: + inline bool vertex_lower(const stl_vertex &a, const stl_vertex &b) { + return (a(0) != b(0)) ? (a(0) < b(0)) : + ((a(1) != b(1)) ? (a(1) < b(1)) : (a(2) < b(2))); + } }; struct HashTableEdges { @@ -440,7 +446,9 @@ void stl_check_facets_exact(stl_file *stl) stl_facet &facet = stl->facet_start[i]; if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) { // Remove the degenerate facet. - facet = stl->facet_start[--stl->stats.number_of_facets]; + facet = stl->facet_start[-- stl->stats.number_of_facets]; + stl->facet_start.pop_back(); + stl->neighbors_start.pop_back(); stl->stats.facets_removed += 1; stl->stats.degenerate_facets += 1; } else @@ -526,23 +534,25 @@ void stl_remove_unconnected_facets(stl_file *stl) assert(false); } - if (facet_number == -- stl->stats.number_of_facets) - // Removing the last face is easy, just forget the last face. - return; - - // Copy the face and neighborship from the last face to facet_number. - stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets]; - neighbors = stl->neighbors_start[stl->stats.number_of_facets]; - // Update neighborship of faces, which used to point to the last face, now moved to facet_number. - for (int i = 0; i < 3; ++ i) - if (neighbors.neighbor[i] != -1) { - int &other_face_idx = stl->neighbors_start[neighbors.neighbor[i]].neighbor[(neighbors.which_vertex_not[i] + 1) % 3]; - if (other_face_idx != stl->stats.number_of_facets) { - BOOST_LOG_TRIVIAL(info) << "in remove_facet: neighbor = " << other_face_idx << " numfacets = " << stl->stats.number_of_facets << " this is wrong"; - return; + if (facet_number < -- stl->stats.number_of_facets) { + // Removing a face, which was not the last one. + // Copy the face and neighborship from the last face to facet_number. + stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets]; + neighbors = stl->neighbors_start[stl->stats.number_of_facets]; + // Update neighborship of faces, which used to point to the last face, now moved to facet_number. + for (int i = 0; i < 3; ++ i) + if (neighbors.neighbor[i] != -1) { + int &other_face_idx = stl->neighbors_start[neighbors.neighbor[i]].neighbor[(neighbors.which_vertex_not[i] + 1) % 3]; + if (other_face_idx != stl->stats.number_of_facets) { + BOOST_LOG_TRIVIAL(info) << "in remove_facet: neighbor = " << other_face_idx << " numfacets = " << stl->stats.number_of_facets << " this is wrong"; + return; + } + other_face_idx = facet_number; } - other_face_idx = facet_number; - } + } + + stl->facet_start.pop_back(); + stl->neighbors_start.pop_back(); }; auto remove_degenerate = [stl, remove_facet](int facet) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index fce23eb3f..c419c567b 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -128,6 +128,8 @@ struct indexed_triangle_set void clear() { indices.clear(); vertices.clear(); } std::vector indices; std::vector vertices; + //FIXME add normals once we get rid of the stl_file from TriangleMesh completely. + //std::vector normals }; extern bool stl_open(stl_file *stl, const char *file); @@ -186,7 +188,7 @@ template inline void stl_transform(stl_file *stl, const Eigen::Transform& t) { const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0); - for (size_t i = 0; i < stl->stats.number_of_facets; ++i) { + for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { stl_facet &f = stl->facet_start[i]; for (size_t j = 0; j < 3; ++j) f.vertex[j] = (t * f.vertex[j].template cast()).template cast().eval(); @@ -199,7 +201,7 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform inline void stl_transform(stl_file *stl, const Eigen::Matrix& m) { - for (size_t i = 0; i < stl->stats.number_of_facets; ++i) { + for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) { stl_facet &f = stl->facet_start[i]; for (size_t j = 0; j < 3; ++j) f.vertex[j] = (m * f.vertex[j].template cast()).template cast().eval(); @@ -209,6 +211,34 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrix +extern void its_transform(indexed_triangle_set &its, T *trafo3x4) +{ + for (stl_vertex &v_dst : its.vertices) { + stl_vertex &v_dst = face.vertex[i_vertex]; + stl_vertex v_src = v_dst; + v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]); + v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]); + v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]); + } +} + +template +inline void its_transform(indexed_triangle_set &its, const Eigen::Transform& t) +{ + const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0); + for (stl_vertex &v : its.vertices) + v = (t * v.template cast()).template cast().eval(); +} + +template +inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix& m) +{ + for (stl_vertex &v : its.vertices) + v = (m * v.template cast()).template cast().eval(); +} + extern void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its); extern bool its_write_obj(const indexed_triangle_set &its, const char *file); extern bool its_write_off(const indexed_triangle_set &its, const char *file); @@ -225,10 +255,6 @@ inline void stl_normalize_vector(stl_normal &normal) { else normal *= float(1.0 / length); } -inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) { - return (a(0) != b(0)) ? (a(0) < b(0)) : - ((a(1) != b(1)) ? (a(1) < b(1)) : (a(2) < b(2))); -} extern void stl_calculate_volume(stl_file *stl); extern void stl_repair(stl_file *stl, bool fixall_flag, bool exact_flag, bool tolerance_flag, float tolerance, bool increment_flag, float increment, bool nearby_flag, int iterations, bool remove_unconnected_flag, bool fill_holes_flag, bool normal_directions_flag, bool normal_values_flag, bool reverse_all_flag, bool verbose_flag); diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index 24fbe9edc..0cc6e50c7 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -147,7 +147,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first) rewind(fp); char normal_buf[3][32]; - for (uint32_t i = first_facet; i < stl->stats.number_of_facets; ++i) { + for (uint32_t i = first_facet; i < stl->stats.number_of_facets; ++ i) { stl_facet facet; if (stl->stats.type == binary) { diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index 6fff8a8ed..70f4ffc27 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -108,7 +108,7 @@ void stl_scale_versor(stl_file *stl, const stl_vertex &versor) static void calculate_normals(stl_file *stl) { stl_normal normal; - for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) { stl_calculate_normal(normal, &stl->facet_start[i]); stl_normalize_vector(normal); stl->facet_start[i].normal = normal; diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 654426528..4793586e3 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1489,10 +1489,10 @@ namespace Slic3r { } // splits volume out of imported geometry - unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1; - ModelVolume* volume = object.add_volume(TriangleMesh()); - stl_file& stl = volume->mesh.stl; - stl.stats.type = inmemory; + TriangleMesh triangle_mesh; + stl_file &stl = triangle_mesh.stl; + unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1; + stl.stats.type = inmemory; stl.stats.number_of_facets = (uint32_t)triangles_count; stl.stats.original_num_facets = (int)stl.stats.number_of_facets; stl_allocate(&stl); @@ -1509,9 +1509,11 @@ namespace Slic3r { } } - stl_get_size(&stl); - volume->mesh.repair(); - volume->center_geometry(); + stl_get_size(&stl); + triangle_mesh.repair(); + + ModelVolume* volume = object.add_volume(std::move(triangle_mesh)); + volume->center_geometry_after_creation(); volume->calculate_convex_hull(); // apply volume's name and config data @@ -1879,11 +1881,14 @@ namespace Slic3r { if (volume == nullptr) continue; + if (!volume->mesh().repaired) + throw std::runtime_error("store_3mf() requires repair()"); + if (!volume->mesh().has_shared_vertices()) + throw std::runtime_error("store_3mf() requires shared vertices"); + volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first; - volume->mesh.require_shared_vertices(); - - const indexed_triangle_set &its = volume->mesh.its; + const indexed_triangle_set &its = volume->mesh().its; if (its.vertices.empty()) { add_error("Found invalid mesh"); @@ -1916,7 +1921,7 @@ namespace Slic3r { VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume); assert(volume_it != volumes_offsets.end()); - const indexed_triangle_set &its = volume->mesh.its; + const indexed_triangle_set &its = volume->mesh().its; // updates triangle offsets volume_it->second.first_triangle_id = triangles_count; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 48887bc78..a33d21c9f 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -522,7 +522,8 @@ void AMFParserContext::endElement(const char * /* name */) case NODE_TYPE_VOLUME: { assert(m_object && m_volume); - stl_file &stl = m_volume->mesh.stl; + TriangleMesh mesh; + stl_file &stl = mesh.stl; stl.stats.type = inmemory; stl.stats.number_of_facets = int(m_volume_facets.size() / 3); stl.stats.original_num_facets = stl.stats.number_of_facets; @@ -533,8 +534,9 @@ void AMFParserContext::endElement(const char * /* name */) memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float)); } stl_get_size(&stl); - m_volume->mesh.repair(); - m_volume->center_geometry(); + mesh.repair(); + m_volume->set_mesh(std::move(mesh)); + m_volume->center_geometry_after_creation(); m_volume->calculate_convex_hull(); m_volume_facets.clear(); m_volume = nullptr; @@ -923,10 +925,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) int num_vertices = 0; for (ModelVolume *volume : object->volumes) { vertices_offsets.push_back(num_vertices); - if (! volume->mesh.repaired) + if (! volume->mesh().repaired) throw std::runtime_error("store_amf() requires repair()"); - volume->mesh.require_shared_vertices(); - const indexed_triangle_set &its = volume->mesh.its; + if (! volume->mesh().has_shared_vertices()) + throw std::runtime_error("store_amf() requires shared vertices"); + const indexed_triangle_set &its = volume->mesh().its; const Transform3d& matrix = volume->get_matrix(); for (size_t i = 0; i < its.vertices.size(); ++i) { stream << " \n"; @@ -955,10 +958,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) if (volume->is_modifier()) stream << " 1\n"; stream << " " << ModelVolume::type_to_string(volume->type()) << "\n"; - for (size_t i = 0; i < (int)volume->mesh.its.indices.size(); ++i) { + const indexed_triangle_set &its = volume->mesh().its; + for (size_t i = 0; i < (int)its.indices.size(); ++i) { stream << " \n"; for (int j = 0; j < 3; ++j) - stream << " " << volume->mesh.its.indices[i][j] + vertices_offset << "\n"; + stream << " " << its.indices[i][j] + vertices_offset << "\n"; stream << " \n"; } stream << " \n"; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 770581c03..113b21503 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -160,12 +160,6 @@ Model Model::read_from_archive(const std::string &input_file, DynamicPrintConfig return model; } -void Model::repair() -{ - for (ModelObject *o : this->objects) - o->repair(); -} - ModelObject* Model::add_object() { this->objects.emplace_back(new ModelObject(this)); @@ -472,7 +466,7 @@ bool Model::looks_like_multipart_object() const if (obj->volumes.size() > 1 || obj->config.keys().size() > 1) return false; for (const ModelVolume *vol : obj->volumes) { - double zmin_this = vol->mesh.bounding_box().min(2); + double zmin_this = vol->mesh().bounding_box().min(2); if (zmin == std::numeric_limits::max()) zmin = zmin_this; else if (std::abs(zmin - zmin_this) > EPSILON) @@ -679,7 +673,7 @@ ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh) { ModelVolume* v = new ModelVolume(this, mesh); this->volumes.push_back(v); - v->center_geometry(); + v->center_geometry_after_creation(); this->invalidate_bounding_box(); return v; } @@ -688,7 +682,7 @@ ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh) { ModelVolume* v = new ModelVolume(this, std::move(mesh)); this->volumes.push_back(v); - v->center_geometry(); + v->center_geometry_after_creation(); this->invalidate_bounding_box(); return v; } @@ -697,7 +691,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other) { ModelVolume* v = new ModelVolume(this, other); this->volumes.push_back(v); - v->center_geometry(); + v->center_geometry_after_creation(); this->invalidate_bounding_box(); return v; } @@ -706,7 +700,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me { ModelVolume* v = new ModelVolume(this, other, std::move(mesh)); this->volumes.push_back(v); - v->center_geometry(); + v->center_geometry_after_creation(); this->invalidate_bounding_box(); return v; } @@ -827,7 +821,7 @@ TriangleMesh ModelObject::raw_mesh() const for (const ModelVolume *v : this->volumes) if (v->is_model_part()) { - TriangleMesh vol_mesh(v->mesh); + TriangleMesh vol_mesh(v->mesh()); vol_mesh.transform(v->get_matrix()); mesh.merge(vol_mesh); } @@ -840,7 +834,7 @@ TriangleMesh ModelObject::full_raw_mesh() const TriangleMesh mesh; for (const ModelVolume *v : this->volumes) { - TriangleMesh vol_mesh(v->mesh); + TriangleMesh vol_mesh(v->mesh()); vol_mesh.transform(v->get_matrix()); mesh.merge(vol_mesh); } @@ -854,7 +848,7 @@ const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const m_raw_mesh_bounding_box.reset(); for (const ModelVolume *v : this->volumes) if (v->is_model_part()) - m_raw_mesh_bounding_box.merge(v->mesh.transformed_bounding_box(v->get_matrix())); + m_raw_mesh_bounding_box.merge(v->mesh().transformed_bounding_box(v->get_matrix())); } return m_raw_mesh_bounding_box; } @@ -863,7 +857,7 @@ BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const { BoundingBoxf3 bb; for (const ModelVolume *v : this->volumes) - bb.merge(v->mesh.transformed_bounding_box(v->get_matrix())); + bb.merge(v->mesh().transformed_bounding_box(v->get_matrix())); return bb; } @@ -881,7 +875,7 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const for (const ModelVolume *v : this->volumes) { if (v->is_model_part()) - m_raw_bounding_box.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix())); + m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix())); } } return m_raw_bounding_box; @@ -895,7 +889,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_ for (ModelVolume *v : this->volumes) { if (v->is_model_part()) - bb.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix())); + bb.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix())); } return bb; } @@ -909,10 +903,10 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const for (const ModelVolume *v : this->volumes) if (v->is_model_part()) { Transform3d trafo = trafo_instance * v->get_matrix(); - const indexed_triangle_set &its = v->mesh.its; + const indexed_triangle_set &its = v->mesh().its; if (its.vertices.empty()) { // Using the STL faces. - const stl_file& stl = v->mesh.stl; + const stl_file& stl = v->mesh().stl; for (const stl_facet &facet : stl.facet_start) for (size_t j = 0; j < 3; ++ j) { Vec3d p = trafo * facet.vertex[j].cast(); @@ -1038,6 +1032,7 @@ void ModelObject::mirror(Axis axis) this->invalidate_bounding_box(); } +// This method could only be called before the meshes of this ModelVolumes are not shared! void ModelObject::scale_mesh(const Vec3d &versor) { for (ModelVolume *v : this->volumes) @@ -1061,14 +1056,14 @@ size_t ModelObject::facets_count() const size_t num = 0; for (const ModelVolume *v : this->volumes) if (v->is_model_part()) - num += v->mesh.stl.stats.number_of_facets; + num += v->mesh().stl.stats.number_of_facets; return num; } bool ModelObject::needed_repair() const { for (const ModelVolume *v : this->volumes) - if (v->is_model_part() && v->mesh.needed_repair()) + if (v->is_model_part() && v->mesh().needed_repair()) return true; return false; } @@ -1134,11 +1129,12 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b // Transform the mesh by the combined transformation matrix. // Flip the triangles in case the composite transformation is left handed. - volume->mesh.transform(instance_matrix * volume_matrix, true); + TriangleMesh mesh(volume->mesh()); + mesh.transform(instance_matrix * volume_matrix, true); + volume->reset_mesh(); // Perform cut - volume->mesh.require_shared_vertices(); // TriangleMeshSlicer needs this - TriangleMeshSlicer tms(&volume->mesh); + TriangleMeshSlicer tms(&mesh); tms.cut(float(z), &upper_mesh, &lower_mesh); // Reset volume transformation except for offset @@ -1157,14 +1153,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b if (keep_upper && upper_mesh.facets_count() > 0) { ModelVolume* vol = upper->add_volume(upper_mesh); - vol->name = volume->name; - vol->config = volume->config; + vol->name = volume->name; + vol->config = volume->config; vol->set_material(volume->material_id(), *volume->material()); } if (keep_lower && lower_mesh.facets_count() > 0) { ModelVolume* vol = lower->add_volume(lower_mesh); - vol->name = volume->name; - vol->config = volume->config; + vol->name = volume->name; + vol->config = volume->config; vol->set_material(volume->material_id(), *volume->material()); // Compute the lower part instances' bounding boxes to figure out where to place @@ -1232,7 +1228,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects) } ModelVolume* volume = this->volumes.front(); - TriangleMeshPtrs meshptrs = volume->mesh.split(); + TriangleMeshPtrs meshptrs = volume->mesh().split(); for (TriangleMesh *mesh : meshptrs) { mesh->repair(); @@ -1259,12 +1255,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects) return; } -void ModelObject::repair() -{ - for (ModelVolume *v : this->volumes) - v->mesh.repair(); -} - // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. // This situation is solved by baking in the instance transformation into the mesh vertices. @@ -1294,8 +1284,8 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx) // Adjust the meshes. // Transformation to be applied to the meshes. - Eigen::Matrix3d mesh_trafo_3x3 = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0); - Transform3d volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix(); + Eigen::Matrix3d mesh_trafo_3x3 = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0); + Transform3d volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix(); for (ModelVolume *model_volume : this->volumes) { const Geometry::Transformation volume_trafo = model_volume->get_transformation(); bool volume_left_handed = volume_trafo.is_left_handed(); @@ -1305,7 +1295,8 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx) double volume_new_scaling_factor = volume_uniform_scaling ? volume_trafo.get_scaling_factor().x() : 1.; // Transform the mesh. Matrix3d volume_trafo_3x3 = volume_trafo.get_matrix(true, false, volume_uniform_scaling, !volume_has_mirrorring).matrix().block<3, 3>(0, 0); - model_volume->transform_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed); + // Following method creates a new shared_ptr + model_volume->transform_this_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed); // Reset the rotation, scaling and mirroring. model_volume->set_rotation(Vec3d(0., 0., 0.)); model_volume->set_scaling_factor(Vec3d(volume_new_scaling_factor, volume_new_scaling_factor, volume_new_scaling_factor)); @@ -1447,7 +1438,7 @@ std::string ModelObject::get_export_filename() const stl_stats ModelObject::get_object_stl_stats() const { if (this->volumes.size() == 1) - return this->volumes[0]->mesh.stl.stats; + return this->volumes[0]->mesh().stl.stats; stl_stats full_stats; memset(&full_stats, 0, sizeof(stl_stats)); @@ -1458,7 +1449,7 @@ stl_stats ModelObject::get_object_stl_stats() const if (volume->id() == this->volumes[0]->id()) continue; - const stl_stats& stats = volume->mesh.stl.stats; + const stl_stats& stats = volume->mesh().stl.stats; // initialize full_stats (for repaired errors) full_stats.degenerate_facets += stats.degenerate_facets; @@ -1526,30 +1517,30 @@ bool ModelVolume::is_splittable() const { // the call mesh.is_splittable() is expensive, so cache the value to calculate it only once if (m_is_splittable == -1) - m_is_splittable = (int)mesh.is_splittable(); + m_is_splittable = (int)this->mesh().is_splittable(); return m_is_splittable == 1; } -void ModelVolume::center_geometry() +void ModelVolume::center_geometry_after_creation() { - Vec3d shift = mesh.bounding_box().center(); + Vec3d shift = this->mesh().bounding_box().center(); if (!shift.isApprox(Vec3d::Zero())) { - mesh.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); - m_convex_hull.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); + m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); + m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); translate(shift); } } void ModelVolume::calculate_convex_hull() { - m_convex_hull = mesh.convex_hull_3d(); + m_convex_hull = std::make_shared(this->mesh().convex_hull_3d()); } int ModelVolume::get_mesh_errors_count() const { - const stl_stats& stats = this->mesh.stl.stats; + const stl_stats& stats = this->mesh().stl.stats; return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + stats.facets_added + stats.facets_reversed + stats.backwards_edges; @@ -1557,7 +1548,7 @@ int ModelVolume::get_mesh_errors_count() const const TriangleMesh& ModelVolume::get_convex_hull() const { - return m_convex_hull; + return *m_convex_hull.get(); } ModelVolumeType ModelVolume::type_from_string(const std::string &s) @@ -1597,7 +1588,7 @@ std::string ModelVolume::type_to_string(const ModelVolumeType t) // This is useful to assign different materials to different volumes of an object. size_t ModelVolume::split(unsigned int max_extruders) { - TriangleMeshPtrs meshptrs = this->mesh.split(); + TriangleMeshPtrs meshptrs = this->mesh().split(); if (meshptrs.size() <= 1) { delete meshptrs.front(); return 1; @@ -1614,7 +1605,7 @@ size_t ModelVolume::split(unsigned int max_extruders) mesh->repair(); if (idx == 0) { - this->mesh = std::move(*mesh); + this->set_mesh(std::move(*mesh)); this->calculate_convex_hull(); // Assign a new unique ID, so that a new GLVolume will be generated. this->set_new_unique_id(); @@ -1623,7 +1614,7 @@ size_t ModelVolume::split(unsigned int max_extruders) this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh))); this->object->volumes[ivolume]->set_offset(Vec3d::Zero()); - this->object->volumes[ivolume]->center_geometry(); + this->object->volumes[ivolume]->center_geometry_after_creation(); this->object->volumes[ivolume]->translate(offset); this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1); this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders)); @@ -1689,24 +1680,33 @@ void ModelVolume::mirror(Axis axis) set_mirror(mirror); } +// This method could only be called before the meshes of this ModelVolumes are not shared! void ModelVolume::scale_geometry(const Vec3d& versor) { - mesh.scale(versor); - m_convex_hull.scale(versor); + m_mesh->scale(versor); + m_convex_hull->scale(versor); } -void ModelVolume::transform_mesh(const Transform3d &mesh_trafo, bool fix_left_handed) +void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_left_handed) { - this->mesh.transform(mesh_trafo, fix_left_handed); - this->m_convex_hull.transform(mesh_trafo, fix_left_handed); + TriangleMesh mesh = this->mesh(); + mesh.transform(mesh_trafo, fix_left_handed); + this->set_mesh(std::move(mesh)); + TriangleMesh convex_hull = this->get_convex_hull(); + convex_hull.transform(mesh_trafo, fix_left_handed); + this->m_convex_hull = std::make_shared(std::move(convex_hull)); // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. this->set_new_unique_id(); } -void ModelVolume::transform_mesh(const Matrix3d &matrix, bool fix_left_handed) +void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_handed) { - this->mesh.transform(matrix, fix_left_handed); - this->m_convex_hull.transform(matrix, fix_left_handed); + TriangleMesh mesh = this->mesh(); + mesh.transform(matrix, fix_left_handed); + this->set_mesh(std::move(mesh)); + TriangleMesh convex_hull = this->get_convex_hull(); + convex_hull.transform(matrix, fix_left_handed); + this->m_convex_hull = std::make_shared(std::move(convex_hull)); // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. this->set_new_unique_id(); } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 41bf5bd4b..0fd1140f0 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -7,7 +7,9 @@ #include "Point.hpp" #include "TriangleMesh.hpp" #include "Slicing.hpp" + #include +#include #include #include #include @@ -261,6 +263,7 @@ public: void rotate(double angle, const Vec3d& axis); void mirror(Axis axis); + // This method could only be called before the meshes of this ModelVolumes are not shared! void scale_mesh(const Vec3d& versor); size_t materials_count() const; @@ -268,7 +271,6 @@ public: bool needed_repair() const; ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates void split(ModelObjectPtrs* new_objects); - void repair(); // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. // This situation is solved by baking in the instance transformation into the mesh vertices. @@ -340,7 +342,12 @@ class ModelVolume : public ModelBase public: std::string name; // The triangular model. - TriangleMesh mesh; + const TriangleMesh& mesh() const { return *m_mesh.get(); } + void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); } + void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared(std::move(mesh)); } + void set_mesh(std::shared_ptr &mesh) { m_mesh = mesh; } + void set_mesh(std::unique_ptr &&mesh) { m_mesh = std::move(mesh); } + void reset_mesh() { m_mesh = std::make_shared(); } // Configuration parameters specific to an object model geometry or a modifier volume, // overriding the global Slic3r settings and the ModelObject settings. DynamicPrintConfig config; @@ -377,13 +384,16 @@ public: void rotate(double angle, const Vec3d& axis); void mirror(Axis axis); + // This method could only be called before the meshes of this ModelVolumes are not shared! void scale_geometry(const Vec3d& versor); - // translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box - void center_geometry(); + // Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box. + // Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared! + void center_geometry_after_creation(); void calculate_convex_hull(); const TriangleMesh& get_convex_hull() const; + std::shared_ptr get_convex_hull_shared_ptr() const { return m_convex_hull; } // Get count of errors in the mesh int get_mesh_errors_count() const; @@ -430,18 +440,20 @@ protected: explicit ModelVolume(const ModelVolume &rhs) = default; void set_model_object(ModelObject *model_object) { object = model_object; } - void transform_mesh(const Transform3d& t, bool fix_left_handed); - void transform_mesh(const Matrix3d& m, bool fix_left_handed); + void transform_this_mesh(const Transform3d& t, bool fix_left_handed); + void transform_this_mesh(const Matrix3d& m, bool fix_left_handed); private: // Parent object owning this ModelVolume. - ModelObject* object; + ModelObject* object; + // The triangular model. + std::shared_ptr m_mesh; // Is it an object to be printed, or a modifier volume? - ModelVolumeType m_type; - t_model_material_id m_material_id; + ModelVolumeType m_type; + t_model_material_id m_material_id; // The convex hull of this model's mesh. - TriangleMesh m_convex_hull; - Geometry::Transformation m_transformation; + std::shared_ptr m_convex_hull; + Geometry::Transformation m_transformation; // flag to optimize the checking if the volume is splittable // -1 -> is unknown value (before first cheking) @@ -449,24 +461,24 @@ private: // 1 -> is splittable mutable int m_is_splittable{ -1 }; - ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object) + ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object) { if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); } ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : - mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(ModelVolumeType::MODEL_PART), object(object) {} + m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {} // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : ModelBase(other), // copy the ID - name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) + name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { this->set_material_id(other.material_id()); } // Providing a new mesh, therefore this volume will get a new unique ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : - name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) + name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { this->set_material_id(other.material_id()); if (mesh.stl.stats.number_of_facets > 1) @@ -597,10 +609,6 @@ public: static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true); static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true); - /// Repair the ModelObjects of the current Model. - /// This function calls repair function on each TriangleMesh of each model object volume - void repair(); - // Add a new ModelObject to this Model, generate a new ID for this ModelObject. ModelObject* add_object(); ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 660a2d939..d99aceabf 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1797,7 +1797,7 @@ std::vector PrintObject::_slice_volumes(const std::vector &z, if (! volumes.empty()) { // Compose mesh. //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. - TriangleMesh mesh(volumes.front()->mesh); + TriangleMesh mesh(volumes.front()->mesh()); mesh.transform(volumes.front()->get_matrix(), true); assert(mesh.repaired); if (volumes.size() == 1 && mesh.repaired) { @@ -1806,7 +1806,7 @@ std::vector PrintObject::_slice_volumes(const std::vector &z, } for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) { const ModelVolume &model_volume = *volumes[idx_volume]; - TriangleMesh vol_mesh(model_volume.mesh); + TriangleMesh vol_mesh(model_volume.mesh()); vol_mesh.transform(model_volume.get_matrix(), true); mesh.merge(vol_mesh); } @@ -1815,10 +1815,11 @@ std::vector PrintObject::_slice_volumes(const std::vector &z, // apply XY shift mesh.translate(- unscale(m_copies_shift(0)), - unscale(m_copies_shift(1)), 0); // perform actual slicing - TriangleMeshSlicer mslicer; const Print *print = this->print(); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); - mesh.require_shared_vertices(); // TriangleMeshSlicer needs this + // TriangleMeshSlicer needs shared vertices, also this calls the repair() function. + mesh.require_shared_vertices(); + TriangleMeshSlicer mslicer; mslicer.init(&mesh, callback); mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); m_print->throw_if_canceled(); @@ -1832,7 +1833,7 @@ std::vector PrintObject::_slice_volume(const std::vector &z, std::vector layers; // Compose mesh. //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. - TriangleMesh mesh(volume.mesh); + TriangleMesh mesh(volume.mesh()); mesh.transform(volume.get_matrix(), true); if (mesh.repaired) { //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it. @@ -1846,7 +1847,8 @@ std::vector PrintObject::_slice_volume(const std::vector &z, TriangleMeshSlicer mslicer; const Print *print = this->print(); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); - mesh.require_shared_vertices(); // TriangleMeshSlicer needs this + // TriangleMeshSlicer needs the shared vertices. + mesh.require_shared_vertices(); mslicer.init(&mesh, callback); mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); m_print->throw_if_canceled(); diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 3a05e9d8a..e1bb4b313 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -227,7 +227,7 @@ std::vector layer_height_profile_adaptive( as.set_slicing_parameters(slicing_params); for (const ModelVolume *volume : volumes) if (volume->is_model_part()) - as.add_mesh(&volume->mesh); + as.add_mesh(&volume->mesh()); as.prepare(); // 2) Generate layers using the algorithm of @platsch diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 5b0ecbd00..11f45147c 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -82,11 +82,14 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other) // #define SLIC3R_TRACE_REPAIR -void TriangleMesh::repair() +void TriangleMesh::repair(bool update_shared_vertices) { - if (this->repaired) + if (this->repaired) { + if (update_shared_vertices) + this->require_shared_vertices(); return; - + } + // admesh fails when repairing empty meshes if (this->stl.stats.number_of_facets == 0) return; @@ -97,6 +100,7 @@ void TriangleMesh::repair() #ifdef SLIC3R_TRACE_REPAIR BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact"; #endif /* SLIC3R_TRACE_REPAIR */ + assert(stl_validate(&this->stl)); stl_check_facets_exact(&stl); assert(stl_validate(&this->stl)); stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge); @@ -179,6 +183,12 @@ void TriangleMesh::repair() this->repaired = true; BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished"; + + // This call should be quite cheap, a lot of code requires the indexed_triangle_set data structure, + // and it is risky to generate such a structure once the meshes are shared. Do it now. + this->its.clear(); + if (update_shared_vertices) + this->require_shared_vertices(); } float TriangleMesh::volume() @@ -238,8 +248,7 @@ bool TriangleMesh::needed_repair() const void TriangleMesh::WriteOBJFile(const char* output_file) { - stl_generate_shared_vertices(&stl, its); - its_write_obj(its, output_file); + its_write_obj(this->its, output_file); } void TriangleMesh::scale(float factor) @@ -294,6 +303,7 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis) Transform3d m = Transform3d::Identity(); m.rotate(Eigen::AngleAxisd(angle, axis_norm)); stl_transform(&stl, m); + its_transform(its, m); } void TriangleMesh::mirror(const Axis &axis) @@ -311,22 +321,26 @@ void TriangleMesh::mirror(const Axis &axis) void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed) { stl_transform(&stl, t); - this->its.clear(); + its_transform(its, t); if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) { // Left handed transformation is being applied. It is a good idea to flip the faces and their normals. - this->repair(); + this->repair(false); stl_reverse_all_facets(&stl); + this->its.clear(); + this->require_shared_vertices(); } } void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed) { stl_transform(&stl, m); - this->its.clear(); + its_transform(its, m); if (fix_left_handed && m.determinant() < 0.) { // Left handed transformation is being applied. It is a good idea to flip the faces and their normals. - this->repair(); + this->repair(false); stl_reverse_all_facets(&stl); + this->its.clear(); + this->require_shared_vertices(); } } @@ -482,7 +496,6 @@ ExPolygons TriangleMesh::horizontal_projection() const // 2D convex hull of a 3D mesh projected into the Z=0 plane. Polygon TriangleMesh::convex_hull() { - this->require_shared_vertices(); Points pp; pp.reserve(this->its.vertices.size()); for (size_t i = 0; i < this->its.vertices.size(); ++ i) { @@ -519,26 +532,32 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c TriangleMesh TriangleMesh::convex_hull_3d() const { - // Helper struct for qhull: - struct PointForQHull{ - PointForQHull(float x_p, float y_p, float z_p) : x((realT)x_p), y((realT)y_p), z((realT)z_p) {} - realT x, y, z; - }; - std::vector src_vertices; - - // We will now fill the vector with input points for computation: - for (const stl_facet &facet : stl.facet_start) - for (int i = 0; i < 3; ++ i) { - const stl_vertex& v = facet.vertex[i]; - src_vertices.emplace_back(v(0), v(1), v(2)); - } - // The qhull call: orgQhull::Qhull qhull; qhull.disableOutputStream(); // we want qhull to be quiet - try + std::vector src_vertices; + try { - qhull.runQhull("", 3, (int)src_vertices.size(), (const realT*)(src_vertices.data()), "Qt"); + if (this->has_shared_vertices()) { +#if REALfloat + qhull.runQhull("", 3, (int)this->its.vertices.size() / 3, (const realT*)(this->its.vertices.front().data()), "Qt"); +#else + src_vertices.reserve(this->its.vertices() * 3); + // We will now fill the vector with input points for computation: + for (const stl_vertex &v : ths->its.vertices.size()) + for (int i = 0; i < 3; ++ i) + src_vertices.emplace_back(v(i)); + qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt"); +#endif + } else { + src_vertices.reserve(this->stl.facet_start.size() * 9); + // We will now fill the vector with input points for computation: + for (const stl_facet &f : this->stl.facet_start) + for (int i = 0; i < 3; ++ i) + for (int j = 0; j < 3; ++ j) + src_vertices.emplace_back(f.vertex[i](j)); + qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt"); + } } catch (...) { @@ -566,7 +585,6 @@ TriangleMesh TriangleMesh::convex_hull_3d() const TriangleMesh output_mesh(dst_vertices, facets); output_mesh.repair(); - output_mesh.require_shared_vertices(); return output_mesh; } diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 75082cfdb..ffd9b7e62 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -33,7 +33,7 @@ public: bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); } bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); } bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); } - void repair(); + void repair(bool update_shared_vertices = true); float volume(); void check_topology(); bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 59480de1c..33ab1f5d1 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -241,8 +241,6 @@ GLVolume::GLVolume(float r, float g, float b, float a) : m_transformed_bounding_box_dirty(true) , m_sla_shift_z(0.0) , m_transformed_convex_hull_bounding_box_dirty(true) - , m_convex_hull(nullptr) - , m_convex_hull_owned(false) // geometry_id == 0 -> invalid , geometry_id(std::pair(0, 0)) , extruder_id(0) @@ -268,12 +266,6 @@ GLVolume::GLVolume(float r, float g, float b, float a) set_render_color(r, g, b, a); } -GLVolume::~GLVolume() -{ - if (m_convex_hull_owned) - delete m_convex_hull; -} - void GLVolume::set_render_color(float r, float g, float b, float a) { render_color[0] = r; @@ -335,12 +327,6 @@ void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume) color[3] = model_volume->is_model_part() ? 1.f : 0.5f; } -void GLVolume::set_convex_hull(const TriangleMesh *convex_hull, bool owned) -{ - m_convex_hull = convex_hull; - m_convex_hull_owned = owned; -} - Transform3d GLVolume::world_matrix() const { Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); @@ -377,7 +363,7 @@ const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const { - return (m_convex_hull != nullptr && m_convex_hull->stl.stats.number_of_facets > 0) ? + return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ? m_convex_hull->transformed_bounding_box(trafo) : bounding_box.transformed(trafo); } @@ -587,7 +573,7 @@ int GLVolumeCollection::load_object_volume( const ModelVolume *model_volume = model_object->volumes[volume_idx]; const int extruder_id = model_volume->extruder_id(); const ModelInstance *instance = model_object->instances[instance_idx]; - const TriangleMesh& mesh = model_volume->mesh; + const TriangleMesh& mesh = model_volume->mesh(); float color[4]; memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); /* if (model_volume->is_support_blocker()) { @@ -613,7 +599,7 @@ int GLVolumeCollection::load_object_volume( if (model_volume->is_model_part()) { // GLVolume will reference a convex hull from model_volume! - v.set_convex_hull(&model_volume->get_convex_hull(), false); + v.set_convex_hull(model_volume->get_convex_hull_shared_ptr()); if (extruder_id != -1) v.extruder_id = extruder_id; } @@ -656,7 +642,10 @@ void GLVolumeCollection::load_object_auxiliary( v.composite_id = GLVolume::CompositeID(obj_idx, - int(milestone), (int)instance_idx.first); v.geometry_id = std::pair(timestamp, model_instance.id().id); // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. - v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true); + if (&instance_idx == &instances.back()) + v.set_convex_hull(std::move(convex_hull)); + else + v.set_convex_hull(convex_hull); v.is_modifier = false; v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree); v.set_instance_transformation(model_instance.get_transformation()); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 2ca11073b..b9ac668e0 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -10,6 +10,7 @@ #include "slic3r/GUI/GLCanvas3DManager.hpp" #include +#include #ifndef NDEBUG #define HAS_GLSAFE @@ -243,7 +244,6 @@ public: 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]) {} - ~GLVolume(); private: Geometry::Transformation m_instance_transformation; @@ -255,10 +255,8 @@ private: mutable BoundingBoxf3 m_transformed_bounding_box; // Whether or not is needed to recalculate the transformed bounding box. mutable bool m_transformed_bounding_box_dirty; - // Pointer to convex hull of the original mesh, if any. - // This object may or may not own the convex hull instance based on m_convex_hull_owned - const TriangleMesh* m_convex_hull; - bool m_convex_hull_owned; + // Convex hull of the volume, if any. + std::shared_ptr m_convex_hull; // Bounding box of this volume, in unscaled coordinates. mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box; // Whether or not is needed to recalculate the transformed convex hull bounding box. @@ -395,7 +393,9 @@ public: double get_sla_shift_z() const { return m_sla_shift_z; } void set_sla_shift_z(double z) { m_sla_shift_z = z; } - void set_convex_hull(const TriangleMesh *convex_hull, bool owned); + void set_convex_hull(std::shared_ptr &convex_hull) { m_convex_hull = convex_hull; } + void set_convex_hull(const TriangleMesh &convex_hull) { m_convex_hull = std::make_shared(convex_hull); } + void set_convex_hull(TriangleMesh &&convex_hull) { m_convex_hull = std::make_shared(std::move(convex_hull)); } int object_idx() const { return this->composite_id.object_id; } int volume_idx() const { return this->composite_id.volume_id; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b8ca905d2..f0a40950d 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5498,7 +5498,7 @@ void GLCanvas3D::_load_sla_shells() v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0)); v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation)); v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.); - v.set_convex_hull(new TriangleMesh(std::move(mesh.convex_hull_3d())), true); + v.set_convex_hull(mesh.convex_hull_3d()); }; // adds objects' volumes diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index dffa02e95..cbf7a5ef5 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -261,7 +261,7 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx / const stl_stats& stats = vol_idx == -1 ? (*m_objects)[obj_idx]->get_object_stl_stats() : - (*m_objects)[obj_idx]->volumes[vol_idx]->mesh.stl.stats; + (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stl.stats; std::map error_msg = { { L("degenerate facets"), stats.degenerate_facets }, @@ -1592,7 +1592,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode // First (any) GLVolume of the selected instance. They all share the same instance matrix. const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); // Transform the new modifier to be aligned with the print bed. - const BoundingBoxf3 mesh_bb = new_volume->mesh.bounding_box(); + const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box(); new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb)); // Set the modifier position. auto offset = (type_name == "Slab") ? diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 89475a058..2a2adcae9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -388,8 +388,7 @@ void GLGizmoSlaSupports::update_mesh() wxBusyCursor wait; // this way we can use that mesh directly. // This mesh does not account for the possible Z up SLA offset. - m_mesh = &m_model_object->volumes.front()->mesh; - const_cast(m_mesh)->require_shared_vertices(); // TriangleMeshSlicer needs this + m_mesh = &m_model_object->volumes.front()->mesh(); m_its = &m_mesh->its; m_current_mesh_model_id = m_model_object->id(); m_editing_mode = false; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 44f77b3f7..18b3078bb 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3565,7 +3565,7 @@ void Plater::export_stl(bool extended, bool selection_only) else { const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - mesh = model_object->volumes[volume->volume_idx()]->mesh; + mesh = model_object->volumes[volume->volume_idx()]->mesh(); mesh.transform(volume->get_volume_transformation().get_matrix()); mesh.translate(-model_object->origin_translation.cast()); } diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index a1c8890ef..6a2cc6080 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -253,7 +253,7 @@ ModelMaterial::attributes() Ref config() %code%{ RETVAL = &THIS->config; %}; Ref mesh() - %code%{ RETVAL = &THIS->mesh; %}; + %code%{ RETVAL = &THIS->mesh(); %}; bool modifier() %code%{ RETVAL = THIS->is_modifier(); %}; From 3872b939e440421dec0fc2cee8fad40fba6f1c0c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 11 Jun 2019 17:15:07 +0200 Subject: [PATCH 24/44] Fix of previous commit --- src/admesh/stl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index c419c567b..40bced2f4 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -216,7 +216,6 @@ template extern void its_transform(indexed_triangle_set &its, T *trafo3x4) { for (stl_vertex &v_dst : its.vertices) { - stl_vertex &v_dst = face.vertex[i_vertex]; stl_vertex v_src = v_dst; v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]); v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]); From 9379fedd439826846d97eb02c7d88455aa50c47e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 13 Jun 2019 16:33:50 +0200 Subject: [PATCH 25/44] Further C++isation of the admesh library & TriangleMesh (copy & move constructors / operators) --- src/admesh/connect.cpp | 2 ++ src/admesh/stl.h | 26 ++++++++++++++- src/admesh/stlinit.cpp | 10 +----- src/admesh/util.cpp | 43 ++++++++++++++++++++----- src/libslic3r/TriangleMesh.cpp | 47 +++++++++++++++------------- src/libslic3r/TriangleMesh.hpp | 14 ++++----- src/slic3r/Config/Version.cpp | 5 +++ src/slic3r/Utils/FixModelByWin10.cpp | 4 +-- 8 files changed, 102 insertions(+), 49 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index 1a7b7965d..5afb85d2c 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -30,6 +30,8 @@ #include #include +// Boost pool: Don't use mutexes to synchronize memory allocation. +#define BOOST_POOL_NO_MT #include #include "stl.h" diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 40bced2f4..87210b3c9 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -89,6 +89,8 @@ struct stl_neighbors { }; struct stl_stats { + stl_stats() { this->reset(); } + void reset() { memset(this, 0, sizeof(stl_stats)); this->volume = -1.0; } char header[81]; stl_type type; uint32_t number_of_facets; @@ -117,6 +119,18 @@ struct stl_stats { }; struct stl_file { + stl_file() {} + stl_file(const stl_file &rhs) : facet_start(rhs.facet_start), neighbors_start(rhs.neighbors_start), stats(rhs.stats) {} + stl_file(stl_file &&rhs) : facet_start(std::move(rhs.facet_start)), neighbors_start(std::move(rhs.neighbors_start)), stats(rhs.stats) {} + stl_file& operator=(const stl_file &rhs) { this->facet_start = rhs.facet_start; this->neighbors_start = rhs.neighbors_start; this->stats = rhs.stats; return *this; } + stl_file& operator=(stl_file &&rhs) { this->facet_start = std::move(rhs.facet_start); this->neighbors_start = std::move(rhs.neighbors_start); this->stats = rhs.stats; return *this; } + + void clear() { + this->facet_start.clear(); + this->neighbors_start.clear(); + this->stats.reset(); + } + std::vector facet_start; std::vector neighbors_start; // Statistics @@ -125,7 +139,14 @@ struct stl_file { struct indexed_triangle_set { + indexed_triangle_set() {} + indexed_triangle_set(const indexed_triangle_set &rhs) : indices(rhs.indices), vertices(rhs.vertices) {} + indexed_triangle_set(indexed_triangle_set &&rhs) : indices(std::move(rhs.indices)), vertices(std::move(rhs.vertices)) {} + indexed_triangle_set& operator=(const indexed_triangle_set &rhs) { this->indices = rhs.indices; this->vertices = rhs.vertices; return *this; } + indexed_triangle_set& operator=(indexed_triangle_set &&rhs) { this->indices = std::move(rhs.indices); this->vertices = std::move(rhs.vertices); return *this; } + void clear() { indices.clear(); vertices.clear(); } + std::vector indices; std::vector vertices; //FIXME add normals once we get rid of the stl_file from TriangleMesh completely. @@ -238,6 +259,10 @@ inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix()).template cast().eval(); } +extern void its_rotate_x(indexed_triangle_set &its, float angle); +extern void its_rotate_y(indexed_triangle_set &its, float angle); +extern void its_rotate_z(indexed_triangle_set &its, float angle); + extern void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its); extern bool its_write_obj(const indexed_triangle_set &its, const char *file); extern bool its_write_off(const indexed_triangle_set &its, const char *file); @@ -258,7 +283,6 @@ extern void stl_calculate_volume(stl_file *stl); extern void stl_repair(stl_file *stl, bool fixall_flag, bool exact_flag, bool tolerance_flag, float tolerance, bool increment_flag, float increment, bool nearby_flag, int iterations, bool remove_unconnected_flag, bool fill_holes_flag, bool normal_directions_flag, bool normal_values_flag, bool reverse_all_flag, bool verbose_flag); -extern void stl_reset(stl_file *stl); extern void stl_allocate(stl_file *stl); extern void stl_read(stl_file *stl, int first_facet, bool first); extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first); diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp index 0cc6e50c7..a328baa75 100644 --- a/src/admesh/stlinit.cpp +++ b/src/admesh/stlinit.cpp @@ -221,7 +221,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first) bool stl_open(stl_file *stl, const char *file) { - stl_reset(stl); + stl->clear(); FILE *fp = stl_open_count_facets(stl, file); if (fp == nullptr) return false; @@ -231,14 +231,6 @@ bool stl_open(stl_file *stl, const char *file) return result; } -void stl_reset(stl_file *stl) -{ - stl->facet_start.clear(); - stl->neighbors_start.clear(); - memset(&stl->stats, 0, sizeof(stl_stats)); - stl->stats.volume = -1.0; -} - #ifndef BOOST_LITTLE_ENDIAN extern void stl_internal_reverse_quads(char *buf, size_t cnt); #endif /* BOOST_LITTLE_ENDIAN */ diff --git a/src/admesh/util.cpp b/src/admesh/util.cpp index 70f4ffc27..029e44a28 100644 --- a/src/admesh/util.cpp +++ b/src/admesh/util.cpp @@ -115,12 +115,12 @@ static void calculate_normals(stl_file *stl) } } -static void rotate_point_2d(float *x, float *y, const double c, const double s) +static inline void rotate_point_2d(float &x, float &y, const double c, const double s) { - double xold = *x; - double yold = *y; - *x = float(c * xold - s * yold); - *y = float(s * xold + c * yold); + double xold = x; + double yold = y; + x = float(c * xold - s * yold); + y = float(s * xold + c * yold); } void stl_rotate_x(stl_file *stl, float angle) @@ -130,7 +130,7 @@ void stl_rotate_x(stl_file *stl, float angle) double s = sin(radian_angle); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) for (int j = 0; j < 3; ++ j) - rotate_point_2d(&stl->facet_start[i].vertex[j](1), &stl->facet_start[i].vertex[j](2), c, s); + rotate_point_2d(stl->facet_start[i].vertex[j](1), stl->facet_start[i].vertex[j](2), c, s); stl_get_size(stl); calculate_normals(stl); } @@ -142,7 +142,7 @@ void stl_rotate_y(stl_file *stl, float angle) double s = sin(radian_angle); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) for (int j = 0; j < 3; ++ j) - rotate_point_2d(&stl->facet_start[i].vertex[j](2), &stl->facet_start[i].vertex[j](0), c, s); + rotate_point_2d(stl->facet_start[i].vertex[j](2), stl->facet_start[i].vertex[j](0), c, s); stl_get_size(stl); calculate_normals(stl); } @@ -154,11 +154,38 @@ void stl_rotate_z(stl_file *stl, float angle) double s = sin(radian_angle); for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) for (int j = 0; j < 3; ++ j) - rotate_point_2d(&stl->facet_start[i].vertex[j](0), &stl->facet_start[i].vertex[j](1), c, s); + rotate_point_2d(stl->facet_start[i].vertex[j](0), stl->facet_start[i].vertex[j](1), c, s); stl_get_size(stl); calculate_normals(stl); } +void its_rotate_x(indexed_triangle_set &its, float angle) +{ + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); + for (stl_vertex &v : its.vertices) + rotate_point_2d(v(1), v(2), c, s); +} + +void its_rotate_y(indexed_triangle_set& its, float angle) +{ + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); + for (stl_vertex& v : its.vertices) + rotate_point_2d(v(2), v(0), c, s); +} + +void its_rotate_z(indexed_triangle_set& its, float angle) +{ + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); + for (stl_vertex& v : its.vertices) + rotate_point_2d(v(0), v(1), c, s); +} + void stl_get_size(stl_file *stl) { if (stl->stats.number_of_facets == 0) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 11f45147c..56accfefa 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -42,10 +42,8 @@ namespace Slic3r { -TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& facets) - : repaired(false) +TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& facets) : repaired(false) { - stl_reset(&this->stl); stl_file &stl = this->stl; stl.stats.type = inmemory; @@ -72,14 +70,6 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f stl_get_size(&stl); } -TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other) -{ - stl_reset(&this->stl); - this->stl = other.stl; - this->repaired = other.repaired; - return *this; -} - // #define SLIC3R_TRACE_REPAIR void TriangleMesh::repair(bool update_shared_vertices) @@ -254,13 +244,18 @@ void TriangleMesh::WriteOBJFile(const char* output_file) void TriangleMesh::scale(float factor) { stl_scale(&(this->stl), factor); - this->its.clear(); + for (stl_vertex& v : this->its.vertices) + v *= factor; } void TriangleMesh::scale(const Vec3d &versor) { stl_scale_versor(&this->stl, versor.cast()); - this->its.clear(); + for (stl_vertex& v : this->its.vertices) { + v.x() *= versor.x(); + v.y() *= versor.y(); + v.z() *= versor.z(); + } } void TriangleMesh::translate(float x, float y, float z) @@ -268,7 +263,9 @@ void TriangleMesh::translate(float x, float y, float z) if (x == 0.f && y == 0.f && z == 0.f) return; stl_translate_relative(&(this->stl), x, y, z); - this->its.clear(); + stl_vertex shift(x, y, z); + for (stl_vertex& v : this->its.vertices) + v += shift; } void TriangleMesh::translate(const Vec3f &displacement) @@ -285,13 +282,15 @@ void TriangleMesh::rotate(float angle, const Axis &axis) angle = Slic3r::Geometry::rad2deg(angle); if (axis == X) { - stl_rotate_x(&(this->stl), angle); + stl_rotate_x(&this->stl, angle); + its_rotate_x(this->its, angle); } else if (axis == Y) { - stl_rotate_y(&(this->stl), angle); + stl_rotate_y(&this->stl, angle); + its_rotate_y(this->its, angle); } else if (axis == Z) { - stl_rotate_z(&(this->stl), angle); + stl_rotate_z(&this->stl, angle); + its_rotate_z(this->its, angle); } - this->its.clear(); } void TriangleMesh::rotate(float angle, const Vec3d& axis) @@ -310,12 +309,17 @@ void TriangleMesh::mirror(const Axis &axis) { if (axis == X) { stl_mirror_yz(&this->stl); + for (stl_vertex &v : this->its.vertices) + v(0) *= -1.0; } else if (axis == Y) { stl_mirror_xz(&this->stl); + for (stl_vertex &v : this->its.vertices) + v(1) *= -1.0; } else if (axis == Z) { stl_mirror_xy(&this->stl); + for (stl_vertex &v : this->its.vertices) + v(2) *= -1.0; } - this->its.clear(); } void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed) @@ -358,7 +362,8 @@ void TriangleMesh::rotate(double angle, Point* center) return; Vec2f c = center->cast(); this->translate(-c(0), -c(1), 0); - stl_rotate_z(&(this->stl), (float)angle); + stl_rotate_z(&this->stl, (float)angle); + its_rotate_z(this->its, (float)angle); this->translate(c(0), c(1), 0); } @@ -540,7 +545,7 @@ TriangleMesh TriangleMesh::convex_hull_3d() const { if (this->has_shared_vertices()) { #if REALfloat - qhull.runQhull("", 3, (int)this->its.vertices.size() / 3, (const realT*)(this->its.vertices.front().data()), "Qt"); + qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt"); #else src_vertices.reserve(this->its.vertices() * 3); // We will now fill the vector with input points for computation: diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index ffd9b7e62..54c6dc5d0 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -21,15 +21,13 @@ typedef std::vector TriangleMeshPtrs; class TriangleMesh { public: - TriangleMesh() : repaired(false) { stl_reset(&this->stl); } + TriangleMesh() : repaired(false) {} TriangleMesh(const Pointf3s &points, const std::vector &facets); - TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_reset(&this->stl); *this = other; } - TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_reset(&this->stl); this->swap(other); } - ~TriangleMesh() { clear(); } - TriangleMesh& operator=(const TriangleMesh &other); - TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; } - void clear() { stl_reset(&this->stl); this->repaired = false; } - void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); } + TriangleMesh(const TriangleMesh& rhs) : stl(rhs.stl), its(rhs.its), repaired(rhs.repaired) {} + TriangleMesh(TriangleMesh&& rhs) : stl(std::move(rhs.stl)), its(std::move(rhs.its)), repaired(rhs.repaired) {} + TriangleMesh& operator=(const TriangleMesh& rhs) { this->stl = rhs.stl; this->its = rhs.its; this->repaired = rhs.repaired; return *this; } + TriangleMesh& operator=(TriangleMesh &&rhs) { this->stl = std::move(rhs.stl); this->its = std::move(rhs.its); this->repaired = rhs.repaired; return *this; } + void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; } bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); } bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); } bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); } diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp index 2eda135d6..fe3adfd7f 100644 --- a/src/slic3r/Config/Version.cpp +++ b/src/slic3r/Config/Version.cpp @@ -198,6 +198,11 @@ size_t Index::load(const boost::filesystem::path &path) size_t idx_line = 0; Version ver; while (std::getline(ifs, line)) { +#ifndef _MSVCVER + // On a Unix system, getline does not remove the trailing carriage returns, if the index is shared over a Windows filesystem. Remove them manually. + while (! line.empty() && line.back() == '\r') + line.pop_back(); +#endif ++ idx_line; // Skip the initial white spaces. char *key = left_trim(const_cast(line.data())); diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index 1daeaff26..710f19090 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -389,10 +389,10 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) throw std::runtime_error(L("Repaired 3MF file does not contain any volume")); if (model.objects.front()->volumes.size() > 1) throw std::runtime_error(L("Repaired 3MF file contains more than one volume")); - meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh)); + meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh())); } for (size_t i = 0; i < volumes.size(); ++ i) { - volumes[i]->mesh = std::move(meshes_repaired[i]); + volumes[i]->set_mesh(std::move(meshes_repaired[i])); volumes[i]->set_new_unique_id(); } model_object.invalidate_bounding_box(); From dbfa4e6c838eda34fd059ca2f6ce71f2130c5180 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 13 Jun 2019 16:55:12 +0200 Subject: [PATCH 26/44] Fix of a smart pointer gymnastics from previous commit --- src/slic3r/GUI/3DScene.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index b9ac668e0..0414a1ed3 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -393,7 +393,7 @@ public: double get_sla_shift_z() const { return m_sla_shift_z; } void set_sla_shift_z(double z) { m_sla_shift_z = z; } - void set_convex_hull(std::shared_ptr &convex_hull) { m_convex_hull = convex_hull; } + void set_convex_hull(std::shared_ptr convex_hull) { m_convex_hull = std::move(convex_hull); } void set_convex_hull(const TriangleMesh &convex_hull) { m_convex_hull = std::make_shared(convex_hull); } void set_convex_hull(TriangleMesh &&convex_hull) { m_convex_hull = std::make_shared(std::move(convex_hull)); } From 9b7bb41db5b949285d28206a4ff44078bce37ecf Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 13 Jun 2019 17:24:37 +0200 Subject: [PATCH 27/44] ModelObject::add_volume(const ModelVolume &other) shall not re-center the volume as it will share meshes (object mesh, convex hull mesh) of the source, which may be in use by the background processing. --- src/libslic3r/Model.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 113b21503..8e879a3e6 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -691,8 +691,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other) { ModelVolume* v = new ModelVolume(this, other); this->volumes.push_back(v); - v->center_geometry_after_creation(); - this->invalidate_bounding_box(); + // The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull. +// v->center_geometry_after_creation(); +// this->invalidate_bounding_box(); return v; } From d750d4f9255bfb388ba7b53d3a366ac16d84a7d0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 13 Jun 2019 17:42:55 +0200 Subject: [PATCH 28/44] Re-enable high power graphics card on Windows. This is a regression issue against 1.41.3 --- src/PrusaSlicer.cpp | 3 +++ src/PrusaSlicer_app_msvc.cpp | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index a3247b929..2becb8071 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -7,10 +7,13 @@ #include #include #ifdef SLIC3R_GUI + extern "C" + { // Let the NVIDIA and AMD know we want to use their graphics card // on a dual graphics card system. __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; + } #endif /* SLIC3R_GUI */ #endif /* WIN32 */ diff --git a/src/PrusaSlicer_app_msvc.cpp b/src/PrusaSlicer_app_msvc.cpp index 5b01751b9..ee8cdf696 100644 --- a/src/PrusaSlicer_app_msvc.cpp +++ b/src/PrusaSlicer_app_msvc.cpp @@ -8,10 +8,14 @@ #include #ifdef SLIC3R_GUI +//Turn on high power graphics for NVidia cards on laptops (with built in graphics cards + Nvidia cards) +extern "C" +{ // Let the NVIDIA and AMD know we want to use their graphics card // on a dual graphics card system. __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +} #endif /* SLIC3R_GUI */ #include From 77954a13b9c34c74a46ac652ef58118b3b9c6977 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Jun 2019 11:07:07 +0200 Subject: [PATCH 29/44] Fix of admesh import due to boost::pool::destroy taking O(n). Why on earth?! --- src/admesh/connect.cpp | 16 +++++----------- src/admesh/normals.cpp | 8 +++++--- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index 5afb85d2c..e729c8922 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -130,18 +130,12 @@ struct HashTableEdges { this->heads[i] = this->tail; } ~HashTableEdges() { - for (int i = 0; i < this->M; ++ i) { - for (HashEdge *temp = this->heads[i]; this->heads[i] != this->tail; temp = this->heads[i]) { - this->heads[i] = this->heads[i]->next; - pool.destroy(temp); #ifndef NDEBUG + for (int i = 0; i < this->M; ++ i) + for (HashEdge *temp = this->heads[i]; this->heads[i] != this->tail; temp = this->heads[i]) ++ this->freed; -#endif /* NDEBUG */ - } - } - this->heads.clear(); - pool.destroy(this->tail); this->tail = nullptr; +#endif /* NDEBUG */ } void insert_edge_exact(stl_file *stl, const HashEdge &edge) @@ -197,7 +191,7 @@ private: match_neighbors(edge, *link); // Delete the matched edge from the list. this->heads[chain_number] = link->next; - pool.destroy(link); + // pool.destroy(link); #ifndef NDEBUG ++ this->freed; #endif /* NDEBUG */ @@ -224,7 +218,7 @@ private: // Delete the matched edge from the list. HashEdge *temp = link->next; link->next = link->next->next; - pool.destroy(temp); + // pool.destroy(temp); #ifndef NDEBUG ++ this->freed; #endif /* NDEBUG */ diff --git a/src/admesh/normals.cpp b/src/admesh/normals.cpp index 4d47573f6..16bb3daab 100644 --- a/src/admesh/normals.cpp +++ b/src/admesh/normals.cpp @@ -25,6 +25,8 @@ #include #include +// Boost pool: Don't use mutexes to synchronize memory allocation. +#define BOOST_POOL_NO_MT #include #include "stl.h" @@ -192,7 +194,7 @@ void stl_fix_normal_directions(stl_file *stl) } stl_normal *temp = head->next; // Delete this facet from the list. head->next = head->next->next; - pool.destroy(temp); + // pool.destroy(temp); } else { // If we ran out of facets to fix: All of the facets in this part have been fixed. ++ stl->stats.number_of_parts; if (checked >= stl->stats.number_of_facets) @@ -214,8 +216,8 @@ void stl_fix_normal_directions(stl_file *stl) } } - pool.destroy(head); - pool.destroy(tail); + // pool.destroy(head); + // pool.destroy(tail); } void stl_fix_normal_values(stl_file *stl) From 9f236bc6030a2a59db714e41d366e63d8855683e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 17 May 2019 14:17:49 +0200 Subject: [PATCH 30/44] Added transformation reset buttons in object manipulation panel --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 91 ++++++++++++++++++++++- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 10 ++- src/slic3r/GUI/OptionsGroup.cpp | 2 +- 3 files changed, 95 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 5bf25f3fc..a67eaa4f0 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -190,8 +190,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : def.set_default_value(new ConfigOptionFloat(0.0)); def.width = field_width/*50*/; - // Add "uniform scaling" button in front of "Scale" option if (option_name == "Scale") { + // Add "uniform scaling" button in front of "Scale" option line.near_label_widget = [this](wxWindow* parent) { auto btn = new LockButton(parent, wxID_ANY); btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event){ @@ -201,8 +201,52 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_lock_bnt = btn; return btn; }; + // Add reset scale button + auto reset_scale_button = [=](wxWindow* parent) { + auto btn = new ScalableButton(parent, wxID_ANY, "colorchange_delete_off.png"); + btn->SetToolTip(_(L("Reset scale"))); + m_reset_scale_button = btn; + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn, wxBU_EXACTFIT); + btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { + change_scale_value(0, 100.); + change_scale_value(1, 100.); + change_scale_value(2, 100.); + }); + return sizer; + }; + line.append_widget(reset_scale_button); } + else if (option_name == "Rotation") { + // Add reset rotation button + auto reset_rotation_button = [=](wxWindow* parent) { + auto btn = new ScalableButton(parent, wxID_ANY, "colorchange_delete_off.png"); + btn->SetToolTip(_(L("Reset rotation"))); + m_reset_rotation_button = btn; + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn, wxBU_EXACTFIT); + btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { + // The following makes sure that the LOCAL coordinate system rotation is reset by calling + // methods that are readily available here - it is not nice but gets the work done. + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + Selection& selection = canvas->get_selection(); + GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); + if (selection.is_single_volume() || selection.is_single_modifier()) + volume->set_volume_rotation(Vec3d::Zero()); + else if (selection.is_single_full_instance()) + volume->set_instance_rotation(Vec3d::Zero()); + else + return; + + canvas->do_rotate(); + + UpdateAndShow(true); + }); + return sizer; + }; + line.append_widget(reset_rotation_button); + } // Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment else if (option_name == "Size") { line.near_label_widget = [this](wxWindow* parent) { @@ -224,8 +268,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : return line; }; - // Settings table + m_og->sidetext_width = 3; m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label); m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label); m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label); @@ -408,9 +452,49 @@ void ObjectManipulation::update_if_dirty() else m_og->disable(); + update_reset_buttons_visibility(); + m_dirty = false; } + + +void ObjectManipulation::update_reset_buttons_visibility() +{ + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + if (!canvas) + return; + const Selection& selection = canvas->get_selection(); + + bool show_rotation = false; + bool show_scale = false; + + if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + Vec3d rotation; + Vec3d scale; + + if (selection.is_single_full_instance()) { + rotation = volume->get_instance_rotation(); + scale = volume->get_instance_scaling_factor(); + } + else { + rotation = volume->get_volume_rotation(); + scale = volume->get_volume_scaling_factor(); + } + show_rotation = !rotation.isApprox(Vec3d::Zero()); + show_scale = !scale.isApprox(Vec3d::Ones()); + } + + wxGetApp().CallAfter([this, show_rotation, show_scale]{ + m_reset_rotation_button->Show(show_rotation); + m_reset_scale_button->Show(show_scale); + }); +} + + + + #ifndef __APPLE__ void ObjectManipulation::emulate_kill_focus() { @@ -493,7 +577,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value) m_cache.rotation = rotation; m_cache.rotation_rounded(axis) = DBL_MAX; - this->UpdateAndShow(true); + this->UpdateAndShow(true); } void ObjectManipulation::change_scale_value(int axis, double value) @@ -511,6 +595,7 @@ void ObjectManipulation::change_scale_value(int axis, double value) this->UpdateAndShow(true); } + void ObjectManipulation::change_size_value(int axis, double value) { if (std::abs(m_cache.size_rounded(axis) - value) < EPSILON) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 7c359f3d7..6a13a3c09 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -53,6 +53,10 @@ class ObjectManipulation : public OG_Settings wxStaticText* m_scale_Label = nullptr; wxStaticText* m_rotate_Label = nullptr; + // Non-owning pointers to the reset buttons, so we can hide and show them. + ScalableButton* m_reset_scale_button = nullptr; + ScalableButton* m_reset_rotation_button = nullptr; + // Needs to be updated from OnIdle? bool m_dirty = false; // Cached labels for the delayed update, not localized! @@ -111,10 +115,8 @@ private: void reset_settings_value(); void update_settings_value(const Selection& selection); - // update size values after scale unit changing or "gizmos" - void update_size_value(const Vec3d& size); - // update rotation value after "gizmos" - void update_rotation_value(const Vec3d& rotation); + // Show or hide scale/rotation reset buttons if needed + void update_reset_buttons_visibility(); // change values void change_position_value(int axis, double value); diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 2ac6b00af..014932900 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -276,7 +276,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n // add sidetext if any if (option.sidetext != "") { auto sidetext = new wxStaticText( this->ctrl_parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, - /*wxSize(sidetext_width*wxGetApp().em_unit(), -1)*/wxDefaultSize, wxALIGN_LEFT); + wxSize(sidetext_width != -1 ? sidetext_width*wxGetApp().em_unit() : -1, -1) /*wxDefaultSize*/, wxALIGN_LEFT); sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT); sidetext->SetFont(wxGetApp().normal_font()); sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); From a3c1644ead545bfac6aab46d5761c8521fc8d48c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 3 Jun 2019 10:40:15 +0200 Subject: [PATCH 31/44] Added mirroring buttons into object manipulation panel --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 75 ++++++++++++++++++++++- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 5 ++ 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index a67eaa4f0..c2782df2d 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -92,6 +92,7 @@ void msw_rescale_word_local_combo(wxBitmapComboBox* combo) combo->SetValue(selection); } + ObjectManipulation::ObjectManipulation(wxWindow* parent) : OG_Settings(parent, true) #ifndef __APPLE__ @@ -162,16 +163,50 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : const int field_width = 5; + // Mirror button size: + const int mirror_btn_width = 3; + // Legend for object modification line = Line{ "", "" }; def.label = ""; def.type = coString; - def.width = field_width/*50*/; + def.width = field_width - mirror_btn_width;//field_width/*50*/; for (const std::string axis : { "x", "y", "z" }) { const std::string label = boost::algorithm::to_upper_copy(axis); def.set_default_value(new ConfigOptionString{ " " + label }); Option option = Option(def, axis + "_axis_legend"); + + // We will add a button to toggle mirroring to each axis: + auto mirror_button = [=](wxWindow* parent) { + m_mirror_bitmap_on = ScalableBitmap(parent, "colorchange_add_on.png"); + m_mirror_bitmap_off = ScalableBitmap(parent, "colorchange_delete_off.png"); + auto btn = new ScalableButton(parent, wxID_ANY, "colorchange_delete_off.png", wxEmptyString, wxSize(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width)); + btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), boost::algorithm::to_upper_copy(axis))); + m_mirror_buttons[axis] = btn; + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn, wxBU_EXACTFIT/* | wxRESERVE_SPACE_EVEN_IF_HIDDEN*/); + btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { + wxWindow* btn = dynamic_cast(e.GetEventObject()); + Axis axis = (btn == m_mirror_buttons["x"] ? X : ( btn == m_mirror_buttons["y"] ? Y : Z)); + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + Selection& selection = canvas->get_selection(); + GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); + + if (selection.is_single_volume() || selection.is_single_modifier()) + volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis)); + else if (selection.is_single_full_instance()) + volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis)); + else + return; + canvas->do_mirror(); + canvas->set_as_dirty(); + UpdateAndShow(true); + }); + return sizer; + }; + + option.side_widget = mirror_button; line.append_option(option); } line.near_label_widget = [this](wxWindow* parent) { @@ -226,8 +261,6 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn, wxBU_EXACTFIT); btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { - // The following makes sure that the LOCAL coordinate system rotation is reset by calling - // methods that are readily available here - it is not nice but gets the work done. GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); @@ -283,6 +316,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : ctrl->msw_rescale(); }; } + + void ObjectManipulation::Show(const bool show) { @@ -453,6 +488,7 @@ void ObjectManipulation::update_if_dirty() m_og->disable(); update_reset_buttons_visibility(); + update_mirror_buttons_visibility(); m_dirty = false; } @@ -494,6 +530,34 @@ void ObjectManipulation::update_reset_buttons_visibility() +void ObjectManipulation::update_mirror_buttons_visibility() +{ + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + Selection& selection = canvas->get_selection(); + std::array show = {false, false, false}; + + if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + Vec3d mirror; + + if (selection.is_single_full_instance()) + mirror = volume->get_instance_mirror(); + else + mirror = volume->get_volume_mirror(); + + for (unsigned char i=0; i<3; ++i) + show[i] = mirror[i] < 0.; + } + + wxGetApp().CallAfter([this, show]{ + m_mirror_buttons["x"]->SetBitmap(show[0] ? m_mirror_bitmap_on.bmp() : m_mirror_bitmap_off.bmp()); + m_mirror_buttons["y"]->SetBitmap(show[1] ? m_mirror_bitmap_on.bmp() : m_mirror_bitmap_off.bmp()); + m_mirror_buttons["z"]->SetBitmap(show[2] ? m_mirror_bitmap_on.bmp() : m_mirror_bitmap_off.bmp()); + }); +} + + + #ifndef __APPLE__ void ObjectManipulation::emulate_kill_focus() @@ -751,6 +815,11 @@ void ObjectManipulation::msw_rescale() m_manifold_warning_bmp.msw_rescale(); m_fix_throught_netfab_bitmap->SetBitmap(m_manifold_warning_bmp.bmp()); + m_mirror_bitmap_on.msw_rescale(); + m_mirror_bitmap_off.msw_rescale(); + m_reset_scale_button->msw_rescale(); + m_reset_rotation_button->msw_rescale(); + get_og()->msw_rescale(); } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 6a13a3c09..11b07cda6 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -56,6 +56,9 @@ class ObjectManipulation : public OG_Settings // Non-owning pointers to the reset buttons, so we can hide and show them. ScalableButton* m_reset_scale_button = nullptr; ScalableButton* m_reset_rotation_button = nullptr; + std::map m_mirror_buttons; + ScalableBitmap m_mirror_bitmap_on; + ScalableBitmap m_mirror_bitmap_off; // Needs to be updated from OnIdle? bool m_dirty = false; @@ -117,6 +120,8 @@ private: // Show or hide scale/rotation reset buttons if needed void update_reset_buttons_visibility(); + //Show or hide mirror buttons + void update_mirror_buttons_visibility(); // change values void change_position_value(int axis, double value); From 980c6673d42e561d7bf53937e8213e2ce1f0e943 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 5 Jun 2019 11:23:30 +0200 Subject: [PATCH 32/44] Reset buttons - fixed rotation of instances with multiple volumes Mirroring buttons now hide where appropriate --- resources/icons/mirroring_off.png | Bin 0 -> 589 bytes resources/icons/mirroring_on.png | Bin 0 -> 600 bytes resources/icons/mirroring_transparent.png | Bin 0 -> 93 bytes src/slic3r/GUI/GUI_ObjectManipulation.cpp | 104 +++++++++++++++------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 12 ++- 5 files changed, 83 insertions(+), 33 deletions(-) create mode 100644 resources/icons/mirroring_off.png create mode 100644 resources/icons/mirroring_on.png create mode 100644 resources/icons/mirroring_transparent.png diff --git a/resources/icons/mirroring_off.png b/resources/icons/mirroring_off.png new file mode 100644 index 0000000000000000000000000000000000000000..c16655271a7e10d9d83047189086d4d6b509abc3 GIT binary patch literal 589 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S0wixl{&NRXEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4>iGH4#PT$HOWYUVmw=D)W4<$2R?NV2M}KA?FdtNTmJp~!15S*|LT8;MoC zXsZ3%W24$-moh2c@9l*fmo+q>Gq62KSXA699k*xgMiDE{G|yhA4QhurtG<}@uu!C_ z<$>}m-=I62B~!k9UHS2l6URxP__|{b{!P~`Som^z%$(k+UE0pWDc5vL>wMPBh1~ac zOgPu;&>~>6W*@_Frwy&D=l0S8q@B+Qm*U`007}-kq7#>;Nm&p5M&yqHoBEQzy zY0CSzuP^(vnT8meSs9vI xnHp;w7+M(^oS(SL7)3*FeoAIqCAtPfD`O*whU@kljsZ0=c)I$ztaD0e0ssxu+3)}W literal 0 HcmV?d00001 diff --git a/resources/icons/mirroring_on.png b/resources/icons/mirroring_on.png new file mode 100644 index 0000000000000000000000000000000000000000..6ddeccbe0a2a82019c5c2efd1773c20c0833c4d4 GIT binary patch literal 600 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBC{YpM6XNqfL!5oT`ng2Uv7KO- z`>oK>c;wi#w-Y8-9OfwbtPfPmnB?v5!uX#__a2bLS>O>_%)p?h48n{ROYO^mg6t)p zzOL*KxFrR+#s37PM*)TAdb&7gOf=4WlTK;F^nd^Dc_ijpxxR1T zX#3n&TDl=r(%}>cpt3=meXk`S|u=mmKH$V*x Mp00i_>zopr04OBpdH?_b literal 0 HcmV?d00001 diff --git a/resources/icons/mirroring_transparent.png b/resources/icons/mirroring_transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..841010fcc14ba7f06f09feae3affb3d9ba64c0f5 GIT binary patch literal 93 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S1|*9D%+3HQmSQK*5Dp-y;YjHK@SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), boost::algorithm::to_upper_copy(axis))); - m_mirror_buttons[axis] = btn; + wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width); + auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off.png", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); + btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), label)); + + m_mirror_buttons[axis_idx].first = btn; + m_mirror_buttons[axis_idx].second = mbShown; auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn, wxBU_EXACTFIT/* | wxRESERVE_SPACE_EVEN_IF_HIDDEN*/); + sizer->Add(btn); + btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { - wxWindow* btn = dynamic_cast(e.GetEventObject()); - Axis axis = (btn == m_mirror_buttons["x"] ? X : ( btn == m_mirror_buttons["y"] ? Y : Z)); + Axis axis = (Axis)(axis_idx + X); + if (m_mirror_buttons[axis_idx].second == mbHidden) + return; + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); - GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); - if (selection.is_single_volume() || selection.is_single_modifier()) + if (selection.is_single_volume() || selection.is_single_modifier()) { + GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis)); - else if (selection.is_single_full_instance()) - volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis)); + } + else if (selection.is_single_full_instance()) { + for (unsigned int idx : selection.get_volume_idxs()){ + GLVolume* volume = const_cast(selection.get_volume(idx)); + volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis)); + } + } else return; + canvas->do_mirror(); canvas->set_as_dirty(); UpdateAndShow(true); @@ -238,7 +255,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : }; // Add reset scale button auto reset_scale_button = [=](wxWindow* parent) { - auto btn = new ScalableButton(parent, wxID_ANY, "colorchange_delete_off.png"); + auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); btn->SetToolTip(_(L("Reset scale"))); m_reset_scale_button = btn; auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -255,7 +272,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else if (option_name == "Rotation") { // Add reset rotation button auto reset_rotation_button = [=](wxWindow* parent) { - auto btn = new ScalableButton(parent, wxID_ANY, "colorchange_delete_off.png"); + auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); btn->SetToolTip(_(L("Reset rotation"))); m_reset_rotation_button = btn; auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -263,12 +280,17 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); - GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); - if (selection.is_single_volume() || selection.is_single_modifier()) + if (selection.is_single_volume() || selection.is_single_modifier()) { + GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); volume->set_volume_rotation(Vec3d::Zero()); - else if (selection.is_single_full_instance()) - volume->set_instance_rotation(Vec3d::Zero()); + } + else if (selection.is_single_full_instance()) { + for (unsigned int idx : selection.get_volume_idxs()){ + GLVolume* volume = const_cast(selection.get_volume(idx)); + volume->set_instance_rotation(Vec3d::Zero()); + } + } else return; @@ -534,25 +556,42 @@ void ObjectManipulation::update_mirror_buttons_visibility() { GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); - std::array show = {false, false, false}; + std::array new_states = {mbHidden, mbHidden, mbHidden}; - if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - Vec3d mirror; + if (!m_world_coordinates) { + if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + Vec3d mirror; - if (selection.is_single_full_instance()) - mirror = volume->get_instance_mirror(); - else - mirror = volume->get_volume_mirror(); + if (selection.is_single_full_instance()) + mirror = volume->get_instance_mirror(); + else + mirror = volume->get_volume_mirror(); - for (unsigned char i=0; i<3; ++i) - show[i] = mirror[i] < 0.; + for (unsigned char i=0; i<3; ++i) + new_states[i] = (mirror[i] < 0. ? mbActive : mbShown); + } + } + else { + // the mirroring buttons should be hidden in world coordinates, + // unless we make it actually mirror in world coords. } - wxGetApp().CallAfter([this, show]{ - m_mirror_buttons["x"]->SetBitmap(show[0] ? m_mirror_bitmap_on.bmp() : m_mirror_bitmap_off.bmp()); - m_mirror_buttons["y"]->SetBitmap(show[1] ? m_mirror_bitmap_on.bmp() : m_mirror_bitmap_off.bmp()); - m_mirror_buttons["z"]->SetBitmap(show[2] ? m_mirror_bitmap_on.bmp() : m_mirror_bitmap_off.bmp()); + // Hiding the buttons through Hide() always messed up the sizers. As a workaround, the button + // is assigned a transparent bitmap. We must of course remember the actual state. + wxGetApp().CallAfter([this, new_states]{ + for (int i=0; i<3; ++i) { + if (new_states[i] != m_mirror_buttons[i].second) { + const wxBitmap* bmp; + switch (new_states[i]) { + case mbHidden : bmp = &m_mirror_bitmap_hidden.bmp(); m_mirror_buttons[i].first->Enable(false); break; + case mbShown : bmp = &m_mirror_bitmap_off.bmp(); m_mirror_buttons[i].first->Enable(true); break; + case mbActive : bmp = &m_mirror_bitmap_on.bmp(); m_mirror_buttons[i].first->Enable(true); break; + } + m_mirror_buttons[i].first->SetBitmap(*bmp); + m_mirror_buttons[i].second = new_states[i]; + } + } }); } @@ -817,6 +856,7 @@ void ObjectManipulation::msw_rescale() m_mirror_bitmap_on.msw_rescale(); m_mirror_bitmap_off.msw_rescale(); + m_mirror_bitmap_hidden.msw_rescale(); m_reset_scale_button->msw_rescale(); m_reset_rotation_button->msw_rescale(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 11b07cda6..cc2154514 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -56,9 +56,19 @@ class ObjectManipulation : public OG_Settings // Non-owning pointers to the reset buttons, so we can hide and show them. ScalableButton* m_reset_scale_button = nullptr; ScalableButton* m_reset_rotation_button = nullptr; - std::map m_mirror_buttons; + + // Mirroring buttons and their current state + enum MirrorButtonState { + mbHidden, + mbShown, + mbActive + }; + std::array, 3> m_mirror_buttons; + + // Bitmaps for the mirroring buttons. ScalableBitmap m_mirror_bitmap_on; ScalableBitmap m_mirror_bitmap_off; + ScalableBitmap m_mirror_bitmap_hidden; // Needs to be updated from OnIdle? bool m_dirty = false; From 7b07a8da83633c5391b5b8ca8db5813ff1c367c9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 5 Jun 2019 15:27:20 +0200 Subject: [PATCH 33/44] Reset buttons: synchronization of instances/volumes --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 5 +++++ src/slic3r/GUI/Selection.hpp | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 2cbd048e5..224c053e4 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -216,6 +216,9 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else return; + selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); + selection.synchronize_unselected_volumes(); + canvas->do_mirror(); canvas->set_as_dirty(); UpdateAndShow(true); @@ -294,6 +297,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else return; + selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); + selection.synchronize_unselected_volumes(); canvas->do_rotate(); UpdateAndShow(true); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 54bf52706..5da1e477b 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -333,6 +333,8 @@ private: void render_sidebar_rotation_hint(Axis axis) const; void render_sidebar_scale_hint(Axis axis) const; void render_sidebar_size_hint(Axis axis, double length) const; + +public: enum SyncRotationType { // Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis. SYNC_ROTATION_NONE = 0, @@ -343,6 +345,8 @@ private: }; void synchronize_unselected_instances(SyncRotationType sync_rotation_type); void synchronize_unselected_volumes(); + +private: void ensure_on_bed(); bool is_from_fully_selected_instance(unsigned int volume_idx) const; From f8c5570155e9a36a28c2147a95c1404d1c7a9f75 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 14 Jun 2019 15:47:40 +0200 Subject: [PATCH 34/44] Removed unnecessary copy / move constructors / assignment operators. --- src/PrusaSlicer_app_msvc.cpp | 1 - src/admesh/stl.h | 8 -------- src/libslic3r/TriangleMesh.hpp | 4 ---- 3 files changed, 13 deletions(-) diff --git a/src/PrusaSlicer_app_msvc.cpp b/src/PrusaSlicer_app_msvc.cpp index ee8cdf696..95dd4fb07 100644 --- a/src/PrusaSlicer_app_msvc.cpp +++ b/src/PrusaSlicer_app_msvc.cpp @@ -8,7 +8,6 @@ #include #ifdef SLIC3R_GUI -//Turn on high power graphics for NVidia cards on laptops (with built in graphics cards + Nvidia cards) extern "C" { // Let the NVIDIA and AMD know we want to use their graphics card diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 87210b3c9..2ac6c7fd2 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -120,10 +120,6 @@ struct stl_stats { struct stl_file { stl_file() {} - stl_file(const stl_file &rhs) : facet_start(rhs.facet_start), neighbors_start(rhs.neighbors_start), stats(rhs.stats) {} - stl_file(stl_file &&rhs) : facet_start(std::move(rhs.facet_start)), neighbors_start(std::move(rhs.neighbors_start)), stats(rhs.stats) {} - stl_file& operator=(const stl_file &rhs) { this->facet_start = rhs.facet_start; this->neighbors_start = rhs.neighbors_start; this->stats = rhs.stats; return *this; } - stl_file& operator=(stl_file &&rhs) { this->facet_start = std::move(rhs.facet_start); this->neighbors_start = std::move(rhs.neighbors_start); this->stats = rhs.stats; return *this; } void clear() { this->facet_start.clear(); @@ -140,10 +136,6 @@ struct stl_file { struct indexed_triangle_set { indexed_triangle_set() {} - indexed_triangle_set(const indexed_triangle_set &rhs) : indices(rhs.indices), vertices(rhs.vertices) {} - indexed_triangle_set(indexed_triangle_set &&rhs) : indices(std::move(rhs.indices)), vertices(std::move(rhs.vertices)) {} - indexed_triangle_set& operator=(const indexed_triangle_set &rhs) { this->indices = rhs.indices; this->vertices = rhs.vertices; return *this; } - indexed_triangle_set& operator=(indexed_triangle_set &&rhs) { this->indices = std::move(rhs.indices); this->vertices = std::move(rhs.vertices); return *this; } void clear() { indices.clear(); vertices.clear(); } diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 54c6dc5d0..054a98935 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -23,10 +23,6 @@ class TriangleMesh public: TriangleMesh() : repaired(false) {} TriangleMesh(const Pointf3s &points, const std::vector &facets); - TriangleMesh(const TriangleMesh& rhs) : stl(rhs.stl), its(rhs.its), repaired(rhs.repaired) {} - TriangleMesh(TriangleMesh&& rhs) : stl(std::move(rhs.stl)), its(std::move(rhs.its)), repaired(rhs.repaired) {} - TriangleMesh& operator=(const TriangleMesh& rhs) { this->stl = rhs.stl; this->its = rhs.its; this->repaired = rhs.repaired; return *this; } - TriangleMesh& operator=(TriangleMesh &&rhs) { this->stl = std::move(rhs.stl); this->its = std::move(rhs.its); this->repaired = rhs.repaired; return *this; } void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; } bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); } bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); } From 5ee695b6291ffe7cce6153f509de0f78f22239aa Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Jun 2019 08:53:13 +0200 Subject: [PATCH 35/44] #2506 - Fixed crash while manually editing SLA supports --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index fb758d240..afd0087aa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -131,6 +131,11 @@ private: protected: void on_set_state() override; + virtual void on_set_hover_id() + { + if ((int)m_editing_mode_cache.size() <= m_hover_id) + m_hover_id = -1; + } void on_start_dragging(const Selection& selection) override; virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) override; From 630883ad0f4d7f602e06773a8f333e65c1385e77 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Jun 2019 08:54:28 +0200 Subject: [PATCH 36/44] Extended the error message when the G-code cannot be copied to the SD card --- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 94fb6481b..b77a272e2 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -89,7 +89,7 @@ void BackgroundSlicingProcess::process_fff() // Perform the final post-processing of the export path by applying the print statistics over the file name. std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path); if (copy_file(m_temp_output_path, export_path) != 0) - throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed"))); + throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?"))); m_print->set_status(95, _utf8(L("Running post-processing scripts"))); run_post_process_scripts(export_path, m_fff_print->config()); m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str()); From f72f55dc08b3526e311bb4b92f127427e57a5b3d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Jun 2019 09:57:07 +0200 Subject: [PATCH 37/44] A bit of documentation of the mirroring and reset buttons at the side panel. --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 224c053e4..310000ecc 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -216,9 +216,10 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else return; + // Update mirroring at the GLVolumes. selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); selection.synchronize_unselected_volumes(); - + // Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. canvas->do_mirror(); canvas->set_as_dirty(); UpdateAndShow(true); @@ -297,8 +298,10 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else return; + // Update rotation at the GLVolumes. selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); selection.synchronize_unselected_volumes(); + // Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. canvas->do_rotate(); UpdateAndShow(true); From b001eca21f21b00af1fcc68e0a54570e1510bc07 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Jun 2019 14:45:10 +0200 Subject: [PATCH 38/44] #2528 - New project command automatically switches to 3D editor view --- src/slic3r/GUI/Plater.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 297cc9f2c..942b77725 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3322,6 +3322,7 @@ SLAPrint& Plater::sla_print() { return p->sla_print; } void Plater::new_project() { + p->select_view_3D("3D"); wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); } From 468516aa315d16d384b5927d130b4bc16aa4a8ae Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 18 Jun 2019 16:24:30 +0200 Subject: [PATCH 39/44] Apply fixes for the ui jobs. - Localization - Mutual exclusion (ExclusiveJobGroup), only one UI job can run at a time, and background processing is stopped - m_range not used for finalization anymore - stop_jobs called before Window is closed --- src/libslic3r/MTUtils.hpp | 9 + src/slic3r/GUI/MainFrame.cpp | 4 +- src/slic3r/GUI/Plater.cpp | 344 ++++++++++++++++++++++------------- 3 files changed, 224 insertions(+), 133 deletions(-) diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 7e91ace32..0afe3563f 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -5,6 +5,7 @@ #include // for std::lock_guard #include // for std::function #include // for std::forward +#include namespace Slic3r { @@ -182,6 +183,14 @@ public: inline bool empty() const { return size() == 0; } }; +template bool all_of(const C &container) { + return std::all_of(container.begin(), + container.end(), + [](const typename C::value_type &v) { + return static_cast(v); + }); +} + } #endif // MTUTILS_HPP diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2dfc1b747..667dcd899 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -103,14 +103,14 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S event.Veto(); return; } + + if(m_plater) m_plater->stop_jobs(); // Weird things happen as the Paint messages are floating around the windows being destructed. // Avoid the Paint messages by hiding the main window. // Also the application closes much faster without these unnecessary screen refreshes. // In addition, there were some crashes due to the Paint events sent to already destructed windows. this->Show(false); - - if(m_plater) m_plater->stop_jobs(); // Save the slic3r.ini.Usually the ini file is saved from "on idle" callback, // but in rare cases it may not have been called yet. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 643cb0c1b..42b864ef2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1270,7 +1270,7 @@ struct Plater::priv std::future m_ftr; priv *m_plater = nullptr; std::atomic m_running {false}, m_canceled {false}; - bool m_stop_slicing = false; + bool m_finalized = false; void run() { m_running.store(true); process(); m_running.store(false); @@ -1293,16 +1293,27 @@ struct Plater::priv priv& plater() { return *m_plater; } bool was_canceled() const { return m_canceled.load(); } + // Launched just before start(), a job can use it to prepare internals + virtual void prepare() {} + + // Launched when the job is finished. It refreshes the 3dscene by def. + virtual void finalize() { + // Do a full refresh of scene tree, including regenerating + // all the GLVolumes. FIXME The update function shall just + // reload the modified matrices. + if(! was_canceled()) + plater().update(true); + } + public: - Job(priv *_plater, bool stop_slicing = false): - m_plater(_plater), m_stop_slicing(stop_slicing) + Job(priv *_plater): m_plater(_plater) { Bind(wxEVT_THREAD, [this](const wxThreadEvent& evt){ auto msg = evt.GetString(); if(! msg.empty()) plater().statusbar()->set_status_text(msg); - if(! m_range) return; + if(m_finalized) return; plater().statusbar()->set_progress(evt.GetInt()); if(evt.GetInt() == status_range()) { @@ -1312,23 +1323,25 @@ struct Plater::priv plater().statusbar()->set_cancel_callback(); wxEndBusyCursor(); - // Do a full refresh of scene tree, including regenerating - // all the GLVolumes. FIXME The update function shall just - // reload the modified matrices. - if(! was_canceled()) plater().update(true); + finalize(); // dont do finalization again for the same process - m_range = 0; + m_finalized = true; } }); } + Job(const Job&) = delete; + Job(Job&&) = default; + Job& operator=(const Job&) = delete; + Job& operator=(Job&&) = default; + virtual void process() = 0; - void start() { // Start the job. No effect if the job is already running + void start() { // Start the job. No effect if the job is already running if(! m_running.load()) { - if(m_stop_slicing) plater().background_process.stop(); + prepare(); // Save the current status indicatior range and push the new one m_range = plater().statusbar()->get_range(); @@ -1340,6 +1353,8 @@ struct Plater::priv m_canceled.store(true); }); + m_finalized = false; + // Changing cursor to busy wxBeginBusyCursor(); @@ -1358,7 +1373,7 @@ struct Plater::priv // To wait for the running job and join the threads. False is returned // if the timeout has been reached and the job is still running. Call // cancel() before this fn if you want to explicitly end the job. - bool join(int timeout_ms = 0) { + bool join(int timeout_ms = 0) const { if(!m_ftr.valid()) return true; if(timeout_ms <= 0) @@ -1374,21 +1389,85 @@ struct Plater::priv void cancel() { m_canceled.store(true); } }; - class ArrangeJob: public Job { - int count = 0; - public: - using Job::Job; - int status_range() const override { return count; } - void set_count(int c) { count = c; } - void process() override; - } arrange_job {this}; + enum class Jobs : size_t { + Arrange, + Rotoptimize + }; - class RotoptimizeJob: public Job { + // Jobs defined inside the group class will be managed so that only one can + // run at a time. Also, the background process will be stopped if a job is + // started. + class ExclusiveJobGroup { + + static const int ABORT_WAIT_MAX_MS = 10000; + + priv * m_plater; + + class ArrangeJob : public Job + { + int count = 0; + + protected: + void prepare() override + { + count = 0; + for (auto obj : plater().model.objects) + count += int(obj->instances.size()); + } + + public: + using Job::Job; + int status_range() const override { return count; } + void set_count(int c) { count = c; } + void process() override; + } arrange_job{m_plater}; + + class RotoptimizeJob : public Job + { + public: + using Job::Job; + void process() override; + } rotoptimize_job{m_plater}; + + std::vector> m_jobs{arrange_job, + rotoptimize_job}; + public: - using Job::Job; - void process() override; - } rotoptimize_job {this}; - + + ExclusiveJobGroup(priv *_plater): m_plater(_plater) {} + + void start(Jobs jid) { + m_plater->background_process.stop(); + stop_all(); + m_jobs[size_t(jid)].get().start(); + } + + void cancel_all() { for (Job& j : m_jobs) j.cancel(); } + + void join_all(int wait_ms = 0) + { + std::vector aborted(m_jobs.size(), false); + + for (size_t jid = 0; jid < m_jobs.size(); ++jid) + aborted[jid] = m_jobs[jid].get().join(wait_ms); + + if (!all_of(aborted)) + BOOST_LOG_TRIVIAL(error) << "Could not abort a job!"; + } + + void stop_all() { cancel_all(); join_all(ABORT_WAIT_MAX_MS); } + + const Job& get(Jobs jobid) const { return m_jobs[size_t(jobid)]; } + + bool is_any_running() const + { + return std::any_of(m_jobs.begin(), + m_jobs.end(), + [](const Job &j) { return j.is_running(); }); + } + + } m_ui_jobs{this}; + bool delayed_scene_refresh; std::string delayed_error_message; @@ -2274,48 +2353,43 @@ void Plater::priv::mirror(Axis axis) void Plater::priv::arrange() { - if(!arrange_job.is_running()) { - int count = 0; - for(auto obj : model.objects) count += int(obj->instances.size()); - arrange_job.set_count(count); - arrange_job.start(); - } + m_ui_jobs.start(Jobs::Arrange); } // This method will find an optimal orientation for the currently selected item // Very similar in nature to the arrange method above... void Plater::priv::sla_optimize_rotation() { - - rotoptimize_job.start(); + m_ui_jobs.start(Jobs::Rotoptimize); } -void Plater::priv::ArrangeJob::process() { - +void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() { // TODO: we should decide whether to allow arrange when the search is // running we should probably disable explicit slicing and background // processing - static const std::string arrangestr = L("Arranging"); - - auto& config = plater().config; - auto& view3D = plater().view3D; - auto& model = plater().model; + static const auto arrangestr = _(L("Arranging")); + + auto &config = plater().config; + auto &view3D = plater().view3D; + auto &model = plater().model; // FIXME: I don't know how to obtain the minimum distance, it depends // on printer technology. I guess the following should work but it crashes. - double dist = 6; //PrintConfig::min_object_distance(config); - if(plater().printer_technology == ptFFF) { + double dist = 6; // PrintConfig::min_object_distance(config); + if (plater().printer_technology == ptFFF) { dist = PrintConfig::min_object_distance(config); } - auto min_obj_distance = coord_t(dist/SCALING_FACTOR); + auto min_obj_distance = coord_t(dist / SCALING_FACTOR); - const auto *bed_shape_opt = config->opt("bed_shape"); + const auto *bed_shape_opt = config->opt( + "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))); + 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))); update_status(0, arrangestr); @@ -2333,94 +2407,115 @@ void Plater::priv::ArrangeJob::process() { bed, hint, false, // create many piles not just one pile - [this](unsigned st) { if(st > 0) update_status(count - int(st), arrangestr); }, - [this] () { return was_canceled(); }); - } catch(std::exception& /*e*/) { - GUI::show_error(plater().q, L("Could not arrange model objects! " - "Some geometries may be invalid.")); + [this](unsigned st) { + if (st > 0) + update_status(count - int(st), arrangestr); + }, + [this]() { return was_canceled(); }); + } catch (std::exception & /*e*/) { + GUI::show_error(plater().q, + L("Could not arrange model objects! " + "Some geometries may be invalid.")); } - - update_status(count, was_canceled() ? L("Arranging canceled.") : L("Arranging done.")); + + update_status(count, + was_canceled() ? _(L("Arranging canceled.")) + : _(L("Arranging done."))); // it remains to move the wipe tower: - view3D->get_canvas3d()->arrange_wipe_tower(wti); + view3D->get_canvas3d()->arrange_wipe_tower(wti); } -void Plater::priv::RotoptimizeJob::process() +void Plater::priv::ExclusiveJobGroup::RotoptimizeJob::process() { - int obj_idx = plater().get_selected_object_idx(); if (obj_idx < 0) { return; } - ModelObject * o = plater().model.objects[size_t(obj_idx)]; - - auto r = sla::find_best_rotation( - *o, .005f, - [this](unsigned s) { if(s < 100) update_status(int(s), L("Searching for optimal orientation")); }, - [this](){ return was_canceled(); } - ); + ModelObject *o = plater().model.objects[size_t(obj_idx)]; - const auto *bed_shape_opt = plater().config->opt("bed_shape"); + auto r = sla::find_best_rotation( + *o, + .005f, + [this](unsigned s) { + if (s < 100) + update_status(int(s), + _(L("Searching for optimal orientation"))); + }, + [this]() { return was_canceled(); }); + + const auto *bed_shape_opt = plater().config->opt( + "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))); + 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; + double offs = mindist / 2.0 - EPSILON; - if(! was_canceled()) // wasn't canceled - for(ModelInstance * oi : o->instances) { - oi->set_rotation({r[X], r[Y], r[Z]}); + if (!was_canceled()) // wasn't canceled + 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()); + 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); + 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 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 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 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; + 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)); + return diff; + }, + opt::initvals(0.0), + opt::bound(-PI / 2, PI / 2)); - double r = std::get<0>(result.optimum); + double r = std::get<0>(result.optimum); - Vec3d rt = oi->get_rotation(); rt(Z) += r; - oi->set_rotation(rt); + Vec3d rt = oi->get_rotation(); + rt(Z) += r; + oi->set_rotation(rt); - arr::WipeTowerInfo wti; // useless in SLA context - arr::find_new_position(plater().model, o->instances, - coord_t(mindist/SCALING_FACTOR), bed, wti); - - // Correct the z offset of the object which was corrupted be the rotation - o->ensure_on_bed(); - - update_status(100, L("Orientation found.")); - } else { - update_status(100, L("Orientation search canceled.")); + arr::WipeTowerInfo wti; // useless in SLA context + arr::find_new_position(plater().model, + o->instances, + coord_t(mindist / SCALING_FACTOR), + bed, + wti); + + // Correct the z offset of the object which was corrupted be + // the rotation + o->ensure_on_bed(); + + update_status(100, _(L("Orientation found."))); + } + else { + update_status(100, _(L("Orientation search canceled."))); } } @@ -2602,7 +2697,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation) // Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState. bool Plater::priv::restart_background_process(unsigned int state) { - if (arrange_job.is_running() || rotoptimize_job.is_running()) { + if (m_ui_jobs.is_any_running()) { // Avoid a race condition return false; } @@ -2833,7 +2928,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) { if (evt.status.percent >= -1) { - if (arrange_job.is_running() || rotoptimize_job.is_running()) { + if (m_ui_jobs.is_any_running()) { // Avoid a race condition return; } @@ -3314,7 +3409,7 @@ bool Plater::priv::can_fix_through_netfabb() const bool Plater::priv::can_increase_instances() const { - if (arrange_job.is_running() || rotoptimize_job.is_running()) { + if (m_ui_jobs.is_any_running()) { return false; } @@ -3324,7 +3419,7 @@ bool Plater::priv::can_increase_instances() const bool Plater::priv::can_decrease_instances() const { - if (arrange_job.is_running() || rotoptimize_job.is_running()) { + if (m_ui_jobs.is_any_running()) { return false; } @@ -3344,7 +3439,7 @@ bool Plater::priv::can_split_to_volumes() const bool Plater::priv::can_arrange() const { - return !model.objects.empty() && !arrange_job.is_running(); + return !model.objects.empty() && !m_ui_jobs.is_any_running(); } bool Plater::priv::can_layers_editing() const @@ -3471,20 +3566,7 @@ void Plater::load_files(const std::vector& input_files, bool load_m void Plater::update() { p->update(); } -void Plater::stop_jobs() -{ - static const int ABORT_WAIT_MAX_MS = 10000; - bool aborted = false; - - p->rotoptimize_job.cancel(); - aborted = p->rotoptimize_job.join(ABORT_WAIT_MAX_MS); - - p->arrange_job.cancel(); - aborted &= p->arrange_job.join(ABORT_WAIT_MAX_MS); - - if(!aborted) - BOOST_LOG_TRIVIAL(error) << "Could not abort an optimization job!"; -} +void Plater::stop_jobs() { p->m_ui_jobs.stop_all(); } void Plater::update_ui_from_settings() { p->update_ui_from_settings(); } @@ -3792,7 +3874,7 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) if (!path.Lower().EndsWith(".3mf")) return; - DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); + DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); const std::string path_u8 = into_u8(path); wxBusyCursor wait; if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { @@ -3846,7 +3928,7 @@ void Plater::reslice_SLA_supports(const ModelObject &object) if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) this->p->view3D->reload_scene(false); - if (this->p->background_process.empty() || (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID)) + if (this->p->background_process.empty() || (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID)) // Nothing to do on empty input or invalid configuration. return; From 78610de4730cc85baea984c91d00e776b94d7574 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Jun 2019 10:18:51 +0200 Subject: [PATCH 40/44] Fix build on vs2013 Fix 2 for vs2013 --- src/slic3r/GUI/Plater.cpp | 45 +++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e729a2ff7..0a8499f70 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1332,10 +1332,22 @@ struct Plater::priv }); } + // TODO: use this when we all migrated to VS2019 + // Job(const Job&) = delete; + // Job(Job&&) = default; + // Job& operator=(const Job&) = delete; + // Job& operator=(Job&&) = default; Job(const Job&) = delete; - Job(Job&&) = default; Job& operator=(const Job&) = delete; - Job& operator=(Job&&) = default; + Job(Job &&o) : + m_range(o.m_range), + m_ftr(std::move(o.m_ftr)), + m_plater(o.m_plater), + m_finalized(o.m_finalized) + { + m_running.store(o.m_running.load()); + m_canceled.store(o.m_canceled.load()); + } virtual void process() = 0; @@ -1417,26 +1429,37 @@ struct Plater::priv } public: - using Job::Job; + //using Job::Job; + ArrangeJob(priv * pltr): Job(pltr) {} int status_range() const override { return count; } void set_count(int c) { count = c; } void process() override; - } arrange_job{m_plater}; + } arrange_job/*{m_plater}*/; class RotoptimizeJob : public Job { public: - using Job::Job; + //using Job::Job; + RotoptimizeJob(priv * pltr): Job(pltr) {} void process() override; - } rotoptimize_job{m_plater}; + } rotoptimize_job/*{m_plater}*/; - std::vector> m_jobs{arrange_job, - rotoptimize_job}; + // To create a new job, just define a new subclass of Job, implement + // the process and the optional prepare() and finalize() methods + // Register the instance of the class in the m_jobs container + // if it cannot run concurrently with other jobs in this group + + std::vector> m_jobs/*{arrange_job, + rotoptimize_job}*/; public: - - ExclusiveJobGroup(priv *_plater): m_plater(_plater) {} - + ExclusiveJobGroup(priv *_plater) + : m_plater(_plater) + , arrange_job(m_plater) + , rotoptimize_job(m_plater) + , m_jobs({arrange_job, rotoptimize_job}) + {} + void start(Jobs jid) { m_plater->background_process.stop(); stop_all(); From b0b54ed0e6fa02a97cc8409b0b5a5c44e8dc3f8f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Jun 2019 11:05:34 +0200 Subject: [PATCH 41/44] Fixed flickering of 3D view when moving objects on NVIDIA graphic cards --- src/slic3r/GUI/Plater.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0a8499f70..8e5ba2d3f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1770,7 +1770,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) void Plater::priv::update(bool force_full_scene_refresh) { - wxWindowUpdateLocker freeze_guard(q); + // the following line, when enabled, causes flickering on NVIDIA graphics cards +// wxWindowUpdateLocker freeze_guard(q); if (get_config("autocenter") == "1") { // auto *bed_shape_opt = config->opt("bed_shape"); // const auto bed_shape = Slic3r::Polygon::new_scale(bed_shape_opt->values); From 670a5632263114efeb18b0e7f8a43a8daa3ac301 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Jun 2019 12:04:11 +0200 Subject: [PATCH 42/44] Fix for issue #2536 --- src/libslic3r/ModelArrange.cpp | 50 ++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index b323285cc..fc03d0a43 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -318,7 +318,7 @@ class AutoArranger {}; // management and spatial index structures for acceleration. template class _ArrBase { -protected: +public: // Useful type shortcuts... using Placer = TPacker; @@ -327,6 +327,8 @@ protected: using PConfig = typename Packer::PlacementConfig; using Distance = TCoord; using Pile = TMultiShape; + +protected: Packer m_pck; PConfig m_pconf; // Placement configuration @@ -564,7 +566,10 @@ public: // 2D shape from top view. using ShapeData2D = std::vector>; -ShapeData2D projectModelFromTop(const Slic3r::Model &model, const WipeTowerInfo& wti) { +ShapeData2D projectModelFromTop(const Slic3r::Model &model, + const WipeTowerInfo &wti, + double tolerance) +{ ShapeData2D ret; // Count all the items on the bin (all the object's instances) @@ -586,21 +591,32 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model, const WipeTowerInfo& // Object instances should carry the same scaling and // x, y rotation that is why we use the first instance. { - ModelInstance *finst = objptr->instances.front(); - Vec3d rotation = finst->get_rotation(); - rotation.z() = 0.; - Transform3d trafo_instance = Geometry::assemble_transform(Vec3d::Zero(), rotation, finst->get_scaling_factor(), finst->get_mirror()); + ModelInstance *finst = objptr->instances.front(); + Vec3d rotation = finst->get_rotation(); + rotation.z() = 0.; + Transform3d trafo_instance = Geometry::assemble_transform( + Vec3d::Zero(), + rotation, + finst->get_scaling_factor(), + finst->get_mirror()); Polygon p = objptr->convex_hull_2d(trafo_instance); - assert(! p.points.empty()); - - // this may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209 - if (p.points.empty()) - continue; + + assert(!p.points.empty()); + // this may happen for malformed models, see: + // https://github.com/prusa3d/PrusaSlicer/issues/2209 + if (p.points.empty()) continue; + + if(tolerance > EPSILON) { + Polygons pp { p }; + pp = p.simplify(double(scaled(tolerance))); + if (!pp.empty()) p = pp.front(); + } + p.reverse(); assert(!p.is_counter_clockwise()); - p.append(p.first_point()); clpath = Slic3rMultiPoint_to_ClipperPath(p); + auto firstp = clpath.front(); clpath.emplace_back(firstp); } Vec3d rotation0 = objptr->instances.front()->get_rotation(); @@ -780,9 +796,9 @@ bool arrange(Model &model, // The model with the geometries std::function stopcondition) { bool ret = true; - + // Get the 2D projected shapes with their 3D model instance pointers - auto shapemap = arr::projectModelFromTop(model, wti); + auto shapemap = arr::projectModelFromTop(model, wti, 0.1); // Copy the references for the shapes only as the arranger expects a // sequence of objects convertible to Item or ClipperPolygon @@ -807,7 +823,7 @@ bool arrange(Model &model, // The model with the geometries static_cast(bbb.min(0)), static_cast(bbb.min(1)) }, - { + { static_cast(bbb.max(0)), static_cast(bbb.max(1)) }); @@ -881,9 +897,9 @@ void find_new_position(const Model &model, coord_t min_obj_distance, const Polyline &bed, WipeTowerInfo& wti) -{ +{ // Get the 2D projected shapes with their 3D model instance pointers - auto shapemap = arr::projectModelFromTop(model, wti); + auto shapemap = arr::projectModelFromTop(model, wti, 0.1); // Copy the references for the shapes only as the arranger expects a // sequence of objects convertible to Item or ClipperPolygon From 89e39e38955b03500e4e08388200168cb1f5bb0c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Jun 2019 13:19:11 +0200 Subject: [PATCH 43/44] Eliminate magic constant --- src/libslic3r/ModelArrange.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index fc03d0a43..a440c3999 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -782,6 +782,8 @@ BedShapeHint bedShape(const Polyline &bed) { return ret; } +static const SLIC3R_CONSTEXPR double SIMPLIFY_TOLERANCE_MM = 0.1; + // The final client function to arrange the Model. A progress indicator and // a stop predicate can be also be passed to control the process. bool arrange(Model &model, // The model with the geometries @@ -798,7 +800,7 @@ bool arrange(Model &model, // The model with the geometries bool ret = true; // Get the 2D projected shapes with their 3D model instance pointers - auto shapemap = arr::projectModelFromTop(model, wti, 0.1); + auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM); // Copy the references for the shapes only as the arranger expects a // sequence of objects convertible to Item or ClipperPolygon @@ -899,7 +901,7 @@ void find_new_position(const Model &model, WipeTowerInfo& wti) { // Get the 2D projected shapes with their 3D model instance pointers - auto shapemap = arr::projectModelFromTop(model, wti, 0.1); + auto shapemap = arr::projectModelFromTop(model, wti, SIMPLIFY_TOLERANCE_MM); // Copy the references for the shapes only as the arranger expects a // sequence of objects convertible to Item or ClipperPolygon From 8ffd79cdd84c1df8f0d3d0b3adc9b7e3ec65152a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Jun 2019 16:23:53 +0200 Subject: [PATCH 44/44] Fix for debug build on Visual Studio --- src/libnest2d/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt index d50c78b17..587b814b2 100644 --- a/src/libnest2d/CMakeLists.txt +++ b/src/libnest2d/CMakeLists.txt @@ -74,7 +74,10 @@ if(TBB_FOUND) target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0) find_package(Threads REQUIRED) - target_link_libraries(libnest2d INTERFACE ${TBB_LIBRARIES} ${CMAKE_DL_LIBS} + target_link_libraries(libnest2d INTERFACE + tbb # VS debug mode needs linking this way: + # ${TBB_LIBRARIES} + ${CMAKE_DL_LIBS} Threads::Threads ) else()