mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into feature_arrange_with_libnest2d
This commit is contained in:
		
						commit
						c430f57187
					
				
					 40 changed files with 829 additions and 653 deletions
				
			
		| 
						 | 
				
			
			@ -25,6 +25,9 @@ THE SOFTWARE.
 | 
			
		|||
#ifndef SHINY_PREREQS_H
 | 
			
		||||
#define SHINY_PREREQS_H
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/*---------------------------------------------------------------------------*/
 | 
			
		||||
 | 
			
		||||
#ifndef FALSE
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,8 +93,11 @@ float ShinyGetTickInvFreq(void) {
 | 
			
		|||
 | 
			
		||||
#elif SHINY_PLATFORM == SHINY_PLATFORM_POSIX
 | 
			
		||||
 | 
			
		||||
//#include <time.h>
 | 
			
		||||
//#include <sys/time.h>
 | 
			
		||||
 | 
			
		||||
void ShinyGetTicks(shinytick_t *p) {
 | 
			
		||||
	timeval time;
 | 
			
		||||
	struct timeval time;
 | 
			
		||||
	gettimeofday(&time, NULL);
 | 
			
		||||
 | 
			
		||||
	*p = time.tv_sec * 1000000 + time.tv_usec;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,6 +103,10 @@ public:
 | 
			
		|||
    bool contains(const BoundingBox3Base<PointClass>& other) const {
 | 
			
		||||
        return contains(other.min) && contains(other.max);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool intersects(const BoundingBox3Base<PointClass>& other) const {
 | 
			
		||||
        return (this->min.x < other.max.x) && (this->max.x > other.min.x) && (this->min.y < other.max.y) && (this->max.y > other.min.y) && (this->min.z < other.max.z) && (this->max.z > other.min.z);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BoundingBox : public BoundingBoxBase<Point>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1989,7 +1989,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
                // stores object's name
 | 
			
		||||
                if (!obj->name.empty())
 | 
			
		||||
                    stream << "  <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << obj->name << "\"/>\n";
 | 
			
		||||
                    stream << "  <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
 | 
			
		||||
 | 
			
		||||
                // stores object's config data
 | 
			
		||||
                for (const std::string& key : obj->config.keys())
 | 
			
		||||
| 
						 | 
				
			
			@ -2012,7 +2012,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
                            // stores volume's name
 | 
			
		||||
                            if (!volume->name.empty())
 | 
			
		||||
                                stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << volume->name << "\"/>\n";
 | 
			
		||||
                                stream << "   <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
 | 
			
		||||
 | 
			
		||||
                            // stores volume's modifier field
 | 
			
		||||
                            if (volume->modifier)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
#include "../libslic3r.h"
 | 
			
		||||
#include "../Model.hpp"
 | 
			
		||||
#include "../GCode.hpp"
 | 
			
		||||
#include "../Utils.hpp"
 | 
			
		||||
#include "../slic3r/GUI/PresetBundle.hpp"
 | 
			
		||||
#include "AMF.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -686,33 +687,6 @@ bool load_amf(const char *path, PresetBundle* bundle, Model *model)
 | 
			
		|||
        return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string xml_escape(std::string text)
 | 
			
		||||
{
 | 
			
		||||
    std::string::size_type pos = 0;
 | 
			
		||||
    for (;;)
 | 
			
		||||
    {
 | 
			
		||||
        pos = text.find_first_of("\"\'&<>", pos);
 | 
			
		||||
        if (pos == std::string::npos)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        std::string replacement;
 | 
			
		||||
        switch (text[pos])
 | 
			
		||||
        {
 | 
			
		||||
        case '\"': replacement = """; break;
 | 
			
		||||
        case '\'': replacement = "'"; break;
 | 
			
		||||
        case '&':  replacement = "&";  break;
 | 
			
		||||
        case '<':  replacement = "<";   break;
 | 
			
		||||
        case '>':  replacement = ">";   break;
 | 
			
		||||
        default: break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        text.replace(pos, 1, replacement);
 | 
			
		||||
        pos += replacement.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool store_amf(const char *path, Model *model, Print* print, bool export_print_config)
 | 
			
		||||
{
 | 
			
		||||
    if ((path == nullptr) || (model == nullptr) || (print == nullptr))
 | 
			
		||||
| 
						 | 
				
			
			@ -761,7 +735,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
 | 
			
		|||
        for (const std::string &key : object->config.keys())
 | 
			
		||||
            stream << "    <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
 | 
			
		||||
        if (!object->name.empty())
 | 
			
		||||
            stream << "    <metadata type=\"name\">" << object->name << "</metadata>\n";
 | 
			
		||||
            stream << "    <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
 | 
			
		||||
        std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
 | 
			
		||||
        if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
 | 
			
		||||
            // Store the layer height profile as a single semicolon separated list.
 | 
			
		||||
| 
						 | 
				
			
			@ -805,7 +779,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
 | 
			
		|||
            for (const std::string &key : volume->config.keys())
 | 
			
		||||
                stream << "        <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
 | 
			
		||||
            if (!volume->name.empty())
 | 
			
		||||
                stream << "        <metadata type=\"name\">" << volume->name << "</metadata>\n";
 | 
			
		||||
                stream << "        <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
 | 
			
		||||
            if (volume->modifier)
 | 
			
		||||
                stream << "        <metadata type=\"slic3r.modifier\">1</metadata>\n";
 | 
			
		||||
            for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -309,10 +309,12 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
 | 
			
		|||
        size_t      object_idx;
 | 
			
		||||
        size_t      layer_idx;
 | 
			
		||||
    };
 | 
			
		||||
    std::vector<std::vector<LayerToPrint>>  per_object(print.objects.size(), std::vector<LayerToPrint>());
 | 
			
		||||
 | 
			
		||||
    PrintObjectPtrs printable_objects = print.get_printable_objects();
 | 
			
		||||
    std::vector<std::vector<LayerToPrint>>  per_object(printable_objects.size(), std::vector<LayerToPrint>());
 | 
			
		||||
    std::vector<OrderingItem>               ordering;
 | 
			
		||||
    for (size_t i = 0; i < print.objects.size(); ++ i) {
 | 
			
		||||
        per_object[i] = collect_layers_to_print(*print.objects[i]);
 | 
			
		||||
    for (size_t i = 0; i < printable_objects.size(); ++i) {
 | 
			
		||||
        per_object[i] = collect_layers_to_print(*printable_objects[i]);
 | 
			
		||||
        OrderingItem ordering_item;
 | 
			
		||||
        ordering_item.object_idx = i;
 | 
			
		||||
        ordering.reserve(ordering.size() + per_object[i].size());
 | 
			
		||||
| 
						 | 
				
			
			@ -337,8 +339,8 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
 | 
			
		|||
        std::pair<coordf_t, std::vector<LayerToPrint>> merged;
 | 
			
		||||
        // Assign an average print_z to the set of layers with nearly equal print_z.
 | 
			
		||||
        merged.first = 0.5 * (ordering[i].print_z + ordering[j-1].print_z);
 | 
			
		||||
        merged.second.assign(print.objects.size(), LayerToPrint());
 | 
			
		||||
        for (; i < j; ++ i) {
 | 
			
		||||
        merged.second.assign(printable_objects.size(), LayerToPrint());
 | 
			
		||||
        for (; i < j; ++i) {
 | 
			
		||||
            const OrderingItem &oi = ordering[i];
 | 
			
		||||
            assert(merged.second[oi.object_idx].layer() == nullptr);
 | 
			
		||||
            merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]);
 | 
			
		||||
| 
						 | 
				
			
			@ -375,10 +377,13 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
 | 
			
		|||
    }
 | 
			
		||||
    fclose(file);
 | 
			
		||||
 | 
			
		||||
    m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
 | 
			
		||||
    if (print->config.gcode_flavor.value == gcfMarlin)
 | 
			
		||||
    {
 | 
			
		||||
        m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
 | 
			
		||||
 | 
			
		||||
    if (m_silent_time_estimator_enabled)
 | 
			
		||||
        m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
 | 
			
		||||
        if (m_silent_time_estimator_enabled)
 | 
			
		||||
            m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (! this->m_placeholder_parser_failed_templates.empty()) {
 | 
			
		||||
        // G-code export proceeded, but some of the PlaceholderParser substitutions failed.
 | 
			
		||||
| 
						 | 
				
			
			@ -472,9 +477,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
    // How many times will be change_layer() called?
 | 
			
		||||
    // change_layer() in turn increments the progress bar status.
 | 
			
		||||
    m_layer_count = 0;
 | 
			
		||||
    PrintObjectPtrs printable_objects = print.get_printable_objects();
 | 
			
		||||
    if (print.config.complete_objects.value) {
 | 
			
		||||
        // Add each of the object's layers separately.
 | 
			
		||||
        for (auto object : print.objects) {
 | 
			
		||||
        for (auto object : printable_objects) {
 | 
			
		||||
            std::vector<coordf_t> zs;
 | 
			
		||||
            zs.reserve(object->layers.size() + object->support_layers.size());
 | 
			
		||||
            for (auto layer : object->layers)
 | 
			
		||||
| 
						 | 
				
			
			@ -487,7 +493,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
    } else {
 | 
			
		||||
        // Print all objects with the same print_z together.
 | 
			
		||||
        std::vector<coordf_t> zs;
 | 
			
		||||
        for (auto object : print.objects) {
 | 
			
		||||
        for (auto object : printable_objects) {
 | 
			
		||||
            zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
 | 
			
		||||
            for (auto layer : object->layers)
 | 
			
		||||
                zs.push_back(layer->print_z);
 | 
			
		||||
| 
						 | 
				
			
			@ -506,8 +512,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
    {
 | 
			
		||||
        // get the minimum cross-section used in the print
 | 
			
		||||
        std::vector<double> mm3_per_mm;
 | 
			
		||||
        for (auto object : print.objects) {
 | 
			
		||||
            for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
 | 
			
		||||
        for (auto object : printable_objects) {
 | 
			
		||||
            for (size_t region_id = 0; region_id < print.regions.size(); ++region_id) {
 | 
			
		||||
                auto region = print.regions[region_id];
 | 
			
		||||
                for (auto layer : object->layers) {
 | 
			
		||||
                    auto layerm = layer->regions[region_id];
 | 
			
		||||
| 
						 | 
				
			
			@ -567,7 +573,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
            _write(file, "\n");
 | 
			
		||||
    }
 | 
			
		||||
    // Write some terse information on the slicing parameters.
 | 
			
		||||
    const PrintObject *first_object         = print.objects.front();
 | 
			
		||||
    const PrintObject *first_object         = printable_objects.front();
 | 
			
		||||
    const double       layer_height         = first_object->config.layer_height.value;
 | 
			
		||||
    const double       first_layer_height   = first_object->config.first_layer_height.get_abs_value(layer_height);
 | 
			
		||||
    for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
 | 
			
		||||
| 
						 | 
				
			
			@ -596,13 +602,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
    size_t       initial_print_object_id = 0;
 | 
			
		||||
    bool         has_wipe_tower      = false;
 | 
			
		||||
    if (print.config.complete_objects.value) {
 | 
			
		||||
		// Find the 1st printing object, find its tool ordering and the initial extruder ID.
 | 
			
		||||
		for (; initial_print_object_id < print.objects.size(); ++initial_print_object_id) {
 | 
			
		||||
			tool_ordering = ToolOrdering(*print.objects[initial_print_object_id], initial_extruder_id);
 | 
			
		||||
			if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
        // Find the 1st printing object, find its tool ordering and the initial extruder ID.
 | 
			
		||||
        for (; initial_print_object_id < printable_objects.size(); ++initial_print_object_id) {
 | 
			
		||||
            tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id);
 | 
			
		||||
            if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
		// Find tool ordering for all the objects at once, and the initial extruder ID.
 | 
			
		||||
        // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
 | 
			
		||||
		tool_ordering = print.m_tool_ordering.empty() ?
 | 
			
		||||
| 
						 | 
				
			
			@ -676,7 +683,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
        // Collect outer contours of all objects over all layers.
 | 
			
		||||
        // Discard objects only containing thin walls (offset would fail on an empty polygon).
 | 
			
		||||
        Polygons islands;
 | 
			
		||||
        for (const PrintObject *object : print.objects)
 | 
			
		||||
        for (const PrintObject *object : printable_objects)
 | 
			
		||||
            for (const Layer *layer : object->layers)
 | 
			
		||||
                for (const ExPolygon &expoly : layer->slices.expolygons)
 | 
			
		||||
                    for (const Point © : object->_shifted_copies) {
 | 
			
		||||
| 
						 | 
				
			
			@ -724,7 +731,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
    if (print.config.complete_objects.value) {
 | 
			
		||||
        // Print objects from the smallest to the tallest to avoid collisions
 | 
			
		||||
        // when moving onto next object starting point.
 | 
			
		||||
        std::vector<PrintObject*> objects(print.objects);
 | 
			
		||||
        std::vector<PrintObject*> objects(printable_objects);
 | 
			
		||||
        std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; });        
 | 
			
		||||
        size_t finished_objects = 0;
 | 
			
		||||
        for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
 | 
			
		||||
| 
						 | 
				
			
			@ -785,7 +792,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
        // Order objects using a nearest neighbor search.
 | 
			
		||||
        std::vector<size_t> object_indices;
 | 
			
		||||
        Points object_reference_points;
 | 
			
		||||
        for (PrintObject *object : print.objects)
 | 
			
		||||
        PrintObjectPtrs printable_objects = print.get_printable_objects();
 | 
			
		||||
        for (PrintObject *object : printable_objects)
 | 
			
		||||
            object_reference_points.push_back(object->_shifted_copies.front());
 | 
			
		||||
        Slic3r::Geometry::chained_path(object_reference_points, object_indices);
 | 
			
		||||
        // Sort layers by Z.
 | 
			
		||||
| 
						 | 
				
			
			@ -799,7 +807,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
            // Verify, whether the print overaps the priming extrusions.
 | 
			
		||||
            BoundingBoxf bbox_print(get_print_extrusions_extents(print));
 | 
			
		||||
            coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
 | 
			
		||||
            for (const PrintObject *print_object : print.objects)
 | 
			
		||||
            for (const PrintObject *print_object : printable_objects)
 | 
			
		||||
                bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
 | 
			
		||||
            bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
 | 
			
		||||
            BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
 | 
			
		||||
| 
						 | 
				
			
			@ -862,9 +870,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
 | 
			
		|||
    _write(file, m_writer.postamble());
 | 
			
		||||
 | 
			
		||||
    // calculates estimated printing time
 | 
			
		||||
    m_normal_time_estimator.calculate_time();
 | 
			
		||||
    m_normal_time_estimator.calculate_time(false);
 | 
			
		||||
    if (m_silent_time_estimator_enabled)
 | 
			
		||||
        m_silent_time_estimator.calculate_time();
 | 
			
		||||
        m_silent_time_estimator.calculate_time(false);
 | 
			
		||||
 | 
			
		||||
    // Get filament stats.
 | 
			
		||||
    print.filament_stats.clear();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,11 +67,13 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
 | 
			
		|||
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
 | 
			
		||||
{
 | 
			
		||||
    m_print_config_ptr = &print.config;
 | 
			
		||||
 | 
			
		||||
    PrintObjectPtrs objects = print.get_printable_objects();
 | 
			
		||||
    // Initialize the print layers for all objects and all layers.
 | 
			
		||||
    coordf_t object_bottom_z = 0.;
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<coordf_t> zs;
 | 
			
		||||
        for (auto object : print.objects) {
 | 
			
		||||
        for (auto object : objects) {
 | 
			
		||||
            zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
 | 
			
		||||
            for (auto layer : object->layers)
 | 
			
		||||
                zs.emplace_back(layer->print_z);
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +86,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    // Collect extruders reuqired to print the layers.
 | 
			
		||||
    for (auto object : print.objects)
 | 
			
		||||
    for (auto object : objects)
 | 
			
		||||
        this->collect_extruders(*object);
 | 
			
		||||
 | 
			
		||||
    // Reorder the extruders to minimize tool switches.
 | 
			
		||||
| 
						 | 
				
			
			@ -449,10 +451,9 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
 | 
			
		|||
        return volume_to_wipe;      // Soluble filament cannot be wiped in a random infill, neither the filament after it
 | 
			
		||||
 | 
			
		||||
    // we will sort objects so that dedicated for wiping are at the beginning:
 | 
			
		||||
    PrintObjectPtrs object_list = print.objects;
 | 
			
		||||
    PrintObjectPtrs object_list = print.get_printable_objects();
 | 
			
		||||
    std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // We will now iterate through
 | 
			
		||||
    //  - first the dedicated objects to mark perimeters or infills (depending on infill_first)
 | 
			
		||||
    //  - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
 | 
			
		||||
| 
						 | 
				
			
			@ -546,7 +547,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
 | 
			
		|||
    unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config);
 | 
			
		||||
    unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config);
 | 
			
		||||
 | 
			
		||||
    for (const PrintObject* object : print.objects) {
 | 
			
		||||
    PrintObjectPtrs printable_objects = print.get_printable_objects();
 | 
			
		||||
    for (const PrintObject* object : printable_objects) {
 | 
			
		||||
        // Finds this layer:
 | 
			
		||||
        auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
 | 
			
		||||
        if (this_layer_it == object->layers.end())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,8 +66,10 @@ public:
 | 
			
		|||
        wipe_tower_partitions(0),
 | 
			
		||||
        wipe_tower_layer_height(0.) {}
 | 
			
		||||
 | 
			
		||||
    bool operator< (const LayerTools &rhs) const { return print_z - EPSILON <  rhs.print_z; }
 | 
			
		||||
    bool operator==(const LayerTools &rhs) const { return std::abs(print_z - rhs.print_z) < EPSILON; }
 | 
			
		||||
    // Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other.
 | 
			
		||||
    // In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports).
 | 
			
		||||
    bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
 | 
			
		||||
    bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
 | 
			
		||||
 | 
			
		||||
    bool is_extruder_order(unsigned int a, unsigned int b) const;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -490,7 +490,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
 | 
			
		|||
//	box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
 | 
			
		||||
 | 
			
		||||
	const float prime_section_width = std::min(240.f / tools.size(), 60.f);
 | 
			
		||||
	box_coordinates cleaning_box(xy(5.f, 0.f), prime_section_width, 100.f);
 | 
			
		||||
	box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
 | 
			
		||||
 | 
			
		||||
	PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
 | 
			
		||||
	writer.set_extrusion_flow(m_extrusion_flow)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,6 @@ namespace Slic3r {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    GCodeTimeEstimator::Block::Block()
 | 
			
		||||
        : st_synchronized(false)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -196,11 +195,14 @@ namespace Slic3r {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::calculate_time()
 | 
			
		||||
    void GCodeTimeEstimator::calculate_time(bool start_from_beginning)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        _reset_time();
 | 
			
		||||
        _set_blocks_st_synchronize(false);
 | 
			
		||||
        if (start_from_beginning)
 | 
			
		||||
        {
 | 
			
		||||
            _reset_time();
 | 
			
		||||
            _last_st_synchronized_block_id = -1;
 | 
			
		||||
        }
 | 
			
		||||
        _calculate_time();
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MOVE_STATS
 | 
			
		||||
| 
						 | 
				
			
			@ -484,6 +486,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    GCodeFlavor GCodeTimeEstimator::get_dialect() const
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        return _state.dialect;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -534,6 +537,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::add_additional_time(float timeSec)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        _state.additional_time += timeSec;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -611,6 +615,8 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
        reset_g1_line_id();
 | 
			
		||||
        _g1_line_ids.clear();
 | 
			
		||||
 | 
			
		||||
        _last_st_synchronized_block_id = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_reset_time()
 | 
			
		||||
| 
						 | 
				
			
			@ -623,26 +629,19 @@ namespace Slic3r {
 | 
			
		|||
        _blocks.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_set_blocks_st_synchronize(bool state)
 | 
			
		||||
    {
 | 
			
		||||
        for (Block& block : _blocks)
 | 
			
		||||
        {
 | 
			
		||||
            block.st_synchronized = state;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_calculate_time()
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        _forward_pass();
 | 
			
		||||
        _reverse_pass();
 | 
			
		||||
        _recalculate_trapezoids();
 | 
			
		||||
 | 
			
		||||
        _time += get_additional_time();
 | 
			
		||||
 | 
			
		||||
        for (Block& block : _blocks)
 | 
			
		||||
        for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            if (block.st_synchronized)
 | 
			
		||||
                continue;
 | 
			
		||||
            Block& block = _blocks[i];
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MOVE_STATS
 | 
			
		||||
            float block_time = 0.0f;
 | 
			
		||||
| 
						 | 
				
			
			@ -665,6 +664,8 @@ namespace Slic3r {
 | 
			
		|||
            block.elapsed_time = _time;
 | 
			
		||||
#endif // ENABLE_MOVE_STATS
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _last_st_synchronized_block_id = _blocks.size() - 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line)
 | 
			
		||||
| 
						 | 
				
			
			@ -800,6 +801,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        increment_g1_line_id();
 | 
			
		||||
 | 
			
		||||
        // updates axes positions from line
 | 
			
		||||
| 
						 | 
				
			
			@ -997,6 +999,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        GCodeFlavor dialect = get_dialect();
 | 
			
		||||
 | 
			
		||||
        float value;
 | 
			
		||||
| 
						 | 
				
			
			@ -1018,31 +1021,37 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_processG20(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        set_units(Inches);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_processG21(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        set_units(Millimeters);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_processG28(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        // TODO
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_processG90(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        set_global_positioning_type(Absolute);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_processG91(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        set_global_positioning_type(Relative);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_processG92(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        float lengthsScaleFactor = (get_units() == Inches) ? INCHES_TO_MM : 1.0f;
 | 
			
		||||
        bool anyFound = false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1083,26 +1092,31 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_processM1(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        _simulate_st_synchronize();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_processM82(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        set_e_local_positioning_type(Absolute);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_processM83(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        set_e_local_positioning_type(Relative);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_processM109(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        // TODO
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_processM201(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        GCodeFlavor dialect = get_dialect();
 | 
			
		||||
 | 
			
		||||
        // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
 | 
			
		||||
| 
						 | 
				
			
			@ -1123,6 +1137,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_processM203(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        GCodeFlavor dialect = get_dialect();
 | 
			
		||||
 | 
			
		||||
        // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate
 | 
			
		||||
| 
						 | 
				
			
			@ -1147,6 +1162,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_processM204(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        float value;
 | 
			
		||||
        if (line.has_value('S', value))
 | 
			
		||||
            set_acceleration(value);
 | 
			
		||||
| 
						 | 
				
			
			@ -1157,6 +1173,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_processM205(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        if (line.has_x())
 | 
			
		||||
        {
 | 
			
		||||
            float max_jerk = line.x();
 | 
			
		||||
| 
						 | 
				
			
			@ -1183,6 +1200,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_processM221(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        float value_s;
 | 
			
		||||
        float value_t;
 | 
			
		||||
        if (line.has_value('S', value_s) && !line.has_value('T', value_t))
 | 
			
		||||
| 
						 | 
				
			
			@ -1191,6 +1209,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_processM566(const GCodeReader::GCodeLine& line)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        if (line.has_x())
 | 
			
		||||
            set_axis_max_jerk(X, line.x() * MMMIN_TO_MMSEC);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1206,19 +1225,17 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_simulate_st_synchronize()
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        _calculate_time();
 | 
			
		||||
        _set_blocks_st_synchronize(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GCodeTimeEstimator::_forward_pass()
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        if (_blocks.size() > 1)
 | 
			
		||||
        {
 | 
			
		||||
            for (unsigned int i = 0; i < (unsigned int)_blocks.size() - 1; ++i)
 | 
			
		||||
            { 
 | 
			
		||||
                if (_blocks[i].st_synchronized || _blocks[i + 1].st_synchronized)
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
            for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size() - 1; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                _planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1226,13 +1243,11 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_reverse_pass()
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        if (_blocks.size() > 1)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = (int)_blocks.size() - 1; i >= 1;  --i)
 | 
			
		||||
            for (int i = (int)_blocks.size() - 1; i >= _last_st_synchronized_block_id + 2; --i)
 | 
			
		||||
            {
 | 
			
		||||
                if (_blocks[i - 1].st_synchronized || _blocks[i].st_synchronized)
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                _planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1240,6 +1255,7 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_planner_forward_pass_kernel(Block& prev, Block& curr)
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        // If the previous block is an acceleration block, but it is not long enough to complete the
 | 
			
		||||
        // full speed change within the block, we need to adjust the entry speed accordingly. Entry
 | 
			
		||||
        // speeds have already been reset, maximized, and reverse planned by reverse planner.
 | 
			
		||||
| 
						 | 
				
			
			@ -1280,13 +1296,13 @@ namespace Slic3r {
 | 
			
		|||
 | 
			
		||||
    void GCodeTimeEstimator::_recalculate_trapezoids()
 | 
			
		||||
    {
 | 
			
		||||
        PROFILE_FUNC();
 | 
			
		||||
        Block* curr = nullptr;
 | 
			
		||||
        Block* next = nullptr;
 | 
			
		||||
 | 
			
		||||
        for (Block& b : _blocks)
 | 
			
		||||
        for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            if (b.st_synchronized)
 | 
			
		||||
                continue;
 | 
			
		||||
            Block& b = _blocks[i];
 | 
			
		||||
 | 
			
		||||
            curr = next;
 | 
			
		||||
            next = &b;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -144,8 +144,6 @@ namespace Slic3r {
 | 
			
		|||
            Trapezoid trapezoid;
 | 
			
		||||
            float elapsed_time;
 | 
			
		||||
 | 
			
		||||
            bool st_synchronized;
 | 
			
		||||
 | 
			
		||||
            Block();
 | 
			
		||||
 | 
			
		||||
            // Returns the length of the move covered by this block, in mm
 | 
			
		||||
| 
						 | 
				
			
			@ -211,6 +209,8 @@ namespace Slic3r {
 | 
			
		|||
        BlocksList _blocks;
 | 
			
		||||
        // Map between g1 line id and blocks id, used to speed up export of remaining times
 | 
			
		||||
        G1LineIdToBlockIdMap _g1_line_ids;
 | 
			
		||||
        // Index of the last block already st_synchronized
 | 
			
		||||
        int _last_st_synchronized_block_id;
 | 
			
		||||
        float _time; // s
 | 
			
		||||
 | 
			
		||||
#if ENABLE_MOVE_STATS
 | 
			
		||||
| 
						 | 
				
			
			@ -227,7 +227,10 @@ namespace Slic3r {
 | 
			
		|||
        void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); }
 | 
			
		||||
 | 
			
		||||
        // Calculates the time estimate from the gcode lines added using add_gcode_line() or add_gcode_block()
 | 
			
		||||
        void calculate_time();
 | 
			
		||||
        // start_from_beginning:
 | 
			
		||||
        // if set to true all blocks will be used to calculate the time estimate,
 | 
			
		||||
        // if set to false only the blocks not yet processed will be used and the calculated time will be added to the current calculated time
 | 
			
		||||
        void calculate_time(bool start_from_beginning);
 | 
			
		||||
 | 
			
		||||
        // Calculates the time estimate from the given gcode in string format
 | 
			
		||||
        void calculate_time_from_text(const std::string& gcode);
 | 
			
		||||
| 
						 | 
				
			
			@ -320,8 +323,6 @@ namespace Slic3r {
 | 
			
		|||
        void _reset_time();
 | 
			
		||||
        void _reset_blocks();
 | 
			
		||||
 | 
			
		||||
        void _set_blocks_st_synchronize(bool state);
 | 
			
		||||
 | 
			
		||||
        // Calculates the time estimate
 | 
			
		||||
        void _calculate_time();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1235,6 +1235,59 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
 | 
			
		|||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
 | 
			
		||||
{
 | 
			
		||||
    for (ModelVolume* vol : this->volumes)
 | 
			
		||||
    {
 | 
			
		||||
        if (!vol->modifier)
 | 
			
		||||
        {
 | 
			
		||||
            for (ModelInstance* inst : this->instances)
 | 
			
		||||
            {
 | 
			
		||||
                BoundingBoxf3 bb;
 | 
			
		||||
 | 
			
		||||
                double c = cos(inst->rotation);
 | 
			
		||||
                double s = sin(inst->rotation);
 | 
			
		||||
 | 
			
		||||
                for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
 | 
			
		||||
                {
 | 
			
		||||
                    const stl_facet& facet = vol->mesh.stl.facet_start[f];
 | 
			
		||||
 | 
			
		||||
                    for (int i = 0; i < 3; ++i)
 | 
			
		||||
                    {
 | 
			
		||||
                        // original point
 | 
			
		||||
                        const stl_vertex& v = facet.vertex[i];
 | 
			
		||||
                        Pointf3 p((double)v.x, (double)v.y, (double)v.z);
 | 
			
		||||
 | 
			
		||||
                        // scale
 | 
			
		||||
                        p.x *= inst->scaling_factor;
 | 
			
		||||
                        p.y *= inst->scaling_factor;
 | 
			
		||||
                        p.z *= inst->scaling_factor;
 | 
			
		||||
 | 
			
		||||
                        // rotate Z
 | 
			
		||||
                        double x = p.x;
 | 
			
		||||
                        double y = p.y;
 | 
			
		||||
                        p.x = c * x - s * y;
 | 
			
		||||
                        p.y = s * x + c * y;
 | 
			
		||||
 | 
			
		||||
                        // translate
 | 
			
		||||
                        p.x += inst->offset.x;
 | 
			
		||||
                        p.y += inst->offset.y;
 | 
			
		||||
 | 
			
		||||
                        bb.merge(p);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (print_volume.contains(bb))
 | 
			
		||||
                    inst->print_volume_state = ModelInstance::PVS_Inside;
 | 
			
		||||
                else if (print_volume.intersects(bb))
 | 
			
		||||
                    inst->print_volume_state = ModelInstance::PVS_Partly_Outside;
 | 
			
		||||
                else
 | 
			
		||||
                    inst->print_volume_state = ModelInstance::PVS_Fully_Outside;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ModelObject::print_info() const
 | 
			
		||||
{
 | 
			
		||||
    using namespace std;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -132,6 +132,8 @@ public:
 | 
			
		|||
    void cut(coordf_t z, Model* model) const;
 | 
			
		||||
    void split(ModelObjectPtrs* new_objects);
 | 
			
		||||
 | 
			
		||||
    void check_instances_print_volume_state(const BoundingBoxf3& print_volume);
 | 
			
		||||
 | 
			
		||||
    // Print object statistics to console.
 | 
			
		||||
    void print_info() const;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -197,12 +199,24 @@ private:
 | 
			
		|||
// Knows the affine transformation of an object.
 | 
			
		||||
class ModelInstance
 | 
			
		||||
{
 | 
			
		||||
    friend class ModelObject;
 | 
			
		||||
public:
 | 
			
		||||
    enum EPrintVolumeState : unsigned char
 | 
			
		||||
    {
 | 
			
		||||
        PVS_Inside,
 | 
			
		||||
        PVS_Partly_Outside,
 | 
			
		||||
        PVS_Fully_Outside,
 | 
			
		||||
        Num_BedStates
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    friend class ModelObject;
 | 
			
		||||
 | 
			
		||||
    double rotation;            // Rotation around the Z axis, in radians around mesh center point
 | 
			
		||||
    double scaling_factor;
 | 
			
		||||
    Pointf offset;              // in unscaled coordinates
 | 
			
		||||
    
 | 
			
		||||
    // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
 | 
			
		||||
    EPrintVolumeState print_volume_state;
 | 
			
		||||
 | 
			
		||||
    ModelObject* get_object() const { return this->object; }
 | 
			
		||||
 | 
			
		||||
    // To be called on an external mesh
 | 
			
		||||
| 
						 | 
				
			
			@ -213,14 +227,16 @@ public:
 | 
			
		|||
    BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const;
 | 
			
		||||
    // To be called on an external polygon. It does not translate the polygon, only rotates and scales.
 | 
			
		||||
    void transform_polygon(Polygon* polygon) const;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    bool is_printable() const { return print_volume_state == PVS_Inside; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    // Parent object, owning this instance.
 | 
			
		||||
    ModelObject* object;
 | 
			
		||||
 | 
			
		||||
    ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object) {}
 | 
			
		||||
    ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object), print_volume_state(PVS_Inside) {}
 | 
			
		||||
    ModelInstance(ModelObject *object, const ModelInstance &other) :
 | 
			
		||||
        rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object) {}
 | 
			
		||||
        rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,6 +71,13 @@ bool Print::reload_model_instances()
 | 
			
		|||
    return invalidated;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PrintObjectPtrs Print::get_printable_objects() const
 | 
			
		||||
{
 | 
			
		||||
    PrintObjectPtrs printable_objects(this->objects);
 | 
			
		||||
    printable_objects.erase(std::remove_if(printable_objects.begin(), printable_objects.end(), [](PrintObject* o) { return !o->is_printable(); }), printable_objects.end());
 | 
			
		||||
    return printable_objects;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PrintRegion* Print::add_region()
 | 
			
		||||
{
 | 
			
		||||
    regions.push_back(new PrintRegion(this));
 | 
			
		||||
| 
						 | 
				
			
			@ -534,11 +541,17 @@ std::string Print::validate() const
 | 
			
		|||
    BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height));
 | 
			
		||||
    // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
 | 
			
		||||
    print_volume.min.z = -1e10;
 | 
			
		||||
    unsigned int printable_count = 0;
 | 
			
		||||
    for (PrintObject *po : this->objects) {
 | 
			
		||||
        if (!print_volume.contains(po->model_object()->tight_bounding_box(false)))
 | 
			
		||||
            return L("Some objects are outside of the print volume.");
 | 
			
		||||
        po->model_object()->check_instances_print_volume_state(print_volume);
 | 
			
		||||
        po->reload_model_instances();
 | 
			
		||||
        if (po->is_printable())
 | 
			
		||||
            ++printable_count;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (printable_count == 0)
 | 
			
		||||
        return L("All objects are outside of the print volume.");
 | 
			
		||||
 | 
			
		||||
    if (this->config.complete_objects) {
 | 
			
		||||
        // Check horizontal clearance.
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -858,8 +871,9 @@ void Print::_make_skirt()
 | 
			
		|||
    // prepended to the first 'n' layers (with 'n' = skirt_height).
 | 
			
		||||
    // $skirt_height_z in this case is the highest possible skirt height for safety.
 | 
			
		||||
    coordf_t skirt_height_z = 0.;
 | 
			
		||||
    for (const PrintObject *object : this->objects) {
 | 
			
		||||
        size_t skirt_layers = this->has_infinite_skirt() ? 
 | 
			
		||||
    PrintObjectPtrs printable_objects = get_printable_objects();
 | 
			
		||||
    for (const PrintObject *object : printable_objects) {
 | 
			
		||||
        size_t skirt_layers = this->has_infinite_skirt() ?
 | 
			
		||||
            object->layer_count() : 
 | 
			
		||||
            std::min(size_t(this->config.skirt_height.value), object->layer_count());
 | 
			
		||||
        skirt_height_z = std::max(skirt_height_z, object->layers[skirt_layers-1]->print_z);
 | 
			
		||||
| 
						 | 
				
			
			@ -867,7 +881,7 @@ void Print::_make_skirt()
 | 
			
		|||
    
 | 
			
		||||
    // Collect points from all layers contained in skirt height.
 | 
			
		||||
    Points points;
 | 
			
		||||
    for (const PrintObject *object : this->objects) {
 | 
			
		||||
    for (const PrintObject *object : printable_objects) {
 | 
			
		||||
        Points object_points;
 | 
			
		||||
        // Get object layers up to skirt_height_z.
 | 
			
		||||
        for (const Layer *layer : object->layers) {
 | 
			
		||||
| 
						 | 
				
			
			@ -980,7 +994,8 @@ void Print::_make_brim()
 | 
			
		|||
    // Brim is only printed on first layer and uses perimeter extruder.
 | 
			
		||||
    Flow        flow = this->brim_flow();
 | 
			
		||||
    Polygons    islands;
 | 
			
		||||
    for (PrintObject *object : this->objects) {
 | 
			
		||||
    PrintObjectPtrs printable_objects = get_printable_objects();
 | 
			
		||||
    for (PrintObject *object : printable_objects) {
 | 
			
		||||
        Polygons object_islands;
 | 
			
		||||
        for (ExPolygon &expoly : object->layers.front()->slices.expolygons)
 | 
			
		||||
            object_islands.push_back(expoly.contour);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -209,6 +209,8 @@ public:
 | 
			
		|||
    void combine_infill();
 | 
			
		||||
    void _generate_support_material();
 | 
			
		||||
 | 
			
		||||
    bool is_printable() const { return !this->_shifted_copies.empty(); }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Print* _print;
 | 
			
		||||
    ModelObject* _model_object;
 | 
			
		||||
| 
						 | 
				
			
			@ -257,6 +259,8 @@ public:
 | 
			
		|||
    void reload_object(size_t idx);
 | 
			
		||||
    bool reload_model_instances();
 | 
			
		||||
 | 
			
		||||
    PrintObjectPtrs get_printable_objects() const;
 | 
			
		||||
 | 
			
		||||
    // methods for handling regions
 | 
			
		||||
    PrintRegion* get_region(size_t idx) { return regions.at(idx); }
 | 
			
		||||
    const PrintRegion* get_region(size_t idx) const  { return regions.at(idx); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,7 +102,10 @@ bool PrintObject::reload_model_instances()
 | 
			
		|||
    Points copies;
 | 
			
		||||
    copies.reserve(this->_model_object->instances.size());
 | 
			
		||||
    for (const ModelInstance *mi : this->_model_object->instances)
 | 
			
		||||
        copies.emplace_back(Point::new_scale(mi->offset.x, mi->offset.y));
 | 
			
		||||
    {
 | 
			
		||||
        if (mi->is_printable())
 | 
			
		||||
            copies.emplace_back(Point::new_scale(mi->offset.x, mi->offset.y));
 | 
			
		||||
    }
 | 
			
		||||
    return this->set_copies(copies);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -291,6 +294,9 @@ bool PrintObject::has_support_material() const
 | 
			
		|||
 | 
			
		||||
void PrintObject::_prepare_infill()
 | 
			
		||||
{
 | 
			
		||||
    if (!this->is_printable())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    // This will assign a type (top/bottom/internal) to $layerm->slices.
 | 
			
		||||
    // Then the classifcation of $layerm->slices is transfered onto 
 | 
			
		||||
    // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces
 | 
			
		||||
| 
						 | 
				
			
			@ -1177,8 +1183,8 @@ void PrintObject::_slice()
 | 
			
		|||
 | 
			
		||||
    this->typed_slices = false;
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    // Disable parallelization for debugging purposes.
 | 
			
		||||
#ifdef SLIC3R_PROFILE
 | 
			
		||||
    // Disable parallelization so the Shiny profiler works
 | 
			
		||||
    static tbb::task_scheduler_init *tbb_init = nullptr;
 | 
			
		||||
    tbb_init = new tbb::task_scheduler_init(1);
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1442,6 +1448,9 @@ void PrintObject::_simplify_slices(double distance)
 | 
			
		|||
 | 
			
		||||
void PrintObject::_make_perimeters()
 | 
			
		||||
{
 | 
			
		||||
    if (!this->is_printable())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (this->state.is_done(posPerimeters)) return;
 | 
			
		||||
    this->state.set_started(posPerimeters);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1550,6 +1559,9 @@ void PrintObject::_make_perimeters()
 | 
			
		|||
 | 
			
		||||
void PrintObject::_infill()
 | 
			
		||||
{
 | 
			
		||||
    if (!this->is_printable())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (this->state.is_done(posInfill)) return;
 | 
			
		||||
    this->state.set_started(posInfill);
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -1954,6 +1966,9 @@ void PrintObject::combine_infill()
 | 
			
		|||
 | 
			
		||||
void PrintObject::_generate_support_material()
 | 
			
		||||
{
 | 
			
		||||
    if (!this->is_printable())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters());
 | 
			
		||||
    support_material.generate(*this);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,6 +84,8 @@ inline T next_highest_power_of_2(T v)
 | 
			
		|||
    return ++ v;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern std::string xml_escape(std::string text);
 | 
			
		||||
 | 
			
		||||
class PerlCallback {
 | 
			
		||||
public:
 | 
			
		||||
    PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@
 | 
			
		|||
#include <boost/thread.hpp>
 | 
			
		||||
 | 
			
		||||
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
 | 
			
		||||
#define SLIC3R_VERSION "1.41.0-alpha"
 | 
			
		||||
#define SLIC3R_VERSION "1.41.0-alpha2"
 | 
			
		||||
#define SLIC3R_BUILD "UNKNOWN"
 | 
			
		||||
 | 
			
		||||
typedef int32_t coord_t;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -387,4 +387,31 @@ unsigned get_current_pid()
 | 
			
		|||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string xml_escape(std::string text)
 | 
			
		||||
{
 | 
			
		||||
    std::string::size_type pos = 0;
 | 
			
		||||
    for (;;)
 | 
			
		||||
    {
 | 
			
		||||
        pos = text.find_first_of("\"\'&<>", pos);
 | 
			
		||||
        if (pos == std::string::npos)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        std::string replacement;
 | 
			
		||||
        switch (text[pos])
 | 
			
		||||
        {
 | 
			
		||||
        case '\"': replacement = """; break;
 | 
			
		||||
        case '\'': replacement = "'"; break;
 | 
			
		||||
        case '&':  replacement = "&";  break;
 | 
			
		||||
        case '<':  replacement = "<";   break;
 | 
			
		||||
        case '>':  replacement = ">";   break;
 | 
			
		||||
        default: break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        text.replace(pos, 1, replacement);
 | 
			
		||||
        pos += replacement.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}; // namespace Slic3r
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,11 +22,6 @@
 | 
			
		|||
#include <tbb/parallel_for.h>
 | 
			
		||||
#include <tbb/spin_mutex.h>
 | 
			
		||||
 | 
			
		||||
#include <wx/bitmap.h>
 | 
			
		||||
#include <wx/dcmemory.h>
 | 
			
		||||
#include <wx/image.h>
 | 
			
		||||
#include <wx/settings.h>
 | 
			
		||||
 | 
			
		||||
#include <Eigen/Dense>
 | 
			
		||||
 | 
			
		||||
#include "GUI.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -540,8 +535,8 @@ double GLVolume::layer_height_texture_z_to_row_id() const
 | 
			
		|||
 | 
			
		||||
void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool force)
 | 
			
		||||
{
 | 
			
		||||
    GLTexture *tex = this->layer_height_texture.get();
 | 
			
		||||
	if (tex == nullptr)
 | 
			
		||||
    LayersTexture *tex = this->layer_height_texture.get();
 | 
			
		||||
    if (tex == nullptr)
 | 
			
		||||
		// No layer_height_texture is assigned to this GLVolume, therefore the layer height texture cannot be filled.
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -588,8 +583,8 @@ std::vector<int> GLVolumeCollection::load_object(
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    // Object will have a single common layer height texture for all volumes.
 | 
			
		||||
    std::shared_ptr<GLTexture> layer_height_texture = std::make_shared<GLTexture>();
 | 
			
		||||
    
 | 
			
		||||
    std::shared_ptr<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>();
 | 
			
		||||
 | 
			
		||||
    std::vector<int> volumes_idx;
 | 
			
		||||
    for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) {
 | 
			
		||||
        const ModelVolume *model_volume = model_object->volumes[volume_idx];
 | 
			
		||||
| 
						 | 
				
			
			@ -743,7 +738,7 @@ void GLVolumeCollection::render_legacy() const
 | 
			
		|||
    glDisable(GL_BLEND);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
 | 
			
		||||
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state)
 | 
			
		||||
{
 | 
			
		||||
    if (config == nullptr)
 | 
			
		||||
        return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -757,18 +752,31 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
 | 
			
		|||
    // Allow the objects to protrude below the print bed
 | 
			
		||||
    print_volume.min.z = -1e10;
 | 
			
		||||
 | 
			
		||||
    bool contained = true;
 | 
			
		||||
    ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside;
 | 
			
		||||
    bool all_contained = true;
 | 
			
		||||
 | 
			
		||||
    for (GLVolume* volume : this->volumes)
 | 
			
		||||
    {
 | 
			
		||||
        if ((volume != nullptr) && !volume->is_modifier)
 | 
			
		||||
        {
 | 
			
		||||
            bool state = print_volume.contains(volume->transformed_bounding_box());
 | 
			
		||||
            contained &= state;
 | 
			
		||||
            volume->is_outside = !state;
 | 
			
		||||
            const BoundingBoxf3& bb = volume->transformed_bounding_box();
 | 
			
		||||
            bool contained = print_volume.contains(bb);
 | 
			
		||||
            all_contained &= contained;
 | 
			
		||||
 | 
			
		||||
            volume->is_outside = !contained;
 | 
			
		||||
 | 
			
		||||
            if ((state == ModelInstance::PVS_Inside) && volume->is_outside)
 | 
			
		||||
                state = ModelInstance::PVS_Fully_Outside;
 | 
			
		||||
 | 
			
		||||
            if ((state == ModelInstance::PVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb))
 | 
			
		||||
                state = ModelInstance::PVS_Partly_Outside;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return contained;
 | 
			
		||||
    if (out_state != nullptr)
 | 
			
		||||
        *out_state = state;
 | 
			
		||||
 | 
			
		||||
    return all_contained;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLVolumeCollection::reset_outside_state()
 | 
			
		||||
| 
						 | 
				
			
			@ -1576,246 +1584,8 @@ void _3DScene::point3_to_verts(const Point3& point, double width, double height,
 | 
			
		|||
    thick_point_to_verts(point, width, height, volume);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_3DScene::LegendTexture _3DScene::s_legend_texture;
 | 
			
		||||
_3DScene::WarningTexture _3DScene::s_warning_texture;
 | 
			
		||||
GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
 | 
			
		||||
 | 
			
		||||
unsigned int _3DScene::TextureBase::finalize()
 | 
			
		||||
{
 | 
			
		||||
    if ((m_tex_id == 0) && !m_data.empty()) {
 | 
			
		||||
        // sends buffer to gpu
 | 
			
		||||
        ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 | 
			
		||||
        ::glGenTextures(1, &m_tex_id);
 | 
			
		||||
        ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_tex_id);
 | 
			
		||||
        ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)m_data.data());
 | 
			
		||||
        ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
			
		||||
        ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
			
		||||
        ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
 | 
			
		||||
        ::glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
        m_data.clear();
 | 
			
		||||
    }
 | 
			
		||||
    return (m_tex_width > 0 && m_tex_height > 0) ? m_tex_id : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _3DScene::TextureBase::_destroy_texture()
 | 
			
		||||
{
 | 
			
		||||
    if (m_tex_id > 0)
 | 
			
		||||
    {
 | 
			
		||||
        ::glDeleteTextures(1, &m_tex_id);
 | 
			
		||||
        m_tex_id = 0;
 | 
			
		||||
        m_tex_height = 0;
 | 
			
		||||
        m_tex_width = 0;
 | 
			
		||||
    }
 | 
			
		||||
    m_data.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const unsigned char _3DScene::WarningTexture::Background_Color[3] = { 9, 91, 134 };
 | 
			
		||||
const unsigned char _3DScene::WarningTexture::Opacity = 255;
 | 
			
		||||
 | 
			
		||||
// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid.
 | 
			
		||||
bool _3DScene::WarningTexture::generate(const std::string& msg)
 | 
			
		||||
{
 | 
			
		||||
    // Mark the texture as released, but don't release the texture from the GPU yet.
 | 
			
		||||
    m_tex_width = m_tex_height = 0;
 | 
			
		||||
    m_data.clear();
 | 
			
		||||
 | 
			
		||||
    if (msg.empty())
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    wxMemoryDC memDC;
 | 
			
		||||
    // select default font
 | 
			
		||||
    memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
 | 
			
		||||
 | 
			
		||||
    // calculates texture size
 | 
			
		||||
    wxCoord w, h;
 | 
			
		||||
    memDC.GetTextExtent(msg, &w, &h);
 | 
			
		||||
    m_tex_width = (unsigned int)w;
 | 
			
		||||
    m_tex_height = (unsigned int)h;
 | 
			
		||||
 | 
			
		||||
    // generates bitmap
 | 
			
		||||
    wxBitmap bitmap(m_tex_width, m_tex_height);
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__) || defined(_MSC_VER)
 | 
			
		||||
    bitmap.UseAlpha();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    memDC.SelectObject(bitmap);
 | 
			
		||||
    memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
 | 
			
		||||
    memDC.Clear();
 | 
			
		||||
 | 
			
		||||
    memDC.SetTextForeground(*wxWHITE);
 | 
			
		||||
 | 
			
		||||
    // draw message
 | 
			
		||||
    memDC.DrawText(msg, 0, 0);
 | 
			
		||||
 | 
			
		||||
    memDC.SelectObject(wxNullBitmap);
 | 
			
		||||
 | 
			
		||||
    // Convert the bitmap into a linear data ready to be loaded into the GPU.
 | 
			
		||||
    {
 | 
			
		||||
        wxImage image = bitmap.ConvertToImage();
 | 
			
		||||
        image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
 | 
			
		||||
 | 
			
		||||
        // prepare buffer
 | 
			
		||||
        m_data.assign(4 * m_tex_width * m_tex_height, 0);
 | 
			
		||||
        for (unsigned int h = 0; h < m_tex_height; ++h)
 | 
			
		||||
        {
 | 
			
		||||
            unsigned int hh = h * m_tex_width;
 | 
			
		||||
            unsigned char* px_ptr = m_data.data() + 4 * hh;
 | 
			
		||||
            for (unsigned int w = 0; w < m_tex_width; ++w)
 | 
			
		||||
            {
 | 
			
		||||
                *px_ptr++ = image.GetRed(w, h);
 | 
			
		||||
                *px_ptr++ = image.GetGreen(w, h);
 | 
			
		||||
                *px_ptr++ = image.GetBlue(w, h);
 | 
			
		||||
                *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const unsigned char _3DScene::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
 | 
			
		||||
const unsigned char _3DScene::LegendTexture::Background_Color[3] = { 9, 91, 134 };
 | 
			
		||||
const unsigned char _3DScene::LegendTexture::Opacity = 255;
 | 
			
		||||
 | 
			
		||||
// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid.
 | 
			
		||||
bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
 | 
			
		||||
{
 | 
			
		||||
    // Mark the texture as released, but don't release the texture from the GPU yet.
 | 
			
		||||
    m_tex_width = m_tex_height = 0;
 | 
			
		||||
    m_data.clear();
 | 
			
		||||
 | 
			
		||||
    // collects items to render
 | 
			
		||||
    auto title = _(preview_data.get_legend_title());
 | 
			
		||||
    const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors);
 | 
			
		||||
 | 
			
		||||
    unsigned int items_count = (unsigned int)items.size();
 | 
			
		||||
    if (items_count == 0)
 | 
			
		||||
        // nothing to render, return
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    wxMemoryDC memDC;
 | 
			
		||||
    // select default font
 | 
			
		||||
    memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
 | 
			
		||||
 | 
			
		||||
    // calculates texture size
 | 
			
		||||
    wxCoord w, h;
 | 
			
		||||
    memDC.GetTextExtent(title, &w, &h);
 | 
			
		||||
    unsigned int title_width = (unsigned int)w;
 | 
			
		||||
    unsigned int title_height = (unsigned int)h;
 | 
			
		||||
 | 
			
		||||
    unsigned int max_text_width = 0;
 | 
			
		||||
    unsigned int max_text_height = 0;
 | 
			
		||||
    for (const GCodePreviewData::LegendItem& item : items)
 | 
			
		||||
    {
 | 
			
		||||
        memDC.GetTextExtent(GUI::from_u8(item.text), &w, &h);
 | 
			
		||||
        max_text_width = std::max(max_text_width, (unsigned int)w);
 | 
			
		||||
        max_text_height = std::max(max_text_height, (unsigned int)h);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_tex_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width);
 | 
			
		||||
    m_tex_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square;
 | 
			
		||||
    if (items_count > 1)
 | 
			
		||||
        m_tex_height += (items_count - 1) * Px_Square_Contour;
 | 
			
		||||
 | 
			
		||||
    // generates bitmap
 | 
			
		||||
    wxBitmap bitmap(m_tex_width, m_tex_height);
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__) || defined(_MSC_VER)
 | 
			
		||||
    bitmap.UseAlpha();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    memDC.SelectObject(bitmap);
 | 
			
		||||
    memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
 | 
			
		||||
    memDC.Clear();
 | 
			
		||||
 | 
			
		||||
    memDC.SetTextForeground(*wxWHITE);
 | 
			
		||||
 | 
			
		||||
    // draw title
 | 
			
		||||
    unsigned int title_x = Px_Border;
 | 
			
		||||
    unsigned int title_y = Px_Border;
 | 
			
		||||
    memDC.DrawText(title, title_x, title_y);
 | 
			
		||||
 | 
			
		||||
    // draw icons contours as background
 | 
			
		||||
    unsigned int squares_contour_x = Px_Border;
 | 
			
		||||
    unsigned int squares_contour_y = Px_Border + title_height + Px_Title_Offset;
 | 
			
		||||
    unsigned int squares_contour_width = Px_Square + 2 * Px_Square_Contour;
 | 
			
		||||
    unsigned int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour;
 | 
			
		||||
    if (items_count > 1)
 | 
			
		||||
        squares_contour_height += (items_count - 1) * Px_Square_Contour;
 | 
			
		||||
 | 
			
		||||
    wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]);
 | 
			
		||||
    wxPen pen(color);
 | 
			
		||||
    wxBrush brush(color);
 | 
			
		||||
    memDC.SetPen(pen);
 | 
			
		||||
    memDC.SetBrush(brush);
 | 
			
		||||
    memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
 | 
			
		||||
 | 
			
		||||
    // draw items (colored icon + text)
 | 
			
		||||
    unsigned int icon_x = squares_contour_x + Px_Square_Contour;
 | 
			
		||||
    unsigned int icon_x_inner = icon_x + 1;
 | 
			
		||||
    unsigned int icon_y = squares_contour_y + Px_Square_Contour;
 | 
			
		||||
    unsigned int icon_y_step = Px_Square + Px_Square_Contour;
 | 
			
		||||
 | 
			
		||||
    unsigned int text_x = icon_x + Px_Square + Px_Text_Offset;
 | 
			
		||||
    unsigned int text_y_offset = (Px_Square - max_text_height) / 2;
 | 
			
		||||
 | 
			
		||||
    unsigned int px_inner_square = Px_Square - 2;
 | 
			
		||||
 | 
			
		||||
    for (const GCodePreviewData::LegendItem& item : items)
 | 
			
		||||
    {
 | 
			
		||||
        // draw darker icon perimeter
 | 
			
		||||
        const std::vector<unsigned char>& item_color_bytes = item.color.as_bytes();
 | 
			
		||||
        wxImage::HSVValue dark_hsv = wxImage::RGBtoHSV(wxImage::RGBValue(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2]));
 | 
			
		||||
        dark_hsv.value *= 0.75;
 | 
			
		||||
        wxImage::RGBValue dark_rgb = wxImage::HSVtoRGB(dark_hsv);
 | 
			
		||||
        color.Set(dark_rgb.red, dark_rgb.green, dark_rgb.blue, item_color_bytes[3]);
 | 
			
		||||
        pen.SetColour(color);
 | 
			
		||||
        brush.SetColour(color);
 | 
			
		||||
        memDC.SetPen(pen);
 | 
			
		||||
        memDC.SetBrush(brush);
 | 
			
		||||
        memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square));
 | 
			
		||||
 | 
			
		||||
        // draw icon interior
 | 
			
		||||
        color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]);
 | 
			
		||||
        pen.SetColour(color);
 | 
			
		||||
        brush.SetColour(color);
 | 
			
		||||
        memDC.SetPen(pen);
 | 
			
		||||
        memDC.SetBrush(brush);
 | 
			
		||||
        memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square));
 | 
			
		||||
 | 
			
		||||
        // draw text
 | 
			
		||||
		memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
 | 
			
		||||
 | 
			
		||||
        // update y
 | 
			
		||||
        icon_y += icon_y_step;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memDC.SelectObject(wxNullBitmap);
 | 
			
		||||
 | 
			
		||||
    // Convert the bitmap into a linear data ready to be loaded into the GPU.
 | 
			
		||||
    {
 | 
			
		||||
        wxImage image = bitmap.ConvertToImage();
 | 
			
		||||
        image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
 | 
			
		||||
 | 
			
		||||
        // prepare buffer
 | 
			
		||||
        m_data.assign(4 * m_tex_width * m_tex_height, 0);
 | 
			
		||||
        for (unsigned int h = 0; h < m_tex_height; ++h)
 | 
			
		||||
        {
 | 
			
		||||
            unsigned int hh = h * m_tex_width;
 | 
			
		||||
            unsigned char* px_ptr = m_data.data() + 4 * hh;
 | 
			
		||||
            for (unsigned int w = 0; w < m_tex_width; ++w)
 | 
			
		||||
            {
 | 
			
		||||
                *px_ptr++ = image.GetRed(w, h);
 | 
			
		||||
                *px_ptr++ = image.GetGreen(w, h);
 | 
			
		||||
                *px_ptr++ = image.GetBlue(w, h);
 | 
			
		||||
                *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _3DScene::init_gl()
 | 
			
		||||
{
 | 
			
		||||
    s_canvas_mgr.init_gl();
 | 
			
		||||
| 
						 | 
				
			
			@ -1881,7 +1651,7 @@ void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector<in
 | 
			
		|||
    s_canvas_mgr.update_volumes_selection(canvas, selections);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config)
 | 
			
		||||
int _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config)
 | 
			
		||||
{
 | 
			
		||||
    return s_canvas_mgr.check_volumes_outside_state(canvas, config);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2218,54 +1988,9 @@ void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* pr
 | 
			
		|||
    s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _3DScene::generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
 | 
			
		||||
{
 | 
			
		||||
    s_legend_texture.generate(preview_data, tool_colors);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int _3DScene::get_legend_texture_width()
 | 
			
		||||
{
 | 
			
		||||
    return s_legend_texture.get_texture_width();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int _3DScene::get_legend_texture_height()
 | 
			
		||||
{
 | 
			
		||||
    return s_legend_texture.get_texture_height();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _3DScene::reset_legend_texture()
 | 
			
		||||
{
 | 
			
		||||
    s_legend_texture.reset_texture();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int _3DScene::finalize_legend_texture()
 | 
			
		||||
{
 | 
			
		||||
    return s_legend_texture.finalize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int _3DScene::get_warning_texture_width()
 | 
			
		||||
{
 | 
			
		||||
    return s_warning_texture.get_texture_width();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int _3DScene::get_warning_texture_height()
 | 
			
		||||
{
 | 
			
		||||
    return s_warning_texture.get_texture_height();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _3DScene::generate_warning_texture(const std::string& msg)
 | 
			
		||||
{
 | 
			
		||||
    s_warning_texture.generate(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _3DScene::reset_warning_texture()
 | 
			
		||||
{
 | 
			
		||||
    s_warning_texture.reset_texture();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int _3DScene::finalize_warning_texture()
 | 
			
		||||
{
 | 
			
		||||
    return s_warning_texture.finalize();
 | 
			
		||||
    s_canvas_mgr.reset_legend_texture();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Slic3r
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@
 | 
			
		|||
#include "../../libslic3r/Line.hpp"
 | 
			
		||||
#include "../../libslic3r/TriangleMesh.hpp"
 | 
			
		||||
#include "../../libslic3r/Utils.hpp"
 | 
			
		||||
#include "../../libslic3r/Model.hpp"
 | 
			
		||||
#include "../../slic3r/GUI/GLCanvas3DManager.hpp"
 | 
			
		||||
 | 
			
		||||
class wxBitmap;
 | 
			
		||||
| 
						 | 
				
			
			@ -199,10 +200,10 @@ private:
 | 
			
		|||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GLTexture
 | 
			
		||||
class LayersTexture
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    GLTexture() : width(0), height(0), levels(0), cells(0) {}
 | 
			
		||||
    LayersTexture() : width(0), height(0), levels(0), cells(0) {}
 | 
			
		||||
 | 
			
		||||
    // Texture data
 | 
			
		||||
    std::vector<char>   data;
 | 
			
		||||
| 
						 | 
				
			
			@ -341,7 +342,7 @@ public:
 | 
			
		|||
    void                release_geometry() { this->indexed_vertex_array.release_geometry(); }
 | 
			
		||||
 | 
			
		||||
    /************************************************ Layer height texture ****************************************************/
 | 
			
		||||
    std::shared_ptr<GLTexture>  layer_height_texture;
 | 
			
		||||
    std::shared_ptr<LayersTexture>  layer_height_texture;
 | 
			
		||||
    // Data to render this volume using the layer height texture
 | 
			
		||||
    LayerHeightTextureData layer_height_texture_data;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -422,7 +423,9 @@ public:
 | 
			
		|||
        print_box_max[0] = max_x; print_box_max[1] = max_y; print_box_max[2] = max_z;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool check_outside_state(const DynamicPrintConfig* config);
 | 
			
		||||
    // returns true if all the volumes are completely contained in the print volume
 | 
			
		||||
    // returns the containment state in the given out_state, if non-null
 | 
			
		||||
    bool check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state);
 | 
			
		||||
    void reset_outside_state();
 | 
			
		||||
 | 
			
		||||
    void update_colors_by_extruder(const DynamicPrintConfig* config);
 | 
			
		||||
| 
						 | 
				
			
			@ -437,65 +440,6 @@ private:
 | 
			
		|||
 | 
			
		||||
class _3DScene
 | 
			
		||||
{
 | 
			
		||||
    class TextureBase
 | 
			
		||||
    {
 | 
			
		||||
    protected:
 | 
			
		||||
        unsigned int m_tex_id;
 | 
			
		||||
        unsigned int m_tex_width;
 | 
			
		||||
        unsigned int m_tex_height;
 | 
			
		||||
 | 
			
		||||
        // generate() fills in m_data with the pixels, while finalize() moves the data to the GPU before rendering.
 | 
			
		||||
        std::vector<unsigned char> m_data;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        TextureBase() : m_tex_id(0), m_tex_width(0), m_tex_height(0) {}
 | 
			
		||||
        virtual ~TextureBase() { _destroy_texture(); }
 | 
			
		||||
 | 
			
		||||
        // If not loaded, load the texture data into the GPU. Return a texture ID or 0 if the texture has zero size.
 | 
			
		||||
        unsigned int finalize();
 | 
			
		||||
 | 
			
		||||
        unsigned int get_texture_id() const { return m_tex_id; }
 | 
			
		||||
        unsigned int get_texture_width() const { return m_tex_width; }
 | 
			
		||||
        unsigned int get_texture_height() const { return m_tex_height; }
 | 
			
		||||
 | 
			
		||||
        void reset_texture() { _destroy_texture(); }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        void _destroy_texture();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class WarningTexture : public TextureBase
 | 
			
		||||
    {
 | 
			
		||||
        static const unsigned char Background_Color[3];
 | 
			
		||||
        static const unsigned char Opacity;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        WarningTexture() : TextureBase() {}
 | 
			
		||||
 | 
			
		||||
        // Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet.
 | 
			
		||||
        bool generate(const std::string& msg);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class LegendTexture : public TextureBase
 | 
			
		||||
    {
 | 
			
		||||
        static const unsigned int Px_Title_Offset = 5;
 | 
			
		||||
        static const unsigned int Px_Text_Offset = 5;
 | 
			
		||||
        static const unsigned int Px_Square = 20;
 | 
			
		||||
        static const unsigned int Px_Square_Contour = 1;
 | 
			
		||||
        static const unsigned int Px_Border = Px_Square / 2;
 | 
			
		||||
        static const unsigned char Squares_Border_Color[3];
 | 
			
		||||
        static const unsigned char Background_Color[3];
 | 
			
		||||
        static const unsigned char Opacity;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        LegendTexture() : TextureBase() {}
 | 
			
		||||
 | 
			
		||||
        // Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet.
 | 
			
		||||
        bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static LegendTexture s_legend_texture;
 | 
			
		||||
    static WarningTexture s_warning_texture;
 | 
			
		||||
    static GUI::GLCanvas3DManager s_canvas_mgr;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
| 
						 | 
				
			
			@ -516,7 +460,7 @@ public:
 | 
			
		|||
    static void deselect_volumes(wxGLCanvas* canvas);
 | 
			
		||||
    static void select_volume(wxGLCanvas* canvas, unsigned int id);
 | 
			
		||||
    static void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections);
 | 
			
		||||
    static bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config);
 | 
			
		||||
    static int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config);
 | 
			
		||||
    static bool move_volume_up(wxGLCanvas* canvas, unsigned int id);
 | 
			
		||||
    static bool move_volume_down(wxGLCanvas* canvas, unsigned int id);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -597,21 +541,7 @@ public:
 | 
			
		|||
    static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
 | 
			
		||||
    static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
 | 
			
		||||
 | 
			
		||||
    // generates the legend texture in dependence of the current shown view type
 | 
			
		||||
    static void generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
 | 
			
		||||
    static unsigned int get_legend_texture_width();
 | 
			
		||||
    static unsigned int get_legend_texture_height();
 | 
			
		||||
 | 
			
		||||
    static void reset_legend_texture();
 | 
			
		||||
    static unsigned int finalize_legend_texture();
 | 
			
		||||
 | 
			
		||||
    static unsigned int get_warning_texture_width();
 | 
			
		||||
    static unsigned int get_warning_texture_height();
 | 
			
		||||
 | 
			
		||||
    // generates a warning texture containing the given message
 | 
			
		||||
    static void generate_warning_texture(const std::string& msg);
 | 
			
		||||
    static void reset_warning_texture();
 | 
			
		||||
    static unsigned int finalize_warning_texture();
 | 
			
		||||
 | 
			
		||||
    static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
 | 
			
		||||
    static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,22 +65,27 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons
 | 
			
		|||
	auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
 | 
			
		||||
	namefont.SetWeight(wxFONTWEIGHT_BOLD);
 | 
			
		||||
 | 
			
		||||
	// wxGrid appends widgets by rows, but we need to construct them in columns.
 | 
			
		||||
	// These vectors are used to hold the elements so that they can be appended in the right order.
 | 
			
		||||
	std::vector<wxStaticText*> titles;
 | 
			
		||||
	std::vector<wxStaticBitmap*> bitmaps;
 | 
			
		||||
	std::vector<wxPanel*> variants_panels;
 | 
			
		||||
 | 
			
		||||
	for (const auto &model : models) {
 | 
			
		||||
		auto *panel = new wxPanel(this);
 | 
			
		||||
		auto *col_sizer = new wxBoxSizer(wxVERTICAL);
 | 
			
		||||
		panel->SetSizer(col_sizer);
 | 
			
		||||
 | 
			
		||||
		auto *title = new wxStaticText(panel, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
 | 
			
		||||
		title->SetFont(namefont);
 | 
			
		||||
		col_sizer->Add(title, 0, wxBOTTOM, 3);
 | 
			
		||||
 | 
			
		||||
		auto bitmap_file = wxString::Format("printers/%s_%s.png", vendor.id, model.id);
 | 
			
		||||
		wxBitmap bitmap(GUI::from_u8(Slic3r::var(bitmap_file.ToStdString())), wxBITMAP_TYPE_PNG);
 | 
			
		||||
		auto *bitmap_widget = new wxStaticBitmap(panel, wxID_ANY, bitmap);
 | 
			
		||||
		col_sizer->Add(bitmap_widget, 0, wxBOTTOM, 3);
 | 
			
		||||
 | 
			
		||||
		col_sizer->AddSpacer(20);
 | 
			
		||||
		auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
 | 
			
		||||
		title->SetFont(namefont);
 | 
			
		||||
		title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth()));
 | 
			
		||||
		titles.push_back(title);
 | 
			
		||||
 | 
			
		||||
		auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap);
 | 
			
		||||
		bitmaps.push_back(bitmap_widget);
 | 
			
		||||
 | 
			
		||||
		auto *variants_panel = new wxPanel(this);
 | 
			
		||||
		auto *variants_sizer = new wxBoxSizer(wxVERTICAL);
 | 
			
		||||
		variants_panel->SetSizer(variants_sizer);
 | 
			
		||||
		const auto model_id = model.id;
 | 
			
		||||
 | 
			
		||||
		bool default_variant = true;   // Mark the first variant as default in the GUI
 | 
			
		||||
| 
						 | 
				
			
			@ -88,22 +93,26 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons
 | 
			
		|||
			const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")),
 | 
			
		||||
				(default_variant ? _(L("(default)")) : wxString()));
 | 
			
		||||
			default_variant = false;
 | 
			
		||||
			auto *cbox = new Checkbox(panel, label, model_id, variant.name);
 | 
			
		||||
			auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
 | 
			
		||||
			const size_t idx = cboxes.size();
 | 
			
		||||
			cboxes.push_back(cbox);
 | 
			
		||||
			bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name);
 | 
			
		||||
			variants_checked += enabled;
 | 
			
		||||
			cbox->SetValue(enabled);
 | 
			
		||||
			col_sizer->Add(cbox, 0, wxBOTTOM, 3);
 | 
			
		||||
			variants_sizer->Add(cbox, 0, wxBOTTOM, 3);
 | 
			
		||||
			cbox->Bind(wxEVT_CHECKBOX, [this, idx](wxCommandEvent &event) {
 | 
			
		||||
				if (idx >= this->cboxes.size()) { return; }
 | 
			
		||||
				this->on_checkbox(this->cboxes[idx], event.IsChecked());
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		printer_grid->Add(panel);
 | 
			
		||||
		variants_panels.push_back(variants_panel);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (auto title : titles)       { printer_grid->Add(title, 0, wxBOTTOM, 3); }
 | 
			
		||||
	for (auto bitmap : bitmaps)     { printer_grid->Add(bitmap, 0, wxBOTTOM, 20); }
 | 
			
		||||
	for (auto vp : variants_panels) { printer_grid->Add(vp); }
 | 
			
		||||
 | 
			
		||||
	auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
	auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all")));
 | 
			
		||||
	auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none")));
 | 
			
		||||
| 
						 | 
				
			
			@ -214,7 +223,7 @@ void ConfigWizardPage::enable_next(bool enable) { parent->p->enable_next(enable)
 | 
			
		|||
 | 
			
		||||
// Wizard pages
 | 
			
		||||
 | 
			
		||||
PageWelcome::PageWelcome(ConfigWizard *parent) :
 | 
			
		||||
PageWelcome::PageWelcome(ConfigWizard *parent, bool check_first_variant) :
 | 
			
		||||
	ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))),
 | 
			
		||||
	printer_picker(nullptr),
 | 
			
		||||
	others_buttons(new wxPanel(parent)),
 | 
			
		||||
| 
						 | 
				
			
			@ -238,7 +247,10 @@ PageWelcome::PageWelcome(ConfigWizard *parent) :
 | 
			
		|||
		AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
 | 
			
		||||
 | 
			
		||||
		printer_picker = new PrinterPicker(this, vendor_prusa->second, appconfig_vendors);
 | 
			
		||||
		printer_picker->select_one(0, true);    // Select the default (first) model/variant on the Prusa vendor
 | 
			
		||||
		if (check_first_variant) {
 | 
			
		||||
			// Select the default (first) model/variant on the Prusa vendor
 | 
			
		||||
			printer_picker->select_one(0, true);
 | 
			
		||||
		}
 | 
			
		||||
		printer_picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) {
 | 
			
		||||
			appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
 | 
			
		||||
			this->on_variant_checked();
 | 
			
		||||
| 
						 | 
				
			
			@ -779,7 +791,6 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
 | 
			
		|||
		app_config->set("version_check", page_update->version_check ? "1" : "0");
 | 
			
		||||
		app_config->set("preset_update", page_update->preset_update ? "1" : "0");
 | 
			
		||||
		app_config->reset_selections();
 | 
			
		||||
		// ^ TODO: replace with appropriate printer selection
 | 
			
		||||
		preset_bundle->load_presets(*app_config);
 | 
			
		||||
	} else {
 | 
			
		||||
		for (ConfigWizardPage *page = page_firmware; page != nullptr; page = page->page_next()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -831,7 +842,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
 | 
			
		|||
	p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING);
 | 
			
		||||
	p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING);
 | 
			
		||||
 | 
			
		||||
	p->add_page(p->page_welcome  = new PageWelcome(this));
 | 
			
		||||
	p->add_page(p->page_welcome  = new PageWelcome(this, reason == RR_DATA_EMPTY || reason == RR_DATA_LEGACY));
 | 
			
		||||
	p->add_page(p->page_update   = new PageUpdate(this));
 | 
			
		||||
	p->add_page(p->page_vendors  = new PageVendors(this));
 | 
			
		||||
	p->add_page(p->page_firmware = new PageFirmware(this));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ namespace GUI {
 | 
			
		|||
 | 
			
		||||
enum {
 | 
			
		||||
	WRAP_WIDTH = 500,
 | 
			
		||||
	MODEL_MIN_WRAP = 150,
 | 
			
		||||
 | 
			
		||||
	DIALOG_MARGIN = 15,
 | 
			
		||||
	INDEX_MARGIN = 40,
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +104,7 @@ struct PageWelcome: ConfigWizardPage
 | 
			
		|||
	wxPanel *others_buttons;
 | 
			
		||||
	wxCheckBox *cbox_reset;
 | 
			
		||||
 | 
			
		||||
	PageWelcome(ConfigWizard *parent);
 | 
			
		||||
	PageWelcome(ConfigWizard *parent, bool check_first_variant);
 | 
			
		||||
 | 
			
		||||
	virtual wxPanel* extra_buttons() { return others_buttons; }
 | 
			
		||||
	virtual void on_page_set();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,10 @@
 | 
			
		|||
 | 
			
		||||
#include <wx/glcanvas.h>
 | 
			
		||||
#include <wx/timer.h>
 | 
			
		||||
#include <wx/bitmap.h>
 | 
			
		||||
#include <wx/dcmemory.h>
 | 
			
		||||
#include <wx/image.h>
 | 
			
		||||
#include <wx/settings.h>
 | 
			
		||||
 | 
			
		||||
#include <tbb/parallel_for.h>
 | 
			
		||||
#include <tbb/spin_mutex.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -1381,7 +1385,8 @@ void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& b
 | 
			
		|||
 | 
			
		||||
    ::glDisable(GL_DEPTH_TEST);
 | 
			
		||||
 | 
			
		||||
    _render_current_gizmo(box);
 | 
			
		||||
    if (box.radius() > 0.0)
 | 
			
		||||
        _render_current_gizmo(box);
 | 
			
		||||
 | 
			
		||||
    ::glPushMatrix();
 | 
			
		||||
    ::glLoadIdentity();
 | 
			
		||||
| 
						 | 
				
			
			@ -1456,6 +1461,224 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const
 | 
			
		|||
    return height;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 9, 91, 134 };
 | 
			
		||||
const unsigned char GLCanvas3D::WarningTexture::Opacity = 255;
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3D::WarningTexture::generate(const std::string& msg)
 | 
			
		||||
{
 | 
			
		||||
    reset();
 | 
			
		||||
 | 
			
		||||
    if (msg.empty())
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    wxMemoryDC memDC;
 | 
			
		||||
    // select default font
 | 
			
		||||
    memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
 | 
			
		||||
 | 
			
		||||
    // calculates texture size
 | 
			
		||||
    wxCoord w, h;
 | 
			
		||||
    memDC.GetTextExtent(msg, &w, &h);
 | 
			
		||||
    m_width = (int)w;
 | 
			
		||||
    m_height = (int)h;
 | 
			
		||||
 | 
			
		||||
    // generates bitmap
 | 
			
		||||
    wxBitmap bitmap(m_width, m_height);
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__) || defined(_MSC_VER)
 | 
			
		||||
    bitmap.UseAlpha();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    memDC.SelectObject(bitmap);
 | 
			
		||||
    memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
 | 
			
		||||
    memDC.Clear();
 | 
			
		||||
 | 
			
		||||
    memDC.SetTextForeground(*wxWHITE);
 | 
			
		||||
 | 
			
		||||
    // draw message
 | 
			
		||||
    memDC.DrawText(msg, 0, 0);
 | 
			
		||||
 | 
			
		||||
    memDC.SelectObject(wxNullBitmap);
 | 
			
		||||
 | 
			
		||||
    // Convert the bitmap into a linear data ready to be loaded into the GPU.
 | 
			
		||||
    wxImage image = bitmap.ConvertToImage();
 | 
			
		||||
    image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
 | 
			
		||||
 | 
			
		||||
    // prepare buffer
 | 
			
		||||
    std::vector<unsigned char> data(4 * m_width * m_height, 0);
 | 
			
		||||
    for (int h = 0; h < m_height; ++h)
 | 
			
		||||
    {
 | 
			
		||||
        int hh = h * m_width;
 | 
			
		||||
        unsigned char* px_ptr = data.data() + 4 * hh;
 | 
			
		||||
        for (int w = 0; w < m_width; ++w)
 | 
			
		||||
        {
 | 
			
		||||
            *px_ptr++ = image.GetRed(w, h);
 | 
			
		||||
            *px_ptr++ = image.GetGreen(w, h);
 | 
			
		||||
            *px_ptr++ = image.GetBlue(w, h);
 | 
			
		||||
            *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // sends buffer to gpu
 | 
			
		||||
    ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 | 
			
		||||
    ::glGenTextures(1, &m_id);
 | 
			
		||||
    ::glBindTexture(GL_TEXTURE_2D, (GLuint)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());
 | 
			
		||||
    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
			
		||||
    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
			
		||||
    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
 | 
			
		||||
    ::glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
 | 
			
		||||
const unsigned char GLCanvas3D::LegendTexture::Background_Color[3] = { 9, 91, 134 };
 | 
			
		||||
const unsigned char GLCanvas3D::LegendTexture::Opacity = 255;
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
 | 
			
		||||
{
 | 
			
		||||
    reset();
 | 
			
		||||
 | 
			
		||||
    // collects items to render
 | 
			
		||||
    auto title = _(preview_data.get_legend_title());
 | 
			
		||||
    const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors);
 | 
			
		||||
 | 
			
		||||
    unsigned int items_count = (unsigned int)items.size();
 | 
			
		||||
    if (items_count == 0)
 | 
			
		||||
        // nothing to render, return
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    wxMemoryDC memDC;
 | 
			
		||||
    // select default font
 | 
			
		||||
    memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
 | 
			
		||||
 | 
			
		||||
    // calculates texture size
 | 
			
		||||
    wxCoord w, h;
 | 
			
		||||
    memDC.GetTextExtent(title, &w, &h);
 | 
			
		||||
    int title_width = (int)w;
 | 
			
		||||
    int title_height = (int)h;
 | 
			
		||||
 | 
			
		||||
    int max_text_width = 0;
 | 
			
		||||
    int max_text_height = 0;
 | 
			
		||||
    for (const GCodePreviewData::LegendItem& item : items)
 | 
			
		||||
    {
 | 
			
		||||
        memDC.GetTextExtent(GUI::from_u8(item.text), &w, &h);
 | 
			
		||||
        max_text_width = std::max(max_text_width, (int)w);
 | 
			
		||||
        max_text_height = std::max(max_text_height, (int)h);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width);
 | 
			
		||||
    m_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square;
 | 
			
		||||
    if (items_count > 1)
 | 
			
		||||
        m_height += (items_count - 1) * Px_Square_Contour;
 | 
			
		||||
 | 
			
		||||
    // generates bitmap
 | 
			
		||||
    wxBitmap bitmap(m_width, m_height);
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__) || defined(_MSC_VER)
 | 
			
		||||
    bitmap.UseAlpha();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    memDC.SelectObject(bitmap);
 | 
			
		||||
    memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
 | 
			
		||||
    memDC.Clear();
 | 
			
		||||
 | 
			
		||||
    memDC.SetTextForeground(*wxWHITE);
 | 
			
		||||
 | 
			
		||||
    // draw title
 | 
			
		||||
    int title_x = Px_Border;
 | 
			
		||||
    int title_y = Px_Border;
 | 
			
		||||
    memDC.DrawText(title, title_x, title_y);
 | 
			
		||||
 | 
			
		||||
    // draw icons contours as background
 | 
			
		||||
    int squares_contour_x = Px_Border;
 | 
			
		||||
    int squares_contour_y = Px_Border + title_height + Px_Title_Offset;
 | 
			
		||||
    int squares_contour_width = Px_Square + 2 * Px_Square_Contour;
 | 
			
		||||
    int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour;
 | 
			
		||||
    if (items_count > 1)
 | 
			
		||||
        squares_contour_height += (items_count - 1) * Px_Square_Contour;
 | 
			
		||||
 | 
			
		||||
    wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]);
 | 
			
		||||
    wxPen pen(color);
 | 
			
		||||
    wxBrush brush(color);
 | 
			
		||||
    memDC.SetPen(pen);
 | 
			
		||||
    memDC.SetBrush(brush);
 | 
			
		||||
    memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
 | 
			
		||||
 | 
			
		||||
    // draw items (colored icon + text)
 | 
			
		||||
    int icon_x = squares_contour_x + Px_Square_Contour;
 | 
			
		||||
    int icon_x_inner = icon_x + 1;
 | 
			
		||||
    int icon_y = squares_contour_y + Px_Square_Contour;
 | 
			
		||||
    int icon_y_step = Px_Square + Px_Square_Contour;
 | 
			
		||||
 | 
			
		||||
    int text_x = icon_x + Px_Square + Px_Text_Offset;
 | 
			
		||||
    int text_y_offset = (Px_Square - max_text_height) / 2;
 | 
			
		||||
 | 
			
		||||
    int px_inner_square = Px_Square - 2;
 | 
			
		||||
 | 
			
		||||
    for (const GCodePreviewData::LegendItem& item : items)
 | 
			
		||||
    {
 | 
			
		||||
        // draw darker icon perimeter
 | 
			
		||||
        const std::vector<unsigned char>& item_color_bytes = item.color.as_bytes();
 | 
			
		||||
        wxImage::HSVValue dark_hsv = wxImage::RGBtoHSV(wxImage::RGBValue(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2]));
 | 
			
		||||
        dark_hsv.value *= 0.75;
 | 
			
		||||
        wxImage::RGBValue dark_rgb = wxImage::HSVtoRGB(dark_hsv);
 | 
			
		||||
        color.Set(dark_rgb.red, dark_rgb.green, dark_rgb.blue, item_color_bytes[3]);
 | 
			
		||||
        pen.SetColour(color);
 | 
			
		||||
        brush.SetColour(color);
 | 
			
		||||
        memDC.SetPen(pen);
 | 
			
		||||
        memDC.SetBrush(brush);
 | 
			
		||||
        memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square));
 | 
			
		||||
 | 
			
		||||
        // draw icon interior
 | 
			
		||||
        color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]);
 | 
			
		||||
        pen.SetColour(color);
 | 
			
		||||
        brush.SetColour(color);
 | 
			
		||||
        memDC.SetPen(pen);
 | 
			
		||||
        memDC.SetBrush(brush);
 | 
			
		||||
        memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square));
 | 
			
		||||
 | 
			
		||||
        // draw text
 | 
			
		||||
        memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
 | 
			
		||||
 | 
			
		||||
        // update y
 | 
			
		||||
        icon_y += icon_y_step;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memDC.SelectObject(wxNullBitmap);
 | 
			
		||||
 | 
			
		||||
    // Convert the bitmap into a linear data ready to be loaded into the GPU.
 | 
			
		||||
    wxImage image = bitmap.ConvertToImage();
 | 
			
		||||
    image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
 | 
			
		||||
 | 
			
		||||
    // prepare buffer
 | 
			
		||||
    std::vector<unsigned char> data(4 * m_width * m_height, 0);
 | 
			
		||||
    for (int h = 0; h < m_height; ++h)
 | 
			
		||||
    {
 | 
			
		||||
        int hh = h * m_width;
 | 
			
		||||
        unsigned char* px_ptr = data.data() + 4 * hh;
 | 
			
		||||
        for (int w = 0; w < m_width; ++w)
 | 
			
		||||
        {
 | 
			
		||||
            *px_ptr++ = image.GetRed(w, h);
 | 
			
		||||
            *px_ptr++ = image.GetGreen(w, h);
 | 
			
		||||
            *px_ptr++ = image.GetBlue(w, h);
 | 
			
		||||
            *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // sends buffer to gpu
 | 
			
		||||
    ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 | 
			
		||||
    ::glGenTextures(1, &m_id);
 | 
			
		||||
    ::glBindTexture(GL_TEXTURE_2D, (GLuint)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());
 | 
			
		||||
    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
			
		||||
    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
			
		||||
    ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
 | 
			
		||||
    ::glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const
 | 
			
		||||
{
 | 
			
		||||
    GizmosMap::const_iterator it = m_gizmos.find(m_current);
 | 
			
		||||
| 
						 | 
				
			
			@ -1655,9 +1878,11 @@ void GLCanvas3D::update_volumes_selection(const std::vector<int>& selections)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const
 | 
			
		||||
int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const
 | 
			
		||||
{
 | 
			
		||||
    return m_volumes.check_outside_state(config);
 | 
			
		||||
    ModelInstance::EPrintVolumeState state;
 | 
			
		||||
    m_volumes.check_outside_state(config, &state);
 | 
			
		||||
    return (int)state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3D::move_volume_up(unsigned int id)
 | 
			
		||||
| 
						 | 
				
			
			@ -1905,7 +2130,7 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
 | 
			
		|||
 | 
			
		||||
void GLCanvas3D::update_gizmos_data()
 | 
			
		||||
{
 | 
			
		||||
    if (!m_gizmos.is_running())
 | 
			
		||||
    if (!m_gizmos.is_enabled())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    int id = _get_first_selected_object_id();
 | 
			
		||||
| 
						 | 
				
			
			@ -1917,26 +2142,16 @@ void GLCanvas3D::update_gizmos_data()
 | 
			
		|||
            ModelInstance* model_instance = model_object->instances[0];
 | 
			
		||||
            if (model_instance != nullptr)
 | 
			
		||||
            {
 | 
			
		||||
                switch (m_gizmos.get_current_type())
 | 
			
		||||
                {
 | 
			
		||||
                case Gizmos::Scale:
 | 
			
		||||
                {
 | 
			
		||||
                    m_gizmos.set_scale(model_instance->scaling_factor);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case Gizmos::Rotate:
 | 
			
		||||
                {
 | 
			
		||||
                    m_gizmos.set_angle_z(model_instance->rotation);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                default:
 | 
			
		||||
                {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                }
 | 
			
		||||
                m_gizmos.set_scale(model_instance->scaling_factor);
 | 
			
		||||
                m_gizmos.set_angle_z(model_instance->rotation);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        m_gizmos.set_scale(1.0f);
 | 
			
		||||
        m_gizmos.set_angle_z(0.0f);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::render()
 | 
			
		||||
| 
						 | 
				
			
			@ -2051,8 +2266,12 @@ void GLCanvas3D::reload_scene(bool force)
 | 
			
		|||
        m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 1st call to reset if no objects left
 | 
			
		||||
    update_gizmos_data();
 | 
			
		||||
    update_volumes_selection(m_objects_selections);
 | 
			
		||||
    // 2nd call to restore if something selected
 | 
			
		||||
    if (!m_objects_selections.empty())
 | 
			
		||||
        update_gizmos_data();
 | 
			
		||||
 | 
			
		||||
    if (m_config->has("nozzle_diameter"))
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -2082,25 +2301,28 @@ void GLCanvas3D::reload_scene(bool force)
 | 
			
		|||
    // checks for geometry outside the print volume to render it accordingly
 | 
			
		||||
    if (!m_volumes.empty())
 | 
			
		||||
    {
 | 
			
		||||
        bool contained = m_volumes.check_outside_state(m_config);
 | 
			
		||||
        ModelInstance::EPrintVolumeState state;
 | 
			
		||||
        bool contained = m_volumes.check_outside_state(m_config, &state);
 | 
			
		||||
 | 
			
		||||
        if (!contained)
 | 
			
		||||
        {
 | 
			
		||||
            enable_warning_texture(true);
 | 
			
		||||
            _3DScene::generate_warning_texture(L("Detected object outside print volume"));
 | 
			
		||||
            m_on_enable_action_buttons_callback.call(false);
 | 
			
		||||
            _generate_warning_texture(L("Detected object outside print volume"));
 | 
			
		||||
            m_on_enable_action_buttons_callback.call(state == ModelInstance::PVS_Fully_Outside);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            enable_warning_texture(false);
 | 
			
		||||
            m_volumes.reset_outside_state();
 | 
			
		||||
            _3DScene::reset_warning_texture();
 | 
			
		||||
            _reset_warning_texture();
 | 
			
		||||
            m_on_enable_action_buttons_callback.call(!m_model->objects.empty());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        enable_warning_texture(false);
 | 
			
		||||
        _3DScene::reset_warning_texture();
 | 
			
		||||
        _reset_warning_texture();
 | 
			
		||||
        m_on_enable_action_buttons_callback.call(false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2490,11 +2712,11 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
 | 
			
		|||
            _load_gcode_unretractions(preview_data);
 | 
			
		||||
            
 | 
			
		||||
            if (m_volumes.empty())
 | 
			
		||||
                _3DScene::reset_legend_texture();
 | 
			
		||||
                reset_legend_texture();
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                _3DScene::generate_legend_texture(preview_data, tool_colors);
 | 
			
		||||
                
 | 
			
		||||
                _generate_legend_texture(preview_data, tool_colors);
 | 
			
		||||
 | 
			
		||||
                // removes empty volumes
 | 
			
		||||
                m_volumes.volumes.erase(std::remove_if(m_volumes.volumes.begin(), m_volumes.volumes.end(),
 | 
			
		||||
                    [](const GLVolume* volume) { return volume->print_zs.empty(); }), m_volumes.volumes.end());
 | 
			
		||||
| 
						 | 
				
			
			@ -3103,6 +3325,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 | 
			
		|||
            {
 | 
			
		||||
                deselect_volumes();
 | 
			
		||||
                _on_select(-1);
 | 
			
		||||
                update_gizmos_data();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (evt.LeftUp() && m_gizmos.is_dragging())
 | 
			
		||||
| 
						 | 
				
			
			@ -3130,6 +3353,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 | 
			
		|||
        m_mouse.set_start_position_3D_as_invalid();
 | 
			
		||||
        m_mouse.set_start_position_2D_as_invalid();
 | 
			
		||||
        m_mouse.dragging = false;
 | 
			
		||||
        m_dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (evt.Moving())
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -3181,6 +3405,14 @@ Point GLCanvas3D::get_local_mouse_position() const
 | 
			
		|||
    return Point(mouse_pos.x, mouse_pos.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::reset_legend_texture()
 | 
			
		||||
{
 | 
			
		||||
    if (!set_current())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    m_legend_texture.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3D::_is_shown_on_screen() const
 | 
			
		||||
{
 | 
			
		||||
    return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
 | 
			
		||||
| 
						 | 
				
			
			@ -3272,7 +3504,7 @@ BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const
 | 
			
		|||
    BoundingBoxf3 bb;
 | 
			
		||||
    for (const GLVolume* volume : m_volumes.volumes)
 | 
			
		||||
    {
 | 
			
		||||
        if ((volume != nullptr) && volume->selected)
 | 
			
		||||
        if ((volume != nullptr) && !volume->is_wipe_tower && volume->selected)
 | 
			
		||||
            bb.merge(volume->transformed_bounding_box());
 | 
			
		||||
    }
 | 
			
		||||
    return bb;
 | 
			
		||||
| 
						 | 
				
			
			@ -3441,6 +3673,7 @@ void GLCanvas3D::_picking_pass() const
 | 
			
		|||
            ::glDisable(GL_MULTISAMPLE);
 | 
			
		||||
 | 
			
		||||
        ::glDisable(GL_BLEND);
 | 
			
		||||
        ::glEnable(GL_DEPTH_TEST);
 | 
			
		||||
 | 
			
		||||
        ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3549,7 +3782,7 @@ void GLCanvas3D::_render_objects() const
 | 
			
		|||
            {
 | 
			
		||||
                const BoundingBoxf3& bed_bb = m_bed.get_bounding_box();
 | 
			
		||||
                m_volumes.set_print_box((float)bed_bb.min.x, (float)bed_bb.min.y, 0.0f, (float)bed_bb.max.x, (float)bed_bb.max.y, (float)m_config->opt_float("max_print_height"));
 | 
			
		||||
                m_volumes.check_outside_state(m_config);
 | 
			
		||||
                m_volumes.check_outside_state(m_config, nullptr);
 | 
			
		||||
            }
 | 
			
		||||
            // do not cull backfaces to show broken geometry, if any
 | 
			
		||||
            ::glDisable(GL_CULL_FACE);
 | 
			
		||||
| 
						 | 
				
			
			@ -3588,11 +3821,11 @@ void GLCanvas3D::_render_warning_texture() const
 | 
			
		|||
        return;
 | 
			
		||||
 | 
			
		||||
    // If the warning texture has not been loaded into the GPU, do it now.
 | 
			
		||||
    unsigned int tex_id = _3DScene::finalize_warning_texture();
 | 
			
		||||
    unsigned int tex_id = m_warning_texture.get_id();
 | 
			
		||||
    if (tex_id > 0)
 | 
			
		||||
    {
 | 
			
		||||
        unsigned int w = _3DScene::get_warning_texture_width();
 | 
			
		||||
        unsigned int h = _3DScene::get_warning_texture_height();
 | 
			
		||||
        int w = m_warning_texture.get_width();
 | 
			
		||||
        int h = m_warning_texture.get_height();
 | 
			
		||||
        if ((w > 0) && (h > 0))
 | 
			
		||||
        {
 | 
			
		||||
            ::glDisable(GL_DEPTH_TEST);
 | 
			
		||||
| 
						 | 
				
			
			@ -3621,11 +3854,11 @@ void GLCanvas3D::_render_legend_texture() const
 | 
			
		|||
        return;
 | 
			
		||||
 | 
			
		||||
    // If the legend texture has not been loaded into the GPU, do it now.
 | 
			
		||||
    unsigned int tex_id = _3DScene::finalize_legend_texture();
 | 
			
		||||
    unsigned int tex_id = m_legend_texture.get_id();
 | 
			
		||||
    if (tex_id > 0)
 | 
			
		||||
    {
 | 
			
		||||
        unsigned int w = _3DScene::get_legend_texture_width();
 | 
			
		||||
        unsigned int h = _3DScene::get_legend_texture_height();
 | 
			
		||||
        int w = m_legend_texture.get_width();
 | 
			
		||||
        int h = m_legend_texture.get_height();
 | 
			
		||||
        if ((w > 0) && (h > 0))
 | 
			
		||||
        {
 | 
			
		||||
            ::glDisable(GL_DEPTH_TEST);
 | 
			
		||||
| 
						 | 
				
			
			@ -4539,5 +4772,29 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col
 | 
			
		|||
    return output;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
 | 
			
		||||
{
 | 
			
		||||
    if (!set_current())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    m_legend_texture.generate(preview_data, tool_colors);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::_generate_warning_texture(const std::string& msg)
 | 
			
		||||
{
 | 
			
		||||
    if (!set_current())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    m_warning_texture.generate(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::_reset_warning_texture()
 | 
			
		||||
{
 | 
			
		||||
    if (!set_current())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    m_warning_texture.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace GUI
 | 
			
		||||
} // namespace Slic3r
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -394,9 +394,35 @@ public:
 | 
			
		|||
        GLGizmoBase* _get_current() const;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class WarningTexture : public GUI::GLTexture
 | 
			
		||||
    {
 | 
			
		||||
        static const unsigned char Background_Color[3];
 | 
			
		||||
        static const unsigned char Opacity;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        bool generate(const std::string& msg);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class LegendTexture : public GUI::GLTexture
 | 
			
		||||
    {
 | 
			
		||||
        static const int Px_Title_Offset = 5;
 | 
			
		||||
        static const int Px_Text_Offset = 5;
 | 
			
		||||
        static const int Px_Square = 20;
 | 
			
		||||
        static const int Px_Square_Contour = 1;
 | 
			
		||||
        static const int Px_Border = Px_Square / 2;
 | 
			
		||||
        static const unsigned char Squares_Border_Color[3];
 | 
			
		||||
        static const unsigned char Background_Color[3];
 | 
			
		||||
        static const unsigned char Opacity;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    wxGLCanvas* m_canvas;
 | 
			
		||||
    wxGLContext* m_context;
 | 
			
		||||
    LegendTexture m_legend_texture;
 | 
			
		||||
    WarningTexture m_warning_texture;
 | 
			
		||||
    wxTimer* m_timer;
 | 
			
		||||
    Camera m_camera;
 | 
			
		||||
    Bed m_bed;
 | 
			
		||||
| 
						 | 
				
			
			@ -469,7 +495,7 @@ public:
 | 
			
		|||
    void deselect_volumes();
 | 
			
		||||
    void select_volume(unsigned int id);
 | 
			
		||||
    void update_volumes_selection(const std::vector<int>& selections);
 | 
			
		||||
    bool check_volumes_outside_state(const DynamicPrintConfig* config) const;
 | 
			
		||||
    int check_volumes_outside_state(const DynamicPrintConfig* config) const;
 | 
			
		||||
    bool move_volume_up(unsigned int id);
 | 
			
		||||
    bool move_volume_down(unsigned int id);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -578,6 +604,8 @@ public:
 | 
			
		|||
    Size get_canvas_size() const;
 | 
			
		||||
    Point get_local_mouse_position() const;
 | 
			
		||||
 | 
			
		||||
    void reset_legend_texture();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    bool _is_shown_on_screen() const;
 | 
			
		||||
    void _force_zoom_to_bed();
 | 
			
		||||
| 
						 | 
				
			
			@ -643,6 +671,13 @@ private:
 | 
			
		|||
    void _on_move(const std::vector<int>& volume_idxs);
 | 
			
		||||
    void _on_select(int volume_idx);
 | 
			
		||||
 | 
			
		||||
    // generates the legend texture in dependence of the current shown view type
 | 
			
		||||
    void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
 | 
			
		||||
 | 
			
		||||
    // generates a warning texture containing the given message
 | 
			
		||||
    void _generate_warning_texture(const std::string& msg);
 | 
			
		||||
    void _reset_warning_texture();
 | 
			
		||||
 | 
			
		||||
    static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -237,7 +237,7 @@ void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std::
 | 
			
		|||
        it->second->update_volumes_selection(selections);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const
 | 
			
		||||
int GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const
 | 
			
		||||
{
 | 
			
		||||
    CanvasesMap::const_iterator it = _get_canvas(canvas);
 | 
			
		||||
    return (it != m_canvases.end()) ? it->second->check_volumes_outside_state(config) : false;
 | 
			
		||||
| 
						 | 
				
			
			@ -550,6 +550,15 @@ void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePrevie
 | 
			
		|||
        it->second->load_gcode_preview(*preview_data, str_tool_colors);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3DManager::reset_legend_texture()
 | 
			
		||||
{
 | 
			
		||||
    for (CanvasesMap::value_type& canvas : m_canvases)
 | 
			
		||||
    {
 | 
			
		||||
        if (canvas.second != nullptr)
 | 
			
		||||
            canvas.second->reset_legend_texture();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback)
 | 
			
		||||
{
 | 
			
		||||
    CanvasesMap::iterator it = _get_canvas(canvas);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,7 +75,7 @@ public:
 | 
			
		|||
    void deselect_volumes(wxGLCanvas* canvas);
 | 
			
		||||
    void select_volume(wxGLCanvas* canvas, unsigned int id);
 | 
			
		||||
    void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections);
 | 
			
		||||
    bool check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const;
 | 
			
		||||
    int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const;
 | 
			
		||||
    bool move_volume_up(wxGLCanvas* canvas, unsigned int id);
 | 
			
		||||
    bool move_volume_down(wxGLCanvas* canvas, unsigned int id);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -137,6 +137,8 @@ public:
 | 
			
		|||
    void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
 | 
			
		||||
    void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
 | 
			
		||||
 | 
			
		||||
    void reset_legend_texture();
 | 
			
		||||
 | 
			
		||||
    void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback);
 | 
			
		||||
    void register_on_double_click_callback(wxGLCanvas* canvas, void* callback);
 | 
			
		||||
    void register_on_right_click_callback(wxGLCanvas* canvas, void* callback);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,8 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap
 | 
			
		|||
    if (generate_mipmaps)
 | 
			
		||||
    {
 | 
			
		||||
        // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
 | 
			
		||||
        _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);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
| 
						 | 
				
			
			@ -149,14 +150,14 @@ void GLTexture::render_texture(unsigned int tex_id, float left, float right, flo
 | 
			
		|||
    ::glDisable(GL_BLEND);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLTexture::_generate_mipmaps(wxImage& image)
 | 
			
		||||
unsigned int GLTexture::_generate_mipmaps(wxImage& image)
 | 
			
		||||
{
 | 
			
		||||
    int w = image.GetWidth();
 | 
			
		||||
    int h = image.GetHeight();
 | 
			
		||||
    GLint level = 0;
 | 
			
		||||
    std::vector<unsigned char> data(w * h * 4, 0);
 | 
			
		||||
 | 
			
		||||
    while ((w > 1) && (h > 1))
 | 
			
		||||
    while ((w > 1) || (h > 1))
 | 
			
		||||
    {
 | 
			
		||||
        ++level;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -183,6 +184,8 @@ void GLTexture::_generate_mipmaps(wxImage& image)
 | 
			
		|||
 | 
			
		||||
        ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (unsigned int)level;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace GUI
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ namespace GUI {
 | 
			
		|||
 | 
			
		||||
    class GLTexture
 | 
			
		||||
    {
 | 
			
		||||
    private:
 | 
			
		||||
    protected:
 | 
			
		||||
        unsigned int m_id;
 | 
			
		||||
        int m_width;
 | 
			
		||||
        int m_height;
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +18,7 @@ namespace GUI {
 | 
			
		|||
 | 
			
		||||
    public:
 | 
			
		||||
        GLTexture();
 | 
			
		||||
        ~GLTexture();
 | 
			
		||||
        virtual ~GLTexture();
 | 
			
		||||
 | 
			
		||||
        bool load_from_file(const std::string& filename, bool generate_mipmaps);
 | 
			
		||||
        void reset();
 | 
			
		||||
| 
						 | 
				
			
			@ -26,12 +26,13 @@ namespace GUI {
 | 
			
		|||
        unsigned int get_id() const;
 | 
			
		||||
        int get_width() const;
 | 
			
		||||
        int get_height() const;
 | 
			
		||||
 | 
			
		||||
        const std::string& get_source() const;
 | 
			
		||||
 | 
			
		||||
        static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        void _generate_mipmaps(wxImage& image);
 | 
			
		||||
    protected:
 | 
			
		||||
        unsigned int _generate_mipmaps(wxImage& image);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
} // namespace GUI
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -268,7 +268,7 @@ void PresetBundle::load_installed_printers(const AppConfig &config)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Load selections (current print, current filaments, current printer) from config.ini
 | 
			
		||||
// This is done just once on application start up.
 | 
			
		||||
// This is done on application start up or after updates are applied.
 | 
			
		||||
void PresetBundle::load_selections(const AppConfig &config)
 | 
			
		||||
{
 | 
			
		||||
	// Update visibility of presets based on application vendor / model / variant configuration.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -322,10 +322,13 @@ Updates PresetUpdater::priv::get_config_updates() const
 | 
			
		|||
 | 
			
		||||
		const auto ver_current = idx.find(vp.config_version);
 | 
			
		||||
		if (ver_current == idx.end()) {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(error) << boost::format("Preset bundle (`%1%`) version not found in index: %2%") % idx.vendor() % vp.config_version.to_string();
 | 
			
		||||
			continue;
 | 
			
		||||
			auto message = (boost::format("Preset bundle `%1%` version not found in index: %2%") % idx.vendor() % vp.config_version.to_string()).str();
 | 
			
		||||
			BOOST_LOG_TRIVIAL(error) << message;
 | 
			
		||||
			throw std::runtime_error(message);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Getting a recommended version from the latest index, wich may have been downloaded
 | 
			
		||||
		// from the internet, or installed / updated from the installation resources.
 | 
			
		||||
		const auto recommended = idx.recommended();
 | 
			
		||||
		if (recommended == idx.end()) {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % idx.vendor();
 | 
			
		||||
| 
						 | 
				
			
			@ -353,25 +356,34 @@ Updates PresetUpdater::priv::get_config_updates() const
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
			auto path_src = cache_path / (idx.vendor() + ".ini");
 | 
			
		||||
			auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini");
 | 
			
		||||
			if (! fs::exists(path_src)) {
 | 
			
		||||
				auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini");
 | 
			
		||||
				if (! fs::exists(path_in_rsrc)) {
 | 
			
		||||
					BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update, but bundle found in neither cache nor resources")
 | 
			
		||||
						% idx.vendor();;
 | 
			
		||||
						% idx.vendor();
 | 
			
		||||
					continue;
 | 
			
		||||
				} else {
 | 
			
		||||
					path_src = std::move(path_in_rsrc);
 | 
			
		||||
					path_in_rsrc.clear();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const auto new_vp = VendorProfile::from_ini(path_src, false);
 | 
			
		||||
			auto new_vp = VendorProfile::from_ini(path_src, false);
 | 
			
		||||
			bool found = false;
 | 
			
		||||
			if (new_vp.config_version == recommended->config_version) {
 | 
			
		||||
				updates.updates.emplace_back(std::move(path_src), std::move(bundle_path), *recommended);
 | 
			
		||||
			} else {
 | 
			
		||||
				found = true;
 | 
			
		||||
			} else if (! path_in_rsrc.empty() && fs::exists(path_in_rsrc)) {
 | 
			
		||||
				new_vp = VendorProfile::from_ini(path_in_rsrc, false);
 | 
			
		||||
				if (new_vp.config_version == recommended->config_version) {
 | 
			
		||||
					updates.updates.emplace_back(std::move(path_in_rsrc), std::move(bundle_path), *recommended);
 | 
			
		||||
					found = true;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (! found)
 | 
			
		||||
				BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources")
 | 
			
		||||
					% idx.vendor()
 | 
			
		||||
					% recommended->config_version.to_string();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -530,10 +542,21 @@ bool PresetUpdater::config_update() const
 | 
			
		|||
		std::unordered_map<std::string, wxString> incompats_map;
 | 
			
		||||
		for (const auto &incompat : updates.incompats) {
 | 
			
		||||
			auto vendor = incompat.name();
 | 
			
		||||
			auto restrictions = wxString::Format(_(L("requires min. %s and max. %s")),
 | 
			
		||||
				incompat.version.min_slic3r_version.to_string(),
 | 
			
		||||
				incompat.version.max_slic3r_version.to_string()
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			const auto min_slic3r = incompat.version.min_slic3r_version;
 | 
			
		||||
			const auto max_slic3r = incompat.version.max_slic3r_version;
 | 
			
		||||
			wxString restrictions;
 | 
			
		||||
			if (min_slic3r != Semver::zero() && max_slic3r != Semver::inf()) {
 | 
			
		||||
				restrictions = wxString::Format(_(L("requires min. %s and max. %s")),
 | 
			
		||||
					min_slic3r.to_string(),
 | 
			
		||||
					max_slic3r.to_string()
 | 
			
		||||
				);
 | 
			
		||||
			} else if (min_slic3r != Semver::zero()) {
 | 
			
		||||
				restrictions = wxString::Format(_(L("requires min. %s")), min_slic3r.to_string());
 | 
			
		||||
			} else {
 | 
			
		||||
				restrictions = wxString::Format(_(L("requires max. %s")), max_slic3r.to_string());
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			incompats_map.emplace(std::make_pair(std::move(vendor), std::move(restrictions)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -545,9 +568,10 @@ bool PresetUpdater::config_update() const
 | 
			
		|||
			BOOST_LOG_TRIVIAL(info) << "User wants to re-configure...";
 | 
			
		||||
			p->perform_updates(std::move(updates));
 | 
			
		||||
			GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT);
 | 
			
		||||
			if (! wizard.run(GUI::get_preset_bundle(), this)) {	
 | 
			
		||||
			if (! wizard.run(GUI::get_preset_bundle(), this)) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			GUI::load_current_presets();
 | 
			
		||||
		} else {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye...";
 | 
			
		||||
			return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -577,7 +601,6 @@ bool PresetUpdater::config_update() const
 | 
			
		|||
 | 
			
		||||
			// Reload global configuration
 | 
			
		||||
			auto *app_config = GUI::get_app_config();
 | 
			
		||||
			app_config->reset_selections();
 | 
			
		||||
			GUI::get_preset_bundle()->load_presets(*app_config);
 | 
			
		||||
			GUI::load_current_presets();
 | 
			
		||||
		} else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -105,10 +105,6 @@
 | 
			
		|||
    void release_geometry();
 | 
			
		||||
 | 
			
		||||
    void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z);
 | 
			
		||||
    bool check_outside_state(DynamicPrintConfig* config)
 | 
			
		||||
        %code%{ 
 | 
			
		||||
                RETVAL = THIS->check_outside_state(config);
 | 
			
		||||
        %};
 | 
			
		||||
    
 | 
			
		||||
    void reset_outside_state();    
 | 
			
		||||
    void update_colors_by_extruder(DynamicPrintConfig* config);
 | 
			
		||||
| 
						 | 
				
			
			@ -234,7 +230,7 @@ update_volumes_selection(canvas, selections)
 | 
			
		|||
    CODE:
 | 
			
		||||
        _3DScene::update_volumes_selection((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), selections);
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
int
 | 
			
		||||
check_volumes_outside_state(canvas, config)
 | 
			
		||||
        SV                 *canvas;
 | 
			
		||||
        DynamicPrintConfig *config;
 | 
			
		||||
| 
						 | 
				
			
			@ -627,64 +623,12 @@ register_on_update_geometry_info_callback(canvas, callback)
 | 
			
		|||
        SV *callback;
 | 
			
		||||
    CODE:
 | 
			
		||||
        _3DScene::register_on_update_geometry_info_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
 | 
			
		||||
 | 
			
		||||
unsigned int
 | 
			
		||||
finalize_legend_texture()
 | 
			
		||||
    CODE:
 | 
			
		||||
        RETVAL = _3DScene::finalize_legend_texture();
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        RETVAL
 | 
			
		||||
 | 
			
		||||
unsigned int
 | 
			
		||||
get_legend_texture_width()
 | 
			
		||||
    CODE:
 | 
			
		||||
        RETVAL = _3DScene::get_legend_texture_width();
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        RETVAL
 | 
			
		||||
 | 
			
		||||
unsigned int
 | 
			
		||||
get_legend_texture_height()
 | 
			
		||||
    CODE:
 | 
			
		||||
        RETVAL = _3DScene::get_legend_texture_height();
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        RETVAL
 | 
			
		||||
             
 | 
			
		||||
void
 | 
			
		||||
reset_legend_texture()
 | 
			
		||||
    CODE:
 | 
			
		||||
        _3DScene::reset_legend_texture();
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
generate_warning_texture(std::string msg)
 | 
			
		||||
    CODE:
 | 
			
		||||
        _3DScene::generate_warning_texture(msg);
 | 
			
		||||
 | 
			
		||||
unsigned int
 | 
			
		||||
finalize_warning_texture()
 | 
			
		||||
    CODE:
 | 
			
		||||
        RETVAL = _3DScene::finalize_warning_texture();
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        RETVAL
 | 
			
		||||
 | 
			
		||||
unsigned int
 | 
			
		||||
get_warning_texture_width()
 | 
			
		||||
    CODE:
 | 
			
		||||
        RETVAL = _3DScene::get_warning_texture_width();
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        RETVAL
 | 
			
		||||
 | 
			
		||||
unsigned int
 | 
			
		||||
get_warning_texture_height()
 | 
			
		||||
    CODE:
 | 
			
		||||
        RETVAL = _3DScene::get_warning_texture_height();
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        RETVAL
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
reset_warning_texture()
 | 
			
		||||
    CODE:
 | 
			
		||||
        _3DScene::reset_warning_texture();
 | 
			
		||||
 | 
			
		||||
std::vector<int>
 | 
			
		||||
load_model_object(canvas, model_object, obj_idx, instance_idxs)
 | 
			
		||||
        SV               *canvas;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue