From c04be58648b663eeb5e1f10572b0c19d94ec9815 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 12 Feb 2019 16:34:42 +0100 Subject: [PATCH 01/20] Implemented estimated printing time for the SLA printing --- src/libslic3r/Print.cpp | 4 +- src/libslic3r/SLAPrint.cpp | 80 ++++++++++++++++++++++++++++++++++++++ src/libslic3r/SLAPrint.hpp | 30 ++++++++++++++ src/libslic3r/Utils.hpp | 63 ++++++++++++++++++++++++++++++ src/slic3r/GUI/Plater.cpp | 21 ++++++++++ 5 files changed, 196 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 6416a709a..3e78c9a85 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1863,7 +1863,7 @@ std::string Print::output_filename() const DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode", &config); } - +/* // Shorten the dhms time by removing the seconds, rounding the dhm to full minutes // and removing spaces. static std::string short_time(const std::string &time) @@ -1903,7 +1903,7 @@ static std::string short_time(const std::string &time) ::sprintf(buffer, "%ds", seconds); return buffer; } - +*/ DynamicConfig PrintStatistics::config() const { DynamicConfig config; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 142428f1d..b848e2698 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -966,6 +966,9 @@ void SLAPrint::process() st += unsigned(PRINT_STEP_LEVELS[currentstep] * pstd); } + // Fill statistics + fill_statistics(); + // If everything vent well report_status(*this, 100, L("Slicing done")); } @@ -1027,6 +1030,46 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vectorget_slice_index().size()) + max_layers_cnt = po->get_slice_index().size(); + } + if (max_layers_cnt == 0) + return; + + float init_exp_time = m_material_config.initial_exposure_time.getFloat();//35; + float exp_time = m_material_config.exposure_time.getFloat();//8; + + // TODO : fade_layers_cnt should be filled in the future + // This variable will be a part of the print(material) preset + const int fade_layers_cnt = 10; // [3;20] + + // TODO : tilt_delay_before_time & tilt_delay_after_time should be filled in the future + // These values are received from the printer after a printing start + const float tilt_delay_before_time = 0.0; + const float tilt_delay_after_time = 0.0; + if (tilt_delay_before_time + tilt_delay_after_time > 0.0) + { + init_exp_time += tilt_delay_before_time + tilt_delay_after_time; + exp_time += tilt_delay_before_time + tilt_delay_after_time; + } + + float estim_time = init_exp_time * 3 + exp_time * (max_layers_cnt - 3 - fade_layers_cnt); + + const float delta_time = (init_exp_time - exp_time) / (fade_layers_cnt+1); + double fade_layer_time = init_exp_time; + while (fade_layer_time > exp_time) + { + fade_layer_time -= delta_time; + estim_time += fade_layer_time; + } + + m_print_statistics.estimated_print_time = get_time_dhms(estim_time); +} + // Returns true if an object step is done on all objects and there's at least one object. bool SLAPrint::is_step_done(SLAPrintObjectStep step) const { @@ -1257,4 +1300,41 @@ std::vector SLAPrintObject::transformed_support_points() const return ret; } +DynamicConfig SLAPrintStatistics::config() const +{ + DynamicConfig config; + const std::string print_time = Slic3r::short_time(this->estimated_print_time); + config.set_key_value("print_time", new ConfigOptionString(print_time)); + config.set_key_value("used_material", new ConfigOptionFloat(this->total_used_material/* / 1000.*/)); + config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost)); + config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight)); + return config; +} + +DynamicConfig SLAPrintStatistics::placeholders() +{ + DynamicConfig config; + for (const std::string &key : { + "print_time", "used_material", "total_cost", "total_weight" }) + config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}")); + return config; +} + +std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in) const +{ + std::string final_path; + try { + boost::filesystem::path path(path_in); + DynamicConfig cfg = this->config(); + PlaceholderParser pp; + std::string new_stem = pp.process(path.stem().string(), 0, &cfg); + final_path = (path.parent_path() / (new_stem + path.extension().string())).string(); + } + catch (const std::exception &ex) { + BOOST_LOG_TRIVIAL(error) << "Failed to apply the print statistics to the export file name: " << ex.what(); + final_path = path_in; + } + return final_path; +} + } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 21503c6f6..0e12fff63 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -171,6 +171,29 @@ using PrintObjects = std::vector; class TriangleMesh; +struct SLAPrintStatistics +{ + SLAPrintStatistics() { clear(); } + std::string estimated_print_time; + double total_used_material; + double total_cost; + double total_weight; + + // Config with the filled in print statistics. + DynamicConfig config() const; + // Config with the statistics keys populated with placeholder strings. + static DynamicConfig placeholders(); + // Replace the print statistics placeholders in the path. + std::string finalize_output_path(const std::string &path_in) const; + + void clear() { + estimated_print_time.clear(); + total_used_material = 0.; + total_cost = 0.; + total_weight = 0.; + } +}; + /** * @brief This class is the high level FSM for the SLA printing process. * @@ -208,6 +231,8 @@ public: std::string output_filename() const override { return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "zip"); } + const SLAPrintStatistics& print_statistics() const { return m_print_statistics; } + private: using SLAPrinter = FilePrinter; using SLAPrinterPtr = std::unique_ptr; @@ -215,6 +240,8 @@ private: // Invalidate steps based on a set of parameters changed. bool invalidate_state_by_config_options(const std::vector &opt_keys); + void fill_statistics(); + SLAPrintConfig m_print_config; SLAPrinterConfig m_printer_config; SLAMaterialConfig m_material_config; @@ -246,6 +273,9 @@ private: // The printer itself SLAPrinterPtr m_printer; + // Estimated print time, material consumed. + SLAPrintStatistics m_print_statistics; + friend SLAPrintObject; }; diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 046745e6f..c13df4546 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -206,6 +206,69 @@ public: void reset() { closure = Closure(); } }; +// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes +// and removing spaces. +static std::string short_time(const std::string &time) +{ + // Parse the dhms time format. + int days = 0; + int hours = 0; + int minutes = 0; + int seconds = 0; + if (time.find('d') != std::string::npos) + ::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds); + else if (time.find('h') != std::string::npos) + ::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds); + else if (time.find('m') != std::string::npos) + ::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds); + else if (time.find('s') != std::string::npos) + ::sscanf(time.c_str(), "%ds", &seconds); + // Round to full minutes. + if (days + hours + minutes > 0 && seconds >= 30) { + if (++minutes == 60) { + minutes = 0; + if (++hours == 24) { + hours = 0; + ++days; + } + } + } + // Format the dhm time. + char buffer[64]; + if (days > 0) + ::sprintf(buffer, "%dd%dh%dm", days, hours, minutes); + else if (hours > 0) + ::sprintf(buffer, "%dh%dm", hours, minutes); + else if (minutes > 0) + ::sprintf(buffer, "%dm", minutes); + else + ::sprintf(buffer, "%ds", seconds); + return buffer; +} + +// Returns the given time is seconds in format DDd HHh MMm SSs +static std::string get_time_dhms(float time_in_secs) +{ + int days = (int)(time_in_secs / 86400.0f); + time_in_secs -= (float)days * 86400.0f; + int hours = (int)(time_in_secs / 3600.0f); + time_in_secs -= (float)hours * 3600.0f; + int minutes = (int)(time_in_secs / 60.0f); + time_in_secs -= (float)minutes * 60.0f; + + char buffer[64]; + if (days > 0) + ::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs); + else if (hours > 0) + ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs); + else if (minutes > 0) + ::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs); + else + ::sprintf(buffer, "%ds", (int)time_in_secs); + + return buffer; +} + } // namespace Slic3r #if WIN32 diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 774e61024..18a923d45 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -153,6 +153,7 @@ enum SlisedInfoIdx siFilament_m, siFilament_mm3, siFilament_g, + siMateril_unit, siCost, siEstimatedTime, siWTNumbetOfToolchanges, @@ -193,6 +194,7 @@ SlicedInfo::SlicedInfo(wxWindow *parent) : init_info_label(_(L("Used Filament (m)"))); init_info_label(_(L("Used Filament (mm³)"))); init_info_label(_(L("Used Filament (g)"))); + init_info_label(_(L("Used Material (unit)"))); init_info_label(_(L("Cost"))); init_info_label(_(L("Estimated printing time"))); init_info_label(_(L("Number of tool changes"))); @@ -817,6 +819,21 @@ void Sidebar::show_sliced_info_sizer(const bool show) p->sliced_info->Show(show); if (show) { + if (p->plater->printer_technology() == ptSLA) + { + const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics(); + p->sliced_info->SetTextAndShow(siMateril_unit, wxString::Format("%.2f", ps.total_used_material)); + p->sliced_info->SetTextAndShow(siCost, wxString::Format("%.2f", ps.total_cost)); + p->sliced_info->SetTextAndShow(siEstimatedTime, ps.estimated_print_time, _(L("Estimated printing time")) + " :"); + + // Hide non-SLA sliced info parameters + p->sliced_info->SetTextAndShow(siFilament_m, "N/A"); + p->sliced_info->SetTextAndShow(siFilament_mm3, "N/A"); + p->sliced_info->SetTextAndShow(siFilament_g, "N/A"); + p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, "N/A"); + } + else + { const PrintStatistics& ps = p->plater->fff_print().print_statistics(); const bool is_wipe_tower = ps.total_wipe_tower_filament > 0; @@ -864,6 +881,10 @@ void Sidebar::show_sliced_info_sizer(const bool show) // if there is a wipe tower, insert number of toolchanges info into the array: p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->fff_print().wipe_tower_data().number_of_toolchanges) : "N/A"); + + // Hide non-FFF sliced info parameters + p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); + } } Layout(); From 589ac889a319506d1c43fdee19db99f10ecc3fa5 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 13 Feb 2019 08:44:42 +0100 Subject: [PATCH 02/20] Fixed OSX build --- src/libslic3r/SLAPrint.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index b848e2698..0e358d8c7 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -8,6 +8,7 @@ #include #include +#include #include //#include //#include "tbb/mutex.h" @@ -1032,6 +1033,8 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vectorget_slice_index().size()) From 88f04e0faef5cc18a9cf0d6761a611abe54db3a7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 13 Feb 2019 15:35:41 +0100 Subject: [PATCH 03/20] Added calculation of the material consumption --- src/libslic3r/SLAPrint.cpp | 56 ++++++++++++++++++++++++++++++++------ src/libslic3r/SLAPrint.hpp | 6 ++-- src/slic3r/GUI/Plater.cpp | 15 ++++++++-- 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 0e358d8c7..08ab56db3 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1033,16 +1033,52 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vectorget_support_slices()) + for (const ExPolygon& polygon : polygons) + supports_volume += polygon.area() *layer_height; + + // Calculate full volume of the object + for (const ExPolygons& polygons : po->get_model_slices()) + for (const ExPolygon& polygon : polygons) + models_volume += polygon.area() *layer_height; + + const SLAPrintObject::SliceIndex& slice_index = po->get_slice_index(); + + // If init_layer_height isn't equivalent to the layer_height, + // let correct volume of the first(initial) layer + if (init_layer_height != layer_height) + { + const auto index = slice_index.begin(); + if (index->second.support_slices_idx != SLAPrintObject::SliceRecord::NONE) + for (const ExPolygon& polygon : po->get_support_slices().front()) + supports_volume += polygon.area() *(init_layer_height - layer_height); + if (index->second.model_slices_idx != SLAPrintObject::SliceRecord::NONE) + for (const ExPolygon& polygon : po->get_model_slices().front()) + models_volume += polygon.area() *(init_layer_height - layer_height); + } + + if (max_layers_cnt < slice_index.size()) + max_layers_cnt = slice_index.size(); + } + + m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR; + m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR; + // Estimated printing time // A layers count o the highest object - int max_layers_cnt = 0; - for (SLAPrintObject * po : m_objects) { - if (max_layers_cnt < po->get_slice_index().size()) - max_layers_cnt = po->get_slice_index().size(); - } if (max_layers_cnt == 0) - return; - + m_print_statistics.estimated_print_time = "N/A"; float init_exp_time = m_material_config.initial_exposure_time.getFloat();//35; float exp_time = m_material_config.exposure_time.getFloat();//8; @@ -1308,7 +1344,8 @@ DynamicConfig SLAPrintStatistics::config() const DynamicConfig config; const std::string print_time = Slic3r::short_time(this->estimated_print_time); config.set_key_value("print_time", new ConfigOptionString(print_time)); - config.set_key_value("used_material", new ConfigOptionFloat(this->total_used_material/* / 1000.*/)); + config.set_key_value("objects_used_material", new ConfigOptionFloat(this->objects_used_material)); + config.set_key_value("support_used_material", new ConfigOptionFloat(this->support_used_material)); config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost)); config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight)); return config; @@ -1318,7 +1355,8 @@ DynamicConfig SLAPrintStatistics::placeholders() { DynamicConfig config; for (const std::string &key : { - "print_time", "used_material", "total_cost", "total_weight" }) + "print_time", "total_cost", "total_weight", + "objects_used_material", "support_used_material" }) config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}")); return config; } diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 0e12fff63..f054921c8 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -175,7 +175,8 @@ struct SLAPrintStatistics { SLAPrintStatistics() { clear(); } std::string estimated_print_time; - double total_used_material; + double objects_used_material; + double support_used_material; double total_cost; double total_weight; @@ -188,7 +189,8 @@ struct SLAPrintStatistics void clear() { estimated_print_time.clear(); - total_used_material = 0.; + objects_used_material = 0.; + support_used_material = 0.; total_cost = 0.; total_weight = 0.; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 18a923d45..f4a3c0b48 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -822,8 +822,19 @@ void Sidebar::show_sliced_info_sizer(const bool show) if (p->plater->printer_technology() == ptSLA) { const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics(); - p->sliced_info->SetTextAndShow(siMateril_unit, wxString::Format("%.2f", ps.total_used_material)); - p->sliced_info->SetTextAndShow(siCost, wxString::Format("%.2f", ps.total_cost)); + wxString new_label = _(L("Used Material (mm³)")) + " :"; + const bool is_supports = ps.support_used_material > 0.0; + if (is_supports) + new_label += wxString::Format("\n - %s\n - %s", _(L("object(s)")), _(L("supports and pad"))); + + wxString info_text = is_supports ? + wxString::Format("%.2f \n%.2f \n%.2f", ps.objects_used_material + ps.support_used_material/* / 1000*/, + ps.objects_used_material/* / 1000*/, + ps.support_used_material/* / 1000*/) : + wxString::Format("%.2f", ps.objects_used_material + ps.support_used_material/* / 1000*/); + p->sliced_info->SetTextAndShow(siMateril_unit, info_text, new_label); + + p->sliced_info->SetTextAndShow(siCost, "N/A"/*wxString::Format("%.2f", ps.total_cost)*/); p->sliced_info->SetTextAndShow(siEstimatedTime, ps.estimated_print_time, _(L("Estimated printing time")) + " :"); // Hide non-SLA sliced info parameters From 3f23bd5224412e76dcb71ba150aa6c999d3c8f9a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 13 Feb 2019 16:30:40 +0100 Subject: [PATCH 04/20] Save statistics values to the config.ini --- src/libslic3r/PrintExport.hpp | 22 +++++++++++++++++++++- src/libslic3r/SLAPrint.cpp | 4 ++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index 5cfb55217..d40b60aad 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -14,6 +14,12 @@ namespace Slic3r { +enum ePrintStatistics +{ + psObjectsUsedMaterial = 0, + psSupportUsedMaterial +}; + enum class FilePrinterFormat { SLA_PNGZIP, SVG @@ -118,6 +124,9 @@ template<> class FilePrinter double m_layer_height = .0; Raster::Origin m_o = Raster::Origin::TOP_LEFT; + double m_objects_used_material = 0.0; + double m_support_used_material = 0.0; + std::string createIniContent(const std::string& projectname) { double layer_height = m_layer_height; @@ -129,6 +138,9 @@ template<> class FilePrinter auto stepnum_str = to_string(static_cast(800*layer_height)); auto layerh_str = to_string(layer_height); + const std::string objects_used_material = to_string(m_objects_used_material); + const std::string support_used_material = to_string(m_support_used_material); + return string( "action = print\n" "jobDir = ") + projectname + "\n" + @@ -143,7 +155,9 @@ template<> class FilePrinter "layerHeight = " + layerh_str + "\n" "noteInfo = " "expTime="+expt_str+"+resinType=generic+layerHeight=" - +layerh_str+"+printer=DWARF3\n"; + +layerh_str+"+printer=DWARF3\n" + "objUsedMaterial=" + objects_used_material + "\n" + "supUsedMaterial=" + support_used_material + "\n"; } public: @@ -277,6 +291,12 @@ public: out.close(); m_layers_rst[i].first.reset(); } + + void set_statistics(const std::vector statistics) + { + m_objects_used_material = statistics[psObjectsUsedMaterial]; + m_support_used_material = statistics[psSupportUsedMaterial]; + } }; } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 08ab56db3..f95b16cfe 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -969,6 +969,10 @@ void SLAPrint::process() // Fill statistics fill_statistics(); + // Set statistics values to the printer + SLAPrinter& printer = *m_printer; + printer.set_statistics({m_print_statistics.objects_used_material, + m_print_statistics.support_used_material}); // If everything vent well report_status(*this, 100, L("Slicing done")); From 2df069323c9de13b7caf9102ab0ce98a49b8ca5e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 18 Feb 2019 12:28:58 +0100 Subject: [PATCH 05/20] Time estimation improvement --- src/libslic3r/PrintExport.hpp | 56 +++++++----- src/libslic3r/SLAPrint.cpp | 164 +++++++++++++++++++++------------- src/libslic3r/SLAPrint.hpp | 4 + src/slic3r/GUI/Plater.cpp | 10 +-- 4 files changed, 147 insertions(+), 87 deletions(-) diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index d40b60aad..64babd507 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -16,8 +16,12 @@ namespace Slic3r { enum ePrintStatistics { - psObjectsUsedMaterial = 0, - psSupportUsedMaterial + psUsedMaterial = 0, + psNumFade, + psNumSlow, + psNumFast, + + psCnt }; enum class FilePrinterFormat { @@ -124,40 +128,45 @@ template<> class FilePrinter double m_layer_height = .0; Raster::Origin m_o = Raster::Origin::TOP_LEFT; - double m_objects_used_material = 0.0; - double m_support_used_material = 0.0; + 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) { - double layer_height = m_layer_height; +// double layer_height = m_layer_height; 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 stepnum_str = to_string(static_cast(800*layer_height)); - auto layerh_str = to_string(layer_height); +// auto stepnum_str = to_string(static_cast(800*layer_height)); + auto layerh_str = to_string(m_layer_height); - const std::string objects_used_material = to_string(m_objects_used_material); - const std::string support_used_material = to_string(m_support_used_material); + 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" - "stepNum = " + stepnum_str + "\n" - "wifiOn = 1\n" - "tiltSlow = 60\n" - "tiltFast = 15\n" - "numFade = 10\n" - "startdelay = 0\n" +// "stepNum = " + stepnum_str + "\n" +// "wifiOn = 1\n" +// "tiltSlow = 60\n" +// "tiltFast = 15\n" + "numFade = " + cnt_fade_layers + "\n" +// "startdelay = 0\n" "layerHeight = " + layerh_str + "\n" "noteInfo = " - "expTime="+expt_str+"+resinType=generic+layerHeight=" - +layerh_str+"+printer=DWARF3\n" - "objUsedMaterial=" + objects_used_material + "\n" - "supUsedMaterial=" + support_used_material + "\n"; + "expTime = "+expt_str+" + resinType = generic+layerHeight = " + +layerh_str+" + printer = DWARF3\n" + "usedMaterial = " + used_material + "\n" + "numSlow = " + cnt_slow_layers + "\n" + "numFast = " + cnt_fast_layers + "\n"; } public: @@ -294,8 +303,13 @@ public: void set_statistics(const std::vector statistics) { - m_objects_used_material = statistics[psObjectsUsedMaterial]; - m_support_used_material = statistics[psSupportUsedMaterial]; + 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]); } }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index f95b16cfe..ecc990d01 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -971,8 +971,11 @@ void SLAPrint::process() fill_statistics(); // Set statistics values to the printer SLAPrinter& printer = *m_printer; - printer.set_statistics({m_print_statistics.objects_used_material, - m_print_statistics.support_used_material}); + printer.set_statistics({(m_print_statistics.objects_used_material + m_print_statistics.support_used_material)/1000, + 10.0, + double(m_print_statistics.slow_layers_count), + double(m_print_statistics.fast_layers_count) + }); // If everything vent well report_status(*this, 100, L("Slicing done")); @@ -1037,80 +1040,119 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vectorget_support_slices()) - for (const ExPolygon& polygon : polygons) - supports_volume += polygon.area() *layer_height; - - // Calculate full volume of the object - for (const ExPolygons& polygons : po->get_model_slices()) - for (const ExPolygon& polygon : polygons) - models_volume += polygon.area() *layer_height; - const SLAPrintObject::SliceIndex& slice_index = po->get_slice_index(); - - // If init_layer_height isn't equivalent to the layer_height, - // let correct volume of the first(initial) layer - if (init_layer_height != layer_height) - { - const auto index = slice_index.begin(); - if (index->second.support_slices_idx != SLAPrintObject::SliceRecord::NONE) - for (const ExPolygon& polygon : po->get_support_slices().front()) - supports_volume += polygon.area() *(init_layer_height - layer_height); - if (index->second.model_slices_idx != SLAPrintObject::SliceRecord::NONE) - for (const ExPolygon& polygon : po->get_model_slices().front()) - models_volume += polygon.area() *(init_layer_height - layer_height); - } - - if (max_layers_cnt < slice_index.size()) + if (max_layers_cnt < slice_index.size()) { max_layers_cnt = slice_index.size(); + highest_obj_idx = std::find(m_objects.begin(), m_objects.end(), po) - m_objects.begin(); + } } - m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR; + const SLAPrintObject * highest_obj = m_objects[highest_obj_idx]; + const SLAPrintObject::SliceIndex& highest_obj_slice_index = highest_obj->get_slice_index(); + + const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); + double fade_layer_time = init_exp_time; + + int sliced_layer_cnt = 0; + for (const auto& layer : highest_obj_slice_index) + { + const double l_height = (layer.first == highest_obj_slice_index.begin()->first && + init_layer_height != layer_height) ? + init_layer_height : layer_height; + + // Calculation of the consumed material + + double layer_model_area = 0; + double layer_support_area = 0; + for (SLAPrintObject * po : m_objects) + { + const auto index = po->get_slice_index(); + if (index.find(layer.first) == index.end()) + continue; + + if (index.at(layer.first).model_slices_idx != SLAPrintObject::SliceRecord::NONE) { + for (const ExPolygon& polygon : po->get_model_slices().at(index.at(layer.first).model_slices_idx)) + layer_model_area += polygon.area(); + } + /*else */if (index.at(layer.first).support_slices_idx != SLAPrintObject::SliceRecord::NONE) { + for (const ExPolygon& polygon : po->get_support_slices().front()) + layer_support_area += polygon.area(); + } + } + models_volume += layer_model_area * l_height; + supports_volume += layer_support_area * l_height; + + // Calculation of the slow and fast layers to the future controlling those values on FW + + const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*slow_fast_limit; + const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; + if (is_fast_layer) + fast_layers++; + else + slow_layers++; + + + // Calculation of the printing time + + if (sliced_layer_cnt < 3) + estim_time += init_exp_time; + else if (fade_layer_time > exp_time) + { + fade_layer_time -= delta_fade_time; + estim_time += fade_layer_time; + } + else + estim_time += exp_time; + + estim_time += tilt_time; + + sliced_layer_cnt++; + } + + m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR; m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR; // Estimated printing time // A layers count o the highest object if (max_layers_cnt == 0) m_print_statistics.estimated_print_time = "N/A"; - float init_exp_time = m_material_config.initial_exposure_time.getFloat();//35; - float exp_time = m_material_config.exposure_time.getFloat();//8; + else + m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time)); - // TODO : fade_layers_cnt should be filled in the future - // This variable will be a part of the print(material) preset - const int fade_layers_cnt = 10; // [3;20] - - // TODO : tilt_delay_before_time & tilt_delay_after_time should be filled in the future - // These values are received from the printer after a printing start - const float tilt_delay_before_time = 0.0; - const float tilt_delay_after_time = 0.0; - if (tilt_delay_before_time + tilt_delay_after_time > 0.0) - { - init_exp_time += tilt_delay_before_time + tilt_delay_after_time; - exp_time += tilt_delay_before_time + tilt_delay_after_time; - } - - float estim_time = init_exp_time * 3 + exp_time * (max_layers_cnt - 3 - fade_layers_cnt); - - const float delta_time = (init_exp_time - exp_time) / (fade_layers_cnt+1); - double fade_layer_time = init_exp_time; - while (fade_layer_time > exp_time) - { - fade_layer_time -= delta_time; - estim_time += fade_layer_time; - } - - m_print_statistics.estimated_print_time = get_time_dhms(estim_time); + m_print_statistics.fast_layers_count = fast_layers; + m_print_statistics.slow_layers_count = slow_layers; } // Returns true if an object step is done on all objects and there's at least one object. diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index f054921c8..ffa451b35 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -177,6 +177,8 @@ struct SLAPrintStatistics std::string estimated_print_time; double objects_used_material; double support_used_material; + size_t slow_layers_count; + size_t fast_layers_count; double total_cost; double total_weight; @@ -191,6 +193,8 @@ struct SLAPrintStatistics estimated_print_time.clear(); objects_used_material = 0.; support_used_material = 0.; + slow_layers_count = 0; + fast_layers_count = 0; total_cost = 0.; total_weight = 0.; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f4a3c0b48..fc5f52161 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -822,16 +822,16 @@ void Sidebar::show_sliced_info_sizer(const bool show) if (p->plater->printer_technology() == ptSLA) { const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics(); - wxString new_label = _(L("Used Material (mm³)")) + " :"; + wxString new_label = _(L("Used Material (ml)")) + " :"; const bool is_supports = ps.support_used_material > 0.0; if (is_supports) new_label += wxString::Format("\n - %s\n - %s", _(L("object(s)")), _(L("supports and pad"))); wxString info_text = is_supports ? - wxString::Format("%.2f \n%.2f \n%.2f", ps.objects_used_material + ps.support_used_material/* / 1000*/, - ps.objects_used_material/* / 1000*/, - ps.support_used_material/* / 1000*/) : - wxString::Format("%.2f", ps.objects_used_material + ps.support_used_material/* / 1000*/); + wxString::Format("%.2f \n%.2f \n%.2f", (ps.objects_used_material + ps.support_used_material) / 1000, + ps.objects_used_material / 1000, + ps.support_used_material / 1000) : + wxString::Format("%.2f", ps.objects_used_material + ps.support_used_material / 1000); p->sliced_info->SetTextAndShow(siMateril_unit, info_text, new_label); p->sliced_info->SetTextAndShow(siCost, "N/A"/*wxString::Format("%.2f", ps.total_cost)*/); From a690466dbfb875f7131225faf278c4e82d4fd5a0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 18 Feb 2019 13:18:47 +0100 Subject: [PATCH 06/20] Fixed a typo for the last commit --- src/libslic3r/SLAPrint.cpp | 4 ++-- src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index ecc990d01..13bb57b5e 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1056,8 +1056,8 @@ void SLAPrint::fill_statistics() // This variable will be a part of the print preset const int fade_layers_cnt = 10; // [3;20] - const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR; - const double height = m_printer_config.display_width.getFloat() / SCALING_FACTOR; + const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR; + const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR; const double display_area = width*height; double supports_volume = 0.0; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fc5f52161..87a950b7f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -831,7 +831,7 @@ void Sidebar::show_sliced_info_sizer(const bool show) wxString::Format("%.2f \n%.2f \n%.2f", (ps.objects_used_material + ps.support_used_material) / 1000, ps.objects_used_material / 1000, ps.support_used_material / 1000) : - wxString::Format("%.2f", ps.objects_used_material + ps.support_used_material / 1000); + wxString::Format("%.2f", (ps.objects_used_material + ps.support_used_material) / 1000); p->sliced_info->SetTextAndShow(siMateril_unit, info_text, new_label); p->sliced_info->SetTextAndShow(siCost, "N/A"/*wxString::Format("%.2f", ps.total_cost)*/); From 9d0acc010d170b3e47dfe166f3fb79a5f27a2105 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 18 Feb 2019 16:04:55 +0100 Subject: [PATCH 07/20] Added new options for SLAPrintSettings (faded_layers) and SLAPrinterSettings (fast/slow_tilt_time and area_fill) --- src/libslic3r/PrintConfig.cpp | 34 ++++++++++++++++++++++++++++++++++ src/libslic3r/PrintConfig.hpp | 10 ++++++++++ src/libslic3r/SLAPrint.cpp | 19 +++++++++---------- src/slic3r/GUI/Preset.cpp | 2 ++ src/slic3r/GUI/Tab.cpp | 8 ++++++++ 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3e1ee9f9f..31d19e317 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2435,6 +2435,32 @@ void PrintConfigDef::init_sla_params() def->enum_labels.push_back(L("Portrait")); def->default_value = new ConfigOptionEnum(sladoPortrait); + def = this->add("fast_tilt_time", coFloat); + def->label = L("Fast"); + def->full_label = L("Fast tilt"); + def->tooltip = L("Time of the fast tilt"); + def->sidetext = L("s"); + def->min = 0; + def->mode = comExpert; + def->default_value = new ConfigOptionFloat(5.); + + def = this->add("slow_tilt_time", coFloat); + def->label = L("Slow"); + def->full_label = L("Slow tilt"); + def->tooltip = L("Time of the slow tilt"); + def->sidetext = L("s"); + def->min = 0; + def->mode = comExpert; + def->default_value = new ConfigOptionFloat(8.); + + def = this->add("area_fill", coFloat); + def->label = L("Area fill"); + def->tooltip = L("The percentage of the bed area. \nIf the print area exceeds the specified value, \nthen a slow tilt will be used, otherwise - a fast tilt"); + def->sidetext = L("%"); + def->min = 0; + def->mode = comExpert; + def->default_value = new ConfigOptionFloat(50.); + def = this->add("printer_correction", coFloats); def->full_label = L("Printer scaling correction"); def->tooltip = L("Printer scaling correction"); @@ -2450,6 +2476,14 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->default_value = new ConfigOptionFloat(0.3); + def = this->add("faded_layers", coInt); + def->label = L("Faded layers"); + def->tooltip = L("Number of the layers needed for the exposure time fade from initial exposure time to the exposure time"); + def->min = 3; + def->max = 20; + def->mode = comExpert; + def->default_value = new ConfigOptionInt(10); + def = this->add("exposure_time", coFloat); def->label = L("Exposure time"); def->tooltip = L("Exposure time"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 20c089d1c..e4648c952 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -958,6 +958,9 @@ class SLAPrintObjectConfig : public StaticPrintConfig public: ConfigOptionFloat layer_height; + //Number of the layers needed for the exposure time fade [3;20] + ConfigOptionInt faded_layers /*= 10*/; + // Enabling or disabling support creation ConfigOptionBool supports_enable; @@ -1028,6 +1031,7 @@ protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { OPT_PTR(layer_height); + OPT_PTR(faded_layers); OPT_PTR(supports_enable); OPT_PTR(support_head_front_diameter); OPT_PTR(support_head_penetration); @@ -1085,6 +1089,9 @@ public: ConfigOptionInt display_pixels_y; ConfigOptionEnum display_orientation; ConfigOptionFloats printer_correction; + ConfigOptionFloat fast_tilt_time; + ConfigOptionFloat slow_tilt_time; + ConfigOptionFloat area_fill; protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { @@ -1097,6 +1104,9 @@ protected: OPT_PTR(display_pixels_y); OPT_PTR(display_orientation); OPT_PTR(printer_correction); + OPT_PTR(fast_tilt_time); + OPT_PTR(slow_tilt_time); + OPT_PTR(area_fill); } }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 13bb57b5e..d24be4504 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1005,7 +1005,10 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector steps; @@ -1043,18 +1046,14 @@ void SLAPrint::fill_statistics() const double init_layer_height = m_material_config.initial_layer_height.getFloat(); const double layer_height = m_default_object_config.layer_height.getFloat(); - // TODO : slow_fast_limit, fast_tilt, slow_tilt should be filled in the future - // These variables will be a part of the printer preset - const double slow_fast_limit = 0.5; // if printing area is more than 50% of the bed area, then use a slow tilt - const double fast_tilt = 5.0; - const double slow_tilt = 8.0; + const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); + const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0; + const double slow_tilt = m_printer_config.slow_tilt_time.getFloat();// 8.0; const double init_exp_time = m_material_config.initial_exposure_time.getFloat(); const double exp_time = m_material_config.exposure_time.getFloat(); - // TODO : fade_layers_cnt should be filled in the future - // This variable will be a part of the print preset - const int fade_layers_cnt = 10; // [3;20] + const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20] const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR; const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR; @@ -1116,7 +1115,7 @@ void SLAPrint::fill_statistics() // Calculation of the slow and fast layers to the future controlling those values on FW - const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*slow_fast_limit; + const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; if (is_fast_layer) fast_layers++; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index ee182a01a..b755cc075 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -408,6 +408,7 @@ const std::vector& Preset::sla_print_options() if (s_opts.empty()) { s_opts = { "layer_height", + "faded_layers", "supports_enable", "support_head_front_diameter", "support_head_penetration", @@ -465,6 +466,7 @@ const std::vector& Preset::sla_printer_options() "bed_shape", "max_print_height", "display_width", "display_height", "display_pixels_x", "display_pixels_y", "display_orientation", + "fast_tilt_time", "slow_tilt_time", "area_fill", "printer_correction", "print_host", "printhost_apikey", "printhost_cafile", "printer_notes", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 28b7cd248..174b3983c 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1949,6 +1949,13 @@ void TabPrinter::build_sla() optgroup->append_line(line); optgroup->append_single_option_line("display_orientation"); + optgroup = page->new_optgroup(_(L("Tilt"))); + line = { _(L("Tilt time")), "" }; + line.append_option(optgroup->get_option("fast_tilt_time")); + line.append_option(optgroup->get_option("slow_tilt_time")); + optgroup->append_line(line); + optgroup->append_single_option_line("area_fill"); + optgroup = page->new_optgroup(_(L("Corrections"))); line = Line{ m_config->def()->get("printer_correction")->full_label, "" }; std::vector axes{ "X", "Y", "Z" }; @@ -3178,6 +3185,7 @@ void TabSLAPrint::build() auto optgroup = page->new_optgroup(_(L("Layers"))); optgroup->append_single_option_line("layer_height"); + optgroup->append_single_option_line("faded_layers"); page = add_options_page(_(L("Supports")), "building.png"); optgroup = page->new_optgroup(_(L("Supports"))); From e861f5a243f0082407a153632f730a08f43e4763 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Feb 2019 10:07:37 +0100 Subject: [PATCH 08/20] Tech ENABLE_PRINT_BED_MODELS set as default --- src/libslic3r/Technologies.hpp | 9 ------- src/slic3r/GUI/3DScene.cpp | 16 ------------- src/slic3r/GUI/3DScene.hpp | 12 ---------- src/slic3r/GUI/GLCanvas3D.cpp | 44 ---------------------------------- src/slic3r/GUI/GLCanvas3D.hpp | 10 -------- 5 files changed, 91 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 8d788ed51..d374b21f6 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -29,15 +29,6 @@ #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) -//==================== -// 1.42.0.alpha2 techs -//==================== -#define ENABLE_1_42_0_ALPHA2 1 - -// Adds print bed models to 3D scene -#define ENABLE_PRINT_BED_MODELS (1 && ENABLE_1_42_0_ALPHA2) - - //==================== // 1.42.0.alpha4 techs //==================== diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6b1df6ab3..2621aac89 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -11,9 +11,7 @@ #include "libslic3r/Slicing.hpp" #include "libslic3r/GCode/Analyzer.hpp" #include "slic3r/GUI/PresetBundle.hpp" -#if ENABLE_PRINT_BED_MODELS #include "libslic3r/Format/STL.hpp" -#endif // ENABLE_PRINT_BED_MODELS #include #include @@ -23,10 +21,8 @@ #include -#if ENABLE_PRINT_BED_MODELS #include #include -#endif // ENABLE_PRINT_BED_MODELS #include #include @@ -1671,27 +1667,19 @@ GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; GLModel::GLModel() : m_useVBOs(false) -#if ENABLE_PRINT_BED_MODELS , m_filename("") -#endif // ENABLE_PRINT_BED_MODELS { m_volume.shader_outside_printer_detection_enabled = false; } GLModel::~GLModel() { -#if ENABLE_PRINT_BED_MODELS reset(); -#else - m_volume.release_geometry(); -#endif // ENABLE_PRINT_BED_MODELS } void GLModel::set_color(const float* color, unsigned int size) { -#if ENABLE_PRINT_BED_MODELS ::memcpy((void*)m_volume.color, (const void*)color, (size_t)(std::min((unsigned int)4, size) * sizeof(float))); -#endif // ENABLE_PRINT_BED_MODELS m_volume.set_render_color(color, size); } @@ -1725,13 +1713,11 @@ void GLModel::set_scale(const Vec3d& scale) m_volume.set_volume_scaling_factor(scale); } -#if ENABLE_PRINT_BED_MODELS void GLModel::reset() { m_volume.release_geometry(); m_filename = ""; } -#endif // ENABLE_PRINT_BED_MODELS void GLModel::render() const { @@ -1968,7 +1954,6 @@ bool GLCurvedArrow::on_init(bool useVBOs) return true; } -#if ENABLE_PRINT_BED_MODELS bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs) { reset(); @@ -2011,7 +1996,6 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs) return true; } -#endif // ENABLE_PRINT_BED_MODELS std::string _3DScene::get_gl_info(bool format_as_html, bool extensions) { diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 2732b5a13..9c4e0f687 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -498,20 +498,16 @@ class GLModel protected: GLVolume m_volume; bool m_useVBOs; -#if ENABLE_PRINT_BED_MODELS std::string m_filename; -#endif // ENABLE_PRINT_BED_MODELS public: GLModel(); virtual ~GLModel(); bool init(bool useVBOs) { return on_init(useVBOs); } -#if ENABLE_PRINT_BED_MODELS bool init_from_file(const std::string& filename, bool useVBOs) { return on_init_from_file(filename, useVBOs); } void center_around(const Vec3d& center) { m_volume.set_volume_offset(center - m_volume.bounding_box.center()); } -#endif // ENABLE_PRINT_BED_MODELS void set_color(const float* color, unsigned int size); const Vec3d& get_offset() const; @@ -521,22 +517,16 @@ public: const Vec3d& get_scale() const; void set_scale(const Vec3d& scale); -#if ENABLE_PRINT_BED_MODELS const std::string& get_filename() const { return m_filename; } const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box; } void reset(); -#endif // ENABLE_PRINT_BED_MODELS void render() const; protected: -#if ENABLE_PRINT_BED_MODELS virtual bool on_init(bool useVBOs) { return false; } virtual bool on_init_from_file(const std::string& filename, bool useVBOs) { return false; } -#else - virtual bool on_init(bool useVBOs) = 0; -#endif // ENABLE_PRINT_BED_MODELS private: void render_VBOs() const; @@ -560,13 +550,11 @@ protected: virtual bool on_init(bool useVBOs); }; -#if ENABLE_PRINT_BED_MODELS class GLBed : public GLModel { protected: virtual bool on_init_from_file(const std::string& filename, bool useVBOs); }; -#endif // ENABLE_PRINT_BED_MODELS class _3DScene { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 812df0d18..06e897499 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -412,7 +412,6 @@ Point GLCanvas3D::Bed::point_projection(const Point& point) const return m_polygon.point_projection(point); } -#if ENABLE_PRINT_BED_MODELS void GLCanvas3D::Bed::render(float theta, bool useVBOs, float scale_factor) const { m_scale_factor = scale_factor; @@ -442,37 +441,6 @@ void GLCanvas3D::Bed::render(float theta, bool useVBOs, float scale_factor) cons } } } -#else -void GLCanvas3D::Bed::render(float theta, float scale_factor) const -{ - m_scale_factor = scale_factor; - - switch (m_type) - { - case MK2: - { - _render_prusa("mk2", theta); - break; - } - case MK3: - { - _render_prusa("mk3", theta); - break; - } - case SL1: - { - _render_prusa("sl1", theta); - break; - } - default: - case Custom: - { - _render_custom(); - break; - } - } -} -#endif // ENABLE_PRINT_BED_MODELS void GLCanvas3D::Bed::_calc_bounding_box() { @@ -588,11 +556,7 @@ GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const return type; } -#if ENABLE_PRINT_BED_MODELS void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta, bool useVBOs) const -#else -void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const -#endif // ENABLE_PRINT_BED_MODELS { std::string tex_path = resources_dir() + "/icons/bed/" + key; @@ -608,9 +572,7 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const else if (max_tex_size >= 4096) tex_path += "_4096"; -#if ENABLE_PRINT_BED_MODELS std::string model_path = resources_dir() + "/models/" + key; -#endif // ENABLE_PRINT_BED_MODELS #if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES // use anisotropic filter if graphic card allows @@ -655,7 +617,6 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const #endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES } -#if ENABLE_PRINT_BED_MODELS if (theta <= 90.0f) { filename = model_path + "_bed.stl"; @@ -681,7 +642,6 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const ::glDisable(GL_LIGHTING); } } -#endif // ENABLE_PRINT_BED_MODELS unsigned int triangles_vcount = m_triangles.get_vertices_count(); if (triangles_vcount > 0) @@ -6533,11 +6493,7 @@ void GLCanvas3D::_render_bed(float theta) const scale_factor = m_retina_helper->get_scale_factor(); #endif -#if ENABLE_PRINT_BED_MODELS m_bed.render(theta, m_use_VBOs, scale_factor); -#else - m_bed.render(theta, scale_factor); -#endif // ENABLE_PRINT_BED_MODELS } void GLCanvas3D::_render_axes() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index db2279ee4..1cba644aa 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -217,9 +217,7 @@ class GLCanvas3D GeometryBuffer m_gridlines; mutable GLTexture m_top_texture; mutable GLTexture m_bottom_texture; -#if ENABLE_PRINT_BED_MODELS mutable GLBed m_model; -#endif // ENABLE_PRINT_BED_MODELS mutable float m_scale_factor; @@ -241,11 +239,7 @@ class GLCanvas3D bool contains(const Point& point) const; Point point_projection(const Point& point) const; -#if ENABLE_PRINT_BED_MODELS void render(float theta, bool useVBOs, float scale_factor) const; -#else - void render(float theta, float scale_factor) const; -#endif // ENABLE_PRINT_BED_MODELS private: void _calc_bounding_box(); @@ -256,11 +250,7 @@ class GLCanvas3D #else EType _detect_type() const; #endif // ENABLE_REWORKED_BED_SHAPE_CHANGE -#if ENABLE_PRINT_BED_MODELS void _render_prusa(const std::string &key, float theta, bool useVBOs) const; -#else - void _render_prusa(const std::string &key, float theta) const; -#endif // ENABLE_PRINT_BED_MODELS void _render_custom() const; #if !ENABLE_REWORKED_BED_SHAPE_CHANGE static bool _are_equal(const Pointfs& bed_1, const Pointfs& bed_2); From d81b957968b2d11672104d7a923581253dfe35f8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Feb 2019 10:28:32 +0100 Subject: [PATCH 09/20] Tech ENABLE_REWORKED_BED_SHAPE_CHANGE set as default --- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GLCanvas3D.cpp | 99 ---------------------------------- src/slic3r/GUI/GLCanvas3D.hpp | 19 ------- src/slic3r/GUI/GUI_Preview.cpp | 8 --- src/slic3r/GUI/Plater.cpp | 20 ------- 5 files changed, 148 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d374b21f6..fe7606041 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -40,8 +40,6 @@ #define ENABLE_MOVE_MIN_THRESHOLD (1 && ENABLE_1_42_0_ALPHA4) // Modified initial default placement of generic subparts #define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4) -// Reworked management of bed shape changes -#define ENABLE_REWORKED_BED_SHAPE_CHANGE (1 && ENABLE_1_42_0_ALPHA4) // Use anisotropic filtering on bed plate texture #define ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES (1 && ENABLE_1_42_0_ALPHA4) // Bunch of fixes related to volumes centering diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 06e897499..8024e6a46 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -367,11 +367,7 @@ const Pointfs& GLCanvas3D::Bed::get_shape() const bool GLCanvas3D::Bed::set_shape(const Pointfs& shape) { -#if ENABLE_REWORKED_BED_SHAPE_CHANGE EType new_type = _detect_type(shape); -#else - EType new_type = _detect_type(); -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE if (m_shape == shape && m_type == new_type) // No change, no need to update the UI. return false; @@ -489,11 +485,7 @@ void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& printf("Unable to create bed grid lines\n"); } -#if ENABLE_REWORKED_BED_SHAPE_CHANGE GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type(const Pointfs& shape) const -#else -GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE { EType type = Custom; @@ -505,7 +497,6 @@ GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const { if (curr->config.has("bed_shape")) { -#if ENABLE_REWORKED_BED_SHAPE_CHANGE if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research") && (shape == dynamic_cast(curr->config.option("bed_shape"))->values)) { if (boost::contains(curr->name, "SL1")) @@ -524,29 +515,6 @@ GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const break; } } -#else - if (boost::contains(curr->name, "SL1")) - { - //FIXME add a condition on the size of the print bed? - type = SL1; - break; - } - else if (_are_equal(m_shape, dynamic_cast(curr->config.option("bed_shape"))->values)) - { - if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research")) - { - if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5")) - { - type = MK3; - break; - } else if (boost::contains(curr->name, "MK2")) - { - type = MK2; - break; - } - } - } -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE } curr = bundle->printers.get_preset_parent(*curr); @@ -718,22 +686,6 @@ void GLCanvas3D::Bed::_render_custom() const } } -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE -bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2) -{ - if (bed_1.size() != bed_2.size()) - return false; - - for (unsigned int i = 0; i < (unsigned int)bed_1.size(); ++i) - { - if (bed_1[i] != bed_2[i]) - return false; - } - - return true; -} -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE - const double GLCanvas3D::Axes::Radius = 0.5; const double GLCanvas3D::Axes::ArrowBaseRadius = 2.5 * GLCanvas3D::Axes::Radius; const double GLCanvas3D::Axes::ArrowLength = 5.0; @@ -4039,11 +3991,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_dirty(true) , m_initialized(false) , m_use_VBOs(false) -#if ENABLE_REWORKED_BED_SHAPE_CHANGE , m_requires_zoom_to_bed(false) -#else - , m_force_zoom_to_bed_enabled(false) -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE , m_apply_zoom_to_volumes_filter(false) , m_hover_volume_id(-1) , m_toolbar_action_running(false) @@ -4236,7 +4184,6 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) { bool new_shape = m_bed.set_shape(shape); -#if ENABLE_REWORKED_BED_SHAPE_CHANGE if (new_shape) { // Set the origin and size for painting of the coordinate system axes. @@ -4247,16 +4194,6 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) m_dirty = true; } -#else - // Set the origin and size for painting of the coordinate system axes. - m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); - set_bed_axes_length(0.1 * m_bed.get_bounding_box().max_size()); - - if (new_shape) - zoom_to_bed(); - - m_dirty = true; -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE } void GLCanvas3D::set_bed_axes_length(double length) @@ -4356,13 +4293,6 @@ void GLCanvas3D::enable_toolbar(bool enable) m_toolbar.set_enabled(enable); } -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE -void GLCanvas3D::enable_force_zoom_to_bed(bool enable) -{ - m_force_zoom_to_bed_enabled = enable; -} -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE - void GLCanvas3D::enable_dynamic_background(bool enable) { m_dynamic_background_enabled = enable; @@ -4501,7 +4431,6 @@ void GLCanvas3D::render() if (!_set_current() || !_3DScene::init(m_canvas)) return; -#if ENABLE_REWORKED_BED_SHAPE_CHANGE if (m_bed.get_shape().empty()) { // this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE @@ -4516,10 +4445,6 @@ void GLCanvas3D::render() _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); m_requires_zoom_to_bed = false; } -#else - if (m_force_zoom_to_bed_enabled) - _force_zoom_to_bed(); -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE _camera_tranform(); @@ -4815,10 +4740,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (m_reload_delayed) return; -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE - set_bed_shape(dynamic_cast(m_config->option("bed_shape"))->values); -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE - if (m_regenerate_volumes) { m_volumes.volumes = std::move(glvolumes_new); @@ -6059,14 +5980,6 @@ bool GLCanvas3D::_is_shown_on_screen() const return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; } -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE -void GLCanvas3D::_force_zoom_to_bed() -{ - zoom_to_bed(); - m_force_zoom_to_bed_enabled = false; -} -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE - bool GLCanvas3D::_init_toolbar() { if (!m_toolbar.is_enabled()) @@ -6292,11 +6205,7 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) viewport_changed(); -#if ENABLE_REWORKED_BED_SHAPE_CHANGE m_dirty = true; -#else - _refresh_if_shown_on_screen(); -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE } } @@ -6376,15 +6285,7 @@ void GLCanvas3D::_refresh_if_shown_on_screen() // Because of performance problems on macOS, where PaintEvents are not delivered // frequently enough, we call render() here directly when we can. -#if ENABLE_REWORKED_BED_SHAPE_CHANGE render(); -#else - // We can't do that when m_force_zoom_to_bed_enabled == true, because then render() - // ends up calling back here via _force_zoom_to_bed(), causing a stack overflow. - if (m_canvas != nullptr) { - m_force_zoom_to_bed_enabled ? m_canvas->Refresh() : render(); - } -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 1cba644aa..44e8ee0b2 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -224,9 +224,7 @@ class GLCanvas3D public: Bed(); -#if ENABLE_REWORKED_BED_SHAPE_CHANGE EType get_type() const { return m_type; } -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE bool is_prusa() const; bool is_custom() const; @@ -245,16 +243,9 @@ class GLCanvas3D void _calc_bounding_box(); void _calc_triangles(const ExPolygon& poly); void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); -#if ENABLE_REWORKED_BED_SHAPE_CHANGE EType _detect_type(const Pointfs& shape) const; -#else - EType _detect_type() const; -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE void _render_prusa(const std::string &key, float theta, bool useVBOs) const; void _render_custom() const; -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE - static bool _are_equal(const Pointfs& bed_1, const Pointfs& bed_2); -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE }; struct Axes @@ -897,11 +888,7 @@ private: bool m_dirty; bool m_initialized; bool m_use_VBOs; -#if ENABLE_REWORKED_BED_SHAPE_CHANGE bool m_requires_zoom_to_bed; -#else - bool m_force_zoom_to_bed_enabled; -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE bool m_apply_zoom_to_volumes_filter; mutable int m_hover_volume_id; bool m_toolbar_action_running; @@ -987,9 +974,6 @@ public: void enable_moving(bool enable); void enable_gizmos(bool enable); void enable_toolbar(bool enable); -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE - void enable_force_zoom_to_bed(bool enable); -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE void enable_dynamic_background(bool enable); void allow_multisample(bool allow); @@ -1074,9 +1058,6 @@ public: private: bool _is_shown_on_screen() const; -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE - void _force_zoom_to_bed(); -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE bool _init_toolbar(); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 0ad6c3562..6f758f375 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -69,9 +69,6 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba m_canvas->set_config(config); m_canvas->enable_gizmos(true); m_canvas->enable_toolbar(true); -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE - m_canvas->enable_force_zoom_to_bed(true); -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE #if !ENABLE_IMGUI m_gizmo_widget = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); @@ -107,12 +104,7 @@ void View3D::set_as_dirty() void View3D::set_bed_shape(const Pointfs& shape) { if (m_canvas != nullptr) - { m_canvas->set_bed_shape(shape); -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE - m_canvas->zoom_to_bed(); -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE - } } void View3D::select_view(const std::string& direction) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f5471cdff..e970a30dc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1136,12 +1136,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->background_process_timer.SetOwner(this->q, 0); this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->update_restart_background_process(false, false); }); -#if !ENABLE_REWORKED_BED_SHAPE_CHANGE - auto *bed_shape = config->opt("bed_shape"); - view3D->set_bed_shape(bed_shape->values); - preview->set_bed_shape(bed_shape->values); -#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE - update(); auto *hsizer = new wxBoxSizer(wxHORIZONTAL); @@ -3023,20 +3017,13 @@ void Plater::on_extruders_change(int num_extruders) void Plater::on_config_change(const DynamicPrintConfig &config) { bool update_scheduled = false; -#if ENABLE_REWORKED_BED_SHAPE_CHANGE bool bed_shape_changed = false; -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE for (auto opt_key : p->config->diff(config)) { p->config->set_key_value(opt_key, config.option(opt_key)->clone()); if (opt_key == "printer_technology") this->set_printer_technology(config.opt_enum(opt_key)); else if (opt_key == "bed_shape") { -#if ENABLE_REWORKED_BED_SHAPE_CHANGE bed_shape_changed = true; -#else - if (p->view3D) p->view3D->set_bed_shape(p->config->option(opt_key)->values); - if (p->preview) p->preview->set_bed_shape(p->config->option(opt_key)->values); -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE update_scheduled = true; } else if (boost::starts_with(opt_key, "wipe_tower") || @@ -3062,12 +3049,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } else if (opt_key == "printer_model") { // update to force bed selection(for texturing) -#if ENABLE_REWORKED_BED_SHAPE_CHANGE bed_shape_changed = true; -#else - if (p->view3D) p->view3D->set_bed_shape(p->config->option("bed_shape")->values); - if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE update_scheduled = true; } else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) { @@ -3080,13 +3062,11 @@ void Plater::on_config_change(const DynamicPrintConfig &config) p->sidebar->show_send(prin_host_opt != nullptr && !prin_host_opt->value.empty()); } -#if ENABLE_REWORKED_BED_SHAPE_CHANGE if (bed_shape_changed) { if (p->view3D) p->view3D->set_bed_shape(p->config->option("bed_shape")->values); if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); } -#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE if (update_scheduled) update(); From 9e3434ecc14ef58454528d035e3b9aa98003e895 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 19 Feb 2019 12:14:13 +0100 Subject: [PATCH 10/20] Fixed calculation of the consumed material for the supports --- src/libslic3r/SLAPrint.cpp | 31 ++++++++++++++++++++++++++----- src/slic3r/GUI/Plater.cpp | 19 ++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d24be4504..21c922a16 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1100,13 +1100,34 @@ void SLAPrint::fill_statistics() const auto index = po->get_slice_index(); if (index.find(layer.first) == index.end()) continue; - - if (index.at(layer.first).model_slices_idx != SLAPrintObject::SliceRecord::NONE) { - for (const ExPolygon& polygon : po->get_model_slices().at(index.at(layer.first).model_slices_idx)) + + const SLAPrintObject::SliceRecord& record = index.at(layer.first); + + if (record.model_slices_idx != SLAPrintObject::SliceRecord::NONE && + record.support_slices_idx != SLAPrintObject::SliceRecord::NONE) + { + double model_area = 0; + for (const ExPolygon& polygon : po->get_model_slices().at(record.model_slices_idx)) + model_area += polygon.area(); + + layer_model_area += model_area; + + Polygons polygons = to_polygons(po->get_model_slices().at(record.model_slices_idx)); + append(polygons, to_polygons(po->get_support_slices().at(record.support_slices_idx))); + polygons = union_(polygons); + double poligons_area = 0; + for (const Polygon& polygon : polygons) + poligons_area += polygon.area(); + + if (poligons_area > model_area) + layer_support_area += (poligons_area-model_area); + } + else if (record.model_slices_idx != SLAPrintObject::SliceRecord::NONE) { + for (const ExPolygon& polygon : po->get_model_slices().at(record.model_slices_idx)) layer_model_area += polygon.area(); } - /*else */if (index.at(layer.first).support_slices_idx != SLAPrintObject::SliceRecord::NONE) { - for (const ExPolygon& polygon : po->get_support_slices().front()) + else if (record.support_slices_idx != SLAPrintObject::SliceRecord::NONE) { + for (const ExPolygon& polygon : po->get_support_slices().at(record.support_slices_idx)) layer_support_area += polygon.area(); } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 87a950b7f..e8944654a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -99,6 +99,11 @@ public: wxStaticText *info_facets; wxStaticText *info_materials; wxStaticText *info_manifold; + + wxStaticText *label_volume; + wxStaticText *label_materials; + std::vector sla_hided_items; + bool showing_manifold_warning_icon; void show_sizer(bool show); }; @@ -120,15 +125,16 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : (*info_label)->SetFont(wxGetApp().small_font()); grid_sizer->Add(text, 0); grid_sizer->Add(*info_label, 0); + return text; }; init_info_label(&info_size, _(L("Size"))); - init_info_label(&info_volume, _(L("Volume"))); + label_volume = init_info_label(&info_volume, _(L("Volume"))); init_info_label(&info_facets, _(L("Facets"))); - init_info_label(&info_materials, _(L("Materials"))); + label_materials = init_info_label(&info_materials, _(L("Materials"))); Add(grid_sizer, 0, wxEXPAND); - auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _(L("Manifold"))); + auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _(L("Manifold")) + ":"); info_manifold_text->SetFont(wxGetApp().small_font()); info_manifold = new wxStaticText(parent, wxID_ANY, ""); info_manifold->SetFont(wxGetApp().small_font()); @@ -139,6 +145,8 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); sizer_manifold->Add(info_manifold, 0, wxLEFT, 2); Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4); + + sla_hided_items = { label_volume, info_volume, label_materials, info_materials }; } void ObjectInfo::show_sizer(bool show) @@ -811,6 +819,11 @@ void Sidebar::show_info_sizer() } p->object_info->show_sizer(true); + + if (p->plater->printer_technology() == ptSLA) { + for (auto item: p->object_info->sla_hided_items) + item->Show(false); + } } void Sidebar::show_sliced_info_sizer(const bool show) From 327114fa3ec8e0f01ffb53878f17c441bd49a361 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 19 Feb 2019 13:47:40 +0100 Subject: [PATCH 11/20] Added missed include for the OSX build --- src/libslic3r/SLAPrint.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index ffa451b35..23c34ada1 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -2,7 +2,7 @@ #define slic3r_SLAPrint_hpp_ #include - +#include "ClipperUtils.hpp" #include "PrintBase.hpp" #include "PrintExport.hpp" #include "Point.hpp" From 75df722fee97bcb6e0d7e0b90f93cfb0819d3ee0 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Feb 2019 15:15:27 +0100 Subject: [PATCH 12/20] Bed and Axes classes moved from GLCanva3d to Plater to have a unique instance of them shared by all views --- src/libslic3r/Technologies.hpp | 9 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/3DBed.cpp | 517 +++++++++++++++++++++++++++++++++ src/slic3r/GUI/3DBed.hpp | 105 +++++++ src/slic3r/GUI/GLCanvas3D.cpp | 68 +++++ src/slic3r/GUI/GLCanvas3D.hpp | 24 ++ src/slic3r/GUI/GUI_Preview.cpp | 34 ++- src/slic3r/GUI/GUI_Preview.hpp | 17 ++ src/slic3r/GUI/Plater.cpp | 41 +++ src/slic3r/GUI/Plater.hpp | 5 + 10 files changed, 821 insertions(+), 1 deletion(-) create mode 100644 src/slic3r/GUI/3DBed.cpp create mode 100644 src/slic3r/GUI/3DBed.hpp diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index fe7606041..b26534c54 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -54,4 +54,13 @@ // Toolbar items hidden/shown in dependence of the user mode #define ENABLE_MODE_AWARE_TOOLBAR_ITEMS (1 && ENABLE_1_42_0_ALPHA5) + +//==================== +// 1.42.0.alpha7 techs +//==================== +#define ENABLE_1_42_0_ALPHA7 1 + +// Moves bed from GLCanva3d to Plater to have a unique instance of it +#define ENABLE_UNIQUE_BED (1 && ENABLE_1_42_0_ALPHA7) + #endif // _technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 9d65a479f..157fc9011 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -74,6 +74,8 @@ set(SLIC3R_GUI_SOURCES GUI/BedShapeDialog.hpp GUI/2DBed.cpp GUI/2DBed.hpp + GUI/3DBed.cpp + GUI/3DBed.hpp GUI/wxExtensions.cpp GUI/wxExtensions.hpp GUI/WipeTowerDialog.cpp diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp new file mode 100644 index 000000000..7ad89776b --- /dev/null +++ b/src/slic3r/GUI/3DBed.cpp @@ -0,0 +1,517 @@ +#include "libslic3r/libslic3r.h" + +#include "3DBed.hpp" + +#include "libslic3r/Polygon.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/BoundingBox.hpp" + +#include "GUI_App.hpp" +#include "PresetBundle.hpp" + +#include + +#include + +static const float GROUND_Z = -0.02f; + +namespace Slic3r { +namespace GUI { + +#if ENABLE_UNIQUE_BED +bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) +{ + m_vertices.clear(); + m_tex_coords.clear(); + + unsigned int v_size = 9 * (unsigned int)triangles.size(); + unsigned int t_size = 6 * (unsigned int)triangles.size(); + if (v_size == 0) + return false; + + m_vertices = std::vector(v_size, 0.0f); + if (generate_tex_coords) + m_tex_coords = std::vector(t_size, 0.0f); + + float min_x = unscale(triangles[0].points[0](0)); + float min_y = unscale(triangles[0].points[0](1)); + float max_x = min_x; + float max_y = min_y; + + unsigned int v_coord = 0; + unsigned int t_coord = 0; + for (const Polygon& t : triangles) + { + for (unsigned int v = 0; v < 3; ++v) + { + const Point& p = t.points[v]; + float x = unscale(p(0)); + float y = unscale(p(1)); + + m_vertices[v_coord++] = x; + m_vertices[v_coord++] = y; + m_vertices[v_coord++] = z; + + if (generate_tex_coords) + { + m_tex_coords[t_coord++] = x; + m_tex_coords[t_coord++] = y; + + min_x = std::min(min_x, x); + max_x = std::max(max_x, x); + min_y = std::min(min_y, y); + max_y = std::max(max_y, y); + } + } + } + + if (generate_tex_coords) + { + float size_x = max_x - min_x; + float size_y = max_y - min_y; + + if ((size_x != 0.0f) && (size_y != 0.0f)) + { + float inv_size_x = 1.0f / size_x; + float inv_size_y = -1.0f / size_y; + for (unsigned int i = 0; i < m_tex_coords.size(); i += 2) + { + m_tex_coords[i] = (m_tex_coords[i] - min_x) * inv_size_x; + m_tex_coords[i + 1] = (m_tex_coords[i + 1] - min_y) * inv_size_y; + } + } + } + + return true; +} + +bool GeometryBuffer::set_from_lines(const Lines& lines, float z) +{ + m_vertices.clear(); + m_tex_coords.clear(); + + unsigned int size = 6 * (unsigned int)lines.size(); + if (size == 0) + return false; + + m_vertices = std::vector(size, 0.0f); + + unsigned int coord = 0; + for (const Line& l : lines) + { + m_vertices[coord++] = unscale(l.a(0)); + m_vertices[coord++] = unscale(l.a(1)); + m_vertices[coord++] = z; + m_vertices[coord++] = unscale(l.b(0)); + m_vertices[coord++] = unscale(l.b(1)); + m_vertices[coord++] = z; + } + + return true; +} + +const double Bed3D::Axes::Radius = 0.5; +const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius; +const double Bed3D::Axes::ArrowLength = 5.0; + +Bed3D::Axes::Axes() +: origin(Vec3d::Zero()) +, length(Vec3d::Zero()) +{ + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +} + +Bed3D::Axes::~Axes() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} + +void Bed3D::Axes::render() const +{ + if (m_quadric == nullptr) + return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_LIGHTING)); + + // x axis + glsafe(::glColor3f(1.0f, 0.0f, 0.0f)); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(origin(0), origin(1), origin(2))); + glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); + render_axis(length(0)); + glsafe(::glPopMatrix()); + + // y axis + glsafe(::glColor3f(0.0f, 1.0f, 0.0f)); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(origin(0), origin(1), origin(2))); + glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); + render_axis(length(1)); + glsafe(::glPopMatrix()); + + // z axis + glsafe(::glColor3f(0.0f, 0.0f, 1.0f)); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(origin(0), origin(1), origin(2))); + render_axis(length(2)); + glsafe(::glPopMatrix()); + + glsafe(::glDisable(GL_LIGHTING)); +} + +void Bed3D::Axes::render_axis(double length) const +{ + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, Radius, Radius, length, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, Radius, 32, 1); + glsafe(::glTranslated(0.0, 0.0, length)); + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); +} + +Bed3D::Bed3D() +: m_type(Custom) +, m_scale_factor(1.0f) +{ +} + +bool Bed3D::set_shape(const Pointfs& shape) +{ + EType new_type = detect_type(shape); + if (m_shape == shape && m_type == new_type) + // No change, no need to update the UI. + return false; + + m_shape = shape; + m_type = new_type; + + calc_bounding_box(); + + ExPolygon poly; + for (const Vec2d& p : m_shape) + { + poly.contour.append(Point(scale_(p(0)), scale_(p(1)))); + } + + calc_triangles(poly); + + const BoundingBox& bed_bbox = poly.contour.bounding_box(); + calc_gridlines(poly, bed_bbox); + + m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour; + + // Set the origin and size for painting of the coordinate system axes. + m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); + m_axes.length = 0.1 * get_bounding_box().max_size() * Vec3d::Ones(); + + // Let the calee to update the UI. + return true; +} + +bool Bed3D::contains(const Point& point) const +{ + return m_polygon.contains(point); +} + +Point Bed3D::point_projection(const Point& point) const +{ + return m_polygon.point_projection(point); +} + +void Bed3D::render(float theta, bool useVBOs, float scale_factor) const +{ + m_scale_factor = scale_factor; + + if (m_shape.empty()) + return; + + switch (m_type) + { + case MK2: + { + render_prusa("mk2", theta, useVBOs); + break; + } + case MK3: + { + render_prusa("mk3", theta, useVBOs); + break; + } + case SL1: + { + render_prusa("sl1", theta, useVBOs); + break; + } + default: + case Custom: + { + render_custom(); + break; + } + } +} + +void Bed3D::render_axes() const +{ + if (!m_shape.empty()) + m_axes.render(); +} + +void Bed3D::calc_bounding_box() +{ + m_bounding_box = BoundingBoxf3(); + for (const Vec2d& p : m_shape) + { + m_bounding_box.merge(Vec3d(p(0), p(1), 0.0)); + } +} + +void Bed3D::calc_triangles(const ExPolygon& poly) +{ + Polygons triangles; + poly.triangulate(&triangles); + + if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom)) + printf("Unable to create bed triangles\n"); +} + +void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) +{ + Polylines axes_lines; + for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0)) + { + Polyline line; + line.append(Point(x, bed_bbox.min(1))); + line.append(Point(x, bed_bbox.max(1))); + axes_lines.push_back(line); + } + for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0)) + { + Polyline line; + line.append(Point(bed_bbox.min(0), y)); + line.append(Point(bed_bbox.max(0), y)); + axes_lines.push_back(line); + } + + // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped + Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON))); + + // append bed contours + Lines contour_lines = to_lines(poly); + std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); + + if (!m_gridlines.set_from_lines(gridlines, GROUND_Z)) + printf("Unable to create bed grid lines\n"); +} + +Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const +{ + EType type = Custom; + + auto bundle = wxGetApp().preset_bundle; + if (bundle != nullptr) + { + const Preset* curr = &bundle->printers.get_selected_preset(); + while (curr != nullptr) + { + if (curr->config.has("bed_shape")) + { + if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research") && (shape == dynamic_cast(curr->config.option("bed_shape"))->values)) + { + if (boost::contains(curr->name, "SL1")) + { + type = SL1; + break; + } + else if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5")) + { + type = MK3; + break; + } + else if (boost::contains(curr->name, "MK2")) + { + type = MK2; + break; + } + } + } + + curr = bundle->printers.get_preset_parent(*curr); + } + } + + return type; +} + +void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) const +{ + std::string tex_path = resources_dir() + "/icons/bed/" + key; + + // use higher resolution images if graphic card allows + GLint max_tex_size; + ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); + + // temporary set to lowest resolution + max_tex_size = 2048; + + if (max_tex_size >= 8192) + tex_path += "_8192"; + else if (max_tex_size >= 4096) + tex_path += "_4096"; + + std::string model_path = resources_dir() + "/models/" + key; + +#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + // use anisotropic filter if graphic card allows + GLfloat max_anisotropy = 0.0f; + if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) + ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); +#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + + std::string filename = tex_path + "_top.png"; + if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) + { + if (!m_top_texture.load_from_file(filename, true)) + { + render_custom(); + return; + } +#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + if (max_anisotropy > 0.0f) + { + glsafe(::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id())); + glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + } +#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + } + + filename = tex_path + "_bottom.png"; + if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) + { + if (!m_bottom_texture.load_from_file(filename, true)) + { + render_custom(); + return; + } +#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + if (max_anisotropy > 0.0f) + { + glsafe(::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id())); + glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + } +#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + } + + if (theta <= 90.0f) + { + filename = model_path + "_bed.stl"; + if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) { + Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2)); + if (key == "mk2") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 7.5, -0.03); + else if (key == "mk3") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 5.5, 2.43); + else if (key == "sl1") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 0.0, -0.03); + + m_model.center_around(offset); + } + + if (!m_model.get_filename().empty()) + { + glsafe(::glEnable(GL_LIGHTING)); + m_model.render(); + glsafe(::glDisable(GL_LIGHTING)); + } + } + + unsigned int triangles_vcount = m_triangles.get_vertices_count(); + if (triangles_vcount > 0) + { + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDepthMask(GL_FALSE)); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + glsafe(::glEnable(GL_TEXTURE_2D)); + glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + + if (theta > 90.0f) + glsafe(::glFrontFace(GL_CW)); + + glsafe(::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id())); + glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices())); + glsafe(::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords())); + glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); + + if (theta > 90.0f) + glsafe(::glFrontFace(GL_CCW)); + + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY)); + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glDisable(GL_TEXTURE_2D)); + + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDepthMask(GL_TRUE)); + } +} + +void Bed3D::render_custom() const +{ + m_top_texture.reset(); + m_bottom_texture.reset(); + + unsigned int triangles_vcount = m_triangles.get_vertices_count(); + if (triangles_vcount > 0) + { + glsafe(::glEnable(GL_LIGHTING)); + glsafe(::glDisable(GL_DEPTH_TEST)); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f)); + glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); + glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices())); + glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); + + // draw grid + unsigned int gridlines_vcount = m_gridlines.get_vertices_count(); + + // we need depth test for grid, otherwise it would disappear when looking the object from below + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glLineWidth(3.0f * m_scale_factor)); + glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f)); + glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices())); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount)); + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDisable(GL_LIGHTING)); + } +} +#endif // ENABLE_UNIQUE_BED + +} // GUI +} // Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp new file mode 100644 index 000000000..51d787c03 --- /dev/null +++ b/src/slic3r/GUI/3DBed.hpp @@ -0,0 +1,105 @@ +#ifndef slic3r_3DBed_hpp_ +#define slic3r_3DBed_hpp_ + +#include "GLTexture.hpp" +#include "3DScene.hpp" + +class GLUquadric; +typedef class GLUquadric GLUquadricObj; + +namespace Slic3r { +namespace GUI { + +#if ENABLE_UNIQUE_BED +class GeometryBuffer +{ + std::vector m_vertices; + std::vector m_tex_coords; + +public: + bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords); + bool set_from_lines(const Lines& lines, float z); + + const float* get_vertices() const { return m_vertices.data(); } + const float* get_tex_coords() const { return m_tex_coords.data(); } + + unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size() / 3; } +}; + +class Bed3D +{ + struct Axes + { + static const double Radius; + static const double ArrowBaseRadius; + static const double ArrowLength; + Vec3d origin; + Vec3d length; + GLUquadricObj* m_quadric; + + Axes(); + ~Axes(); + + void render() const; + + private: + void render_axis(double length) const; + }; + +public: + enum EType : unsigned char + { + MK2, + MK3, + SL1, + Custom, + Num_Types + }; + +private: + EType m_type; + Pointfs m_shape; + BoundingBoxf3 m_bounding_box; + Polygon m_polygon; + GeometryBuffer m_triangles; + GeometryBuffer m_gridlines; + mutable GLTexture m_top_texture; + mutable GLTexture m_bottom_texture; + mutable GLBed m_model; + Axes m_axes; + + mutable float m_scale_factor; + +public: + Bed3D(); + + EType get_type() const { return m_type; } + + bool is_prusa() const { return (m_type == MK2) || (m_type == MK3) || (m_type == SL1); } + bool is_custom() const { return m_type == Custom; } + + const Pointfs& get_shape() const { return m_shape; } + // Return true if the bed shape changed, so the calee will update the UI. + bool set_shape(const Pointfs& shape); + + const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } + bool contains(const Point& point) const; + Point point_projection(const Point& point) const; + + void render(float theta, bool useVBOs, float scale_factor) const; + void render_axes() const; + +private: + void calc_bounding_box(); + void calc_triangles(const ExPolygon& poly); + void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); + EType detect_type(const Pointfs& shape) const; + void render_prusa(const std::string &key, float theta, bool useVBOs) const; + void render_custom() const; +}; +#endif // ENABLE_UNIQUE_BED + +} // GUI +} // Slic3r + +#endif // slic3r_3DBed_hpp_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8024e6a46..e1c5138b4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -84,6 +84,7 @@ static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f namespace Slic3r { namespace GUI { +#if !ENABLE_UNIQUE_BED bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) { m_vertices.clear(); @@ -189,6 +190,7 @@ unsigned int GeometryBuffer::get_vertices_count() const { return (unsigned int)m_vertices.size() / 3; } +#endif // !ENABLE_UNIQUE_BED Size::Size() : m_width(0) @@ -344,6 +346,7 @@ void GLCanvas3D::Camera::set_scene_box(const BoundingBoxf3& box, GLCanvas3D& can } } +#if !ENABLE_UNIQUE_BED GLCanvas3D::Bed::Bed() : m_type(Custom) , m_scale_factor(1.0f) @@ -751,6 +754,7 @@ void GLCanvas3D::Axes::render_axis(double length) const ::gluQuadricOrientation(m_quadric, GLU_INSIDE); ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); } +#endif // !ENABLE_UNIQUE_BED GLCanvas3D::Shader::Shader() : m_shader(nullptr) @@ -3973,6 +3977,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); +#if ENABLE_UNIQUE_BED +wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); +#endif // ENABLE_UNIQUE_BED GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) @@ -3981,6 +3988,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_retina_helper(nullptr) #endif , m_in_render(false) +#if ENABLE_UNIQUE_BED + , m_bed(nullptr) +#endif // ENABLE_UNIQUE_BED , m_toolbar(GLToolbar::Normal) , m_view_toolbar(nullptr) , m_use_clipping_planes(false) @@ -4180,6 +4190,15 @@ void GLCanvas3D::set_model(Model* model) m_selection.set_model(m_model); } +#if ENABLE_UNIQUE_BED +void GLCanvas3D::bed_shape_changed() +{ + m_camera.set_scene_box(scene_bounding_box(), *this); + m_requires_zoom_to_bed = true; + + m_dirty = true; +} +#else void GLCanvas3D::set_bed_shape(const Pointfs& shape) { bool new_shape = m_bed.set_shape(shape); @@ -4200,6 +4219,7 @@ void GLCanvas3D::set_bed_axes_length(double length) { m_axes.length = length * Vec3d::Ones(); } +#endif // ENABLE_UNIQUE_BED void GLCanvas3D::set_color_by(const std::string& value) { @@ -4225,7 +4245,12 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); +#if ENABLE_UNIQUE_BED + if (m_bed != nullptr) + bb.merge(m_bed->get_bounding_box()); +#else bb.merge(m_bed.get_bounding_box()); +#endif // ENABLE_UNIQUE_BED if (m_config != nullptr) { double h = m_config->opt_float("max_print_height"); @@ -4318,7 +4343,12 @@ bool GLCanvas3D::is_toolbar_item_pressed(const std::string& name) const void GLCanvas3D::zoom_to_bed() { +#if ENABLE_UNIQUE_BED + if (m_bed != nullptr) + _zoom_to_bounding_box(m_bed->get_bounding_box()); +#else _zoom_to_bounding_box(m_bed.get_bounding_box()); +#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::zoom_to_volumes() @@ -4431,12 +4461,20 @@ void GLCanvas3D::render() if (!_set_current() || !_3DScene::init(m_canvas)) return; +#if ENABLE_UNIQUE_BED + if ((m_bed != nullptr) && m_bed->get_shape().empty()) + { + // this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE + post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE)); + } +#else if (m_bed.get_shape().empty()) { // this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE if (m_config != nullptr) set_bed_shape(m_config->opt("bed_shape")->values); } +#endif // ENABLE_UNIQUE_BED if (m_requires_zoom_to_bed) { @@ -4458,7 +4496,11 @@ void GLCanvas3D::render() // absolute value of the rotation theta = 360.f - theta; +#if ENABLE_UNIQUE_BED + bool is_custom_bed = (m_bed == nullptr) || m_bed->is_custom(); +#else bool is_custom_bed = m_bed.is_custom(); +#endif // ENABLE_UNIQUE_BED #if ENABLE_IMGUI wxGetApp().imgui()->new_frame(); @@ -6188,9 +6230,16 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) BoundingBoxf3 GLCanvas3D::_max_bounding_box() const { +#if ENABLE_UNIQUE_BED + BoundingBoxf3 bb = volumes_bounding_box(); + if (m_bed != nullptr) + bb.merge(m_bed->get_bounding_box()); + return bb; +#else BoundingBoxf3 bb = m_bed.get_bounding_box(); bb.merge(volumes_bounding_box()); return bb; +#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) @@ -6394,12 +6443,22 @@ void GLCanvas3D::_render_bed(float theta) const scale_factor = m_retina_helper->get_scale_factor(); #endif +#if ENABLE_UNIQUE_BED + if (m_bed != nullptr) + m_bed->render(theta, m_use_VBOs, scale_factor); +#else m_bed.render(theta, m_use_VBOs, scale_factor); +#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::_render_axes() const { +#if ENABLE_UNIQUE_BED + if (m_bed != nullptr) + m_bed->render_axes(); +#else m_axes.render(); +#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::_render_objects() const @@ -6417,12 +6476,21 @@ void GLCanvas3D::_render_objects() const // Update the layer editing selection to the first object selected, update the current object maximum Z. const_cast(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1); +#if ENABLE_UNIQUE_BED + if ((m_config != nullptr) && (m_bed != nullptr)) + { + const BoundingBoxf3& bed_bb = m_bed->get_bounding_box(); + m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); + m_volumes.check_outside_state(m_config, nullptr); + } +#else if (m_config != nullptr) { const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(); m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); m_volumes.check_outside_state(m_config, nullptr); } +#endif // ENABLE_UNIQUE_BED } if (m_use_clipping_planes) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 44e8ee0b2..2136adca7 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -8,6 +8,9 @@ #include "3DScene.hpp" #include "GLToolbar.hpp" #include "Event.hpp" +#if ENABLE_UNIQUE_BED +#include "3DBed.hpp" +#endif // ENABLE_UNIQUE_BED #include @@ -25,8 +28,10 @@ class wxGLCanvas; // Support for Retina OpenGL on Mac OS #define ENABLE_RETINA_GL __APPLE__ +#if !ENABLE_UNIQUE_BED class GLUquadric; typedef class GLUquadric GLUquadricObj; +#endif // !ENABLE_UNIQUE_BED namespace Slic3r { @@ -45,6 +50,7 @@ class GLGizmoBase; class RetinaHelper; #endif +#if !ENABLE_UNIQUE_BED class GeometryBuffer { std::vector m_vertices; @@ -59,6 +65,7 @@ public: unsigned int get_vertices_count() const; }; +#endif // !ENABLE_UNIQUE_BED class Size { @@ -131,6 +138,9 @@ wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); +#if ENABLE_UNIQUE_BED +wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); +#endif // ENABLE_UNIQUE_BED class GLCanvas3D { @@ -196,6 +206,7 @@ class GLCanvas3D void set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas); }; +#if !ENABLE_UNIQUE_BED class Bed { public: @@ -265,6 +276,7 @@ class GLCanvas3D private: void render_axis(double length) const; }; +#endif // !ENABLE_UNIQUE_BED class Shader { @@ -865,8 +877,12 @@ private: WarningTexture m_warning_texture; wxTimer m_timer; Camera m_camera; +#if ENABLE_UNIQUE_BED + Bed3D* m_bed; +#else Bed m_bed; Axes m_axes; +#endif // ENABLE_UNIQUE_BED LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; @@ -920,6 +936,10 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas; } const wxGLCanvas* get_wxglcanvas() const { return m_canvas; } +#if ENABLE_UNIQUE_BED + void set_bed(Bed3D* bed) { m_bed = bed; } +#endif // ENABLE_UNIQUE_BED + void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; } bool init(bool useVBOs, bool use_legacy_opengl); @@ -938,12 +958,16 @@ public: const Selection& get_selection() const { return m_selection; } Selection& get_selection() { return m_selection; } +#if ENABLE_UNIQUE_BED + void bed_shape_changed(); +#else // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, // fills the m_bed.m_grid_lines and sets m_bed.m_origin. // Sets m_bed.m_polygon to limit the object placement. void set_bed_shape(const Pointfs& shape); void set_bed_axes_length(double length); +#endif // ENABLE_UNIQUE_BED void set_clipping_plane(unsigned int id, const ClippingPlane& plane) { diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 6f758f375..0c3dc7025 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -89,6 +89,14 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba return true; } +#if ENABLE_UNIQUE_BED +void View3D::set_bed(Bed3D* bed) +{ + if (m_canvas != nullptr) + m_canvas->set_bed(bed); +} +#endif // ENABLE_UNIQUE_BED + void View3D::set_view_toolbar(GLToolbar* toolbar) { if (m_canvas != nullptr) @@ -101,11 +109,19 @@ void View3D::set_as_dirty() m_canvas->set_as_dirty(); } +#if ENABLE_UNIQUE_BED +void View3D::bed_shape_changed() +{ + if (m_canvas != nullptr) + m_canvas->bed_shape_changed(); +} +#else void View3D::set_bed_shape(const Pointfs& shape) { if (m_canvas != nullptr) m_canvas->set_bed_shape(shape); } +#endif // ENABLE_UNIQUE_BED void View3D::select_view(const std::string& direction) { @@ -337,6 +353,14 @@ Preview::~Preview() } } +#if ENABLE_UNIQUE_BED +void Preview::set_bed(Bed3D* bed) +{ + if (m_canvas != nullptr) + m_canvas->set_bed(bed); +} +#endif // ENABLE_UNIQUE_BED + void Preview::set_view_toolbar(GLToolbar* toolbar) { if (m_canvas != nullptr) @@ -368,10 +392,18 @@ void Preview::set_enabled(bool enabled) m_enabled = enabled; } -void Preview::set_bed_shape(const Pointfs& shape) +#if ENABLE_UNIQUE_BED +void Preview::bed_shape_changed() +{ + if (m_canvas != nullptr) + m_canvas->bed_shape_changed(); +} +#else +vvoid Preview::set_bed_shape(const Pointfs& shape) { m_canvas->set_bed_shape(shape); } +#endif // ENABLE_UNIQUE_BED void Preview::select_view(const std::string& direction) { diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 49dbed44d..3dec8bcbc 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -27,6 +27,9 @@ namespace GUI { class GLCanvas3D; class GLToolbar; +#if ENABLE_UNIQUE_BED +class Bed3D; +#endif // ENABLE_UNIQUE_BED class View3D : public wxPanel { @@ -48,10 +51,17 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } GLCanvas3D* get_canvas3d() { return m_canvas; } +#if ENABLE_UNIQUE_BED + void set_bed(Bed3D* bed); +#endif // ENABLE_UNIQUE_BED void set_view_toolbar(GLToolbar* toolbar); void set_as_dirty(); +#if ENABLE_UNIQUE_BED + void bed_shape_changed(); +#else void set_bed_shape(const Pointfs& shape); +#endif // ENABLE_UNIQUE_BED void select_view(const std::string& direction); void select_all(); @@ -114,12 +124,19 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } GLCanvas3D* get_canvas3d() { return m_canvas; } +#if ENABLE_UNIQUE_BED + void set_bed(Bed3D* bed); +#endif // ENABLE_UNIQUE_BED void set_view_toolbar(GLToolbar* toolbar); void set_number_extruders(unsigned int number_extruders); void set_canvas_as_dirty(); void set_enabled(bool enabled); +#if ENABLE_UNIQUE_BED + void bed_shape_changed(); +#else void set_bed_shape(const Pointfs& shape); +#endif // ENABLE_UNIQUE_BED void select_view(const std::string& direction); void set_viewport_from_scene(GLCanvas3D* canvas); void set_viewport_into_scene(GLCanvas3D* canvas); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e970a30dc..860d63ef8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -49,6 +49,9 @@ #include "GLCanvas3D.hpp" #include "GLToolbar.hpp" #include "GUI_Preview.hpp" +#if ENABLE_UNIQUE_BED +#include "3DBed.hpp" +#endif // ENABLE_UNIQUE_BED #include "Tab.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" @@ -952,6 +955,9 @@ struct Plater::priv wxPanel* current_panel; std::vector panels; Sidebar *sidebar; +#if ENABLE_UNIQUE_BED + Bed3D bed; +#endif // ENABLE_UNIQUE_BED View3D* view3D; GLToolbar view_toolbar; Preview *preview; @@ -1055,6 +1061,14 @@ struct Plater::priv void update_object_menu(); +#if ENABLE_UNIQUE_BED + // Set the bed shape to a single closed 2D polygon(array of two element arrays), + // triangulate the bed and store the triangles into m_bed.m_triangles, + // fills the m_bed.m_grid_lines and sets m_bed.m_origin. + // Sets m_bed.m_polygon to limit the object placement. + void set_bed_shape(const Pointfs& shape); +#endif // ENABLE_UNIQUE_BED + private: bool init_object_menu(); bool init_common_menu(wxMenu* menu, const bool is_part = false); @@ -1130,6 +1144,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); preview->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); +#if ENABLE_UNIQUE_BED + view3D->set_bed(&bed); + preview->set_bed(&bed); +#endif // ENABLE_UNIQUE_BED + panels.push_back(view3D); panels.push_back(preview); @@ -1186,10 +1205,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this); view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); +#if ENABLE_UNIQUE_BED + view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option("bed_shape")->values); }); +#endif // ENABLE_UNIQUE_BED // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); +#if ENABLE_UNIQUE_BED + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option("bed_shape")->values); }); +#endif // ENABLE_UNIQUE_BED view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); @@ -2624,6 +2649,18 @@ bool Plater::priv::can_mirror() const return get_selection().is_from_single_instance(); } +#if ENABLE_UNIQUE_BED +void Plater::priv::set_bed_shape(const Pointfs& shape) +{ + bool new_shape = bed.set_shape(shape); + if (new_shape) + { + if (view3D) view3D->bed_shape_changed(); + if (preview) preview->bed_shape_changed(); + } +} +#endif // ENABLE_UNIQUE_BED + void Plater::priv::update_object_menu() { sidebar->obj_list()->append_menu_items_add_volume(&object_menu); @@ -3064,8 +3101,12 @@ void Plater::on_config_change(const DynamicPrintConfig &config) if (bed_shape_changed) { +#if ENABLE_UNIQUE_BED + p->set_bed_shape(p->config->option("bed_shape")->values); +#else if (p->view3D) p->view3D->set_bed_shape(p->config->option("bed_shape")->values); if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); +#endif // ENABLE_UNIQUE_BED } if (update_scheduled) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index e3601b65c..5f921008f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -10,6 +10,11 @@ #include "Preset.hpp" +#if ENABLE_UNIQUE_BED +#include "3DScene.hpp" +#include "GLTexture.hpp" +#endif // ENABLE_UNIQUE_BED + class wxButton; class wxBoxSizer; class wxGLCanvas; From cb1ef36ceb6ecb018c0909be84d234696538e217 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 20 Feb 2019 13:50:35 +0100 Subject: [PATCH 13/20] ENABLE_UNIQUE_BED set as default --- src/libslic3r/Technologies.hpp | 8 - src/slic3r/GUI/3DBed.cpp | 2 - src/slic3r/GUI/3DBed.hpp | 2 - src/slic3r/GUI/GLCanvas3D.cpp | 590 +-------------------------------- src/slic3r/GUI/GLCanvas3D.hpp | 114 ------- src/slic3r/GUI/GUI_Preview.cpp | 19 -- src/slic3r/GUI/GUI_Preview.hpp | 14 - src/slic3r/GUI/Plater.cpp | 21 -- src/slic3r/GUI/Plater.hpp | 2 - 9 files changed, 1 insertion(+), 771 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 31ae19f36..7eb6efecc 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -53,12 +53,4 @@ #define ENABLE_MODE_AWARE_TOOLBAR_ITEMS (1 && ENABLE_1_42_0_ALPHA5) -//==================== -// 1.42.0.alpha7 techs -//==================== -#define ENABLE_1_42_0_ALPHA7 1 - -// Moves bed from GLCanva3d to Plater to have a unique instance of it -#define ENABLE_UNIQUE_BED (1 && ENABLE_1_42_0_ALPHA7) - #endif // _technologies_h_ diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 7ad89776b..ed41068f2 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -18,7 +18,6 @@ static const float GROUND_Z = -0.02f; namespace Slic3r { namespace GUI { -#if ENABLE_UNIQUE_BED bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) { m_vertices.clear(); @@ -511,7 +510,6 @@ void Bed3D::render_custom() const glsafe(::glDisable(GL_LIGHTING)); } } -#endif // ENABLE_UNIQUE_BED } // GUI } // Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 51d787c03..11c5faa64 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -10,7 +10,6 @@ typedef class GLUquadric GLUquadricObj; namespace Slic3r { namespace GUI { -#if ENABLE_UNIQUE_BED class GeometryBuffer { std::vector m_vertices; @@ -97,7 +96,6 @@ private: void render_prusa(const std::string &key, float theta, bool useVBOs) const; void render_custom() const; }; -#endif // ENABLE_UNIQUE_BED } // GUI } // Slic3r diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 16a6dd682..3e2dd68a0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -84,114 +84,6 @@ static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f namespace Slic3r { namespace GUI { -#if !ENABLE_UNIQUE_BED -bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) -{ - m_vertices.clear(); - m_tex_coords.clear(); - - unsigned int v_size = 9 * (unsigned int)triangles.size(); - unsigned int t_size = 6 * (unsigned int)triangles.size(); - if (v_size == 0) - return false; - - m_vertices = std::vector(v_size, 0.0f); - if (generate_tex_coords) - m_tex_coords = std::vector(t_size, 0.0f); - - float min_x = unscale(triangles[0].points[0](0)); - float min_y = unscale(triangles[0].points[0](1)); - float max_x = min_x; - float max_y = min_y; - - unsigned int v_coord = 0; - unsigned int t_coord = 0; - for (const Polygon& t : triangles) - { - for (unsigned int v = 0; v < 3; ++v) - { - const Point& p = t.points[v]; - float x = unscale(p(0)); - float y = unscale(p(1)); - - m_vertices[v_coord++] = x; - m_vertices[v_coord++] = y; - m_vertices[v_coord++] = z; - - if (generate_tex_coords) - { - m_tex_coords[t_coord++] = x; - m_tex_coords[t_coord++] = y; - - min_x = std::min(min_x, x); - max_x = std::max(max_x, x); - min_y = std::min(min_y, y); - max_y = std::max(max_y, y); - } - } - } - - if (generate_tex_coords) - { - float size_x = max_x - min_x; - float size_y = max_y - min_y; - - if ((size_x != 0.0f) && (size_y != 0.0f)) - { - float inv_size_x = 1.0f / size_x; - float inv_size_y = -1.0f / size_y; - for (unsigned int i = 0; i < m_tex_coords.size(); i += 2) - { - m_tex_coords[i] = (m_tex_coords[i] - min_x) * inv_size_x; - m_tex_coords[i + 1] = (m_tex_coords[i + 1] - min_y) * inv_size_y; - } - } - } - - return true; -} - -bool GeometryBuffer::set_from_lines(const Lines& lines, float z) -{ - m_vertices.clear(); - m_tex_coords.clear(); - - unsigned int size = 6 * (unsigned int)lines.size(); - if (size == 0) - return false; - - m_vertices = std::vector(size, 0.0f); - - unsigned int coord = 0; - for (const Line& l : lines) - { - m_vertices[coord++] = unscale(l.a(0)); - m_vertices[coord++] = unscale(l.a(1)); - m_vertices[coord++] = z; - m_vertices[coord++] = unscale(l.b(0)); - m_vertices[coord++] = unscale(l.b(1)); - m_vertices[coord++] = z; - } - - return true; -} - -const float* GeometryBuffer::get_vertices() const -{ - return m_vertices.data(); -} - -const float* GeometryBuffer::get_tex_coords() const -{ - return m_tex_coords.data(); -} - -unsigned int GeometryBuffer::get_vertices_count() const -{ - return (unsigned int)m_vertices.size() / 3; -} -#endif // !ENABLE_UNIQUE_BED - Size::Size() : m_width(0) , m_height(0) @@ -346,416 +238,6 @@ void GLCanvas3D::Camera::set_scene_box(const BoundingBoxf3& box, GLCanvas3D& can } } -#if !ENABLE_UNIQUE_BED -GLCanvas3D::Bed::Bed() - : m_type(Custom) - , m_scale_factor(1.0f) -{ -} - -bool GLCanvas3D::Bed::is_prusa() const -{ - return (m_type == MK2) || (m_type == MK3) || (m_type == SL1); -} - -bool GLCanvas3D::Bed::is_custom() const -{ - return m_type == Custom; -} - -const Pointfs& GLCanvas3D::Bed::get_shape() const -{ - return m_shape; -} - -bool GLCanvas3D::Bed::set_shape(const Pointfs& shape) -{ - EType new_type = _detect_type(shape); - if (m_shape == shape && m_type == new_type) - // No change, no need to update the UI. - return false; - - m_shape = shape; - m_type = new_type; - - _calc_bounding_box(); - - ExPolygon poly; - for (const Vec2d& p : m_shape) - { - poly.contour.append(Point(scale_(p(0)), scale_(p(1)))); - } - - _calc_triangles(poly); - - const BoundingBox& bed_bbox = poly.contour.bounding_box(); - _calc_gridlines(poly, bed_bbox); - - m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour; - // Let the calee to update the UI. - return true; -} - -const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const -{ - return m_bounding_box; -} - -bool GLCanvas3D::Bed::contains(const Point& point) const -{ - return m_polygon.contains(point); -} - -Point GLCanvas3D::Bed::point_projection(const Point& point) const -{ - return m_polygon.point_projection(point); -} - -void GLCanvas3D::Bed::render(float theta, bool useVBOs, float scale_factor) const -{ - m_scale_factor = scale_factor; - - switch (m_type) - { - case MK2: - { - _render_prusa("mk2", theta, useVBOs); - break; - } - case MK3: - { - _render_prusa("mk3", theta, useVBOs); - break; - } - case SL1: - { - _render_prusa("sl1", theta, useVBOs); - break; - } - default: - case Custom: - { - _render_custom(); - break; - } - } -} - -void GLCanvas3D::Bed::_calc_bounding_box() -{ - m_bounding_box = BoundingBoxf3(); - for (const Vec2d& p : m_shape) - { - m_bounding_box.merge(Vec3d(p(0), p(1), 0.0)); - } -} - -void GLCanvas3D::Bed::_calc_triangles(const ExPolygon& poly) -{ - Polygons triangles; - poly.triangulate(&triangles); - - if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom)) - printf("Unable to create bed triangles\n"); -} - -void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) -{ - Polylines axes_lines; - for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0)) - { - Polyline line; - line.append(Point(x, bed_bbox.min(1))); - line.append(Point(x, bed_bbox.max(1))); - axes_lines.push_back(line); - } - for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0)) - { - Polyline line; - line.append(Point(bed_bbox.min(0), y)); - line.append(Point(bed_bbox.max(0), y)); - axes_lines.push_back(line); - } - - // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped - Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON))); - - // append bed contours - Lines contour_lines = to_lines(poly); - std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); - - if (!m_gridlines.set_from_lines(gridlines, GROUND_Z)) - printf("Unable to create bed grid lines\n"); -} - -GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type(const Pointfs& shape) const -{ - EType type = Custom; - - auto bundle = wxGetApp().preset_bundle; - if (bundle != nullptr) - { - const Preset* curr = &bundle->printers.get_selected_preset(); - while (curr != nullptr) - { - if (curr->config.has("bed_shape")) - { - if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research") && (shape == dynamic_cast(curr->config.option("bed_shape"))->values)) - { - if (boost::contains(curr->name, "SL1")) - { - type = SL1; - break; - } - else if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5")) - { - type = MK3; - break; - } - else if (boost::contains(curr->name, "MK2")) - { - type = MK2; - break; - } - } - } - - curr = bundle->printers.get_preset_parent(*curr); - } - } - - return type; -} - -void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta, bool useVBOs) const -{ - std::string tex_path = resources_dir() + "/icons/bed/" + key; - - // use higher resolution images if graphic card allows - GLint max_tex_size; - ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); - - // temporary set to lowest resolution - max_tex_size = 2048; - - if (max_tex_size >= 8192) - tex_path += "_8192"; - else if (max_tex_size >= 4096) - tex_path += "_4096"; - - std::string model_path = resources_dir() + "/models/" + key; - -#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES - // use anisotropic filter if graphic card allows - GLfloat max_anisotropy = 0.0f; - if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) - ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); -#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES - - std::string filename = tex_path + "_top.png"; - if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) - { - if (!m_top_texture.load_from_file(filename, true)) - { - _render_custom(); - return; - } -#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES - if (max_anisotropy > 0.0f) - { - ::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id()); - ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); - ::glBindTexture(GL_TEXTURE_2D, 0); - } -#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES - } - - filename = tex_path + "_bottom.png"; - if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) - { - if (!m_bottom_texture.load_from_file(filename, true)) - { - _render_custom(); - return; - } -#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES - if (max_anisotropy > 0.0f) - { - ::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id()); - ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); - ::glBindTexture(GL_TEXTURE_2D, 0); - } -#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES - } - - if (theta <= 90.0f) - { - filename = model_path + "_bed.stl"; - if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) { - Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2)); - if (key == "mk2") - // hardcoded value to match the stl model - offset += Vec3d(0.0, 7.5, -0.03); - else if (key == "mk3") - // hardcoded value to match the stl model - offset += Vec3d(0.0, 5.5, 2.43); - else if (key == "sl1") - // hardcoded value to match the stl model - offset += Vec3d(0.0, 0.0, -0.03); - - m_model.center_around(offset); - } - - if (!m_model.get_filename().empty()) - { - ::glEnable(GL_LIGHTING); - m_model.render(); - ::glDisable(GL_LIGHTING); - } - } - - unsigned int triangles_vcount = m_triangles.get_vertices_count(); - if (triangles_vcount > 0) - { - ::glEnable(GL_DEPTH_TEST); - ::glDepthMask(GL_FALSE); - - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - ::glEnable(GL_TEXTURE_2D); - ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - if (theta > 90.0f) - ::glFrontFace(GL_CW); - - ::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id()); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()); - ::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords()); - ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount); - - if (theta > 90.0f) - ::glFrontFace(GL_CCW); - - ::glBindTexture(GL_TEXTURE_2D, 0); - ::glDisableClientState(GL_TEXTURE_COORD_ARRAY); - ::glDisableClientState(GL_VERTEX_ARRAY); - - ::glDisable(GL_TEXTURE_2D); - - ::glDisable(GL_BLEND); - ::glDepthMask(GL_TRUE); - } -} - -void GLCanvas3D::Bed::_render_custom() const -{ - m_top_texture.reset(); - m_bottom_texture.reset(); - - unsigned int triangles_vcount = m_triangles.get_vertices_count(); - if (triangles_vcount > 0) - { - ::glEnable(GL_LIGHTING); - ::glDisable(GL_DEPTH_TEST); - - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - ::glEnableClientState(GL_VERTEX_ARRAY); - - ::glColor4f(0.35f, 0.35f, 0.35f, 0.4f); - ::glNormal3d(0.0f, 0.0f, 1.0f); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()); - ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount); - - // draw grid - unsigned int gridlines_vcount = m_gridlines.get_vertices_count(); - - // we need depth test for grid, otherwise it would disappear when looking the object from below - ::glEnable(GL_DEPTH_TEST); - ::glLineWidth(3.0f * m_scale_factor); - ::glColor4f(0.2f, 0.2f, 0.2f, 0.4f); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices()); - ::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount); - - ::glDisableClientState(GL_VERTEX_ARRAY); - - ::glDisable(GL_BLEND); - ::glDisable(GL_LIGHTING); - } -} - -const double GLCanvas3D::Axes::Radius = 0.5; -const double GLCanvas3D::Axes::ArrowBaseRadius = 2.5 * GLCanvas3D::Axes::Radius; -const double GLCanvas3D::Axes::ArrowLength = 5.0; - -GLCanvas3D::Axes::Axes() - : origin(Vec3d::Zero()) - , length(Vec3d::Zero()) -{ - m_quadric = ::gluNewQuadric(); - if (m_quadric != nullptr) - ::gluQuadricDrawStyle(m_quadric, GLU_FILL); -} - -GLCanvas3D::Axes::~Axes() -{ - if (m_quadric != nullptr) - ::gluDeleteQuadric(m_quadric); -} - -void GLCanvas3D::Axes::render() const -{ - if (m_quadric == nullptr) - return; - - ::glEnable(GL_DEPTH_TEST); - ::glEnable(GL_LIGHTING); - - // x axis - ::glColor3f(1.0f, 0.0f, 0.0f); - ::glPushMatrix(); - ::glTranslated(origin(0), origin(1), origin(2)); - ::glRotated(90.0, 0.0, 1.0, 0.0); - render_axis(length(0)); - ::glPopMatrix(); - - // y axis - ::glColor3f(0.0f, 1.0f, 0.0f); - ::glPushMatrix(); - ::glTranslated(origin(0), origin(1), origin(2)); - ::glRotated(-90.0, 1.0, 0.0, 0.0); - render_axis(length(1)); - ::glPopMatrix(); - - // z axis - ::glColor3f(0.0f, 0.0f, 1.0f); - ::glPushMatrix(); - ::glTranslated(origin(0), origin(1), origin(2)); - render_axis(length(2)); - ::glPopMatrix(); - - ::glDisable(GL_LIGHTING); -} - -void GLCanvas3D::Axes::render_axis(double length) const -{ - ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); - ::gluCylinder(m_quadric, Radius, Radius, length, 32, 1); - ::gluQuadricOrientation(m_quadric, GLU_INSIDE); - ::gluDisk(m_quadric, 0.0, Radius, 32, 1); - ::glTranslated(0.0, 0.0, length); - ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); - ::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1); - ::gluQuadricOrientation(m_quadric, GLU_INSIDE); - ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); -} -#endif // !ENABLE_UNIQUE_BED - GLCanvas3D::Shader::Shader() : m_shader(nullptr) { @@ -3964,9 +3446,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); -#if ENABLE_UNIQUE_BED wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); -#endif // ENABLE_UNIQUE_BED GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) @@ -3975,9 +3455,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_retina_helper(nullptr) #endif , m_in_render(false) -#if ENABLE_UNIQUE_BED , m_bed(nullptr) -#endif // ENABLE_UNIQUE_BED , m_toolbar(GLToolbar::Normal) , m_view_toolbar(nullptr) , m_use_clipping_planes(false) @@ -4200,7 +3678,6 @@ void GLCanvas3D::set_model(Model* model) m_selection.set_model(m_model); } -#if ENABLE_UNIQUE_BED void GLCanvas3D::bed_shape_changed() { m_camera.set_scene_box(scene_bounding_box(), *this); @@ -4208,28 +3685,6 @@ void GLCanvas3D::bed_shape_changed() m_dirty = true; } -#else -void GLCanvas3D::set_bed_shape(const Pointfs& shape) -{ - bool new_shape = m_bed.set_shape(shape); - - if (new_shape) - { - // Set the origin and size for painting of the coordinate system axes. - m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); - set_bed_axes_length(0.1 * m_bed.get_bounding_box().max_size()); - m_camera.set_scene_box(scene_bounding_box(), *this); - m_requires_zoom_to_bed = true; - - m_dirty = true; - } -} - -void GLCanvas3D::set_bed_axes_length(double length) -{ - m_axes.length = length * Vec3d::Ones(); -} -#endif // ENABLE_UNIQUE_BED void GLCanvas3D::set_color_by(const std::string& value) { @@ -4255,12 +3710,9 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); -#if ENABLE_UNIQUE_BED if (m_bed != nullptr) bb.merge(m_bed->get_bounding_box()); -#else - bb.merge(m_bed.get_bounding_box()); -#endif // ENABLE_UNIQUE_BED + if (m_config != nullptr) { double h = m_config->opt_float("max_print_height"); @@ -4353,12 +3805,8 @@ bool GLCanvas3D::is_toolbar_item_pressed(const std::string& name) const void GLCanvas3D::zoom_to_bed() { -#if ENABLE_UNIQUE_BED if (m_bed != nullptr) _zoom_to_bounding_box(m_bed->get_bounding_box()); -#else - _zoom_to_bounding_box(m_bed.get_bounding_box()); -#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::zoom_to_volumes() @@ -4471,20 +3919,11 @@ void GLCanvas3D::render() if (!_set_current() || !_3DScene::init(m_canvas)) return; -#if ENABLE_UNIQUE_BED if ((m_bed != nullptr) && m_bed->get_shape().empty()) { // this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE)); } -#else - if (m_bed.get_shape().empty()) - { - // this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE - if (m_config != nullptr) - set_bed_shape(m_config->opt("bed_shape")->values); - } -#endif // ENABLE_UNIQUE_BED if (m_requires_zoom_to_bed) { @@ -4506,11 +3945,7 @@ void GLCanvas3D::render() // absolute value of the rotation theta = 360.f - theta; -#if ENABLE_UNIQUE_BED bool is_custom_bed = (m_bed == nullptr) || m_bed->is_custom(); -#else - bool is_custom_bed = m_bed.is_custom(); -#endif // ENABLE_UNIQUE_BED #if ENABLE_IMGUI wxGetApp().imgui()->new_frame(); @@ -6259,16 +5694,10 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) BoundingBoxf3 GLCanvas3D::_max_bounding_box() const { -#if ENABLE_UNIQUE_BED BoundingBoxf3 bb = volumes_bounding_box(); if (m_bed != nullptr) bb.merge(m_bed->get_bounding_box()); return bb; -#else - BoundingBoxf3 bb = m_bed.get_bounding_box(); - bb.merge(volumes_bounding_box()); - return bb; -#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) @@ -6472,22 +5901,14 @@ void GLCanvas3D::_render_bed(float theta) const scale_factor = m_retina_helper->get_scale_factor(); #endif -#if ENABLE_UNIQUE_BED if (m_bed != nullptr) m_bed->render(theta, m_use_VBOs, scale_factor); -#else - m_bed.render(theta, m_use_VBOs, scale_factor); -#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::_render_axes() const { -#if ENABLE_UNIQUE_BED if (m_bed != nullptr) m_bed->render_axes(); -#else - m_axes.render(); -#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::_render_objects() const @@ -6505,21 +5926,12 @@ void GLCanvas3D::_render_objects() const // Update the layer editing selection to the first object selected, update the current object maximum Z. const_cast(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1); -#if ENABLE_UNIQUE_BED if ((m_config != nullptr) && (m_bed != nullptr)) { const BoundingBoxf3& bed_bb = m_bed->get_bounding_box(); m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); m_volumes.check_outside_state(m_config, nullptr); } -#else - if (m_config != nullptr) - { - const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(); - m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); - m_volumes.check_outside_state(m_config, nullptr); - } -#endif // ENABLE_UNIQUE_BED } if (m_use_clipping_planes) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 680761b37..6ab681e0b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -8,9 +8,7 @@ #include "3DScene.hpp" #include "GLToolbar.hpp" #include "Event.hpp" -#if ENABLE_UNIQUE_BED #include "3DBed.hpp" -#endif // ENABLE_UNIQUE_BED #include @@ -28,11 +26,6 @@ class wxGLCanvas; // Support for Retina OpenGL on Mac OS #define ENABLE_RETINA_GL __APPLE__ -#if !ENABLE_UNIQUE_BED -class GLUquadric; -typedef class GLUquadric GLUquadricObj; -#endif // !ENABLE_UNIQUE_BED - namespace Slic3r { class GLShader; @@ -50,23 +43,6 @@ class GLGizmoBase; class RetinaHelper; #endif -#if !ENABLE_UNIQUE_BED -class GeometryBuffer -{ - std::vector m_vertices; - std::vector m_tex_coords; - -public: - bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords); - bool set_from_lines(const Lines& lines, float z); - - const float* get_vertices() const; - const float* get_tex_coords() const; - - unsigned int get_vertices_count() const; -}; -#endif // !ENABLE_UNIQUE_BED - class Size { int m_width; @@ -138,9 +114,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); -#if ENABLE_UNIQUE_BED wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); -#endif // ENABLE_UNIQUE_BED // this describes events being passed from GLCanvas3D to SlaSupport gizmo enum class SLAGizmoEventType { @@ -218,78 +192,6 @@ class GLCanvas3D void set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas); }; -#if !ENABLE_UNIQUE_BED - class Bed - { - public: - enum EType : unsigned char - { - MK2, - MK3, - SL1, - Custom, - Num_Types - }; - - private: - EType m_type; - Pointfs m_shape; - BoundingBoxf3 m_bounding_box; - Polygon m_polygon; - GeometryBuffer m_triangles; - GeometryBuffer m_gridlines; - mutable GLTexture m_top_texture; - mutable GLTexture m_bottom_texture; - mutable GLBed m_model; - - mutable float m_scale_factor; - - public: - Bed(); - - EType get_type() const { return m_type; } - - bool is_prusa() const; - bool is_custom() const; - - const Pointfs& get_shape() const; - // Return true if the bed shape changed, so the calee will update the UI. - bool set_shape(const Pointfs& shape); - - const BoundingBoxf3& get_bounding_box() const; - bool contains(const Point& point) const; - Point point_projection(const Point& point) const; - - void render(float theta, bool useVBOs, float scale_factor) const; - - private: - void _calc_bounding_box(); - void _calc_triangles(const ExPolygon& poly); - void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); - EType _detect_type(const Pointfs& shape) const; - void _render_prusa(const std::string &key, float theta, bool useVBOs) const; - void _render_custom() const; - }; - - struct Axes - { - static const double Radius; - static const double ArrowBaseRadius; - static const double ArrowLength; - Vec3d origin; - Vec3d length; - GLUquadricObj* m_quadric; - - Axes(); - ~Axes(); - - void render() const; - - private: - void render_axis(double length) const; - }; -#endif // !ENABLE_UNIQUE_BED - class Shader { GLShader* m_shader; @@ -885,12 +787,7 @@ private: WarningTexture m_warning_texture; wxTimer m_timer; Camera m_camera; -#if ENABLE_UNIQUE_BED Bed3D* m_bed; -#else - Bed m_bed; - Axes m_axes; -#endif // ENABLE_UNIQUE_BED LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; @@ -945,9 +842,7 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas; } const wxGLCanvas* get_wxglcanvas() const { return m_canvas; } -#if ENABLE_UNIQUE_BED void set_bed(Bed3D* bed) { m_bed = bed; } -#endif // ENABLE_UNIQUE_BED void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; } @@ -970,16 +865,7 @@ public: const Selection& get_selection() const { return m_selection; } Selection& get_selection() { return m_selection; } -#if ENABLE_UNIQUE_BED void bed_shape_changed(); -#else - // Set the bed shape to a single closed 2D polygon(array of two element arrays), - // triangulate the bed and store the triangles into m_bed.m_triangles, - // fills the m_bed.m_grid_lines and sets m_bed.m_origin. - // Sets m_bed.m_polygon to limit the object placement. - void set_bed_shape(const Pointfs& shape); - void set_bed_axes_length(double length); -#endif // ENABLE_UNIQUE_BED void set_clipping_plane(unsigned int id, const ClippingPlane& plane) { diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 0c3dc7025..75a6c8ca5 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -89,13 +89,11 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba return true; } -#if ENABLE_UNIQUE_BED void View3D::set_bed(Bed3D* bed) { if (m_canvas != nullptr) m_canvas->set_bed(bed); } -#endif // ENABLE_UNIQUE_BED void View3D::set_view_toolbar(GLToolbar* toolbar) { @@ -109,19 +107,11 @@ void View3D::set_as_dirty() m_canvas->set_as_dirty(); } -#if ENABLE_UNIQUE_BED void View3D::bed_shape_changed() { if (m_canvas != nullptr) m_canvas->bed_shape_changed(); } -#else -void View3D::set_bed_shape(const Pointfs& shape) -{ - if (m_canvas != nullptr) - m_canvas->set_bed_shape(shape); -} -#endif // ENABLE_UNIQUE_BED void View3D::select_view(const std::string& direction) { @@ -353,13 +343,11 @@ Preview::~Preview() } } -#if ENABLE_UNIQUE_BED void Preview::set_bed(Bed3D* bed) { if (m_canvas != nullptr) m_canvas->set_bed(bed); } -#endif // ENABLE_UNIQUE_BED void Preview::set_view_toolbar(GLToolbar* toolbar) { @@ -392,18 +380,11 @@ void Preview::set_enabled(bool enabled) m_enabled = enabled; } -#if ENABLE_UNIQUE_BED void Preview::bed_shape_changed() { if (m_canvas != nullptr) m_canvas->bed_shape_changed(); } -#else -vvoid Preview::set_bed_shape(const Pointfs& shape) -{ - m_canvas->set_bed_shape(shape); -} -#endif // ENABLE_UNIQUE_BED void Preview::select_view(const std::string& direction) { diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 3dec8bcbc..6cd67013c 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -27,9 +27,7 @@ namespace GUI { class GLCanvas3D; class GLToolbar; -#if ENABLE_UNIQUE_BED class Bed3D; -#endif // ENABLE_UNIQUE_BED class View3D : public wxPanel { @@ -51,17 +49,11 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } GLCanvas3D* get_canvas3d() { return m_canvas; } -#if ENABLE_UNIQUE_BED void set_bed(Bed3D* bed); -#endif // ENABLE_UNIQUE_BED void set_view_toolbar(GLToolbar* toolbar); void set_as_dirty(); -#if ENABLE_UNIQUE_BED void bed_shape_changed(); -#else - void set_bed_shape(const Pointfs& shape); -#endif // ENABLE_UNIQUE_BED void select_view(const std::string& direction); void select_all(); @@ -124,19 +116,13 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } GLCanvas3D* get_canvas3d() { return m_canvas; } -#if ENABLE_UNIQUE_BED void set_bed(Bed3D* bed); -#endif // ENABLE_UNIQUE_BED void set_view_toolbar(GLToolbar* toolbar); void set_number_extruders(unsigned int number_extruders); void set_canvas_as_dirty(); void set_enabled(bool enabled); -#if ENABLE_UNIQUE_BED void bed_shape_changed(); -#else - void set_bed_shape(const Pointfs& shape); -#endif // ENABLE_UNIQUE_BED void select_view(const std::string& direction); void set_viewport_from_scene(GLCanvas3D* canvas); void set_viewport_into_scene(GLCanvas3D* canvas); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3e26a667a..8eba9c44b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -49,9 +49,7 @@ #include "GLCanvas3D.hpp" #include "GLToolbar.hpp" #include "GUI_Preview.hpp" -#if ENABLE_UNIQUE_BED #include "3DBed.hpp" -#endif // ENABLE_UNIQUE_BED #include "Tab.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" @@ -955,9 +953,7 @@ struct Plater::priv wxPanel* current_panel; std::vector panels; Sidebar *sidebar; -#if ENABLE_UNIQUE_BED Bed3D bed; -#endif // ENABLE_UNIQUE_BED View3D* view3D; GLToolbar view_toolbar; Preview *preview; @@ -1062,13 +1058,11 @@ struct Plater::priv void update_object_menu(); -#if ENABLE_UNIQUE_BED // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, // fills the m_bed.m_grid_lines and sets m_bed.m_origin. // Sets m_bed.m_polygon to limit the object placement. void set_bed_shape(const Pointfs& shape); -#endif // ENABLE_UNIQUE_BED private: bool init_object_menu(); @@ -1145,10 +1139,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); preview->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); -#if ENABLE_UNIQUE_BED view3D->set_bed(&bed); preview->set_bed(&bed); -#endif // ENABLE_UNIQUE_BED panels.push_back(view3D); panels.push_back(preview); @@ -1206,16 +1198,12 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this); view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); -#if ENABLE_UNIQUE_BED view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option("bed_shape")->values); }); -#endif // ENABLE_UNIQUE_BED // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); -#if ENABLE_UNIQUE_BED preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option("bed_shape")->values); }); -#endif // ENABLE_UNIQUE_BED view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); @@ -2666,7 +2654,6 @@ bool Plater::priv::can_mirror() const return get_selection().is_from_single_instance(); } -#if ENABLE_UNIQUE_BED void Plater::priv::set_bed_shape(const Pointfs& shape) { bool new_shape = bed.set_shape(shape); @@ -2676,7 +2663,6 @@ void Plater::priv::set_bed_shape(const Pointfs& shape) if (preview) preview->bed_shape_changed(); } } -#endif // ENABLE_UNIQUE_BED void Plater::priv::update_object_menu() { @@ -3117,14 +3103,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } if (bed_shape_changed) - { -#if ENABLE_UNIQUE_BED p->set_bed_shape(p->config->option("bed_shape")->values); -#else - if (p->view3D) p->view3D->set_bed_shape(p->config->option("bed_shape")->values); - if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); -#endif // ENABLE_UNIQUE_BED - } if (update_scheduled) update(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index aff6fe404..c8c17bed8 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -10,10 +10,8 @@ #include "Preset.hpp" -#if ENABLE_UNIQUE_BED #include "3DScene.hpp" #include "GLTexture.hpp" -#endif // ENABLE_UNIQUE_BED class wxButton; class wxBoxSizer; From 0b0457186b8fb8cfd8dc809c5c0d80e2409f7569 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 20 Feb 2019 13:53:33 +0100 Subject: [PATCH 14/20] ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES set as default --- src/libslic3r/Technologies.hpp | 2 -- src/slic3r/GUI/3DBed.cpp | 8 ++------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 7eb6efecc..417c6e311 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -38,8 +38,6 @@ #define ENABLE_MOVE_MIN_THRESHOLD (1 && ENABLE_1_42_0_ALPHA4) // Modified initial default placement of generic subparts #define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4) -// Use anisotropic filtering on bed plate texture -#define ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES (1 && ENABLE_1_42_0_ALPHA4) // Bunch of fixes related to volumes centering #define ENABLE_VOLUMES_CENTERING_FIXES (1 && ENABLE_1_42_0_ALPHA4) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index ed41068f2..0e80bd542 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -367,12 +367,10 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons std::string model_path = resources_dir() + "/models/" + key; -#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES // use anisotropic filter if graphic card allows GLfloat max_anisotropy = 0.0f; if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); -#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES std::string filename = tex_path + "_top.png"; if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) @@ -382,14 +380,13 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons render_custom(); return; } -#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + if (max_anisotropy > 0.0f) { glsafe(::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id())); glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); } -#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES } filename = tex_path + "_bottom.png"; @@ -400,14 +397,13 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons render_custom(); return; } -#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + if (max_anisotropy > 0.0f) { glsafe(::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id())); glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); } -#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES } if (theta <= 90.0f) From 98a551587ce74b051f4ac6538763c3557801577f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 20 Feb 2019 15:13:03 +0100 Subject: [PATCH 15/20] Improved used material estimation (take instances too) --- src/libslic3r/SLAPrint.cpp | 98 +++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 34 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 21c922a16..6252fe188 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1059,6 +1059,24 @@ void SLAPrint::fill_statistics() const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR; const double display_area = width*height; + // get polygons for all instances in the object + auto get_all_polygons = [](const ExPolygons& input_polygons, const std::vector& instances) { + ExPolygons polygons; + const size_t inst_cnt = instances.size(); + + polygons.reserve(input_polygons.size()*inst_cnt); + for (const ExPolygon& polygon : input_polygons) { + for (size_t i = 0; i < inst_cnt; ++i) + { + ExPolygon tmp = polygon; + tmp.rotate(Geometry::rad2deg(instances[i].rotation)); + tmp.translate(instances[i].shift.x(), instances[i].shift.y()); + polygons.push_back(tmp); + } + } + return polygons; + }; + double supports_volume = 0.0; double models_volume = 0.0; @@ -1093,46 +1111,58 @@ void SLAPrint::fill_statistics() // Calculation of the consumed material - double layer_model_area = 0; - double layer_support_area = 0; + Polygons model_polygons; + Polygons supports_polygons; + for (SLAPrintObject * po : m_objects) { - const auto index = po->get_slice_index(); - if (index.find(layer.first) == index.end()) - continue; - - const SLAPrintObject::SliceRecord& record = index.at(layer.first); - - if (record.model_slices_idx != SLAPrintObject::SliceRecord::NONE && - record.support_slices_idx != SLAPrintObject::SliceRecord::NONE) - { - double model_area = 0; - for (const ExPolygon& polygon : po->get_model_slices().at(record.model_slices_idx)) - model_area += polygon.area(); - - layer_model_area += model_area; - - Polygons polygons = to_polygons(po->get_model_slices().at(record.model_slices_idx)); - append(polygons, to_polygons(po->get_support_slices().at(record.support_slices_idx))); - polygons = union_(polygons); - double poligons_area = 0; - for (const Polygon& polygon : polygons) - poligons_area += polygon.area(); - - if (poligons_area > model_area) - layer_support_area += (poligons_area-model_area); + const SLAPrintObject::SliceIndex& index = po->get_slice_index(); + auto key = layer.first; + if (index.find(layer.first) == index.end()) { + const SLAPrintObject::SliceIndex::const_iterator it_key = std::find_if(index.begin(), index.end(), + [key](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(key - id.first) < EPSILON; }); + if (it_key == index.end()) + continue; + key = it_key->first; } - else if (record.model_slices_idx != SLAPrintObject::SliceRecord::NONE) { - for (const ExPolygon& polygon : po->get_model_slices().at(record.model_slices_idx)) - layer_model_area += polygon.area(); + + const SLAPrintObject::SliceRecord& record = index.at(key); + + if (record.model_slices_idx != SLAPrintObject::SliceRecord::NONE) { + const ExPolygons& expolygons = po->get_model_slices().at(record.model_slices_idx); + const ExPolygons model_expolygons = get_all_polygons(expolygons, po->instances()); + + append(model_polygons, to_polygons(model_expolygons)); } - else if (record.support_slices_idx != SLAPrintObject::SliceRecord::NONE) { - for (const ExPolygon& polygon : po->get_support_slices().at(record.support_slices_idx)) - layer_support_area += polygon.area(); + + if (record.support_slices_idx != SLAPrintObject::SliceRecord::NONE) { + const ExPolygons& expolygons = po->get_support_slices().at(record.support_slices_idx); + const ExPolygons support_expolygons = get_all_polygons(expolygons, po->instances()); + + append(supports_polygons, to_polygons(support_expolygons)); } } - models_volume += layer_model_area * l_height; - supports_volume += layer_support_area * l_height; + + model_polygons = union_(model_polygons); + double layer_model_area = 0; + for (const Polygon& polygon : model_polygons) + layer_model_area += polygon.area(); + + if (layer_model_area != 0) + models_volume += layer_model_area * l_height; + + if (!supports_polygons.empty() && !model_polygons.empty()) + append(supports_polygons, model_polygons); + supports_polygons = union_(supports_polygons); + double layer_support_area = 0; + for (const Polygon& polygon : supports_polygons) + layer_support_area += polygon.area(); + + if (layer_support_area != 0) { + layer_support_area -= layer_model_area; + supports_volume += layer_support_area * l_height; + } + // Calculation of the slow and fast layers to the future controlling those values on FW From 11fc849b1a92dddfab6b463e4b3a01825bd5692c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 20 Feb 2019 15:23:23 +0100 Subject: [PATCH 16/20] Printbed textures generated from svg files --- resources/icons/bed/mk2.svg | 1 + resources/icons/bed/mk3.svg | 1 + resources/icons/bed/sl1.svg | 1 + resources/shaders/printbed.fs | 20 + resources/shaders/printbed.vs | 11 + src/libslic3r/Technologies.hpp | 8 + src/nanosvg/nanosvg.h | 2975 ++++++++++++++++++++++++++++++++ src/nanosvg/nanosvgrast.h | 1452 ++++++++++++++++ src/slic3r/GUI/3DBed.cpp | 289 +++- src/slic3r/GUI/3DBed.hpp | 46 +- src/slic3r/GUI/GLCanvas3D.cpp | 2 + src/slic3r/GUI/GLCanvas3D.hpp | 2 + src/slic3r/GUI/GLShader.cpp | 87 + src/slic3r/GUI/GLShader.hpp | 28 + src/slic3r/GUI/GLTexture.cpp | 214 ++- src/slic3r/GUI/GLTexture.hpp | 20 +- 16 files changed, 5124 insertions(+), 33 deletions(-) create mode 100644 resources/icons/bed/mk2.svg create mode 100644 resources/icons/bed/mk3.svg create mode 100644 resources/icons/bed/sl1.svg create mode 100644 resources/shaders/printbed.fs create mode 100644 resources/shaders/printbed.vs create mode 100644 src/nanosvg/nanosvg.h create mode 100644 src/nanosvg/nanosvgrast.h diff --git a/resources/icons/bed/mk2.svg b/resources/icons/bed/mk2.svg new file mode 100644 index 000000000..b8fa8d0cd --- /dev/null +++ b/resources/icons/bed/mk2.svg @@ -0,0 +1 @@ +MK2_bottom \ No newline at end of file diff --git a/resources/icons/bed/mk3.svg b/resources/icons/bed/mk3.svg new file mode 100644 index 000000000..c8f53373b --- /dev/null +++ b/resources/icons/bed/mk3.svg @@ -0,0 +1 @@ +MK3_bottom \ No newline at end of file diff --git a/resources/icons/bed/sl1.svg b/resources/icons/bed/sl1.svg new file mode 100644 index 000000000..c1098b9df --- /dev/null +++ b/resources/icons/bed/sl1.svg @@ -0,0 +1 @@ +SL1_Bottom \ No newline at end of file diff --git a/resources/shaders/printbed.fs b/resources/shaders/printbed.fs new file mode 100644 index 000000000..be14347c2 --- /dev/null +++ b/resources/shaders/printbed.fs @@ -0,0 +1,20 @@ +#version 110 + +const vec3 back_color_dark = vec3(0.235, 0.235, 0.235); +const vec3 back_color_light = vec3(0.365, 0.365, 0.365); + +uniform sampler2D texture; +uniform bool transparent_background; + +varying vec2 tex_coords; + +void main() +{ + // calculates radial gradient + vec3 back_color = vec3(mix(back_color_light, back_color_dark, smoothstep(0.0, 0.5, length(abs(tex_coords.xy) - vec2(0.5))))); + + vec4 fore_color = texture2D(texture, tex_coords); + + // blends foreground with background + gl_FragColor = vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0); +} \ No newline at end of file diff --git a/resources/shaders/printbed.vs b/resources/shaders/printbed.vs new file mode 100644 index 000000000..968bcce16 --- /dev/null +++ b/resources/shaders/printbed.vs @@ -0,0 +1,11 @@ +#version 110 + +attribute vec2 v_tex_coords; + +varying vec2 tex_coords; + +void main() +{ + gl_Position = ftransform(); + tex_coords = v_tex_coords; +} \ No newline at end of file diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 417c6e311..755ca5ffa 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -51,4 +51,12 @@ #define ENABLE_MODE_AWARE_TOOLBAR_ITEMS (1 && ENABLE_1_42_0_ALPHA5) +//==================== +// 1.42.0.alpha7 techs +//==================== +#define ENABLE_1_42_0_ALPHA7 1 + +// Printbed textures generated from svg files +#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7) + #endif // _technologies_h_ diff --git a/src/nanosvg/nanosvg.h b/src/nanosvg/nanosvg.h new file mode 100644 index 000000000..8c8b061cd --- /dev/null +++ b/src/nanosvg/nanosvg.h @@ -0,0 +1,2975 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example + * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) + * + * Arc calculation code based on canvg (https://code.google.com/p/canvg/) + * + * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html + * + */ + +#ifndef NANOSVG_H +#define NANOSVG_H + +#ifndef NANOSVG_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. +// +// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. +// +// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request! +// +// The shapes in the SVG images are transformed by the viewBox and converted to specified units. +// That is, you should get the same looking data as your designed in your favorite app. +// +// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose +// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. +// +// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. +// DPI (dots-per-inch) controls how the unit conversion is done. +// +// If you don't know or care about the units stuff, "px" and 96 should get you going. + + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + printf("size: %f x %f\n", image->width, image->height); + // Use... + for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { + for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { + for (int i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } + } + } + // Delete + nsvgDelete(image); +*/ + +enum NSVGpaintType { + NSVG_PAINT_NONE = 0, + NSVG_PAINT_COLOR = 1, + NSVG_PAINT_LINEAR_GRADIENT = 2, + NSVG_PAINT_RADIAL_GRADIENT = 3 +}; + +enum NSVGspreadType { + NSVG_SPREAD_PAD = 0, + NSVG_SPREAD_REFLECT = 1, + NSVG_SPREAD_REPEAT = 2 +}; + +enum NSVGlineJoin { + NSVG_JOIN_MITER = 0, + NSVG_JOIN_ROUND = 1, + NSVG_JOIN_BEVEL = 2 +}; + +enum NSVGlineCap { + NSVG_CAP_BUTT = 0, + NSVG_CAP_ROUND = 1, + NSVG_CAP_SQUARE = 2 +}; + +enum NSVGfillRule { + NSVG_FILLRULE_NONZERO = 0, + NSVG_FILLRULE_EVENODD = 1 +}; + +enum NSVGflags { + NSVG_FLAGS_VISIBLE = 0x01 +}; + +typedef struct NSVGgradientStop { + unsigned int color; + float offset; +} NSVGgradientStop; + +typedef struct NSVGgradient { + float xform[6]; + char spread; + float fx, fy; + int nstops; + NSVGgradientStop stops[1]; +} NSVGgradient; + +typedef struct NSVGpaint { + char type; + union { + unsigned int color; + NSVGgradient* gradient; + }; +} NSVGpaint; + +typedef struct NSVGpath +{ + float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... + int npts; // Total number of bezier points. + char closed; // Flag indicating if shapes should be treated as closed. + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + struct NSVGpath* next; // Pointer to next path, or NULL if last element. +} NSVGpath; + +typedef struct NSVGshape +{ + char id[64]; // Optional 'id' attr of the shape or its group + NSVGpaint fill; // Fill paint + NSVGpaint stroke; // Stroke paint + float opacity; // Opacity of the shape. + float strokeWidth; // Stroke width (scaled). + float strokeDashOffset; // Stroke dash offset (scaled). + float strokeDashArray[8]; // Stroke dash array (scaled). + char strokeDashCount; // Number of dash values in dash array. + char strokeLineJoin; // Stroke join type. + char strokeLineCap; // Stroke cap type. + float miterLimit; // Miter limit + char fillRule; // Fill rule, see NSVGfillRule. + unsigned char flags; // Logical or of NSVG_FLAGS_* flags + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + NSVGpath* paths; // Linked list of paths in the image. + struct NSVGshape* next; // Pointer to next shape, or NULL if last element. +} NSVGshape; + +typedef struct NSVGimage +{ + float width; // Width of the image. + float height; // Height of the image. + NSVGshape* shapes; // Linked list of shapes in the image. +} NSVGimage; + +// Parses SVG file from a file, returns SVG image as paths. +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); + +// Parses SVG file from a null terminated string, returns SVG image as paths. +// Important note: changes the string. +NSVGimage* nsvgParse(char* input, const char* units, float dpi); + +// Duplicates a path. +NSVGpath* nsvgDuplicatePath(NSVGpath* p); + +// Deletes an image. +void nsvgDelete(NSVGimage* image); + +#ifndef NANOSVG_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#endif // NANOSVG_H + +#ifdef NANOSVG_IMPLEMENTATION + +#include +#include +#include + +#define NSVG_PI (3.14159265358979323846264338327f) +#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. + +#define NSVG_ALIGN_MIN 0 +#define NSVG_ALIGN_MID 1 +#define NSVG_ALIGN_MAX 2 +#define NSVG_ALIGN_NONE 0 +#define NSVG_ALIGN_MEET 1 +#define NSVG_ALIGN_SLICE 2 + +#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) +#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) + +#ifdef _MSC_VER + #pragma warning (disable: 4996) // Switch off security warnings + #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings + #ifdef __cplusplus + #define NSVG_INLINE inline + #else + #define NSVG_INLINE + #endif +#else + #define NSVG_INLINE inline +#endif + + +static int nsvg__isspace(char c) +{ + return strchr(" \t\n\v\f\r", c) != 0; +} + +static int nsvg__isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int nsvg__isnum(char c) +{ + return strchr("0123456789+-.eE", c) != 0; +} + +static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } +static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } + + +// Simple XML parser + +#define NSVG_XML_TAG 1 +#define NSVG_XML_CONTENT 2 +#define NSVG_XML_MAX_ATTRIBS 256 + +static void nsvg__parseContent(char* s, + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + // Trim start white spaces + while (*s && nsvg__isspace(*s)) s++; + if (!*s) return; + + if (contentCb) + (*contentCb)(ud, s); +} + +static void nsvg__parseElement(char* s, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void* ud) +{ + const char* attr[NSVG_XML_MAX_ATTRIBS]; + int nattr = 0; + char* name; + int start = 0; + int end = 0; + char quote; + + // Skip white space after the '<' + while (*s && nsvg__isspace(*s)) s++; + + // Check if the tag is end tag + if (*s == '/') { + s++; + end = 1; + } else { + start = 1; + } + + // Skip comments, data and preprocessor stuff. + if (!*s || *s == '?' || *s == '!') + return; + + // Get tag name + name = s; + while (*s && !nsvg__isspace(*s)) s++; + if (*s) { *s++ = '\0'; } + + // Get attribs + while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { + char* name = NULL; + char* value = NULL; + + // Skip white space before the attrib name + while (*s && nsvg__isspace(*s)) s++; + if (!*s) break; + if (*s == '/') { + end = 1; + break; + } + name = s; + // Find end of the attrib name. + while (*s && !nsvg__isspace(*s) && *s != '=') s++; + if (*s) { *s++ = '\0'; } + // Skip until the beginning of the value. + while (*s && *s != '\"' && *s != '\'') s++; + if (!*s) break; + quote = *s; + s++; + // Store value and find the end of it. + value = s; + while (*s && *s != quote) s++; + if (*s) { *s++ = '\0'; } + + // Store only well formed attributes + if (name && value) { + attr[nattr++] = name; + attr[nattr++] = value; + } + } + + // List terminator + attr[nattr++] = 0; + attr[nattr++] = 0; + + // Call callbacks. + if (start && startelCb) + (*startelCb)(ud, name, attr); + if (end && endelCb) + (*endelCb)(ud, name); +} + +int nsvg__parseXML(char* input, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + char* s = input; + char* mark = s; + int state = NSVG_XML_CONTENT; + while (*s) { + if (*s == '<' && state == NSVG_XML_CONTENT) { + // Start of a tag + *s++ = '\0'; + nsvg__parseContent(mark, contentCb, ud); + mark = s; + state = NSVG_XML_TAG; + } else if (*s == '>' && state == NSVG_XML_TAG) { + // Start of a content or new tag. + *s++ = '\0'; + nsvg__parseElement(mark, startelCb, endelCb, ud); + mark = s; + state = NSVG_XML_CONTENT; + } else { + s++; + } + } + + return 1; +} + + +/* Simple SVG parser. */ + +#define NSVG_MAX_ATTR 128 + +enum NSVGgradientUnits { + NSVG_USER_SPACE = 0, + NSVG_OBJECT_SPACE = 1 +}; + +#define NSVG_MAX_DASHES 8 + +enum NSVGunits { + NSVG_UNITS_USER, + NSVG_UNITS_PX, + NSVG_UNITS_PT, + NSVG_UNITS_PC, + NSVG_UNITS_MM, + NSVG_UNITS_CM, + NSVG_UNITS_IN, + NSVG_UNITS_PERCENT, + NSVG_UNITS_EM, + NSVG_UNITS_EX +}; + +typedef struct NSVGcoordinate { + float value; + int units; +} NSVGcoordinate; + +typedef struct NSVGlinearData { + NSVGcoordinate x1, y1, x2, y2; +} NSVGlinearData; + +typedef struct NSVGradialData { + NSVGcoordinate cx, cy, r, fx, fy; +} NSVGradialData; + +typedef struct NSVGgradientData +{ + char id[64]; + char ref[64]; + char type; + union { + NSVGlinearData linear; + NSVGradialData radial; + }; + char spread; + char units; + float xform[6]; + int nstops; + NSVGgradientStop* stops; + struct NSVGgradientData* next; +} NSVGgradientData; + +typedef struct NSVGattrib +{ + char id[64]; + float xform[6]; + unsigned int fillColor; + unsigned int strokeColor; + float opacity; + float fillOpacity; + float strokeOpacity; + char fillGradient[64]; + char strokeGradient[64]; + float strokeWidth; + float strokeDashOffset; + float strokeDashArray[NSVG_MAX_DASHES]; + int strokeDashCount; + char strokeLineJoin; + char strokeLineCap; + float miterLimit; + char fillRule; + float fontSize; + unsigned int stopColor; + float stopOpacity; + float stopOffset; + char hasFill; + char hasStroke; + char visible; +} NSVGattrib; + +typedef struct NSVGparser +{ + NSVGattrib attr[NSVG_MAX_ATTR]; + int attrHead; + float* pts; + int npts; + int cpts; + NSVGpath* plist; + NSVGimage* image; + NSVGgradientData* gradients; + NSVGshape* shapesTail; + float viewMinx, viewMiny, viewWidth, viewHeight; + int alignX, alignY, alignType; + float dpi; + char pathFlag; + char defsFlag; +} NSVGparser; + +static void nsvg__xformIdentity(float* t) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetTranslation(float* t, float tx, float ty) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = tx; t[5] = ty; +} + +static void nsvg__xformSetScale(float* t, float sx, float sy) +{ + t[0] = sx; t[1] = 0.0f; + t[2] = 0.0f; t[3] = sy; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewX(float* t, float a) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = tanf(a); t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewY(float* t, float a) +{ + t[0] = 1.0f; t[1] = tanf(a); + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetRotation(float* t, float a) +{ + float cs = cosf(a), sn = sinf(a); + t[0] = cs; t[1] = sn; + t[2] = -sn; t[3] = cs; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformMultiply(float* t, float* s) +{ + float t0 = t[0] * s[0] + t[1] * s[2]; + float t2 = t[2] * s[0] + t[3] * s[2]; + float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; + t[1] = t[0] * s[1] + t[1] * s[3]; + t[3] = t[2] * s[1] + t[3] * s[3]; + t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; + t[0] = t0; + t[2] = t2; + t[4] = t4; +} + +static void nsvg__xformInverse(float* inv, float* t) +{ + double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; + if (det > -1e-6 && det < 1e-6) { + nsvg__xformIdentity(t); + return; + } + invdet = 1.0 / det; + inv[0] = (float)(t[3] * invdet); + inv[2] = (float)(-t[2] * invdet); + inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); + inv[1] = (float)(-t[1] * invdet); + inv[3] = (float)(t[0] * invdet); + inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); +} + +static void nsvg__xformPremultiply(float* t, float* s) +{ + float s2[6]; + memcpy(s2, s, sizeof(float)*6); + nsvg__xformMultiply(s2, t); + memcpy(t, s2, sizeof(float)*6); +} + +static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2] + t[4]; + *dy = x*t[1] + y*t[3] + t[5]; +} + +static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2]; + *dy = x*t[1] + y*t[3]; +} + +#define NSVG_EPSILON (1e-12) + +static int nsvg__ptInBounds(float* pt, float* bounds) +{ + return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; +} + + +static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) +{ + double it = 1.0-t; + return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; +} + +static void nsvg__curveBounds(float* bounds, float* curve) +{ + int i, j, count; + double roots[2], a, b, c, b2ac, t, v; + float* v0 = &curve[0]; + float* v1 = &curve[2]; + float* v2 = &curve[4]; + float* v3 = &curve[6]; + + // Start the bounding box by end points + bounds[0] = nsvg__minf(v0[0], v3[0]); + bounds[1] = nsvg__minf(v0[1], v3[1]); + bounds[2] = nsvg__maxf(v0[0], v3[0]); + bounds[3] = nsvg__maxf(v0[1], v3[1]); + + // Bezier curve fits inside the convex hull of it's control points. + // If control points are inside the bounds, we're done. + if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) + return; + + // Add bezier curve inflection points in X and Y. + for (i = 0; i < 2; i++) { + a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; + b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; + c = 3.0 * v1[i] - 3.0 * v0[i]; + count = 0; + if (fabs(a) < NSVG_EPSILON) { + if (fabs(b) > NSVG_EPSILON) { + t = -c / b; + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } else { + b2ac = b*b - 4.0*c*a; + if (b2ac > NSVG_EPSILON) { + t = (-b + sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + t = (-b - sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } + for (j = 0; j < count; j++) { + v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); + bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); + bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); + } + } +} + +static NSVGparser* nsvg__createParser() +{ + NSVGparser* p; + p = (NSVGparser*)malloc(sizeof(NSVGparser)); + if (p == NULL) goto error; + memset(p, 0, sizeof(NSVGparser)); + + p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); + if (p->image == NULL) goto error; + memset(p->image, 0, sizeof(NSVGimage)); + + // Init style + nsvg__xformIdentity(p->attr[0].xform); + memset(p->attr[0].id, 0, sizeof p->attr[0].id); + p->attr[0].fillColor = NSVG_RGB(0,0,0); + p->attr[0].strokeColor = NSVG_RGB(0,0,0); + p->attr[0].opacity = 1; + p->attr[0].fillOpacity = 1; + p->attr[0].strokeOpacity = 1; + p->attr[0].stopOpacity = 1; + p->attr[0].strokeWidth = 1; + p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; + p->attr[0].strokeLineCap = NSVG_CAP_BUTT; + p->attr[0].miterLimit = 4; + p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; + p->attr[0].hasFill = 1; + p->attr[0].visible = 1; + + return p; + +error: + if (p) { + if (p->image) free(p->image); + free(p); + } + return NULL; +} + +static void nsvg__deletePaths(NSVGpath* path) +{ + while (path) { + NSVGpath *next = path->next; + if (path->pts != NULL) + free(path->pts); + free(path); + path = next; + } +} + +static void nsvg__deletePaint(NSVGpaint* paint) +{ + if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) + free(paint->gradient); +} + +static void nsvg__deleteGradientData(NSVGgradientData* grad) +{ + NSVGgradientData* next; + while (grad != NULL) { + next = grad->next; + free(grad->stops); + free(grad); + grad = next; + } +} + +static void nsvg__deleteParser(NSVGparser* p) +{ + if (p != NULL) { + nsvg__deletePaths(p->plist); + nsvg__deleteGradientData(p->gradients); + nsvgDelete(p->image); + free(p->pts); + free(p); + } +} + +static void nsvg__resetPath(NSVGparser* p) +{ + p->npts = 0; +} + +static void nsvg__addPoint(NSVGparser* p, float x, float y) +{ + if (p->npts+1 > p->cpts) { + p->cpts = p->cpts ? p->cpts*2 : 8; + p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); + if (!p->pts) return; + } + p->pts[p->npts*2+0] = x; + p->pts[p->npts*2+1] = y; + p->npts++; +} + +static void nsvg__moveTo(NSVGparser* p, float x, float y) +{ + if (p->npts > 0) { + p->pts[(p->npts-1)*2+0] = x; + p->pts[(p->npts-1)*2+1] = y; + } else { + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__lineTo(NSVGparser* p, float x, float y) +{ + float px,py, dx,dy; + if (p->npts > 0) { + px = p->pts[(p->npts-1)*2+0]; + py = p->pts[(p->npts-1)*2+1]; + dx = x - px; + dy = y - py; + nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); + nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) +{ + nsvg__addPoint(p, cpx1, cpy1); + nsvg__addPoint(p, cpx2, cpy2); + nsvg__addPoint(p, x, y); +} + +static NSVGattrib* nsvg__getAttr(NSVGparser* p) +{ + return &p->attr[p->attrHead]; +} + +static void nsvg__pushAttr(NSVGparser* p) +{ + if (p->attrHead < NSVG_MAX_ATTR-1) { + p->attrHead++; + memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); + } +} + +static void nsvg__popAttr(NSVGparser* p) +{ + if (p->attrHead > 0) + p->attrHead--; +} + +static float nsvg__actualOrigX(NSVGparser* p) +{ + return p->viewMinx; +} + +static float nsvg__actualOrigY(NSVGparser* p) +{ + return p->viewMiny; +} + +static float nsvg__actualWidth(NSVGparser* p) +{ + return p->viewWidth; +} + +static float nsvg__actualHeight(NSVGparser* p) +{ + return p->viewHeight; +} + +static float nsvg__actualLength(NSVGparser* p) +{ + float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); + return sqrtf(w*w + h*h) / sqrtf(2.0f); +} + +static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) +{ + NSVGattrib* attr = nsvg__getAttr(p); + switch (c.units) { + case NSVG_UNITS_USER: return c.value; + case NSVG_UNITS_PX: return c.value; + case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; + case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; + case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; + case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; + case NSVG_UNITS_IN: return c.value * p->dpi; + case NSVG_UNITS_EM: return c.value * attr->fontSize; + case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. + case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; + default: return c.value; + } + return c.value; +} + +static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) +{ + NSVGgradientData* grad = p->gradients; + while (grad) { + if (strcmp(grad->id, id) == 0) + return grad; + grad = grad->next; + } + return NULL; +} + +static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGgradientData* data = NULL; + NSVGgradientData* ref = NULL; + NSVGgradientStop* stops = NULL; + NSVGgradient* grad; + float ox, oy, sw, sh, sl; + int nstops = 0; + + data = nsvg__findGradientData(p, id); + if (data == NULL) return NULL; + + // TODO: use ref to fill in all unset values too. + ref = data; + while (ref != NULL) { + if (stops == NULL && ref->stops != NULL) { + stops = ref->stops; + nstops = ref->nstops; + break; + } + ref = nsvg__findGradientData(p, ref->ref); + } + if (stops == NULL) return NULL; + + grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); + if (grad == NULL) return NULL; + + // The shape width and height. + if (data->units == NSVG_OBJECT_SPACE) { + ox = localBounds[0]; + oy = localBounds[1]; + sw = localBounds[2] - localBounds[0]; + sh = localBounds[3] - localBounds[1]; + } else { + ox = nsvg__actualOrigX(p); + oy = nsvg__actualOrigY(p); + sw = nsvg__actualWidth(p); + sh = nsvg__actualHeight(p); + } + sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); + + if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { + float x1, y1, x2, y2, dx, dy; + x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); + y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); + x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); + y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); + // Calculate transform aligned to the line + dx = x2 - x1; + dy = y2 - y1; + grad->xform[0] = dy; grad->xform[1] = -dx; + grad->xform[2] = dx; grad->xform[3] = dy; + grad->xform[4] = x1; grad->xform[5] = y1; + } else { + float cx, cy, fx, fy, r; + cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); + cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); + fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); + fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); + r = nsvg__convertToPixels(p, data->radial.r, 0, sl); + // Calculate transform aligned to the circle + grad->xform[0] = r; grad->xform[1] = 0; + grad->xform[2] = 0; grad->xform[3] = r; + grad->xform[4] = cx; grad->xform[5] = cy; + grad->fx = fx / r; + grad->fy = fy / r; + } + + nsvg__xformMultiply(grad->xform, data->xform); + nsvg__xformMultiply(grad->xform, attr->xform); + + grad->spread = data->spread; + memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); + grad->nstops = nstops; + + *paintType = data->type; + + return grad; +} + +static float nsvg__getAverageScale(float* t) +{ + float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); + float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); + return (sx + sy) * 0.5f; +} + +static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) +{ + NSVGpath* path; + float curve[4*2], curveBounds[4]; + int i, first = 1; + for (path = shape->paths; path != NULL; path = path->next) { + nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); + for (i = 0; i < path->npts-1; i += 3) { + nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); + nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); + nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); + nsvg__curveBounds(curveBounds, curve); + if (first) { + bounds[0] = curveBounds[0]; + bounds[1] = curveBounds[1]; + bounds[2] = curveBounds[2]; + bounds[3] = curveBounds[3]; + first = 0; + } else { + bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); + bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); + bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); + bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); + } + curve[0] = curve[6]; + curve[1] = curve[7]; + } + } +} + +static void nsvg__addShape(NSVGparser* p) +{ + NSVGattrib* attr = nsvg__getAttr(p); + float scale = 1.0f; + NSVGshape* shape; + NSVGpath* path; + int i; + + if (p->plist == NULL) + return; + + shape = (NSVGshape*)malloc(sizeof(NSVGshape)); + if (shape == NULL) goto error; + memset(shape, 0, sizeof(NSVGshape)); + + memcpy(shape->id, attr->id, sizeof shape->id); + scale = nsvg__getAverageScale(attr->xform); + shape->strokeWidth = attr->strokeWidth * scale; + shape->strokeDashOffset = attr->strokeDashOffset * scale; + shape->strokeDashCount = (char)attr->strokeDashCount; + for (i = 0; i < attr->strokeDashCount; i++) + shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; + shape->strokeLineJoin = attr->strokeLineJoin; + shape->strokeLineCap = attr->strokeLineCap; + shape->miterLimit = attr->miterLimit; + shape->fillRule = attr->fillRule; + shape->opacity = attr->opacity; + + shape->paths = p->plist; + p->plist = NULL; + + // Calculate shape bounds + shape->bounds[0] = shape->paths->bounds[0]; + shape->bounds[1] = shape->paths->bounds[1]; + shape->bounds[2] = shape->paths->bounds[2]; + shape->bounds[3] = shape->paths->bounds[3]; + for (path = shape->paths->next; path != NULL; path = path->next) { + shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); + shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); + shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); + shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); + } + + // Set fill + if (attr->hasFill == 0) { + shape->fill.type = NSVG_PAINT_NONE; + } else if (attr->hasFill == 1) { + shape->fill.type = NSVG_PAINT_COLOR; + shape->fill.color = attr->fillColor; + shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; + } else if (attr->hasFill == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type); + if (shape->fill.gradient == NULL) { + shape->fill.type = NSVG_PAINT_NONE; + } + } + + // Set stroke + if (attr->hasStroke == 0) { + shape->stroke.type = NSVG_PAINT_NONE; + } else if (attr->hasStroke == 1) { + shape->stroke.type = NSVG_PAINT_COLOR; + shape->stroke.color = attr->strokeColor; + shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; + } else if (attr->hasStroke == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type); + if (shape->stroke.gradient == NULL) + shape->stroke.type = NSVG_PAINT_NONE; + } + + // Set flags + shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); + + // Add to tail + if (p->image->shapes == NULL) + p->image->shapes = shape; + else + p->shapesTail->next = shape; + p->shapesTail = shape; + + return; + +error: + if (shape) free(shape); +} + +static void nsvg__addPath(NSVGparser* p, char closed) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGpath* path = NULL; + float bounds[4]; + float* curve; + int i; + + if (p->npts < 4) + return; + + if (closed) + nsvg__lineTo(p, p->pts[0], p->pts[1]); + + path = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (path == NULL) goto error; + memset(path, 0, sizeof(NSVGpath)); + + path->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (path->pts == NULL) goto error; + path->closed = closed; + path->npts = p->npts; + + // Transform path. + for (i = 0; i < p->npts; ++i) + nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); + + // Find bounds + for (i = 0; i < path->npts-1; i += 3) { + curve = &path->pts[i*2]; + nsvg__curveBounds(bounds, curve); + if (i == 0) { + path->bounds[0] = bounds[0]; + path->bounds[1] = bounds[1]; + path->bounds[2] = bounds[2]; + path->bounds[3] = bounds[3]; + } else { + path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); + path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); + path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); + path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); + } + } + + path->next = p->plist; + p->plist = path; + + return; + +error: + if (path != NULL) { + if (path->pts != NULL) free(path->pts); + free(path); + } +} + +// We roll our own string to float because the std library one uses locale and messes things up. +static double nsvg__atof(const char* s) +{ + char* cur = (char*)s; + char* end = NULL; + double res = 0.0, sign = 1.0; + long long intPart = 0, fracPart = 0; + char hasIntPart = 0, hasFracPart = 0; + + // Parse optional sign + if (*cur == '+') { + cur++; + } else if (*cur == '-') { + sign = -1; + cur++; + } + + // Parse integer part + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + intPart = (double)strtoll(cur, &end, 10); + if (cur != end) { + res = (double)intPart; + hasIntPart = 1; + cur = end; + } + } + + // Parse fractional part. + if (*cur == '.') { + cur++; // Skip '.' + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + fracPart = strtoll(cur, &end, 10); + if (cur != end) { + res += (double)fracPart / pow(10.0, (double)(end - cur)); + hasFracPart = 1; + cur = end; + } + } + } + + // A valid number should have integer or fractional part. + if (!hasIntPart && !hasFracPart) + return 0.0; + + // Parse optional exponent + if (*cur == 'e' || *cur == 'E') { + int expPart = 0; + cur++; // skip 'E' + expPart = strtol(cur, &end, 10); // Parse digit sequence with sign + if (cur != end) { + res *= pow(10.0, (double)expPart); + } + } + + return res * sign; +} + + +static const char* nsvg__parseNumber(const char* s, char* it, const int size) +{ + const int last = size-1; + int i = 0; + + // sign + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + // integer part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + if (*s == '.') { + // decimal point + if (i < last) it[i++] = *s; + s++; + // fraction part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + // exponent + if (*s == 'e' || *s == 'E') { + if (i < last) it[i++] = *s; + s++; + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + it[i] = '\0'; + + return s; +} + +static const char* nsvg__getNextPathItem(const char* s, char* it) +{ + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + if (!*s) return s; + if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { + s = nsvg__parseNumber(s, it, 64); + } else { + // Parse command + it[0] = *s++; + it[1] = '\0'; + return s; + } + + return s; +} + +static unsigned int nsvg__parseColorHex(const char* str) +{ + unsigned int c = 0, r = 0, g = 0, b = 0; + int n = 0; + str++; // skip # + // Calculate number of characters. + while(str[n] && !nsvg__isspace(str[n])) + n++; + if (n == 6) { + sscanf(str, "%x", &c); + } else if (n == 3) { + sscanf(str, "%x", &c); + c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8); + c |= c<<4; + } + r = (c >> 16) & 0xff; + g = (c >> 8) & 0xff; + b = c & 0xff; + return NSVG_RGB(r,g,b); +} + +static unsigned int nsvg__parseColorRGB(const char* str) +{ + int r = -1, g = -1, b = -1; + char s1[32]="", s2[32]=""; + sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b); + if (strchr(s1, '%')) { + return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100); + } else { + return NSVG_RGB(r,g,b); + } +} + +typedef struct NSVGNamedColor { + const char* name; + unsigned int color; +} NSVGNamedColor; + +NSVGNamedColor nsvg__colors[] = { + + { "red", NSVG_RGB(255, 0, 0) }, + { "green", NSVG_RGB( 0, 128, 0) }, + { "blue", NSVG_RGB( 0, 0, 255) }, + { "yellow", NSVG_RGB(255, 255, 0) }, + { "cyan", NSVG_RGB( 0, 255, 255) }, + { "magenta", NSVG_RGB(255, 0, 255) }, + { "black", NSVG_RGB( 0, 0, 0) }, + { "grey", NSVG_RGB(128, 128, 128) }, + { "gray", NSVG_RGB(128, 128, 128) }, + { "white", NSVG_RGB(255, 255, 255) }, + +#ifdef NANOSVG_ALL_COLOR_KEYWORDS + { "aliceblue", NSVG_RGB(240, 248, 255) }, + { "antiquewhite", NSVG_RGB(250, 235, 215) }, + { "aqua", NSVG_RGB( 0, 255, 255) }, + { "aquamarine", NSVG_RGB(127, 255, 212) }, + { "azure", NSVG_RGB(240, 255, 255) }, + { "beige", NSVG_RGB(245, 245, 220) }, + { "bisque", NSVG_RGB(255, 228, 196) }, + { "blanchedalmond", NSVG_RGB(255, 235, 205) }, + { "blueviolet", NSVG_RGB(138, 43, 226) }, + { "brown", NSVG_RGB(165, 42, 42) }, + { "burlywood", NSVG_RGB(222, 184, 135) }, + { "cadetblue", NSVG_RGB( 95, 158, 160) }, + { "chartreuse", NSVG_RGB(127, 255, 0) }, + { "chocolate", NSVG_RGB(210, 105, 30) }, + { "coral", NSVG_RGB(255, 127, 80) }, + { "cornflowerblue", NSVG_RGB(100, 149, 237) }, + { "cornsilk", NSVG_RGB(255, 248, 220) }, + { "crimson", NSVG_RGB(220, 20, 60) }, + { "darkblue", NSVG_RGB( 0, 0, 139) }, + { "darkcyan", NSVG_RGB( 0, 139, 139) }, + { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, + { "darkgray", NSVG_RGB(169, 169, 169) }, + { "darkgreen", NSVG_RGB( 0, 100, 0) }, + { "darkgrey", NSVG_RGB(169, 169, 169) }, + { "darkkhaki", NSVG_RGB(189, 183, 107) }, + { "darkmagenta", NSVG_RGB(139, 0, 139) }, + { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, + { "darkorange", NSVG_RGB(255, 140, 0) }, + { "darkorchid", NSVG_RGB(153, 50, 204) }, + { "darkred", NSVG_RGB(139, 0, 0) }, + { "darksalmon", NSVG_RGB(233, 150, 122) }, + { "darkseagreen", NSVG_RGB(143, 188, 143) }, + { "darkslateblue", NSVG_RGB( 72, 61, 139) }, + { "darkslategray", NSVG_RGB( 47, 79, 79) }, + { "darkslategrey", NSVG_RGB( 47, 79, 79) }, + { "darkturquoise", NSVG_RGB( 0, 206, 209) }, + { "darkviolet", NSVG_RGB(148, 0, 211) }, + { "deeppink", NSVG_RGB(255, 20, 147) }, + { "deepskyblue", NSVG_RGB( 0, 191, 255) }, + { "dimgray", NSVG_RGB(105, 105, 105) }, + { "dimgrey", NSVG_RGB(105, 105, 105) }, + { "dodgerblue", NSVG_RGB( 30, 144, 255) }, + { "firebrick", NSVG_RGB(178, 34, 34) }, + { "floralwhite", NSVG_RGB(255, 250, 240) }, + { "forestgreen", NSVG_RGB( 34, 139, 34) }, + { "fuchsia", NSVG_RGB(255, 0, 255) }, + { "gainsboro", NSVG_RGB(220, 220, 220) }, + { "ghostwhite", NSVG_RGB(248, 248, 255) }, + { "gold", NSVG_RGB(255, 215, 0) }, + { "goldenrod", NSVG_RGB(218, 165, 32) }, + { "greenyellow", NSVG_RGB(173, 255, 47) }, + { "honeydew", NSVG_RGB(240, 255, 240) }, + { "hotpink", NSVG_RGB(255, 105, 180) }, + { "indianred", NSVG_RGB(205, 92, 92) }, + { "indigo", NSVG_RGB( 75, 0, 130) }, + { "ivory", NSVG_RGB(255, 255, 240) }, + { "khaki", NSVG_RGB(240, 230, 140) }, + { "lavender", NSVG_RGB(230, 230, 250) }, + { "lavenderblush", NSVG_RGB(255, 240, 245) }, + { "lawngreen", NSVG_RGB(124, 252, 0) }, + { "lemonchiffon", NSVG_RGB(255, 250, 205) }, + { "lightblue", NSVG_RGB(173, 216, 230) }, + { "lightcoral", NSVG_RGB(240, 128, 128) }, + { "lightcyan", NSVG_RGB(224, 255, 255) }, + { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, + { "lightgray", NSVG_RGB(211, 211, 211) }, + { "lightgreen", NSVG_RGB(144, 238, 144) }, + { "lightgrey", NSVG_RGB(211, 211, 211) }, + { "lightpink", NSVG_RGB(255, 182, 193) }, + { "lightsalmon", NSVG_RGB(255, 160, 122) }, + { "lightseagreen", NSVG_RGB( 32, 178, 170) }, + { "lightskyblue", NSVG_RGB(135, 206, 250) }, + { "lightslategray", NSVG_RGB(119, 136, 153) }, + { "lightslategrey", NSVG_RGB(119, 136, 153) }, + { "lightsteelblue", NSVG_RGB(176, 196, 222) }, + { "lightyellow", NSVG_RGB(255, 255, 224) }, + { "lime", NSVG_RGB( 0, 255, 0) }, + { "limegreen", NSVG_RGB( 50, 205, 50) }, + { "linen", NSVG_RGB(250, 240, 230) }, + { "maroon", NSVG_RGB(128, 0, 0) }, + { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, + { "mediumblue", NSVG_RGB( 0, 0, 205) }, + { "mediumorchid", NSVG_RGB(186, 85, 211) }, + { "mediumpurple", NSVG_RGB(147, 112, 219) }, + { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, + { "mediumslateblue", NSVG_RGB(123, 104, 238) }, + { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, + { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, + { "mediumvioletred", NSVG_RGB(199, 21, 133) }, + { "midnightblue", NSVG_RGB( 25, 25, 112) }, + { "mintcream", NSVG_RGB(245, 255, 250) }, + { "mistyrose", NSVG_RGB(255, 228, 225) }, + { "moccasin", NSVG_RGB(255, 228, 181) }, + { "navajowhite", NSVG_RGB(255, 222, 173) }, + { "navy", NSVG_RGB( 0, 0, 128) }, + { "oldlace", NSVG_RGB(253, 245, 230) }, + { "olive", NSVG_RGB(128, 128, 0) }, + { "olivedrab", NSVG_RGB(107, 142, 35) }, + { "orange", NSVG_RGB(255, 165, 0) }, + { "orangered", NSVG_RGB(255, 69, 0) }, + { "orchid", NSVG_RGB(218, 112, 214) }, + { "palegoldenrod", NSVG_RGB(238, 232, 170) }, + { "palegreen", NSVG_RGB(152, 251, 152) }, + { "paleturquoise", NSVG_RGB(175, 238, 238) }, + { "palevioletred", NSVG_RGB(219, 112, 147) }, + { "papayawhip", NSVG_RGB(255, 239, 213) }, + { "peachpuff", NSVG_RGB(255, 218, 185) }, + { "peru", NSVG_RGB(205, 133, 63) }, + { "pink", NSVG_RGB(255, 192, 203) }, + { "plum", NSVG_RGB(221, 160, 221) }, + { "powderblue", NSVG_RGB(176, 224, 230) }, + { "purple", NSVG_RGB(128, 0, 128) }, + { "rosybrown", NSVG_RGB(188, 143, 143) }, + { "royalblue", NSVG_RGB( 65, 105, 225) }, + { "saddlebrown", NSVG_RGB(139, 69, 19) }, + { "salmon", NSVG_RGB(250, 128, 114) }, + { "sandybrown", NSVG_RGB(244, 164, 96) }, + { "seagreen", NSVG_RGB( 46, 139, 87) }, + { "seashell", NSVG_RGB(255, 245, 238) }, + { "sienna", NSVG_RGB(160, 82, 45) }, + { "silver", NSVG_RGB(192, 192, 192) }, + { "skyblue", NSVG_RGB(135, 206, 235) }, + { "slateblue", NSVG_RGB(106, 90, 205) }, + { "slategray", NSVG_RGB(112, 128, 144) }, + { "slategrey", NSVG_RGB(112, 128, 144) }, + { "snow", NSVG_RGB(255, 250, 250) }, + { "springgreen", NSVG_RGB( 0, 255, 127) }, + { "steelblue", NSVG_RGB( 70, 130, 180) }, + { "tan", NSVG_RGB(210, 180, 140) }, + { "teal", NSVG_RGB( 0, 128, 128) }, + { "thistle", NSVG_RGB(216, 191, 216) }, + { "tomato", NSVG_RGB(255, 99, 71) }, + { "turquoise", NSVG_RGB( 64, 224, 208) }, + { "violet", NSVG_RGB(238, 130, 238) }, + { "wheat", NSVG_RGB(245, 222, 179) }, + { "whitesmoke", NSVG_RGB(245, 245, 245) }, + { "yellowgreen", NSVG_RGB(154, 205, 50) }, +#endif +}; + +static unsigned int nsvg__parseColorName(const char* str) +{ + int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); + + for (i = 0; i < ncolors; i++) { + if (strcmp(nsvg__colors[i].name, str) == 0) { + return nsvg__colors[i].color; + } + } + + return NSVG_RGB(128, 128, 128); +} + +static unsigned int nsvg__parseColor(const char* str) +{ + size_t len = 0; + while(*str == ' ') ++str; + len = strlen(str); + if (len >= 1 && *str == '#') + return nsvg__parseColorHex(str); + else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') + return nsvg__parseColorRGB(str); + return nsvg__parseColorName(str); +} + +static float nsvg__parseOpacity(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + if (val > 1.0f) val = 1.0f; + return val; +} + +static float nsvg__parseMiterLimit(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + return val; +} + +static int nsvg__parseUnits(const char* units) +{ + if (units[0] == 'p' && units[1] == 'x') + return NSVG_UNITS_PX; + else if (units[0] == 'p' && units[1] == 't') + return NSVG_UNITS_PT; + else if (units[0] == 'p' && units[1] == 'c') + return NSVG_UNITS_PC; + else if (units[0] == 'm' && units[1] == 'm') + return NSVG_UNITS_MM; + else if (units[0] == 'c' && units[1] == 'm') + return NSVG_UNITS_CM; + else if (units[0] == 'i' && units[1] == 'n') + return NSVG_UNITS_IN; + else if (units[0] == '%') + return NSVG_UNITS_PERCENT; + else if (units[0] == 'e' && units[1] == 'm') + return NSVG_UNITS_EM; + else if (units[0] == 'e' && units[1] == 'x') + return NSVG_UNITS_EX; + return NSVG_UNITS_USER; +} + +static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) +{ + NSVGcoordinate coord = {0, NSVG_UNITS_USER}; + char buf[64]; + coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); + coord.value = nsvg__atof(buf); + return coord; +} + +static NSVGcoordinate nsvg__coord(float v, int units) +{ + NSVGcoordinate coord = {v, units}; + return coord; +} + +static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) +{ + NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); + return nsvg__convertToPixels(p, coord, orig, length); +} + +static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) +{ + const char* end; + const char* ptr; + char it[64]; + + *na = 0; + ptr = str; + while (*ptr && *ptr != '(') ++ptr; + if (*ptr == 0) + return 1; + end = ptr; + while (*end && *end != ')') ++end; + if (*end == 0) + return 1; + + while (ptr < end) { + if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { + if (*na >= maxNa) return 0; + ptr = nsvg__parseNumber(ptr, it, 64); + args[(*na)++] = (float)nsvg__atof(it); + } else { + ++ptr; + } + } + return (int)(end - str); +} + + +static int nsvg__parseMatrix(float* xform, const char* str) +{ + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, t, 6, &na); + if (na != 6) return len; + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseTranslate(float* xform, const char* str) +{ + float args[2]; + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = 0.0; + + nsvg__xformSetTranslation(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseScale(float* xform, const char* str) +{ + float args[2]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = args[0]; + nsvg__xformSetScale(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewX(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewY(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseRotate(float* xform, const char* str) +{ + float args[3]; + int na = 0; + float m[6]; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 3, &na); + if (na == 1) + args[1] = args[2] = 0.0f; + nsvg__xformIdentity(m); + + if (na > 1) { + nsvg__xformSetTranslation(t, -args[1], -args[2]); + nsvg__xformMultiply(m, t); + } + + nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); + nsvg__xformMultiply(m, t); + + if (na > 1) { + nsvg__xformSetTranslation(t, args[1], args[2]); + nsvg__xformMultiply(m, t); + } + + memcpy(xform, m, sizeof(float)*6); + + return len; +} + +static void nsvg__parseTransform(float* xform, const char* str) +{ + float t[6]; + nsvg__xformIdentity(xform); + while (*str) + { + if (strncmp(str, "matrix", 6) == 0) + str += nsvg__parseMatrix(t, str); + else if (strncmp(str, "translate", 9) == 0) + str += nsvg__parseTranslate(t, str); + else if (strncmp(str, "scale", 5) == 0) + str += nsvg__parseScale(t, str); + else if (strncmp(str, "rotate", 6) == 0) + str += nsvg__parseRotate(t, str); + else if (strncmp(str, "skewX", 5) == 0) + str += nsvg__parseSkewX(t, str); + else if (strncmp(str, "skewY", 5) == 0) + str += nsvg__parseSkewY(t, str); + else{ + ++str; + continue; + } + + nsvg__xformPremultiply(xform, t); + } +} + +static void nsvg__parseUrl(char* id, const char* str) +{ + int i = 0; + str += 4; // "url("; + if (*str == '#') + str++; + while (i < 63 && *str != ')') { + id[i] = *str++; + i++; + } + id[i] = '\0'; +} + +static char nsvg__parseLineCap(const char* str) +{ + if (strcmp(str, "butt") == 0) + return NSVG_CAP_BUTT; + else if (strcmp(str, "round") == 0) + return NSVG_CAP_ROUND; + else if (strcmp(str, "square") == 0) + return NSVG_CAP_SQUARE; + // TODO: handle inherit. + return NSVG_CAP_BUTT; +} + +static char nsvg__parseLineJoin(const char* str) +{ + if (strcmp(str, "miter") == 0) + return NSVG_JOIN_MITER; + else if (strcmp(str, "round") == 0) + return NSVG_JOIN_ROUND; + else if (strcmp(str, "bevel") == 0) + return NSVG_JOIN_BEVEL; + // TODO: handle inherit. + return NSVG_JOIN_MITER; +} + +static char nsvg__parseFillRule(const char* str) +{ + if (strcmp(str, "nonzero") == 0) + return NSVG_FILLRULE_NONZERO; + else if (strcmp(str, "evenodd") == 0) + return NSVG_FILLRULE_EVENODD; + // TODO: handle inherit. + return NSVG_FILLRULE_NONZERO; +} + +static const char* nsvg__getNextDashItem(const char* s, char* it) +{ + int n = 0; + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + // Advance until whitespace, comma or end. + while (*s && (!nsvg__isspace(*s) && *s != ',')) { + if (n < 63) + it[n++] = *s; + s++; + } + it[n++] = '\0'; + return s; +} + +static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) +{ + char item[64]; + int count = 0, i; + float sum = 0.0f; + + // Handle "none" + if (str[0] == 'n') + return 0; + + // Parse dashes + while (*str) { + str = nsvg__getNextDashItem(str, item); + if (!*item) break; + if (count < NSVG_MAX_DASHES) + strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); + } + + for (i = 0; i < count; i++) + sum += strokeDashArray[i]; + if (sum <= 1e-6f) + count = 0; + + return count; +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str); + +static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) +{ + float xform[6]; + NSVGattrib* attr = nsvg__getAttr(p); + if (!attr) return 0; + + if (strcmp(name, "style") == 0) { + nsvg__parseStyle(p, value); + } else if (strcmp(name, "display") == 0) { + if (strcmp(value, "none") == 0) + attr->visible = 0; + // Don't reset ->visible on display:inline, one display:none hides the whole subtree + + } else if (strcmp(name, "fill") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasFill = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasFill = 2; + nsvg__parseUrl(attr->fillGradient, value); + } else { + attr->hasFill = 1; + attr->fillColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "opacity") == 0) { + attr->opacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "fill-opacity") == 0) { + attr->fillOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasStroke = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasStroke = 2; + nsvg__parseUrl(attr->strokeGradient, value); + } else { + attr->hasStroke = 1; + attr->strokeColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "stroke-width") == 0) { + attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-dasharray") == 0) { + attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); + } else if (strcmp(name, "stroke-dashoffset") == 0) { + attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-opacity") == 0) { + attr->strokeOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke-linecap") == 0) { + attr->strokeLineCap = nsvg__parseLineCap(value); + } else if (strcmp(name, "stroke-linejoin") == 0) { + attr->strokeLineJoin = nsvg__parseLineJoin(value); + } else if (strcmp(name, "stroke-miterlimit") == 0) { + attr->miterLimit = nsvg__parseMiterLimit(value); + } else if (strcmp(name, "fill-rule") == 0) { + attr->fillRule = nsvg__parseFillRule(value); + } else if (strcmp(name, "font-size") == 0) { + attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "transform") == 0) { + nsvg__parseTransform(xform, value); + nsvg__xformPremultiply(attr->xform, xform); + } else if (strcmp(name, "stop-color") == 0) { + attr->stopColor = nsvg__parseColor(value); + } else if (strcmp(name, "stop-opacity") == 0) { + attr->stopOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "offset") == 0) { + attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); + } else if (strcmp(name, "id") == 0) { + strncpy(attr->id, value, 63); + attr->id[63] = '\0'; + } else { + return 0; + } + return 1; +} + +static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) +{ + const char* str; + const char* val; + char name[512]; + char value[512]; + int n; + + str = start; + while (str < end && *str != ':') ++str; + + val = str; + + // Right Trim + while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; + ++str; + + n = (int)(str - start); + if (n > 511) n = 511; + if (n) memcpy(name, start, n); + name[n] = 0; + + while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; + + n = (int)(end - val); + if (n > 511) n = 511; + if (n) memcpy(value, val, n); + value[n] = 0; + + return nsvg__parseAttr(p, name, value); +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str) +{ + const char* start; + const char* end; + + while (*str) { + // Left Trim + while(*str && nsvg__isspace(*str)) ++str; + start = str; + while(*str && *str != ';') ++str; + end = str; + + // Right Trim + while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; + ++end; + + nsvg__parseNameValue(p, start, end); + if (*str) ++str; + } +} + +static void nsvg__parseAttribs(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) + { + if (strcmp(attr[i], "style") == 0) + nsvg__parseStyle(p, attr[i + 1]); + else + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } +} + +static int nsvg__getArgsPerElement(char cmd) +{ + switch (cmd) { + case 'v': + case 'V': + case 'h': + case 'H': + return 1; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + return 2; + case 'q': + case 'Q': + case 's': + case 'S': + return 4; + case 'c': + case 'C': + return 6; + case 'a': + case 'A': + return 7; + } + return 0; +} + +static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__moveTo(p, *cpx, *cpy); +} + +static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpx += args[0]; + else + *cpx = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpy += args[0]; + else + *cpy = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x2, y2, cx1, cy1, cx2, cy2; + + if (rel) { + cx1 = *cpx + args[0]; + cy1 = *cpy + args[1]; + cx2 = *cpx + args[2]; + cy2 = *cpy + args[3]; + x2 = *cpx + args[4]; + y2 = *cpy + args[5]; + } else { + cx1 = args[0]; + cy1 = args[1]; + cx2 = args[2]; + cy2 = args[3]; + x2 = args[4]; + y2 = args[5]; + } + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx2 = *cpx + args[0]; + cy2 = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx2 = args[0]; + cy2 = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + cx1 = 2*x1 - *cpx2; + cy1 = 2*y1 - *cpy2; + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx = *cpx + args[0]; + cy = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx = args[0]; + cy = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + // Convert to cubic bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + x2 = *cpx + args[0]; + y2 = *cpy + args[1]; + } else { + x2 = args[0]; + y2 = args[1]; + } + + cx = 2*x1 - *cpx2; + cy = 2*y1 - *cpy2; + + // Convert to cubix bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static float nsvg__sqr(float x) { return x*x; } +static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } + +static float nsvg__vecrat(float ux, float uy, float vx, float vy) +{ + return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); +} + +static float nsvg__vecang(float ux, float uy, float vx, float vy) +{ + float r = nsvg__vecrat(ux,uy, vx,vy); + if (r < -1.0f) r = -1.0f; + if (r > 1.0f) r = 1.0f; + return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); +} + +static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + // Ported from canvg (https://code.google.com/p/canvg/) + float rx, ry, rotx; + float x1, y1, x2, y2, cx, cy, dx, dy, d; + float x1p, y1p, cxp, cyp, s, sa, sb; + float ux, uy, vx, vy, a1, da; + float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; + float sinrx, cosrx; + int fa, fs; + int i, ndivs; + float hda, kappa; + + rx = fabsf(args[0]); // y radius + ry = fabsf(args[1]); // x radius + rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle + fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc + fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction + x1 = *cpx; // start point + y1 = *cpy; + if (rel) { // end point + x2 = *cpx + args[5]; + y2 = *cpy + args[6]; + } else { + x2 = args[5]; + y2 = args[6]; + } + + dx = x1 - x2; + dy = y1 - y2; + d = sqrtf(dx*dx + dy*dy); + if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { + // The arc degenerates to a line + nsvg__lineTo(p, x2, y2); + *cpx = x2; + *cpy = y2; + return; + } + + sinrx = sinf(rotx); + cosrx = cosf(rotx); + + // Convert to center point parameterization. + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + // 1) Compute x1', y1' + x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; + y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; + d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); + if (d > 1) { + d = sqrtf(d); + rx *= d; + ry *= d; + } + // 2) Compute cx', cy' + s = 0.0f; + sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); + sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); + if (sa < 0.0f) sa = 0.0f; + if (sb > 0.0f) + s = sqrtf(sa / sb); + if (fa == fs) + s = -s; + cxp = s * rx * y1p / ry; + cyp = s * -ry * x1p / rx; + + // 3) Compute cx,cy from cx',cy' + cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; + cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; + + // 4) Calculate theta1, and delta theta. + ux = (x1p - cxp) / rx; + uy = (y1p - cyp) / ry; + vx = (-x1p - cxp) / rx; + vy = (-y1p - cyp) / ry; + a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle + da = nsvg__vecang(ux,uy, vx,vy); // Delta angle + +// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; +// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; + + if (fs == 0 && da > 0) + da -= 2 * NSVG_PI; + else if (fs == 1 && da < 0) + da += 2 * NSVG_PI; + + // Approximate the arc using cubic spline segments. + t[0] = cosrx; t[1] = sinrx; + t[2] = -sinrx; t[3] = cosrx; + t[4] = cx; t[5] = cy; + + // Split arc into max 90 degree segments. + // The loop assumes an iteration per end point (including start and end), this +1. + ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); + hda = (da / (float)ndivs) / 2.0f; + kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda)); + if (da < 0.0f) + kappa = -kappa; + + for (i = 0; i <= ndivs; i++) { + a = a1 + da * ((float)i/(float)ndivs); + dx = cosf(a); + dy = sinf(a); + nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position + nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent + if (i > 0) + nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); + px = x; + py = y; + ptanx = tanx; + ptany = tany; + } + + *cpx = x2; + *cpy = y2; +} + +static void nsvg__parsePath(NSVGparser* p, const char** attr) +{ + const char* s = NULL; + char cmd = '\0'; + float args[10]; + int nargs; + int rargs = 0; + float cpx, cpy, cpx2, cpy2; + const char* tmp[4]; + char closedFlag; + int i; + char item[64]; + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "d") == 0) { + s = attr[i + 1]; + } else { + tmp[0] = attr[i]; + tmp[1] = attr[i + 1]; + tmp[2] = 0; + tmp[3] = 0; + nsvg__parseAttribs(p, tmp); + } + } + + if (s) { + nsvg__resetPath(p); + cpx = 0; cpy = 0; + cpx2 = 0; cpy2 = 0; + closedFlag = 0; + nargs = 0; + + while (*s) { + s = nsvg__getNextPathItem(s, item); + if (!*item) break; + if (nsvg__isnum(item[0])) { + if (nargs < 10) + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= rargs) { + switch (cmd) { + case 'm': + case 'M': + nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); + // Moveto can be followed by multiple coordinate pairs, + // which should be treated as linetos. + cmd = (cmd == 'm') ? 'l' : 'L'; + rargs = nsvg__getArgsPerElement(cmd); + cpx2 = cpx; cpy2 = cpy; + break; + case 'l': + case 'L': + nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'H': + case 'h': + nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'V': + case 'v': + nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'C': + case 'c': + nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); + break; + case 'S': + case 's': + nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); + break; + case 'Q': + case 'q': + nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); + break; + case 'T': + case 't': + nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); + break; + case 'A': + case 'a': + nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + default: + if (nargs >= 2) { + cpx = args[nargs-2]; + cpy = args[nargs-1]; + cpx2 = cpx; cpy2 = cpy; + } + break; + } + nargs = 0; + } + } else { + cmd = item[0]; + rargs = nsvg__getArgsPerElement(cmd); + if (cmd == 'M' || cmd == 'm') { + // Commit path. + if (p->npts > 0) + nsvg__addPath(p, closedFlag); + // Start new subpath. + nsvg__resetPath(p); + closedFlag = 0; + nargs = 0; + } else if (cmd == 'Z' || cmd == 'z') { + closedFlag = 1; + // Commit path. + if (p->npts > 0) { + // Move current point to first point + cpx = p->pts[0]; + cpy = p->pts[1]; + cpx2 = cpx; cpy2 = cpy; + nsvg__addPath(p, closedFlag); + } + // Start new subpath. + nsvg__resetPath(p); + nsvg__moveTo(p, cpx, cpy); + closedFlag = 0; + nargs = 0; + } + } + } + // Commit path. + if (p->npts) + nsvg__addPath(p, closedFlag); + } + + nsvg__addShape(p); +} + +static void nsvg__parseRect(NSVGparser* p, const char** attr) +{ + float x = 0.0f; + float y = 0.0f; + float w = 0.0f; + float h = 0.0f; + float rx = -1.0f; // marks not set + float ry = -1.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); + if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx < 0.0f && ry > 0.0f) rx = ry; + if (ry < 0.0f && rx > 0.0f) ry = rx; + if (rx < 0.0f) rx = 0.0f; + if (ry < 0.0f) ry = 0.0f; + if (rx > w/2.0f) rx = w/2.0f; + if (ry > h/2.0f) ry = h/2.0f; + + if (w != 0.0f && h != 0.0f) { + nsvg__resetPath(p); + + if (rx < 0.00001f || ry < 0.0001f) { + nsvg__moveTo(p, x, y); + nsvg__lineTo(p, x+w, y); + nsvg__lineTo(p, x+w, y+h); + nsvg__lineTo(p, x, y+h); + } else { + // Rounded rectangle + nsvg__moveTo(p, x+rx, y); + nsvg__lineTo(p, x+w-rx, y); + nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); + nsvg__lineTo(p, x+w, y+h-ry); + nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); + nsvg__lineTo(p, x+rx, y+h); + nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); + nsvg__lineTo(p, x, y+ry); + nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); + } + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseCircle(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float r = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); + } + } + + if (r > 0.0f) { + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+r, cy); + nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); + nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); + nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); + nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseEllipse(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float rx = 0.0f; + float ry = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx > 0.0f && ry > 0.0f) { + + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+rx, cy); + nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); + nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); + nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); + nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseLine(NSVGparser* p, const char** attr) +{ + float x1 = 0.0; + float y1 = 0.0; + float x2 = 0.0; + float y2 = 0.0; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + } + } + + nsvg__resetPath(p); + + nsvg__moveTo(p, x1, y1); + nsvg__lineTo(p, x2, y2); + + nsvg__addPath(p, 0); + + nsvg__addShape(p); +} + +static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) +{ + int i; + const char* s; + float args[2]; + int nargs, npts = 0; + char item[64]; + + nsvg__resetPath(p); + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "points") == 0) { + s = attr[i + 1]; + nargs = 0; + while (*s) { + s = nsvg__getNextPathItem(s, item); + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= 2) { + if (npts == 0) + nsvg__moveTo(p, args[0], args[1]); + else + nsvg__lineTo(p, args[0], args[1]); + nargs = 0; + npts++; + } + } + } + } + } + + nsvg__addPath(p, (char)closeFlag); + + nsvg__addShape(p); +} + +static void nsvg__parseSVG(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "width") == 0) { + p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "height") == 0) { + p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "viewBox") == 0) { + const char *s = attr[i + 1]; + char buf[64]; + s = nsvg__parseNumber(s, buf, 64); + p->viewMinx = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewMiny = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewWidth = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewHeight = nsvg__atof(buf); + } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { + if (strstr(attr[i + 1], "none") != 0) { + // No uniform scaling + p->alignType = NSVG_ALIGN_NONE; + } else { + // Parse X align + if (strstr(attr[i + 1], "xMin") != 0) + p->alignX = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "xMid") != 0) + p->alignX = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "xMax") != 0) + p->alignX = NSVG_ALIGN_MAX; + // Parse X align + if (strstr(attr[i + 1], "yMin") != 0) + p->alignY = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "yMid") != 0) + p->alignY = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "yMax") != 0) + p->alignY = NSVG_ALIGN_MAX; + // Parse meet/slice + p->alignType = NSVG_ALIGN_MEET; + if (strstr(attr[i + 1], "slice") != 0) + p->alignType = NSVG_ALIGN_SLICE; + } + } + } + } +} + +static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) +{ + int i; + NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); + if (grad == NULL) return; + memset(grad, 0, sizeof(NSVGgradientData)); + grad->units = NSVG_OBJECT_SPACE; + grad->type = type; + if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { + grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); + grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { + grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + } + + nsvg__xformIdentity(grad->xform); + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "id") == 0) { + strncpy(grad->id, attr[i+1], 63); + grad->id[63] = '\0'; + } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "gradientUnits") == 0) { + if (strcmp(attr[i+1], "objectBoundingBox") == 0) + grad->units = NSVG_OBJECT_SPACE; + else + grad->units = NSVG_USER_SPACE; + } else if (strcmp(attr[i], "gradientTransform") == 0) { + nsvg__parseTransform(grad->xform, attr[i + 1]); + } else if (strcmp(attr[i], "cx") == 0) { + grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "cy") == 0) { + grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "r") == 0) { + grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fx") == 0) { + grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fy") == 0) { + grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x1") == 0) { + grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y1") == 0) { + grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x2") == 0) { + grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y2") == 0) { + grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "spreadMethod") == 0) { + if (strcmp(attr[i+1], "pad") == 0) + grad->spread = NSVG_SPREAD_PAD; + else if (strcmp(attr[i+1], "reflect") == 0) + grad->spread = NSVG_SPREAD_REFLECT; + else if (strcmp(attr[i+1], "repeat") == 0) + grad->spread = NSVG_SPREAD_REPEAT; + } else if (strcmp(attr[i], "xlink:href") == 0) { + const char *href = attr[i+1]; + strncpy(grad->ref, href+1, 62); + grad->ref[62] = '\0'; + } + } + } + + grad->next = p->gradients; + p->gradients = grad; +} + +static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) +{ + NSVGattrib* curAttr = nsvg__getAttr(p); + NSVGgradientData* grad; + NSVGgradientStop* stop; + int i, idx; + + curAttr->stopOffset = 0; + curAttr->stopColor = 0; + curAttr->stopOpacity = 1.0f; + + for (i = 0; attr[i]; i += 2) { + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } + + // Add stop to the last gradient. + grad = p->gradients; + if (grad == NULL) return; + + grad->nstops++; + grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); + if (grad->stops == NULL) return; + + // Insert + idx = grad->nstops-1; + for (i = 0; i < grad->nstops-1; i++) { + if (curAttr->stopOffset < grad->stops[i].offset) { + idx = i; + break; + } + } + if (idx != grad->nstops-1) { + for (i = grad->nstops-1; i > idx; i--) + grad->stops[i] = grad->stops[i-1]; + } + + stop = &grad->stops[idx]; + stop->color = curAttr->stopColor; + stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; + stop->offset = curAttr->stopOffset; +} + +static void nsvg__startElement(void* ud, const char* el, const char** attr) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (p->defsFlag) { + // Skip everything but gradients in defs + if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } + return; + } + + if (strcmp(el, "g") == 0) { + nsvg__pushAttr(p); + nsvg__parseAttribs(p, attr); + } else if (strcmp(el, "path") == 0) { + if (p->pathFlag) // Do not allow nested paths. + return; + nsvg__pushAttr(p); + nsvg__parsePath(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "rect") == 0) { + nsvg__pushAttr(p); + nsvg__parseRect(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "circle") == 0) { + nsvg__pushAttr(p); + nsvg__parseCircle(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "ellipse") == 0) { + nsvg__pushAttr(p); + nsvg__parseEllipse(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "line") == 0) { + nsvg__pushAttr(p); + nsvg__parseLine(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "polyline") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 0); + nsvg__popAttr(p); + } else if (strcmp(el, "polygon") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 1); + nsvg__popAttr(p); + } else if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 1; + } else if (strcmp(el, "svg") == 0) { + nsvg__parseSVG(p, attr); + } +} + +static void nsvg__endElement(void* ud, const char* el) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (strcmp(el, "g") == 0) { + nsvg__popAttr(p); + } else if (strcmp(el, "path") == 0) { + p->pathFlag = 0; + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 0; + } +} + +static void nsvg__content(void* ud, const char* s) +{ + NSVG_NOTUSED(ud); + NSVG_NOTUSED(s); + // empty +} + +static void nsvg__imageBounds(NSVGparser* p, float* bounds) +{ + NSVGshape* shape; + shape = p->image->shapes; + if (shape == NULL) { + bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; + return; + } + bounds[0] = shape->bounds[0]; + bounds[1] = shape->bounds[1]; + bounds[2] = shape->bounds[2]; + bounds[3] = shape->bounds[3]; + for (shape = shape->next; shape != NULL; shape = shape->next) { + bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); + bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); + bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); + bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); + } +} + +static float nsvg__viewAlign(float content, float container, int type) +{ + if (type == NSVG_ALIGN_MIN) + return 0; + else if (type == NSVG_ALIGN_MAX) + return container - content; + // mid + return (container - content) * 0.5f; +} + +static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) +{ + float t[6]; + nsvg__xformSetTranslation(t, tx, ty); + nsvg__xformMultiply (grad->xform, t); + + nsvg__xformSetScale(t, sx, sy); + nsvg__xformMultiply (grad->xform, t); +} + +static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) +{ + NSVGshape* shape; + NSVGpath* path; + float tx, ty, sx, sy, us, bounds[4], t[6], avgs; + int i; + float* pt; + + // Guess image size if not set completely. + nsvg__imageBounds(p, bounds); + + if (p->viewWidth == 0) { + if (p->image->width > 0) { + p->viewWidth = p->image->width; + } else { + p->viewMinx = bounds[0]; + p->viewWidth = bounds[2] - bounds[0]; + } + } + if (p->viewHeight == 0) { + if (p->image->height > 0) { + p->viewHeight = p->image->height; + } else { + p->viewMiny = bounds[1]; + p->viewHeight = bounds[3] - bounds[1]; + } + } + if (p->image->width == 0) + p->image->width = p->viewWidth; + if (p->image->height == 0) + p->image->height = p->viewHeight; + + tx = -p->viewMinx; + ty = -p->viewMiny; + sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; + sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; + // Unit scaling + us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); + + // Fix aspect ratio + if (p->alignType == NSVG_ALIGN_MEET) { + // fit whole image into viewbox + sx = sy = nsvg__minf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } else if (p->alignType == NSVG_ALIGN_SLICE) { + // fill whole viewbox with image + sx = sy = nsvg__maxf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } + + // Transform + sx *= us; + sy *= us; + avgs = (sx+sy) / 2.0f; + for (shape = p->image->shapes; shape != NULL; shape = shape->next) { + shape->bounds[0] = (shape->bounds[0] + tx) * sx; + shape->bounds[1] = (shape->bounds[1] + ty) * sy; + shape->bounds[2] = (shape->bounds[2] + tx) * sx; + shape->bounds[3] = (shape->bounds[3] + ty) * sy; + for (path = shape->paths; path != NULL; path = path->next) { + path->bounds[0] = (path->bounds[0] + tx) * sx; + path->bounds[1] = (path->bounds[1] + ty) * sy; + path->bounds[2] = (path->bounds[2] + tx) * sx; + path->bounds[3] = (path->bounds[3] + ty) * sy; + for (i =0; i < path->npts; i++) { + pt = &path->pts[i*2]; + pt[0] = (pt[0] + tx) * sx; + pt[1] = (pt[1] + ty) * sy; + } + } + + if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); + memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->fill.gradient->xform, t); + } + if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); + memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->stroke.gradient->xform, t); + } + + shape->strokeWidth *= avgs; + shape->strokeDashOffset *= avgs; + for (i = 0; i < shape->strokeDashCount; i++) + shape->strokeDashArray[i] *= avgs; + } +} + +NSVGimage* nsvgParse(char* input, const char* units, float dpi) +{ + NSVGparser* p; + NSVGimage* ret = 0; + + p = nsvg__createParser(); + if (p == NULL) { + return NULL; + } + p->dpi = dpi; + + nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); + + // Scale to viewBox + nsvg__scaleToViewbox(p, units); + + ret = p->image; + p->image = NULL; + + nsvg__deleteParser(p); + + return ret; +} + +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) +{ + FILE* fp = NULL; + size_t size; + char* data = NULL; + NSVGimage* image = NULL; + + fp = fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size+1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + image = nsvgParse(data, units, dpi); + free(data); + + return image; + +error: + if (fp) fclose(fp); + if (data) free(data); + if (image) nsvgDelete(image); + return NULL; +} + +NSVGpath* nsvgDuplicatePath(NSVGpath* p) +{ + NSVGpath* res = NULL; + + if (p == NULL) + return NULL; + + res = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (res == NULL) goto error; + memset(res, 0, sizeof(NSVGpath)); + + res->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (res->pts == NULL) goto error; + memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); + res->npts = p->npts; + + memcpy(res->bounds, p->bounds, sizeof(p->bounds)); + + res->closed = p->closed; + + return res; + +error: + if (res != NULL) { + free(res->pts); + free(res); + } + return NULL; +} + +void nsvgDelete(NSVGimage* image) +{ + NSVGshape *snext, *shape; + if (image == NULL) return; + shape = image->shapes; + while (shape != NULL) { + snext = shape->next; + nsvg__deletePaths(shape->paths); + nsvg__deletePaint(&shape->fill); + nsvg__deletePaint(&shape->stroke); + free(shape); + shape = snext; + } + free(image); +} + +#endif diff --git a/src/nanosvg/nanosvgrast.h b/src/nanosvg/nanosvgrast.h new file mode 100644 index 000000000..b740c316c --- /dev/null +++ b/src/nanosvg/nanosvgrast.h @@ -0,0 +1,1452 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The polygon rasterization is heavily based on stb_truetype rasterizer + * by Sean Barrett - http://nothings.org/ + * + */ + +#ifndef NANOSVGRAST_H +#define NANOSVGRAST_H + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +typedef struct NSVGrasterizer NSVGrasterizer; + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + + // Create rasterizer (can be used to render multiple images). + struct NSVGrasterizer* rast = nsvgCreateRasterizer(); + // Allocate memory for image + unsigned char* img = malloc(w*h*4); + // Rasterize + nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); +*/ + +// Allocated rasterizer context. +NSVGrasterizer* nsvgCreateRasterizer(); + +// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) +// r - pointer to rasterizer context +// image - pointer to image to rasterize +// tx,ty - image offset (applied after scaling) +// scale - image scale +// dst - pointer to destination image data, 4 bytes per pixel (RGBA) +// w - width of the image to render +// h - height of the image to render +// stride - number of bytes per scaleline in the destination buffer +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride); + +// Deletes rasterizer context. +void nsvgDeleteRasterizer(NSVGrasterizer*); + + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#endif // NANOSVGRAST_H + +#ifdef NANOSVGRAST_IMPLEMENTATION + +#include + +#define NSVG__SUBSAMPLES 5 +#define NSVG__FIXSHIFT 10 +#define NSVG__FIX (1 << NSVG__FIXSHIFT) +#define NSVG__FIXMASK (NSVG__FIX-1) +#define NSVG__MEMPAGE_SIZE 1024 + +typedef struct NSVGedge { + float x0,y0, x1,y1; + int dir; + struct NSVGedge* next; +} NSVGedge; + +typedef struct NSVGpoint { + float x, y; + float dx, dy; + float len; + float dmx, dmy; + unsigned char flags; +} NSVGpoint; + +typedef struct NSVGactiveEdge { + int x,dx; + float ey; + int dir; + struct NSVGactiveEdge *next; +} NSVGactiveEdge; + +typedef struct NSVGmemPage { + unsigned char mem[NSVG__MEMPAGE_SIZE]; + int size; + struct NSVGmemPage* next; +} NSVGmemPage; + +typedef struct NSVGcachedPaint { + char type; + char spread; + float xform[6]; + unsigned int colors[256]; +} NSVGcachedPaint; + +struct NSVGrasterizer +{ + float px, py; + + float tessTol; + float distTol; + + NSVGedge* edges; + int nedges; + int cedges; + + NSVGpoint* points; + int npoints; + int cpoints; + + NSVGpoint* points2; + int npoints2; + int cpoints2; + + NSVGactiveEdge* freelist; + NSVGmemPage* pages; + NSVGmemPage* curpage; + + unsigned char* scanline; + int cscanline; + + unsigned char* bitmap; + int width, height, stride; +}; + +NSVGrasterizer* nsvgCreateRasterizer() +{ + NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); + if (r == NULL) goto error; + memset(r, 0, sizeof(NSVGrasterizer)); + + r->tessTol = 0.25f; + r->distTol = 0.01f; + + return r; + +error: + nsvgDeleteRasterizer(r); + return NULL; +} + +void nsvgDeleteRasterizer(NSVGrasterizer* r) +{ + NSVGmemPage* p; + + if (r == NULL) return; + + p = r->pages; + while (p != NULL) { + NSVGmemPage* next = p->next; + free(p); + p = next; + } + + if (r->edges) free(r->edges); + if (r->points) free(r->points); + if (r->points2) free(r->points2); + if (r->scanline) free(r->scanline); + + free(r); +} + +static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) +{ + NSVGmemPage *newp; + + // If using existing chain, return the next page in chain + if (cur != NULL && cur->next != NULL) { + return cur->next; + } + + // Alloc new page + newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); + if (newp == NULL) return NULL; + memset(newp, 0, sizeof(NSVGmemPage)); + + // Add to linked list + if (cur != NULL) + cur->next = newp; + else + r->pages = newp; + + return newp; +} + +static void nsvg__resetPool(NSVGrasterizer* r) +{ + NSVGmemPage* p = r->pages; + while (p != NULL) { + p->size = 0; + p = p->next; + } + r->curpage = r->pages; +} + +static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) +{ + unsigned char* buf; + if (size > NSVG__MEMPAGE_SIZE) return NULL; + if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { + r->curpage = nsvg__nextPage(r, r->curpage); + } + buf = &r->curpage->mem[r->curpage->size]; + r->curpage->size += size; + return buf; +} + +static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) +{ + float dx = x2 - x1; + float dy = y2 - y1; + return dx*dx + dy*dy < tol*tol; +} + +static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) +{ + NSVGpoint* pt; + + if (r->npoints > 0) { + pt = &r->points[r->npoints-1]; + if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { + pt->flags = (unsigned char)(pt->flags | flags); + return; + } + } + + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + + pt = &r->points[r->npoints]; + pt->x = x; + pt->y = y; + pt->flags = (unsigned char)flags; + r->npoints++; +} + +static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) +{ + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + r->points[r->npoints] = pt; + r->npoints++; +} + +static void nsvg__duplicatePoints(NSVGrasterizer* r) +{ + if (r->npoints > r->cpoints2) { + r->cpoints2 = r->npoints; + r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); + if (r->points2 == NULL) return; + } + + memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); + r->npoints2 = r->npoints; +} + +static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) +{ + NSVGedge* e; + + // Skip horizontal edges + if (y0 == y1) + return; + + if (r->nedges+1 > r->cedges) { + r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; + r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); + if (r->edges == NULL) return; + } + + e = &r->edges[r->nedges]; + r->nedges++; + + if (y0 < y1) { + e->x0 = x0; + e->y0 = y0; + e->x1 = x1; + e->y1 = y1; + e->dir = 1; + } else { + e->x0 = x1; + e->y0 = y1; + e->x1 = x0; + e->y1 = y0; + e->dir = -1; + } +} + +static float nsvg__normalize(float *x, float* y) +{ + float d = sqrtf((*x)*(*x) + (*y)*(*y)); + if (d > 1e-6f) { + float id = 1.0f / d; + *x *= id; + *y *= id; + } + return d; +} + +static float nsvg__absf(float x) { return x < 0 ? -x : x; } + +static void nsvg__flattenCubicBez(NSVGrasterizer* r, + float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, + int level, int type) +{ + float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; + float dx,dy,d2,d3; + + if (level > 10) return; + + x12 = (x1+x2)*0.5f; + y12 = (y1+y2)*0.5f; + x23 = (x2+x3)*0.5f; + y23 = (y2+y3)*0.5f; + x34 = (x3+x4)*0.5f; + y34 = (y3+y4)*0.5f; + x123 = (x12+x23)*0.5f; + y123 = (y12+y23)*0.5f; + + dx = x4 - x1; + dy = y4 - y1; + d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); + d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); + + if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { + nsvg__addPathPoint(r, x4, y4, type); + return; + } + + x234 = (x23+x34)*0.5f; + y234 = (y23+y34)*0.5f; + x1234 = (x123+x234)*0.5f; + y1234 = (y123+y234)*0.5f; + + nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); + nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); +} + +static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j; + NSVGpath* path; + + for (path = shape->paths; path != NULL; path = path->next) { + r->npoints = 0; + // Flatten path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); + } + // Close path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + // Build edges + for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) + nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); + } +} + +enum NSVGpointFlags +{ + NSVG_PT_CORNER = 0x01, + NSVG_PT_BEVEL = 0x02, + NSVG_PT_LEFT = 0x04 +}; + +static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + float len = nsvg__normalize(&dx, &dy); + float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x - dx*w, py = p->y - dy*w; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +#ifndef NSVG_PI +#define NSVG_PI (3.14159265358979323846264338327f) +#endif + +static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) +{ + int i; + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; + + for (i = 0; i < ncap; i++) { + float a = (float)i/(float)(ncap-1)*NSVG_PI; + float ax = cosf(a) * w, ay = sinf(a) * w; + float x = px - dlx*ax - dx*ay; + float y = py - dly*ax - dy*ay; + + if (i > 0) + nsvg__addEdge(r, prevx, prevy, x, y); + + prevx = x; + prevy = y; + + if (i == 0) { + lx = x; ly = y; + } else if (i == ncap-1) { + rx = x; ry = y; + } + } + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); + float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); + float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); + float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); + + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0, rx0, lx1, rx1; + float ly0, ry0, ly1, ry1; + + if (p1->flags & NSVG_PT_LEFT) { + lx0 = lx1 = p1->x - p1->dmx * w; + ly0 = ly1 = p1->y - p1->dmy * w; + nsvg__addEdge(r, lx1, ly1, left->x, left->y); + + rx0 = p1->x + (dlx0 * w); + ry0 = p1->y + (dly0 * w); + rx1 = p1->x + (dlx1 * w); + ry1 = p1->y + (dly1 * w); + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + } else { + lx0 = p1->x - (dlx0 * w); + ly0 = p1->y - (dly0 * w); + lx1 = p1->x - (dlx1 * w); + ly1 = p1->y - (dly1 * w); + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + rx0 = rx1 = p1->x + p1->dmx * w; + ry0 = ry1 = p1->y + p1->dmy * w; + nsvg__addEdge(r, right->x, right->y, rx1, ry1); + } + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) +{ + int i, n; + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float a0 = atan2f(dly0, dlx0); + float a1 = atan2f(dly1, dlx1); + float da = a1 - a0; + float lx, ly, rx, ry; + + if (da < NSVG_PI) da += NSVG_PI*2; + if (da > NSVG_PI) da -= NSVG_PI*2; + + n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); + if (n < 2) n = 2; + if (n > ncap) n = ncap; + + lx = left->x; + ly = left->y; + rx = right->x; + ry = right->y; + + for (i = 0; i < n; i++) { + float u = (float)i/(float)(n-1); + float a = a0 + u*da; + float ax = cosf(a) * w, ay = sinf(a) * w; + float lx1 = p1->x - ax, ly1 = p1->y - ay; + float rx1 = p1->x + ax, ry1 = p1->y + ay; + + nsvg__addEdge(r, lx1, ly1, lx, ly); + nsvg__addEdge(r, rx, ry, rx1, ry1); + + lx = lx1; ly = ly1; + rx = rx1; ry = ry1; + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); + float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); + + nsvg__addEdge(r, lx, ly, left->x, left->y); + nsvg__addEdge(r, right->x, right->y, rx, ry); + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static int nsvg__curveDivs(float r, float arc, float tol) +{ + float da = acosf(r / (r + tol)) * 2.0f; + int divs = (int)ceilf(arc / da); + if (divs < 2) divs = 2; + return divs; +} + +static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) +{ + int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. + NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; + NSVGpoint* p0, *p1; + int j, s, e; + + // Build stroke edges + if (closed) { + // Looping + p0 = &points[npoints-1]; + p1 = &points[0]; + s = 0; + e = npoints; + } else { + // Add cap + p0 = &points[0]; + p1 = &points[1]; + s = 1; + e = npoints-1; + } + + if (closed) { + nsvg__initClosed(&left, &right, p0, p1, lineWidth); + firstLeft = left; + firstRight = right; + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); + } + + for (j = s; j < e; ++j) { + if (p1->flags & NSVG_PT_CORNER) { + if (lineJoin == NSVG_JOIN_ROUND) + nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); + else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) + nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); + else + nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); + } else { + nsvg__straightJoin(r, &left, &right, p1, lineWidth); + } + p0 = p1++; + } + + if (closed) { + // Loop it + nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); + nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); + } +} + +static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) +{ + int i, j; + NSVGpoint* p0, *p1; + + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (i = 0; i < r->npoints; i++) { + // Calculate segment direction and length + p0->dx = p1->x - p0->x; + p0->dy = p1->y - p0->y; + p0->len = nsvg__normalize(&p0->dx, &p0->dy); + // Advance + p0 = p1++; + } + + // calculate joins + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (j = 0; j < r->npoints; j++) { + float dlx0, dly0, dlx1, dly1, dmr2, cross; + dlx0 = p0->dy; + dly0 = -p0->dx; + dlx1 = p1->dy; + dly1 = -p1->dx; + // Calculate extrusions + p1->dmx = (dlx0 + dlx1) * 0.5f; + p1->dmy = (dly0 + dly1) * 0.5f; + dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; + if (dmr2 > 0.000001f) { + float s2 = 1.0f / dmr2; + if (s2 > 600.0f) { + s2 = 600.0f; + } + p1->dmx *= s2; + p1->dmy *= s2; + } + + // Clear flags, but keep the corner. + p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; + + // Keep track of left turns. + cross = p1->dx * p0->dy - p0->dx * p1->dy; + if (cross > 0.0f) + p1->flags |= NSVG_PT_LEFT; + + // Check to see if the corner needs to be beveled. + if (p1->flags & NSVG_PT_CORNER) { + if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { + p1->flags |= NSVG_PT_BEVEL; + } + } + + p0 = p1++; + } +} + +static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j, closed; + NSVGpath* path; + NSVGpoint* p0, *p1; + float miterLimit = shape->miterLimit; + int lineJoin = shape->strokeLineJoin; + int lineCap = shape->strokeLineCap; + float lineWidth = shape->strokeWidth * scale; + + for (path = shape->paths; path != NULL; path = path->next) { + // Flatten path + r->npoints = 0; + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER); + } + if (r->npoints < 2) + continue; + + closed = path->closed; + + // If the first and last points are the same, remove the last, mark as closed path. + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { + r->npoints--; + p0 = &r->points[r->npoints-1]; + closed = 1; + } + + if (shape->strokeDashCount > 0) { + int idash = 0, dashState = 1; + float totalDist = 0, dashLen, allDashLen, dashOffset; + NSVGpoint cur; + + if (closed) + nsvg__appendPathPoint(r, r->points[0]); + + // Duplicate points -> points2. + nsvg__duplicatePoints(r); + + r->npoints = 0; + cur = r->points2[0]; + nsvg__appendPathPoint(r, cur); + + // Figure out dash offset. + allDashLen = 0; + for (j = 0; j < shape->strokeDashCount; j++) + allDashLen += shape->strokeDashArray[j]; + if (shape->strokeDashCount & 1) + allDashLen *= 2.0f; + // Find location inside pattern + dashOffset = fmodf(shape->strokeDashOffset, allDashLen); + if (dashOffset < 0.0f) + dashOffset += allDashLen; + + while (dashOffset > shape->strokeDashArray[idash]) { + dashOffset -= shape->strokeDashArray[idash]; + idash = (idash + 1) % shape->strokeDashCount; + } + dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; + + for (j = 1; j < r->npoints2; ) { + float dx = r->points2[j].x - cur.x; + float dy = r->points2[j].y - cur.y; + float dist = sqrtf(dx*dx + dy*dy); + + if ((totalDist + dist) > dashLen) { + // Calculate intermediate point + float d = (dashLen - totalDist) / dist; + float x = cur.x + dx * d; + float y = cur.y + dy * d; + nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); + + // Stroke + if (r->npoints > 1 && dashState) { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } + // Advance dash pattern + dashState = !dashState; + idash = (idash+1) % shape->strokeDashCount; + dashLen = shape->strokeDashArray[idash] * scale; + // Restart + cur.x = x; + cur.y = y; + cur.flags = NSVG_PT_CORNER; + totalDist = 0.0f; + r->npoints = 0; + nsvg__appendPathPoint(r, cur); + } else { + totalDist += dist; + cur = r->points2[j]; + nsvg__appendPathPoint(r, cur); + j++; + } + } + // Stroke any leftover path + if (r->npoints > 1 && dashState) + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } else { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); + } + } +} + +static int nsvg__cmpEdge(const void *p, const void *q) +{ + const NSVGedge* a = (const NSVGedge*)p; + const NSVGedge* b = (const NSVGedge*)q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + + +static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) +{ + NSVGactiveEdge* z; + + if (r->freelist != NULL) { + // Restore from freelist. + z = r->freelist; + r->freelist = z->next; + } else { + // Alloc new edge. + z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); + if (z == NULL) return NULL; + } + + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); +// STBTT_assert(e->y0 <= start_point); + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); + else + z->dx = (int)floorf(NSVG__FIX * dxdy); + z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); +// z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->dir = e->dir; + + return z; +} + +static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) +{ + z->next = r->freelist; + r->freelist = z; +} + +static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) +{ + int i = x0 >> NSVG__FIXSHIFT; + int j = x1 >> NSVG__FIXSHIFT; + if (i < *xmin) *xmin = i; + if (j > *xmax) *xmax = j; + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = (unsigned char)(scanline[i] + maxWeight); + } + } +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) +{ + // non-zero winding fill + int x0 = 0, w = 0; + + if (fillRule == NSVG_FILLRULE_NONZERO) { + // Non-zero + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->dir; + } else { + int x1 = e->x; w += e->dir; + // if we went to zero, we need to draw + if (w == 0) + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } else if (fillRule == NSVG_FILLRULE_EVENODD) { + // Even-odd + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w = 1; + } else { + int x1 = e->x; w = 0; + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } +} + +static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } + +static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return (r) | (g << 8) | (b << 16) | (a << 24); +} + +static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; + int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; + int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; + int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static unsigned int nsvg__applyOpacity(unsigned int c, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (c) & 0xff; + int g = (c>>8) & 0xff; + int b = (c>>16) & 0xff; + int a = (((c>>24) & 0xff)*iu) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static inline int nsvg__div255(int x) +{ + return ((x+1) * 257) >> 16; +} + +static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, + float tx, float ty, float scale, NSVGcachedPaint* cache) +{ + + if (cache->type == NSVG_PAINT_COLOR) { + int i, cr, cg, cb, ca; + cr = cache->colors[0] & 0xff; + cg = (cache->colors[0] >> 8) & 0xff; + cb = (cache->colors[0] >> 16) & 0xff; + ca = (cache->colors[0] >> 24) & 0xff; + + for (i = 0; i < count; i++) { + int r,g,b; + int a = nsvg__div255((int)cover[0] * ca); + int ia = 255 - a; + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + } + } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + float fx, fy, dx, gy; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gy = fx*t[1] + fy*t[3] + t[5]; + c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + // TODO: focus (fx,fy) + float fx, fy, dx, gx, gy, gd; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gx = fx*t[0] + fy*t[2] + t[4]; + gy = fx*t[1] + fy*t[3] + t[5]; + gd = sqrtf(gx*gx + gy*gy); + c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } +} + +static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) +{ + NSVGactiveEdge *active = NULL; + int y, s; + int e = 0; + int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline + int xmin, xmax; + + for (y = 0; y < r->height; y++) { + memset(r->scanline, 0, r->width); + xmin = r->width; + xmax = 0; + for (s = 0; s < NSVG__SUBSAMPLES; ++s) { + // find center of pixel for this scanline + float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; + NSVGactiveEdge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + NSVGactiveEdge *z = *step; + if (z->ey <= scany) { + *step = z->next; // delete from list +// NSVG__assert(z->valid); + nsvg__freeActive(r, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for (;;) { + int changed = 0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + NSVGactiveEdge* t = *step; + NSVGactiveEdge* q = t->next; + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e < r->nedges && r->edges[e].y0 <= scany) { + if (r->edges[e].y1 > scany) { + NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); + if (z == NULL) break; + // find insertion point + if (active == NULL) { + active = z; + } else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + NSVGactiveEdge* p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + e++; + } + + // now process all active edges in non-zero fashion + if (active != NULL) + nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); + } + // Blit + if (xmin < 0) xmin = 0; + if (xmax > r->width-1) xmax = r->width-1; + if (xmin <= xmax) { + nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); + } + } + +} + +static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) +{ + int x,y; + + // Unpremultiply + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = row[0], g = row[1], b = row[2], a = row[3]; + if (a != 0) { + row[0] = (unsigned char)(r*255/a); + row[1] = (unsigned char)(g*255/a); + row[2] = (unsigned char)(b*255/a); + } + row += 4; + } + } + + // Defringe + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = 0, g = 0, b = 0, a = row[3], n = 0; + if (a == 0) { + if (x-1 > 0 && row[-1] != 0) { + r += row[-4]; + g += row[-3]; + b += row[-2]; + n++; + } + if (x+1 < w && row[7] != 0) { + r += row[4]; + g += row[5]; + b += row[6]; + n++; + } + if (y-1 > 0 && row[-stride+3] != 0) { + r += row[-stride]; + g += row[-stride+1]; + b += row[-stride+2]; + n++; + } + if (y+1 < h && row[stride+3] != 0) { + r += row[stride]; + g += row[stride+1]; + b += row[stride+2]; + n++; + } + if (n > 0) { + row[0] = (unsigned char)(r/n); + row[1] = (unsigned char)(g/n); + row[2] = (unsigned char)(b/n); + } + } + row += 4; + } + } +} + + +static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) +{ + int i, j; + NSVGgradient* grad; + + cache->type = paint->type; + + if (paint->type == NSVG_PAINT_COLOR) { + cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); + return; + } + + grad = paint->gradient; + + cache->spread = grad->spread; + memcpy(cache->xform, grad->xform, sizeof(float)*6); + + if (grad->nstops == 0) { + for (i = 0; i < 256; i++) + cache->colors[i] = 0; + } if (grad->nstops == 1) { + for (i = 0; i < 256; i++) + cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); + } else { + unsigned int ca, cb = 0; + float ua, ub, du, u; + int ia, ib, count; + + ca = nsvg__applyOpacity(grad->stops[0].color, opacity); + ua = nsvg__clampf(grad->stops[0].offset, 0, 1); + ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + for (i = 0; i < ia; i++) { + cache->colors[i] = ca; + } + + for (i = 0; i < grad->nstops-1; i++) { + ca = nsvg__applyOpacity(grad->stops[i].color, opacity); + cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); + ua = nsvg__clampf(grad->stops[i].offset, 0, 1); + ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + count = ib - ia; + if (count <= 0) continue; + u = 0; + du = 1.0f / (float)count; + for (j = 0; j < count; j++) { + cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); + u += du; + } + } + + for (i = ib; i < 256; i++) + cache->colors[i] = cb; + } + +} + +/* +static void dumpEdges(NSVGrasterizer* r, const char* name) +{ + float xmin = 0, xmax = 0, ymin = 0, ymax = 0; + NSVGedge *e = NULL; + int i; + if (r->nedges == 0) return; + FILE* fp = fopen(name, "w"); + if (fp == NULL) return; + + xmin = xmax = r->edges[0].x0; + ymin = ymax = r->edges[0].y0; + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + xmin = nsvg__minf(xmin, e->x0); + xmin = nsvg__minf(xmin, e->x1); + xmax = nsvg__maxf(xmax, e->x0); + xmax = nsvg__maxf(xmax, e->x1); + ymin = nsvg__minf(ymin, e->y0); + ymin = nsvg__minf(ymin, e->y1); + ymax = nsvg__maxf(ymax, e->y0); + ymax = nsvg__maxf(ymax, e->y1); + } + + fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); + + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); + } + + for (i = 0; i < r->npoints; i++) { + if (i+1 < r->npoints) + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); + } + + fprintf(fp, ""); + fclose(fp); +} +*/ + +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride) +{ + NSVGshape *shape = NULL; + NSVGedge *e = NULL; + NSVGcachedPaint cache; + int i; + + r->bitmap = dst; + r->width = w; + r->height = h; + r->stride = stride; + + if (w > r->cscanline) { + r->cscanline = w; + r->scanline = (unsigned char*)realloc(r->scanline, w); + if (r->scanline == NULL) return; + } + + for (i = 0; i < h; i++) + memset(&dst[i*stride], 0, w*4); + + for (shape = image->shapes; shape != NULL; shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE)) + continue; + + if (shape->fill.type != NSVG_PAINT_NONE) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShape(r, shape, scale); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->fill, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); + } + if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShapeStroke(r, shape, scale); + +// dumpEdges(r, "edge.svg"); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->stroke, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); + } + } + + nsvg__unpremultiplyAlpha(dst, w, h, stride); + + r->bitmap = NULL; + r->width = 0; + r->height = 0; + r->stride = 0; +} + +#endif diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 0e80bd542..d6d25b550 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -20,6 +20,67 @@ namespace GUI { bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) { +#if ENABLE_TEXTURES_FROM_SVG + m_vertices.clear(); + unsigned int v_size = 3 * (unsigned int)triangles.size(); + + if (v_size == 0) + return false; + + m_vertices = std::vector(v_size, Vertex()); + + float min_x = unscale(triangles[0].points[0](0)); + float min_y = unscale(triangles[0].points[0](1)); + float max_x = min_x; + float max_y = min_y; + + unsigned int v_count = 0; + for (const Polygon& t : triangles) + { + for (unsigned int i = 0; i < 3; ++i) + { + Vertex& v = m_vertices[v_count]; + + const Point& p = t.points[i]; + float x = unscale(p(0)); + float y = unscale(p(1)); + + v.position[0] = x; + v.position[1] = y; + v.position[2] = z; + + if (generate_tex_coords) + { + v.tex_coords[0] = x; + v.tex_coords[1] = y; + + min_x = std::min(min_x, x); + max_x = std::max(max_x, x); + min_y = std::min(min_y, y); + max_y = std::max(max_y, y); + } + + ++v_count; + } + } + + if (generate_tex_coords) + { + float size_x = max_x - min_x; + float size_y = max_y - min_y; + + if ((size_x != 0.0f) && (size_y != 0.0f)) + { + float inv_size_x = 1.0f / size_x; + float inv_size_y = -1.0f / size_y; + for (Vertex& v : m_vertices) + { + v.tex_coords[0] = (v.tex_coords[0] - min_x) * inv_size_x; + v.tex_coords[1] = (v.tex_coords[1] - min_y) * inv_size_y; + } + } + } +#else m_vertices.clear(); m_tex_coords.clear(); @@ -80,12 +141,38 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool } } } +#endif // ENABLE_TEXTURES_FROM_SVG return true; } bool GeometryBuffer::set_from_lines(const Lines& lines, float z) { +#if ENABLE_TEXTURES_FROM_SVG + m_vertices.clear(); + + unsigned int v_size = 2 * (unsigned int)lines.size(); + if (v_size == 0) + return false; + + m_vertices = std::vector(v_size, Vertex()); + + unsigned int v_count = 0; + for (const Line& l : lines) + { + Vertex& v1 = m_vertices[v_count]; + v1.position[0] = unscale(l.a(0)); + v1.position[1] = unscale(l.a(1)); + v1.position[2] = z; + ++v_count; + + Vertex& v2 = m_vertices[v_count]; + v2.position[0] = unscale(l.b(0)); + v2.position[1] = unscale(l.b(1)); + v2.position[2] = z; + ++v_count; + } +#else m_vertices.clear(); m_tex_coords.clear(); @@ -105,10 +192,18 @@ bool GeometryBuffer::set_from_lines(const Lines& lines, float z) m_vertices[coord++] = unscale(l.b(1)); m_vertices[coord++] = z; } +#endif // ENABLE_TEXTURES_FROM_SVG return true; } +#if ENABLE_TEXTURES_FROM_SVG +const float* GeometryBuffer::get_vertices_data() const +{ + return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr; +} +#endif // ENABLE_TEXTURES_FROM_SVG + const double Bed3D::Axes::Radius = 0.5; const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius; const double Bed3D::Axes::ArrowLength = 5.0; @@ -176,8 +271,11 @@ void Bed3D::Axes::render_axis(double length) const } Bed3D::Bed3D() -: m_type(Custom) -, m_scale_factor(1.0f) + : m_type(Custom) +#if ENABLE_TEXTURES_FROM_SVG + , m_vbo_id(0) +#endif // ENABLE_TEXTURES_FROM_SVG + , m_scale_factor(1.0f) { } @@ -206,6 +304,10 @@ bool Bed3D::set_shape(const Pointfs& shape) m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour; +#if ENABLE_TEXTURES_FROM_SVG + reset(); +#endif // ENABLE_TEXTURES_FROM_SVG + // Set the origin and size for painting of the coordinate system axes. m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); m_axes.length = 0.1 * get_bounding_box().max_size() * Vec3d::Ones(); @@ -224,6 +326,39 @@ Point Bed3D::point_projection(const Point& point) const return m_polygon.point_projection(point); } +#if ENABLE_TEXTURES_FROM_SVG +void Bed3D::render(float theta, bool useVBOs, float scale_factor) const +{ + m_scale_factor = scale_factor; + + EType type = useVBOs ? m_type : Custom; + switch (type) + + { + case MK2: + { + render_prusa("mk2", theta > 90.0f); + break; + } + case MK3: + { + render_prusa("mk3", theta > 90.0f); + break; + } + case SL1: + { + render_prusa("sl1", theta > 90.0f); + break; + } + default: + case Custom: + { + render_custom(); + break; + } + } +} +#else void Bed3D::render(float theta, bool useVBOs, float scale_factor) const { m_scale_factor = scale_factor; @@ -256,6 +391,7 @@ void Bed3D::render(float theta, bool useVBOs, float scale_factor) const } } } +#endif // ENABLE_TEXTURES_FROM_SVG void Bed3D::render_axes() const { @@ -349,6 +485,131 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const return type; } +#if ENABLE_TEXTURES_FROM_SVG +void Bed3D::render_prusa(const std::string &key, bool bottom) const +{ + std::string tex_path = resources_dir() + "/icons/bed/" + key; + + std::string model_path = resources_dir() + "/models/" + key; + + // use anisotropic filter if graphic card allows + GLfloat max_anisotropy = 0.0f; + if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) + ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); + + // use higher resolution images if graphic card allows + GLint max_tex_size; + ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); + + // clamp or the texture generation becomes too slow + max_tex_size = std::min(max_tex_size, 8192); + + std::string filename = tex_path + ".svg"; + + if ((m_texture.get_id() == 0) || (m_texture.get_source() != filename)) + { + if (!m_texture.load_from_svg_file(filename, true, max_tex_size)) + { + render_custom(); + return; + } + + if (max_anisotropy > 0.0f) + { + ::glBindTexture(GL_TEXTURE_2D, m_texture.get_id()); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); + ::glBindTexture(GL_TEXTURE_2D, 0); + } + } + + if (!bottom) + { + filename = model_path + "_bed.stl"; + if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, true)) { + Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2)); + if (key == "mk2") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 7.5, -0.03); + else if (key == "mk3") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 5.5, 2.43); + else if (key == "sl1") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 0.0, -0.03); + + m_model.center_around(offset); + } + + if (!m_model.get_filename().empty()) + { + ::glEnable(GL_LIGHTING); + m_model.render(); + ::glDisable(GL_LIGHTING); + } + } + + unsigned int triangles_vcount = m_triangles.get_vertices_count(); + if (triangles_vcount > 0) + { + if (m_vbo_id == 0) + { + ::glGenBuffers(1, &m_vbo_id); + ::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id); + ::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW); + ::glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_position_offset()); + ::glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_tex_coords_offset()); + ::glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + ::glEnable(GL_DEPTH_TEST); + ::glDepthMask(GL_FALSE); + + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glEnable(GL_TEXTURE_2D); + ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + if (bottom) + ::glFrontFace(GL_CW); + + render_prusa_shader(triangles_vcount, bottom); + + if (bottom) + ::glFrontFace(GL_CCW); + + ::glDisable(GL_TEXTURE_2D); + + ::glDisable(GL_BLEND); + ::glDepthMask(GL_TRUE); + } +} + +void Bed3D::render_prusa_shader(unsigned int vertices_count, bool transparent) const +{ + if (m_shader.get_shader_program_id() == 0) + m_shader.init("printbed.vs", "printbed.fs"); + + if (m_shader.is_initialized()) + { + m_shader.start_using(); + m_shader.set_uniform("transparent_background", transparent); + + ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_texture.get_id()); + ::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id); + ::glEnableVertexAttribArray(0); + ::glEnableVertexAttribArray(1); + ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices_count); + ::glDisableVertexAttribArray(1); + ::glDisableVertexAttribArray(0); + ::glBindBuffer(GL_ARRAY_BUFFER, 0); + ::glBindTexture(GL_TEXTURE_2D, 0); + + m_shader.stop_using(); + } +} + +#else void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) const { std::string tex_path = resources_dir() + "/icons/bed/" + key; @@ -468,11 +729,16 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons glsafe(::glDepthMask(GL_TRUE)); } } +#endif // ENABLE_TEXTURES_FROM_SVG void Bed3D::render_custom() const { +#if ENABLE_TEXTURES_FROM_SVG + m_texture.reset(); +#else m_top_texture.reset(); m_bottom_texture.reset(); +#endif // ENABLE_TEXTURES_FROM_SVG unsigned int triangles_vcount = m_triangles.get_vertices_count(); if (triangles_vcount > 0) @@ -487,7 +753,11 @@ void Bed3D::render_custom() const glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f)); glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); +#if ENABLE_TEXTURES_FROM_SVG + ::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()); +#else glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices())); +#endif // ENABLE_TEXTURES_FROM_SVG glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); // draw grid @@ -497,7 +767,11 @@ void Bed3D::render_custom() const glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glLineWidth(3.0f * m_scale_factor)); glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f)); +#if ENABLE_TEXTURES_FROM_SVG + ::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()); +#else glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices())); +#endif // ENABLE_TEXTURES_FROM_SVG glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount)); glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); @@ -507,5 +781,16 @@ void Bed3D::render_custom() const } } +#if ENABLE_TEXTURES_FROM_SVG +void Bed3D::reset() +{ + if (m_vbo_id > 0) + { + ::glDeleteBuffers(1, &m_vbo_id); + m_vbo_id = 0; + } +} +#endif // ENABLE_TEXTURES_FROM_SVG + } // GUI } // Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 11c5faa64..edf3e5ee7 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -3,6 +3,9 @@ #include "GLTexture.hpp" #include "3DScene.hpp" +#if ENABLE_TEXTURES_FROM_SVG +#include "GLShader.hpp" +#endif // ENABLE_TEXTURES_FROM_SVG class GLUquadric; typedef class GLUquadric GLUquadricObj; @@ -12,17 +15,41 @@ namespace GUI { class GeometryBuffer { +#if ENABLE_TEXTURES_FROM_SVG + struct Vertex + { + float position[3]; + float tex_coords[2]; + + Vertex() + { + position[0] = 0.0f; position[1] = 0.0f; position[2] = 0.0f; + tex_coords[0] = 0.0f; tex_coords[1] = 0.0f; + } + }; + + std::vector m_vertices; +#else std::vector m_vertices; std::vector m_tex_coords; +#endif // ENABLE_TEXTURES_FROM_SVG public: bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords); bool set_from_lines(const Lines& lines, float z); +#if ENABLE_TEXTURES_FROM_SVG + const float* get_vertices_data() const; + unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); } + unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); } + unsigned int get_position_offset() const { return 0; } + unsigned int get_tex_coords_offset() const { return (unsigned int)(3 * sizeof(float)); } + unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); } +#else const float* get_vertices() const { return m_vertices.data(); } const float* get_tex_coords() const { return m_tex_coords.data(); } - unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size() / 3; } +#endif // ENABLE_TEXTURES_FROM_SVG }; class Bed3D @@ -62,8 +89,14 @@ private: Polygon m_polygon; GeometryBuffer m_triangles; GeometryBuffer m_gridlines; +#if ENABLE_TEXTURES_FROM_SVG + mutable GLTexture m_texture; + mutable Shader m_shader; + mutable unsigned int m_vbo_id; +#else mutable GLTexture m_top_texture; mutable GLTexture m_bottom_texture; +#endif // ENABLE_TEXTURES_FROM_SVG mutable GLBed m_model; Axes m_axes; @@ -71,6 +104,9 @@ private: public: Bed3D(); +#if ENABLE_TEXTURES_FROM_SVG + ~Bed3D() { reset(); } +#endif // ENABLE_TEXTURES_FROM_SVG EType get_type() const { return m_type; } @@ -93,8 +129,16 @@ private: void calc_triangles(const ExPolygon& poly); void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); EType detect_type(const Pointfs& shape) const; +#if ENABLE_TEXTURES_FROM_SVG + void render_prusa(const std::string& key, bool bottom) const; + void render_prusa_shader(unsigned int vertices_count, bool transparent) const; +#else void render_prusa(const std::string &key, float theta, bool useVBOs) const; +#endif // ENABLE_TEXTURES_FROM_SVG void render_custom() const; +#if ENABLE_TEXTURES_FROM_SVG + void reset(); +#endif // ENABLE_TEXTURES_FROM_SVG }; } // GUI diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 844353c42..f906ce5a3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -238,6 +238,7 @@ void GLCanvas3D::Camera::set_scene_box(const BoundingBoxf3& box, GLCanvas3D& can } } +#if !ENABLE_TEXTURES_FROM_SVG GLCanvas3D::Shader::Shader() : m_shader(nullptr) { @@ -316,6 +317,7 @@ void GLCanvas3D::Shader::_reset() m_shader = nullptr; } } +#endif // !ENABLE_TEXTURES_FROM_SVG GLCanvas3D::LayersEditing::LayersEditing() : m_use_legacy_opengl(false) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d7861afe1..1358e0b8d 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -192,6 +192,7 @@ class GLCanvas3D void set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas); }; +#if !ENABLE_TEXTURES_FROM_SVG class Shader { GLShader* m_shader; @@ -215,6 +216,7 @@ class GLCanvas3D private: void _reset(); }; +#endif // !ENABLE_TEXTURES_FROM_SVG class LayersEditing { diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 791f452ef..24d8d41b2 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -253,4 +253,91 @@ sub SetMatrix } */ +#if ENABLE_TEXTURES_FROM_SVG +Shader::Shader() + : m_shader(nullptr) +{ +} + +Shader::~Shader() +{ + reset(); +} + +bool Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) +{ + if (is_initialized()) + return true; + + m_shader = new GLShader(); + if (m_shader != nullptr) + { + if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str())) + { + std::cout << "Compilaton of shader failed:" << std::endl; + std::cout << m_shader->last_error << std::endl; + reset(); + return false; + } + } + + return true; +} + +bool Shader::is_initialized() const +{ + return (m_shader != nullptr); +} + +bool Shader::start_using() const +{ + if (is_initialized()) + { + m_shader->enable(); + return true; + } + else + return false; +} + +void Shader::stop_using() const +{ + if (m_shader != nullptr) + m_shader->disable(); +} + +void Shader::set_uniform(const std::string& name, float value) const +{ + if (m_shader != nullptr) + m_shader->set_uniform(name.c_str(), value); +} + +void Shader::set_uniform(const std::string& name, const float* matrix) const +{ + if (m_shader != nullptr) + m_shader->set_uniform(name.c_str(), matrix); +} + +void Shader::set_uniform(const std::string& name, bool value) const +{ + if (m_shader != nullptr) + m_shader->set_uniform(name.c_str(), value); +} + +unsigned int Shader::get_shader_program_id() const +{ + return (m_shader != nullptr) ? m_shader->shader_program_id : 0; +} + +void Shader::reset() +{ + if (m_shader != nullptr) + { + m_shader->release(); + delete m_shader; + m_shader = nullptr; + } +} +#endif // ENABLE_TEXTURES_FROM_SVG + } // namespace Slic3r diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index 0634cce6e..2f88d0393 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -36,6 +36,34 @@ public: std::string last_error; }; +#if ENABLE_TEXTURES_FROM_SVG +class Shader +{ + GLShader* m_shader; + +public: + Shader(); + ~Shader(); + + bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); + + bool is_initialized() const; + + bool start_using() const; + void stop_using() const; + + void set_uniform(const std::string& name, float value) const; + void set_uniform(const std::string& name, const float* matrix) const; + void set_uniform(const std::string& name, bool value) const; + + const GLShader* get_shader() const { return m_shader; } + unsigned int get_shader_program_id() const; + +private: + void reset(); +}; +#endif // ENABLE_TEXTURES_FROM_SVG + } #endif /* slic3r_GLShader_hpp_ */ diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 292ef472a..e7e20b27e 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -1,3 +1,4 @@ +#include "libslic3r/libslic3r.h" #include "GLTexture.hpp" #include @@ -5,10 +6,20 @@ #include #include +#if ENABLE_TEXTURES_FROM_SVG +#include +#endif // ENABLE_TEXTURES_FROM_SVG #include #include +#if ENABLE_TEXTURES_FROM_SVG +#define NANOSVG_IMPLEMENTATION +#include "nanosvg/nanosvg.h" +#define NANOSVGRAST_IMPLEMENTATION +#include "nanosvg/nanosvgrast.h" +#endif // ENABLE_TEXTURES_FROM_SVG + namespace Slic3r { namespace GUI { @@ -27,7 +38,34 @@ GLTexture::~GLTexture() reset(); } -bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmaps) +#if ENABLE_TEXTURES_FROM_SVG +bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps) +{ + reset(); + + if (!boost::filesystem::exists(filename)) + return false; + + if (boost::algorithm::iends_with(filename, ".png")) + return load_from_png(filename, use_mipmaps); + else + return false; +} + +bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px) +{ + reset(); + + if (!boost::filesystem::exists(filename)) + return false; + + if (boost::algorithm::iends_with(filename, ".svg")) + return load_from_svg(filename, use_mipmaps, max_size_px); + else + return false; +} +#else +bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps) { reset(); @@ -78,10 +116,10 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap ::glGenTextures(1, &m_id); ::glBindTexture(GL_TEXTURE_2D, m_id); ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); - if (generate_mipmaps) + if (use_mipmaps) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards - unsigned int levels_count = _generate_mipmaps(image); + unsigned int levels_count = generate_mipmaps(image); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } @@ -97,6 +135,7 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap m_source = filename; return true; } +#endif // ENABLE_TEXTURES_FROM_SVG void GLTexture::reset() { @@ -109,26 +148,6 @@ void GLTexture::reset() m_source = ""; } -unsigned int GLTexture::get_id() const -{ - return m_id; -} - -int GLTexture::get_width() const -{ - return m_width; -} - -int GLTexture::get_height() const -{ - return m_height; -} - -const std::string& GLTexture::get_source() const -{ - return m_source; -} - void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) { render_sub_texture(tex_id, left, right, bottom, top, FullTextureUVs); @@ -157,7 +176,7 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, ::glDisable(GL_BLEND); } -unsigned int GLTexture::_generate_mipmaps(wxImage& image) +unsigned int GLTexture::generate_mipmaps(wxImage& image) { int w = image.GetWidth(); int h = image.GetHeight(); @@ -195,5 +214,152 @@ unsigned int GLTexture::_generate_mipmaps(wxImage& image) return (unsigned int)level; } +#if ENABLE_TEXTURES_FROM_SVG +bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps) +{ + // Load a PNG with an alpha channel. + wxImage image; + if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG)) + { + reset(); + return false; + } + + m_width = image.GetWidth(); + m_height = image.GetHeight(); + int n_pixels = m_width * m_height; + + if (n_pixels <= 0) + { + reset(); + return false; + } + + // Get RGB & alpha raw data from wxImage, pack them into an array. + unsigned char* img_rgb = image.GetData(); + if (img_rgb == nullptr) + { + reset(); + return false; + } + + unsigned char* img_alpha = image.GetAlpha(); + + std::vector data(n_pixels * 4, 0); + for (int i = 0; i < n_pixels; ++i) + { + int data_id = i * 4; + int img_id = i * 3; + data[data_id + 0] = img_rgb[img_id + 0]; + data[data_id + 1] = img_rgb[img_id + 1]; + data[data_id + 2] = img_rgb[img_id + 2]; + data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; + } + + // sends data to gpu + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + ::glGenTextures(1, &m_id); + ::glBindTexture(GL_TEXTURE_2D, m_id); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + if (use_mipmaps) + { + // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards + unsigned int levels_count = generate_mipmaps(image); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } + else + { + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + } + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + ::glBindTexture(GL_TEXTURE_2D, 0); + + m_source = filename; + + return true; +} + +bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px) +{ + NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f); + if (image == nullptr) + { +// printf("Could not open SVG image.\n"); + reset(); + return false; + } + + float scale = (float)max_size_px / std::max(image->width, image->height); + + m_width = (int)(scale * image->width); + m_height = (int)(scale * image->height); + int n_pixels = m_width * m_height; + + if (n_pixels <= 0) + { + reset(); + return false; + } + + NSVGrasterizer* rast = nsvgCreateRasterizer(); + if (rast == nullptr) + { +// printf("Could not init rasterizer.\n"); + nsvgDelete(image); + reset(); + return false; + } + + // creates the temporary buffer only once, with max size, and reuse it for all the levels, if generating mipmaps + std::vector data(n_pixels * 4, 0); + nsvgRasterize(rast, image, 0, 0, scale, data.data(), m_width, m_height, m_width * 4); + + // sends data to gpu + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + ::glGenTextures(1, &m_id); + ::glBindTexture(GL_TEXTURE_2D, m_id); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + if (use_mipmaps) + { + // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards + int lod_w = m_width; + int lod_h = m_height; + GLint level = 0; + while ((lod_w > 1) || (lod_h > 1)) + { + ++level; + + lod_w = std::max(lod_w / 2, 1); + lod_h = std::max(lod_h / 2, 1); + scale /= 2.0f; + + nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4); + ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + } + + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + level); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } + else + { + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + } + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + ::glBindTexture(GL_TEXTURE_2D, 0); + + m_source = filename; + + nsvgDeleteRasterizer(rast); + nsvgDelete(image); + + return true; +} +#endif // ENABLE_TEXTURES_FROM_SVG + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp index e027bd152..af41ac342 100644 --- a/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -37,20 +37,28 @@ namespace GUI { GLTexture(); virtual ~GLTexture(); - bool load_from_file(const std::string& filename, bool generate_mipmaps); + bool load_from_file(const std::string& filename, bool use_mipmaps); +#if ENABLE_TEXTURES_FROM_SVG + bool load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px); +#endif // ENABLE_TEXTURES_FROM_SVG void reset(); - unsigned int get_id() const; - int get_width() const; - int get_height() const; + unsigned int get_id() const { return m_id; } + int get_width() const { return m_width; } + int get_height() const { return m_height; } - const std::string& get_source() const; + const std::string& get_source() const { return m_source; } static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs); protected: - unsigned int _generate_mipmaps(wxImage& image); + unsigned int generate_mipmaps(wxImage& image); +#if ENABLE_TEXTURES_FROM_SVG + private: + bool load_from_png(const std::string& filename, bool use_mipmaps); + bool load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px); +#endif // ENABLE_TEXTURES_FROM_SVG }; } // namespace GUI From bf699462c3c0b01c6a8389cd66db8d0d1df2a442 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 21 Feb 2019 11:54:18 +0100 Subject: [PATCH 17/20] imgui: Attempt to fix Tab key --- src/slic3r/GUI/GLCanvas3D.cpp | 12 +++++++++--- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GUI_Preview.cpp | 2 +- src/slic3r/GUI/ImGuiWrapper.cpp | 7 ++----- src/slic3r/GUI/Plater.cpp | 5 ++--- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e68f3673b..ad19b08bd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4085,6 +4085,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) @@ -5321,10 +5322,15 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) #endif // ENABLE_IMGUI if (evt.GetEventType() == wxEVT_KEY_UP) { const int keyCode = evt.GetKeyCode(); - - // shift has been just released - SLA gizmo might want to close rectangular selection. - if (m_gizmos.get_current_type() == Gizmos::SlaSupports && keyCode == WXK_SHIFT && m_gizmos.mouse_event(SLAGizmoEventType::ShiftUp)) + + if (keyCode == WXK_TAB) { + // Enable switching between 3D and Preview with Tab + // m_canvas->HandleAsNavigationKey(evt); // XXX: Doesn't work in some cases / on Linux + post_event(SimpleEvent(EVT_GLCANVAS_TAB)); + } else if (m_gizmos.get_current_type() == Gizmos::SlaSupports && keyCode == WXK_SHIFT && m_gizmos.mouse_event(SLAGizmoEventType::ShiftUp)) { + // shift has been just released - SLA gizmo might want to close rectangular selection. m_dirty = true; + } } evt.Skip(); // Needed to have EVT_CHAR generated as well diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 3d44aa13f..ff136e190 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -131,6 +131,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); // this describes events being passed from GLCanvas3D to SlaSupport gizmo enum class SLAGizmoEventType { diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 717d6de00..f730496ce 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -229,7 +229,7 @@ bool Preview::init(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlici if ((config == nullptr) || (process == nullptr) || (gcode_preview_data == nullptr)) return false; - if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)) + if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */)) return false; m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 0ec0050e6..0e437ef6d 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -138,11 +138,8 @@ bool ImGuiWrapper::update_key_data(wxKeyEvent &evt) io.KeyAlt = evt.AltDown(); io.KeySuper = evt.MetaDown(); - // XXX: Unfortunatelly this seems broken due to some interference with wxWidgets, - // we have to return true always (perform re-render). - // new_frame(); - // return want_keyboard() || want_text_input(); - return true; + new_frame(); + return want_keyboard() || want_text_input(); } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4da9f6652..08f444d91 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1146,9 +1146,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D = new View3D(q, &model, config, &background_process); preview = new Preview(q, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); - // Let the Tab key switch between the 3D view and the layer preview. - view3D->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); - preview->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); panels.push_back(view3D); panels.push_back(preview); @@ -1201,6 +1198,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); }); view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this); view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this); + view3D_canvas->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); }); // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); }); @@ -1216,6 +1214,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); }); view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); From ac0c7e80653c3e0109e558c32f07cf6d73e54eee Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 21 Feb 2019 15:46:04 +0100 Subject: [PATCH 18/20] Optimization of SLA print time estimation, moved SLA print time estimation calculation into rasterization step, so that the print time estimation is tracked by some SLAPrintStep. Extended the output file name generator to use the SLA print statistics. --- src/libslic3r/PrintExport.hpp | 1 + src/libslic3r/SLAPrint.cpp | 103 ++++++++++++++++++---------------- src/libslic3r/SLAPrint.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 88 ++++++++++++++--------------- 4 files changed, 100 insertions(+), 96 deletions(-) diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index 64babd507..e196cde5f 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -14,6 +14,7 @@ namespace Slic3r { +// Used for addressing parameters of FilePrinter::set_statistics() enum ePrintStatistics { psUsedMaterial = 0, diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index db8789fa0..01f35149d 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -2,6 +2,7 @@ #include "SLA/SLASupportTree.hpp" #include "SLA/SLABasePool.hpp" #include "SLA/SLAAutoSupports.hpp" +#include "ClipperUtils.hpp" #include "MTUtils.hpp" #include @@ -477,6 +478,15 @@ void SLAPrint::finalize() m_stepmask[istep] = true; } +// Generate a recommended output file name based on the format template, default extension, and template parameters +// (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. +// Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before the output is finalized). +std::string SLAPrint::output_filename() const +{ + DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); + return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "zip", &config); +} + namespace { // Compile the argument for support creation from the static print config. sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) { @@ -960,6 +970,15 @@ void SLAPrint::process() // Print all the layers in parallel tbb::parallel_for(0, lvlcnt, lvlfn); + + // Fill statistics + this->fill_statistics(); + // Set statistics values to the printer + m_printer->set_statistics({(m_print_statistics.objects_used_material + m_print_statistics.support_used_material)/1000, + 10.0, + double(m_print_statistics.slow_layers_count), + double(m_print_statistics.fast_layers_count) + }); }; using slaposFn = std::function; @@ -1042,16 +1061,6 @@ void SLAPrint::process() st += unsigned(PRINT_STEP_LEVELS[currentstep] * pstd); } - // Fill statistics - fill_statistics(); - // Set statistics values to the printer - SLAPrinter& printer = *m_printer; - printer.set_statistics({(m_print_statistics.objects_used_material + m_print_statistics.support_used_material)/1000, - 10.0, - double(m_print_statistics.slow_layers_count), - double(m_print_statistics.fast_layers_count) - }); - // If everything vent well report_status(*this, 100, L("Slicing done")); } @@ -1128,7 +1137,7 @@ void SLAPrint::fill_statistics() const double init_exp_time = m_material_config.initial_exposure_time.getFloat(); const double exp_time = m_material_config.exposure_time.getFloat(); - const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20] + const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20] const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR; const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR; @@ -1136,17 +1145,21 @@ void SLAPrint::fill_statistics() // get polygons for all instances in the object auto get_all_polygons = [](const ExPolygons& input_polygons, const std::vector& instances) { - ExPolygons polygons; const size_t inst_cnt = instances.size(); - polygons.reserve(input_polygons.size()*inst_cnt); + size_t polygon_cnt = 0; + for (const ExPolygon& polygon : input_polygons) + polygon_cnt += polygon.holes.size() + 1; + + Polygons polygons; + polygons.reserve(polygon_cnt * inst_cnt); for (const ExPolygon& polygon : input_polygons) { for (size_t i = 0; i < inst_cnt; ++i) { ExPolygon tmp = polygon; tmp.rotate(Geometry::rad2deg(instances[i].rotation)); tmp.translate(instances[i].shift.x(), instances[i].shift.y()); - polygons.push_back(tmp); + polygons_append(polygons, to_polygons(std::move(tmp))); } } return polygons; @@ -1161,13 +1174,21 @@ void SLAPrint::fill_statistics() size_t fast_layers = 0; // find highest object - size_t max_layers_cnt = 0; + // Which is a better bet? To compare by max_z or by number of layers in the index? + double max_z = 0.; + size_t max_layers_cnt = 0; size_t highest_obj_idx = 0; - for (SLAPrintObject * po : m_objects) { + for (SLAPrintObject *&po : m_objects) { const SLAPrintObject::SliceIndex& slice_index = po->get_slice_index(); - if (max_layers_cnt < slice_index.size()) { - max_layers_cnt = slice_index.size(); - highest_obj_idx = std::find(m_objects.begin(), m_objects.end(), po) - m_objects.begin(); + if (! slice_index.empty()) { + double z = (-- slice_index.end())->first; + size_t cnt = slice_index.size(); + //if (z > max_z) { + if (cnt > max_layers_cnt) { + max_layers_cnt = cnt; + max_z = z; + highest_obj_idx = &po - &m_objects.front(); + } } } @@ -1180,9 +1201,7 @@ void SLAPrint::fill_statistics() int sliced_layer_cnt = 0; for (const auto& layer : highest_obj_slice_index) { - const double l_height = (layer.first == highest_obj_slice_index.begin()->first && - init_layer_height != layer_height) ? - init_layer_height : layer_height; + const double l_height = (layer.first == highest_obj_slice_index.begin()->first) ? init_layer_height : layer_height; // Calculation of the consumed material @@ -1191,31 +1210,21 @@ void SLAPrint::fill_statistics() for (SLAPrintObject * po : m_objects) { - const SLAPrintObject::SliceIndex& index = po->get_slice_index(); - auto key = layer.first; - if (index.find(layer.first) == index.end()) { - const SLAPrintObject::SliceIndex::const_iterator it_key = std::find_if(index.begin(), index.end(), - [key](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(key - id.first) < EPSILON; }); - if (it_key == index.end()) + const SLAPrintObject::SliceRecord *record = nullptr; + { + const SLAPrintObject::SliceIndex& index = po->get_slice_index(); + auto key = layer.first; + const SLAPrintObject::SliceIndex::const_iterator it_key = index.lower_bound(key - float(EPSILON)); + if (it_key == index.end() || it_key->first > key + EPSILON) continue; - key = it_key->first; + record = &it_key->second; } - const SLAPrintObject::SliceRecord& record = index.at(key); - - if (record.model_slices_idx != SLAPrintObject::SliceRecord::NONE) { - const ExPolygons& expolygons = po->get_model_slices().at(record.model_slices_idx); - const ExPolygons model_expolygons = get_all_polygons(expolygons, po->instances()); - - append(model_polygons, to_polygons(model_expolygons)); - } + if (record->model_slices_idx != SLAPrintObject::SliceRecord::NONE) + append(model_polygons, get_all_polygons(po->get_model_slices()[record->model_slices_idx], po->instances())); - if (record.support_slices_idx != SLAPrintObject::SliceRecord::NONE) { - const ExPolygons& expolygons = po->get_support_slices().at(record.support_slices_idx); - const ExPolygons support_expolygons = get_all_polygons(expolygons, po->instances()); - - append(supports_polygons, to_polygons(support_expolygons)); - } + if (record->support_slices_idx != SLAPrintObject::SliceRecord::NONE) + append(supports_polygons, get_all_polygons(po->get_support_slices()[record->support_slices_idx], po->instances())); } model_polygons = union_(model_polygons); @@ -1227,17 +1236,13 @@ void SLAPrint::fill_statistics() models_volume += layer_model_area * l_height; if (!supports_polygons.empty() && !model_polygons.empty()) - append(supports_polygons, model_polygons); - supports_polygons = union_(supports_polygons); + supports_polygons = diff(supports_polygons, model_polygons); double layer_support_area = 0; for (const Polygon& polygon : supports_polygons) layer_support_area += polygon.area(); - if (layer_support_area != 0) { - layer_support_area -= layer_model_area; + if (layer_support_area != 0) supports_volume += layer_support_area * l_height; - } - // Calculation of the slow and fast layers to the future controlling those values on FW diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 570021561..4ac17f7b5 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -2,7 +2,6 @@ #define slic3r_SLAPrint_hpp_ #include -#include "ClipperUtils.hpp" #include "PrintBase.hpp" #include "PrintExport.hpp" #include "Point.hpp" @@ -237,8 +236,7 @@ public: } const PrintObjects& objects() const { return m_objects; } - std::string output_filename() const override - { return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "zip"); } + std::string output_filename() const override; const SLAPrintStatistics& print_statistics() const { return m_print_statistics; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 103463f87..8c035215b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -101,7 +101,7 @@ public: wxStaticText *label_volume; wxStaticText *label_materials; - std::vector sla_hided_items; + std::vector sla_hidden_items; bool showing_manifold_warning_icon; void show_sizer(bool show); @@ -145,7 +145,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : sizer_manifold->Add(info_manifold, 0, wxLEFT, 2); Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4); - sla_hided_items = { label_volume, info_volume, label_materials, info_materials }; + sla_hidden_items = { label_volume, info_volume, label_materials, info_materials }; } void ObjectInfo::show_sizer(bool show) @@ -839,7 +839,7 @@ void Sidebar::show_info_sizer() p->object_info->show_sizer(true); if (p->plater->printer_technology() == ptSLA) { - for (auto item: p->object_info->sla_hided_items) + for (auto item: p->object_info->sla_hidden_items) item->Show(false); } } @@ -876,56 +876,56 @@ void Sidebar::show_sliced_info_sizer(const bool show) } else { - const PrintStatistics& ps = p->plater->fff_print().print_statistics(); - const bool is_wipe_tower = ps.total_wipe_tower_filament > 0; + const PrintStatistics& ps = p->plater->fff_print().print_statistics(); + const bool is_wipe_tower = ps.total_wipe_tower_filament > 0; - wxString new_label = _(L("Used Filament (m)")); - if (is_wipe_tower) - new_label += wxString::Format(" :\n - %s\n - %s", _(L("objects")), _(L("wipe tower"))); + wxString new_label = _(L("Used Filament (m)")); + if (is_wipe_tower) + new_label += wxString::Format(" :\n - %s\n - %s", _(L("objects")), _(L("wipe tower"))); - wxString info_text = is_wipe_tower ? - wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / 1000, - (ps.total_used_filament - ps.total_wipe_tower_filament) / 1000, - ps.total_wipe_tower_filament / 1000) : - wxString::Format("%.2f", ps.total_used_filament / 1000); - p->sliced_info->SetTextAndShow(siFilament_m, info_text, new_label); + wxString info_text = is_wipe_tower ? + wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / 1000, + (ps.total_used_filament - ps.total_wipe_tower_filament) / 1000, + ps.total_wipe_tower_filament / 1000) : + wxString::Format("%.2f", ps.total_used_filament / 1000); + p->sliced_info->SetTextAndShow(siFilament_m, info_text, new_label); - p->sliced_info->SetTextAndShow(siFilament_mm3, wxString::Format("%.2f", ps.total_extruded_volume)); - p->sliced_info->SetTextAndShow(siFilament_g, wxString::Format("%.2f", ps.total_weight)); + p->sliced_info->SetTextAndShow(siFilament_mm3, wxString::Format("%.2f", ps.total_extruded_volume)); + p->sliced_info->SetTextAndShow(siFilament_g, wxString::Format("%.2f", ps.total_weight)); - new_label = _(L("Cost")); - if (is_wipe_tower) - new_label += wxString::Format(" :\n - %s\n - %s", _(L("objects")), _(L("wipe tower"))); - - info_text = is_wipe_tower ? - wxString::Format("%.2f \n%.2f \n%.2f", ps.total_cost, - (ps.total_cost - ps.total_wipe_tower_cost), - ps.total_wipe_tower_cost) : - wxString::Format("%.2f", ps.total_cost); - p->sliced_info->SetTextAndShow(siCost, info_text, new_label); + new_label = _(L("Cost")); + if (is_wipe_tower) + new_label += wxString::Format(" :\n - %s\n - %s", _(L("objects")), _(L("wipe tower"))); + + info_text = is_wipe_tower ? + wxString::Format("%.2f \n%.2f \n%.2f", ps.total_cost, + (ps.total_cost - ps.total_wipe_tower_cost), + ps.total_wipe_tower_cost) : + wxString::Format("%.2f", ps.total_cost); + p->sliced_info->SetTextAndShow(siCost, info_text, new_label); - if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") - p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A"); - else { - new_label = _(L("Estimated printing time")) +" :"; - info_text = ""; - if (ps.estimated_normal_print_time != "N/A") { - new_label += wxString::Format("\n - %s", _(L("normal mode"))); - info_text += wxString::Format("\n%s", ps.estimated_normal_print_time); + if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") + p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A"); + else { + new_label = _(L("Estimated printing time")) +" :"; + info_text = ""; + if (ps.estimated_normal_print_time != "N/A") { + new_label += wxString::Format("\n - %s", _(L("normal mode"))); + info_text += wxString::Format("\n%s", ps.estimated_normal_print_time); + } + if (ps.estimated_silent_print_time != "N/A") { + new_label += wxString::Format("\n - %s", _(L("silent mode"))); + info_text += wxString::Format("\n%s", ps.estimated_silent_print_time); + } + p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); } - if (ps.estimated_silent_print_time != "N/A") { - new_label += wxString::Format("\n - %s", _(L("silent mode"))); - info_text += wxString::Format("\n%s", ps.estimated_silent_print_time); - } - p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); - } - // if there is a wipe tower, insert number of toolchanges info into the array: - p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->fff_print().wipe_tower_data().number_of_toolchanges) : "N/A"); + // if there is a wipe tower, insert number of toolchanges info into the array: + p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->fff_print().wipe_tower_data().number_of_toolchanges) : "N/A"); - // Hide non-FFF sliced info parameters - p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); + // Hide non-FFF sliced info parameters + p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); } } From 051ca410f675b20e2829fdfc96342571abf04427 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 21 Feb 2019 18:31:01 +0100 Subject: [PATCH 19/20] More input handling fixes --- src/slic3r/GUI/GLCanvas3D.cpp | 17 +++++++++++++---- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GUI_Preview.cpp | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ad19b08bd..b194ba138 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4119,6 +4119,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_multisample_allowed(false) , m_regenerate_volumes(true) , m_moving(false) + , m_tab_down(false) , m_color_by("volume") , m_reload_delayed(false) , m_render_sla_auxiliaries(true) @@ -5314,6 +5315,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) void GLCanvas3D::on_key(wxKeyEvent& evt) { + const int keyCode = evt.GetKeyCode(); + #if ENABLE_IMGUI auto imgui = wxGetApp().imgui(); if (imgui->update_key_data(evt)) { @@ -5321,9 +5324,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else #endif // ENABLE_IMGUI if (evt.GetEventType() == wxEVT_KEY_UP) { - const int keyCode = evt.GetKeyCode(); - - if (keyCode == WXK_TAB) { + if (m_tab_down && keyCode == WXK_TAB && !evt.HasAnyModifiers()) { // Enable switching between 3D and Preview with Tab // m_canvas->HandleAsNavigationKey(evt); // XXX: Doesn't work in some cases / on Linux post_event(SimpleEvent(EVT_GLCANVAS_TAB)); @@ -5331,9 +5332,17 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) // shift has been just released - SLA gizmo might want to close rectangular selection. m_dirty = true; } + } else if (evt.GetEventType() == wxEVT_KEY_DOWN) { + m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers(); } - evt.Skip(); // Needed to have EVT_CHAR generated as well + if (keyCode != WXK_TAB + && keyCode != WXK_LEFT + && keyCode != WXK_UP + && keyCode != WXK_RIGHT + && keyCode != WXK_DOWN) { + evt.Skip(); // Needed to have EVT_CHAR generated as well + } } void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index ff136e190..f3e1bc918 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -946,6 +946,7 @@ private: bool m_multisample_allowed; bool m_regenerate_volumes; bool m_moving; + bool m_tab_down; bool m_render_sla_auxiliaries; std::string m_color_by; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index f730496ce..b311cde51 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -52,7 +52,7 @@ View3D::~View3D() bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) { - if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)) + if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */)) return false; m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this); From 14b4685ecb5ee6c0c0ad36789b4e2c4cb29a5d64 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 21 Feb 2019 18:59:21 +0100 Subject: [PATCH 20/20] Scaling in PrintHostDialogs, FirmwareDialog, UpdateDialogs --- src/slic3r/GUI/FirmwareDialog.cpp | 23 ++++++++++++++--------- src/slic3r/GUI/GUI_App.cpp | 1 + src/slic3r/GUI/MsgDialog.hpp | 4 ++-- src/slic3r/GUI/PrintHostDialogs.cpp | 9 +++++---- src/slic3r/GUI/UpdateDialogs.cpp | 14 ++++++++------ 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp index 2df1f0bc9..90e2cbc9c 100644 --- a/src/slic3r/GUI/FirmwareDialog.cpp +++ b/src/slic3r/GUI/FirmwareDialog.cpp @@ -13,6 +13,7 @@ #include "libslic3r/Utils.hpp" #include "avrdude/avrdude-slic3r.hpp" #include "GUI.hpp" +#include "GUI_App.hpp" #include "I18N.hpp" #include "MsgDialog.hpp" #include "../Utils/HexFile.hpp" @@ -36,7 +37,6 @@ #include #include #include -#include "GUI_App.hpp" namespace fs = boost::filesystem; @@ -693,11 +693,16 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) : enum { DIALOG_MARGIN = 15, SPACING = 10, - MIN_WIDTH = 600, - MIN_HEIGHT = 200, - MIN_HEIGHT_EXPANDED = 500, + MIN_WIDTH = 50, + MIN_HEIGHT = 18, + MIN_HEIGHT_EXPANDED = 40, }; + const int em = GUI::wxGetApp().em_unit(); + int min_width = MIN_WIDTH * em; + int min_height = MIN_HEIGHT * em; + int min_height_expanded = MIN_HEIGHT_EXPANDED * em; + wxFont status_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); status_font.MakeBold(); wxFont mono_font(wxFontInfo().Family(wxFONTFAMILY_TELETYPE)); @@ -769,10 +774,10 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) : auto *topsizer = new wxBoxSizer(wxVERTICAL); topsizer->Add(panel, 1, wxEXPAND | wxALL, DIALOG_MARGIN); - SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT)); + SetMinSize(wxSize(min_width, min_height)); SetSizerAndFit(topsizer); const auto size = GetSize(); - SetSize(std::max(size.GetWidth(), static_cast(MIN_WIDTH)), std::max(size.GetHeight(), static_cast(MIN_HEIGHT))); + SetSize(std::max(size.GetWidth(), static_cast(min_width)), std::max(size.GetHeight(), static_cast(min_height))); Layout(); SetEscapeId(wxID_CLOSE); // To close the dialog using "Esc" button @@ -786,13 +791,13 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) : } }); - p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [this](wxCollapsiblePaneEvent &evt) { + p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [=](wxCollapsiblePaneEvent &evt) { if (evt.GetCollapsed()) { - this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT)); + this->SetMinSize(wxSize(min_width, min_height)); const auto new_height = this->GetSize().GetHeight() - this->p->txt_stdout->GetSize().GetHeight(); this->SetSize(this->GetSize().GetWidth(), new_height); } else { - this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT_EXPANDED)); + this->SetMinSize(wxSize(min_width, min_height_expanded)); } this->Layout(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 0e1f73a1a..1847b2898 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -78,6 +78,7 @@ IMPLEMENT_APP(GUI_App) GUI_App::GUI_App() : wxApp() + , m_em_unit(10) #if ENABLE_IMGUI , m_imgui(new ImGuiWrapper()) #endif // ENABLE_IMGUI diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index a5af6afe2..09bb89e4f 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -32,8 +32,8 @@ struct MsgDialog : wxDialog protected: enum { - CONTENT_WIDTH = 50,//500, - CONTENT_MAX_HEIGHT = 60,//600, + CONTENT_WIDTH = 50, + CONTENT_MAX_HEIGHT = 60, BORDER = 30, VERT_SPACING = 15, HORIZ_SPACING = 5, diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 6dfa11889..0d70027c0 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -38,7 +38,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path) #endif auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed."))); - label_dir_hint->Wrap(CONTENT_WIDTH); + label_dir_hint->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(txt_filename, 0, wxEXPAND); content_sizer->Add(label_dir_hint); @@ -135,10 +135,11 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) , on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this) , on_cancel_evt(this, EVT_PRINTHOST_CANCEL, &PrintHostQueueDialog::on_cancel, this) { - enum { HEIGHT = 800, WIDTH = 400, SPACING = 5 }; + enum { HEIGHT = 60, WIDTH = 30, SPACING = 5 }; - SetSize(wxSize(HEIGHT, WIDTH)); - SetSize(GetMinSize()); + const auto em = GetTextExtent("m").x; + + SetSize(wxSize(HEIGHT * em, WIDTH * em)); auto *topsizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index 346a9e231..c4b78eb1a 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -12,6 +12,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" #include "GUI.hpp" +#include "GUI_App.hpp" #include "I18N.hpp" #include "ConfigWizard.hpp" @@ -34,7 +35,8 @@ MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_on auto *text = new wxStaticText(this, wxID_ANY, _(L("To download, follow the link below."))); const auto link_width = link->GetSize().GetWidth(); - text->Wrap(CONTENT_WIDTH > link_width ? CONTENT_WIDTH : link_width); + const int content_width = CONTENT_WIDTH * wxGetApp().em_unit(); + text->Wrap(content_width > link_width ? content_width : link_width); content_sizer->Add(text); content_sizer->AddSpacer(VERT_SPACING); @@ -75,7 +77,7 @@ MsgUpdateConfig::MsgUpdateConfig(const std::unordered_mapWrap(CONTENT_WIDTH); + text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text); content_sizer->AddSpacer(VERT_SPACING); @@ -115,16 +117,16 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_mapWrap(CONTENT_WIDTH); + text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text); auto *text2 = new wxStaticText(this, wxID_ANY, wxString::Format(_(L("This Slic3r PE version: %s")), SLIC3R_VERSION)); - text2->Wrap(CONTENT_WIDTH); + text2->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text2); content_sizer->AddSpacer(VERT_SPACING); auto *text3 = new wxStaticText(this, wxID_ANY, _(L("Incompatible bundles:"))); - text3->Wrap(CONTENT_WIDTH); + text3->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text3); content_sizer->AddSpacer(VERT_SPACING); @@ -175,7 +177,7 @@ MsgDataLegacy::MsgDataLegacy() : )), ConfigWizard::name() )); - text->Wrap(CONTENT_WIDTH); + text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text); content_sizer->AddSpacer(VERT_SPACING);