mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Merged the branch time_estimate
This commit is contained in:
		
						commit
						02256e900f
					
				
					 7 changed files with 1391 additions and 147 deletions
				
			
		| 
						 | 
				
			
			@ -267,22 +267,6 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen)
 | 
			
		|||
 | 
			
		||||
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id())
 | 
			
		||||
 | 
			
		||||
inline void write(FILE *file, const std::string &what)
 | 
			
		||||
{
 | 
			
		||||
    fwrite(what.data(), 1, what.size(), file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Write a string into a file. Add a newline, if the string does not end with a newline already.
 | 
			
		||||
// Used to export a custom G-code section processed by the PlaceholderParser.
 | 
			
		||||
inline void writeln(FILE *file, const std::string &what)
 | 
			
		||||
{
 | 
			
		||||
    if (! what.empty()) {
 | 
			
		||||
        write(file, what);
 | 
			
		||||
        if (what.back() != '\n')
 | 
			
		||||
            fprintf(file, "\n");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Collect pairs of object_layer + support_layer sorted by print_z.
 | 
			
		||||
// object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON.
 | 
			
		||||
std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObject &object)
 | 
			
		||||
| 
						 | 
				
			
			@ -395,6 +379,7 @@ void GCode::do_export(Print *print, const char *path)
 | 
			
		|||
        msg += "        !!!!! End of an error report for the custom G-code template ...\n";
 | 
			
		||||
        throw std::runtime_error(msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (boost::nowide::rename(path_tmp.c_str(), path) != 0)
 | 
			
		||||
        throw std::runtime_error(
 | 
			
		||||
            std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
 | 
			
		||||
| 
						 | 
				
			
			@ -403,6 +388,9 @@ void GCode::do_export(Print *print, const char *path)
 | 
			
		|||
 | 
			
		||||
void GCode::_do_export(Print &print, FILE *file)
 | 
			
		||||
{
 | 
			
		||||
    // resets time estimator
 | 
			
		||||
    m_time_estimator.reset();
 | 
			
		||||
 | 
			
		||||
    // How many times will be change_layer() called?
 | 
			
		||||
    // change_layer() in turn increments the progress bar status.
 | 
			
		||||
    m_layer_count = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -486,7 +474,7 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
    m_enable_extrusion_role_markers = (bool)m_pressure_equalizer;
 | 
			
		||||
 | 
			
		||||
    // Write information on the generator.
 | 
			
		||||
    fprintf(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
 | 
			
		||||
    _write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
 | 
			
		||||
    // Write notes (content of the Print Settings tab -> Notes)
 | 
			
		||||
    {
 | 
			
		||||
        std::list<std::string> lines;
 | 
			
		||||
| 
						 | 
				
			
			@ -495,10 +483,10 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
            // Remove the trailing '\r' from the '\r\n' sequence.
 | 
			
		||||
            if (! line.empty() && line.back() == '\r')
 | 
			
		||||
                line.pop_back();
 | 
			
		||||
            fprintf(file, "; %s\n", line.c_str());
 | 
			
		||||
            _write_format(file, "; %s\n", line.c_str());
 | 
			
		||||
        }
 | 
			
		||||
        if (! lines.empty())
 | 
			
		||||
            fprintf(file, "\n");
 | 
			
		||||
            _write(file, "\n");
 | 
			
		||||
    }
 | 
			
		||||
    // Write some terse information on the slicing parameters.
 | 
			
		||||
    const PrintObject *first_object         = print.objects.front();
 | 
			
		||||
| 
						 | 
				
			
			@ -506,16 +494,16 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
    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) {
 | 
			
		||||
        auto region = print.regions[region_id];
 | 
			
		||||
        fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        fprintf(file, "; perimeters extrusion width = %.2fmm\n",          region->flow(frPerimeter,         layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        fprintf(file, "; infill extrusion width = %.2fmm\n",              region->flow(frInfill,            layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        fprintf(file, "; solid infill extrusion width = %.2fmm\n",        region->flow(frSolidInfill,       layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        fprintf(file, "; top infill extrusion width = %.2fmm\n",          region->flow(frTopSolidInfill,    layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        _write_format(file, "; perimeters extrusion width = %.2fmm\n",          region->flow(frPerimeter,         layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        _write_format(file, "; infill extrusion width = %.2fmm\n",              region->flow(frInfill,            layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        _write_format(file, "; solid infill extrusion width = %.2fmm\n",        region->flow(frSolidInfill,       layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        _write_format(file, "; top infill extrusion width = %.2fmm\n",          region->flow(frTopSolidInfill,    layer_height, false, false, -1., *first_object).width);
 | 
			
		||||
        if (print.has_support_material())
 | 
			
		||||
            fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
 | 
			
		||||
            _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
 | 
			
		||||
        if (print.config.first_layer_extrusion_width.value > 0)
 | 
			
		||||
            fprintf(file, "; first layer extrusion width = %.2fmm\n",   region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
 | 
			
		||||
        fprintf(file, "\n");
 | 
			
		||||
            _write_format(file, "; first layer extrusion width = %.2fmm\n",   region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
 | 
			
		||||
        _write_format(file, "\n");
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Prepare the helper object for replacing placeholders in custom G-code and output filename.
 | 
			
		||||
| 
						 | 
				
			
			@ -558,7 +546,7 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
 | 
			
		||||
    // Disable fan.
 | 
			
		||||
    if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id))
 | 
			
		||||
        write(file, m_writer.set_fan(0, true));
 | 
			
		||||
        _write(file, m_writer.set_fan(0, true));
 | 
			
		||||
 | 
			
		||||
    // Let the start-up script prime the 1st printing tool.
 | 
			
		||||
    m_placeholder_parser.set("initial_tool", initial_extruder_id);
 | 
			
		||||
| 
						 | 
				
			
			@ -575,24 +563,24 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
    // Set extruder(s) temperature before and after start G-code.
 | 
			
		||||
    this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
 | 
			
		||||
    // Write the custom start G-code
 | 
			
		||||
    writeln(file, start_gcode);
 | 
			
		||||
    _writeln(file, start_gcode);
 | 
			
		||||
    // Process filament-specific gcode in extruder order.
 | 
			
		||||
    if (print.config.single_extruder_multi_material) {
 | 
			
		||||
        if (has_wipe_tower) {
 | 
			
		||||
            // Wipe tower will control the extruder switching, it will call the start_filament_gcode.
 | 
			
		||||
        } else {
 | 
			
		||||
            // Only initialize the initial extruder.
 | 
			
		||||
            writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
 | 
			
		||||
            _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        for (const std::string &start_gcode : print.config.start_filament_gcode.values)
 | 
			
		||||
            writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
 | 
			
		||||
            _writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
 | 
			
		||||
    }
 | 
			
		||||
    this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
 | 
			
		||||
    
 | 
			
		||||
    // Set other general things.
 | 
			
		||||
    write(file, this->preamble());
 | 
			
		||||
    
 | 
			
		||||
    _write(file, this->preamble());
 | 
			
		||||
 | 
			
		||||
    // Initialize a motion planner for object-to-object travel moves.
 | 
			
		||||
    if (print.config.avoid_crossing_perimeters.value) {
 | 
			
		||||
        // Collect outer contours of all objects over all layers.
 | 
			
		||||
| 
						 | 
				
			
			@ -640,7 +628,7 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Set initial extruder only after custom start G-code.
 | 
			
		||||
    write(file, this->set_extruder(initial_extruder_id));
 | 
			
		||||
    _write(file, this->set_extruder(initial_extruder_id));
 | 
			
		||||
 | 
			
		||||
    // Do all objects for each layer.
 | 
			
		||||
    if (print.config.complete_objects.value) {
 | 
			
		||||
| 
						 | 
				
			
			@ -670,8 +658,8 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
                    // This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
 | 
			
		||||
                    m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
 | 
			
		||||
                    m_avoid_crossing_perimeters.use_external_mp_once = true;
 | 
			
		||||
                    write(file, this->retract());
 | 
			
		||||
                    write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
 | 
			
		||||
                    _write(file, this->retract());
 | 
			
		||||
                    _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
 | 
			
		||||
                    m_enable_cooling_markers = true;
 | 
			
		||||
                    // Disable motion planner when traveling to first object point.
 | 
			
		||||
                    m_avoid_crossing_perimeters.disable_once = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -683,7 +671,7 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
                    // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
 | 
			
		||||
                    this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
 | 
			
		||||
                    this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
 | 
			
		||||
                    writeln(file, between_objects_gcode);
 | 
			
		||||
                    _writeln(file, between_objects_gcode);
 | 
			
		||||
                }
 | 
			
		||||
                // Reset the cooling buffer internal state (the current position, feed rate, accelerations).
 | 
			
		||||
                m_cooling_buffer->reset();
 | 
			
		||||
| 
						 | 
				
			
			@ -696,7 +684,7 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
                    this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - object._shifted_copies.data());
 | 
			
		||||
                }
 | 
			
		||||
                if (m_pressure_equalizer)
 | 
			
		||||
                    write(file, m_pressure_equalizer->process("", true));
 | 
			
		||||
                    _write(file, m_pressure_equalizer->process("", true));
 | 
			
		||||
                ++ finished_objects;
 | 
			
		||||
                // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
 | 
			
		||||
                // Reset it when starting another object from 1st layer.
 | 
			
		||||
| 
						 | 
				
			
			@ -716,8 +704,8 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
        // Prusa Multi-Material wipe tower.
 | 
			
		||||
        if (has_wipe_tower && ! layers_to_print.empty()) {
 | 
			
		||||
            m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
 | 
			
		||||
            write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
 | 
			
		||||
		    write(file, m_wipe_tower->prime(*this));
 | 
			
		||||
            _write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
 | 
			
		||||
		    _write(file, m_wipe_tower->prime(*this));
 | 
			
		||||
            // 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;
 | 
			
		||||
| 
						 | 
				
			
			@ -727,16 +715,17 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
            BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
 | 
			
		||||
            bbox_prime.offset(0.5f);
 | 
			
		||||
            // Beep for 500ms, tone 800Hz. Yet better, play some Morse.
 | 
			
		||||
            write(file, this->retract());
 | 
			
		||||
            fprintf(file, "M300 S800 P500\n");
 | 
			
		||||
            _write(file, this->retract());
 | 
			
		||||
            _write(file, "M300 S800 P500\n");
 | 
			
		||||
            if (bbox_prime.overlap(bbox_print)) {
 | 
			
		||||
                // Wait for the user to remove the priming extrusions, otherwise they would
 | 
			
		||||
                // get covered by the print.
 | 
			
		||||
                fprintf(file, "M1 Remove priming towers and click button.\n");
 | 
			
		||||
            } else {
 | 
			
		||||
                _write(file, "M1 Remove priming towers and click button.\n");
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // Just wait for a bit to let the user check, that the priming succeeded.
 | 
			
		||||
                //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
 | 
			
		||||
                fprintf(file, "M1 S10\n");
 | 
			
		||||
                _write(file, "M1 S10\n");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Extrude the layers.
 | 
			
		||||
| 
						 | 
				
			
			@ -747,26 +736,29 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
            this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
 | 
			
		||||
        }
 | 
			
		||||
        if (m_pressure_equalizer)
 | 
			
		||||
            write(file, m_pressure_equalizer->process("", true));
 | 
			
		||||
            _write(file, m_pressure_equalizer->process("", true));
 | 
			
		||||
        if (m_wipe_tower)
 | 
			
		||||
            // Purge the extruder, pull out the active filament.
 | 
			
		||||
            write(file, m_wipe_tower->finalize(*this));
 | 
			
		||||
            _write(file, m_wipe_tower->finalize(*this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Write end commands to file.
 | 
			
		||||
    write(file, this->retract());
 | 
			
		||||
    write(file, m_writer.set_fan(false));
 | 
			
		||||
    _write(file, this->retract());
 | 
			
		||||
    _write(file, m_writer.set_fan(false));
 | 
			
		||||
    // Process filament-specific gcode in extruder order.
 | 
			
		||||
    if (print.config.single_extruder_multi_material) {
 | 
			
		||||
        // Process the end_filament_gcode for the active filament only.
 | 
			
		||||
        writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id()));
 | 
			
		||||
        _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id()));
 | 
			
		||||
    } else {
 | 
			
		||||
        for (const std::string &end_gcode : print.config.end_filament_gcode.values)
 | 
			
		||||
            writeln(file, this->placeholder_parser_process("end_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
 | 
			
		||||
            _writeln(file, this->placeholder_parser_process("end_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
 | 
			
		||||
    }
 | 
			
		||||
    writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id()));
 | 
			
		||||
    write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
 | 
			
		||||
    write(file, m_writer.postamble());
 | 
			
		||||
    _writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id()));
 | 
			
		||||
    _write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
 | 
			
		||||
    _write(file, m_writer.postamble());
 | 
			
		||||
 | 
			
		||||
    // calculates estimated printing time
 | 
			
		||||
    m_time_estimator.calculate_time();
 | 
			
		||||
 | 
			
		||||
    // Get filament stats.
 | 
			
		||||
    print.filament_stats.clear();
 | 
			
		||||
| 
						 | 
				
			
			@ -774,35 +766,37 @@ void GCode::_do_export(Print &print, FILE *file)
 | 
			
		|||
    print.total_extruded_volume  = 0.;
 | 
			
		||||
    print.total_weight           = 0.;
 | 
			
		||||
    print.total_cost             = 0.;
 | 
			
		||||
    print.estimated_print_time   = m_time_estimator.get_time_hms();
 | 
			
		||||
    for (const Extruder &extruder : m_writer.extruders()) {
 | 
			
		||||
        double used_filament   = extruder.used_filament();
 | 
			
		||||
        double extruded_volume = extruder.extruded_volume();
 | 
			
		||||
        double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
 | 
			
		||||
        double filament_cost   = filament_weight * extruder.filament_cost()    * 0.001;
 | 
			
		||||
        print.filament_stats.insert(std::pair<size_t,float>(extruder.id(), used_filament));
 | 
			
		||||
        fprintf(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
 | 
			
		||||
        _write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
 | 
			
		||||
        if (filament_weight > 0.) {
 | 
			
		||||
            print.total_weight = print.total_weight + filament_weight;
 | 
			
		||||
            fprintf(file, "; filament used = %.1lf\n", filament_weight);
 | 
			
		||||
            _write_format(file, "; filament used = %.1lf\n", filament_weight);
 | 
			
		||||
            if (filament_cost > 0.) {
 | 
			
		||||
                print.total_cost = print.total_cost + filament_cost;
 | 
			
		||||
                fprintf(file, "; filament cost = %.1lf\n", filament_cost);
 | 
			
		||||
                _write_format(file, "; filament cost = %.1lf\n", filament_cost);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        print.total_used_filament   = print.total_used_filament + used_filament;
 | 
			
		||||
        print.total_used_filament = print.total_used_filament + used_filament;
 | 
			
		||||
        print.total_extruded_volume = print.total_extruded_volume + extruded_volume;
 | 
			
		||||
    }
 | 
			
		||||
    fprintf(file, "; total filament cost = %.1lf\n", print.total_cost);
 | 
			
		||||
    _write_format(file, "; total filament cost = %.1lf\n", print.total_cost);
 | 
			
		||||
    _write_format(file, "; estimated printing time = %s\n", m_time_estimator.get_time_hms());
 | 
			
		||||
 | 
			
		||||
    // Append full config.
 | 
			
		||||
    fprintf(file, "\n");
 | 
			
		||||
    _write(file, "\n");
 | 
			
		||||
    {
 | 
			
		||||
        StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
 | 
			
		||||
        for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++ i) {
 | 
			
		||||
            StaticPrintConfig *cfg = configs[i];
 | 
			
		||||
        for (const std::string &key : cfg->keys())
 | 
			
		||||
            if (key != "compatible_printers")
 | 
			
		||||
                fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
 | 
			
		||||
                _write_format(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -893,7 +887,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
 | 
			
		|||
    // the custom start G-code emited these.
 | 
			
		||||
    std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait);
 | 
			
		||||
    if (! temp_set_by_gcode)
 | 
			
		||||
        write(file, set_temp_gcode);
 | 
			
		||||
        _write(file, set_temp_gcode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Write 1st layer extruder temperatures into the G-code.
 | 
			
		||||
| 
						 | 
				
			
			@ -916,7 +910,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
 | 
			
		|||
            // Set temperature of the first printing extruder only.
 | 
			
		||||
            int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id);
 | 
			
		||||
            if (temp > 0)
 | 
			
		||||
                write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
 | 
			
		||||
                _write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
 | 
			
		||||
        } else {
 | 
			
		||||
            // Set temperatures of all the printing extruders.
 | 
			
		||||
            for (unsigned int tool_id : print.extruders()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -924,7 +918,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
 | 
			
		|||
                if (print.config.ooze_prevention.value)
 | 
			
		||||
                    temp += print.config.standby_temperature_delta.value;
 | 
			
		||||
                if (temp > 0)
 | 
			
		||||
                    write(file, m_writer.set_temperature(temp, wait, tool_id));
 | 
			
		||||
                    _write(file, m_writer.set_temperature(temp, wait, tool_id));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1358,7 +1352,7 @@ void GCode::process_layer(
 | 
			
		|||
        gcode = m_pressure_equalizer->process(gcode.c_str(), false);
 | 
			
		||||
    // printf("G-code after filter:\n%s\n", out.c_str());
 | 
			
		||||
 | 
			
		||||
    write(file, gcode);
 | 
			
		||||
    _write(file, gcode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GCode::apply_print_config(const PrintConfig &print_config)
 | 
			
		||||
| 
						 | 
				
			
			@ -1993,6 +1987,46 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
 | 
			
		|||
    return gcode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GCode::_write(FILE* file, const std::string& what)
 | 
			
		||||
{
 | 
			
		||||
    if (!what.empty()) {
 | 
			
		||||
        // writes string to file
 | 
			
		||||
        fwrite(what.data(), 1, what.size(), file);
 | 
			
		||||
        // updates time estimator and gcode lines vector
 | 
			
		||||
        const char endLine = '\n';
 | 
			
		||||
        std::string::size_type beginPos = 0;
 | 
			
		||||
        std::string::size_type endPos = what.find_first_of(endLine, beginPos);
 | 
			
		||||
        while (endPos != std::string::npos) {
 | 
			
		||||
            std::string line = what.substr(beginPos, endPos - beginPos + 1);
 | 
			
		||||
            m_time_estimator.add_gcode_line(line);
 | 
			
		||||
            beginPos = endPos + 1;
 | 
			
		||||
            endPos = what.find_first_of(endLine, beginPos);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GCode::_writeln(FILE* file, const std::string& what)
 | 
			
		||||
{
 | 
			
		||||
    if (!what.empty()) {
 | 
			
		||||
        if (what.back() != '\n')
 | 
			
		||||
            _write_format(file, "%s\n", what.c_str());
 | 
			
		||||
        else
 | 
			
		||||
            _write(file, what);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GCode::_write_format(FILE* file, const char* format, ...)
 | 
			
		||||
{
 | 
			
		||||
    char buffer[1024];
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start(args, format);
 | 
			
		||||
    int res = ::vsnprintf(buffer, 1024, format, args);
 | 
			
		||||
    va_end(args);
 | 
			
		||||
 | 
			
		||||
    if (res >= 0)
 | 
			
		||||
        _writeln(file, buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed)
 | 
			
		||||
{
 | 
			
		||||
    std::string gcode;
 | 
			
		||||
| 
						 | 
				
			
			@ -2182,8 +2216,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string
 | 
			
		||||
GCode::retract(bool toolchange)
 | 
			
		||||
std::string GCode::retract(bool toolchange)
 | 
			
		||||
{
 | 
			
		||||
    std::string gcode;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
#include "GCode/SpiralVase.hpp"
 | 
			
		||||
#include "GCode/ToolOrdering.hpp"
 | 
			
		||||
#include "GCode/WipeTower.hpp"
 | 
			
		||||
#include "GCodeTimeEstimator.hpp"
 | 
			
		||||
#include "EdgeGrid.hpp"
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
| 
						 | 
				
			
			@ -273,6 +274,20 @@ protected:
 | 
			
		|||
    // Index of a last object copy extruded.
 | 
			
		||||
    std::pair<const PrintObject*, Point> m_last_obj_copy;
 | 
			
		||||
 | 
			
		||||
    // Time estimator
 | 
			
		||||
    GCodeTimeEstimator m_time_estimator;
 | 
			
		||||
 | 
			
		||||
    // Write a string into a file.
 | 
			
		||||
    void _write(FILE* file, const std::string& what);
 | 
			
		||||
 | 
			
		||||
    // Write a string into a file. 
 | 
			
		||||
    // Add a newline, if the string does not end with a newline already.
 | 
			
		||||
    // Used to export a custom G-code section processed by the PlaceholderParser.
 | 
			
		||||
    void _writeln(FILE* file, const std::string& what);
 | 
			
		||||
 | 
			
		||||
    // Formats and write into a file the given data. 
 | 
			
		||||
    void _write_format(FILE* file, const char* format, ...);
 | 
			
		||||
 | 
			
		||||
    std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
 | 
			
		||||
    void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
 | 
			
		||||
    void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -6,18 +6,307 @@
 | 
			
		|||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
class GCodeTimeEstimator : public GCodeReader {
 | 
			
		||||
    public:
 | 
			
		||||
    float time = 0;  // in seconds
 | 
			
		||||
    
 | 
			
		||||
    void parse(const std::string &gcode);
 | 
			
		||||
    void parse_file(const std::string &file);
 | 
			
		||||
    
 | 
			
		||||
    protected:
 | 
			
		||||
    float acceleration = 9000;
 | 
			
		||||
    void _parser(GCodeReader&, const GCodeReader::GCodeLine &line);
 | 
			
		||||
    static float _accelerated_move(double length, double v, double acceleration);
 | 
			
		||||
};
 | 
			
		||||
  class GCodeTimeEstimator
 | 
			
		||||
  {
 | 
			
		||||
  public:
 | 
			
		||||
    enum EUnits : unsigned char
 | 
			
		||||
    {
 | 
			
		||||
      Millimeters,
 | 
			
		||||
      Inches
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    enum EAxis : unsigned char
 | 
			
		||||
    {
 | 
			
		||||
      X,
 | 
			
		||||
      Y,
 | 
			
		||||
      Z,
 | 
			
		||||
      E,
 | 
			
		||||
      Num_Axis
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    enum EDialect : unsigned char
 | 
			
		||||
    {
 | 
			
		||||
      Unknown,
 | 
			
		||||
      Marlin,
 | 
			
		||||
      Repetier,
 | 
			
		||||
      Smoothieware,
 | 
			
		||||
      RepRapFirmware,
 | 
			
		||||
      Teacup,
 | 
			
		||||
      Num_Dialects
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    enum EPositioningType : unsigned char
 | 
			
		||||
    {
 | 
			
		||||
      Absolute,
 | 
			
		||||
      Relative
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    struct Axis
 | 
			
		||||
    {
 | 
			
		||||
      float position;         // mm
 | 
			
		||||
      float max_feedrate;     // mm/s
 | 
			
		||||
      float max_acceleration; // mm/s^2
 | 
			
		||||
      float max_jerk;         // mm/s
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct Feedrates
 | 
			
		||||
    {
 | 
			
		||||
      float feedrate;                    // mm/s
 | 
			
		||||
      float axis_feedrate[Num_Axis];     // mm/s
 | 
			
		||||
      float abs_axis_feedrate[Num_Axis]; // mm/s
 | 
			
		||||
      float safe_feedrate;               // mm/s
 | 
			
		||||
 | 
			
		||||
      void reset();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct State
 | 
			
		||||
    {
 | 
			
		||||
      EDialect dialect;
 | 
			
		||||
      EUnits units;
 | 
			
		||||
      EPositioningType positioning_xyz_type;
 | 
			
		||||
      EPositioningType positioning_e_type;
 | 
			
		||||
      Axis axis[Num_Axis];
 | 
			
		||||
      float feedrate;                     // mm/s
 | 
			
		||||
      float acceleration;                 // mm/s^2
 | 
			
		||||
      float retract_acceleration;         // mm/s^2
 | 
			
		||||
      float additional_time;              // s
 | 
			
		||||
      float minimum_feedrate;             // mm/s
 | 
			
		||||
      float minimum_travel_feedrate;      // mm/s
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    struct Block
 | 
			
		||||
    {
 | 
			
		||||
      struct FeedrateProfile
 | 
			
		||||
      {
 | 
			
		||||
        float entry;  // mm/s
 | 
			
		||||
        float cruise; // mm/s
 | 
			
		||||
        float exit;   // mm/s
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      struct Trapezoid
 | 
			
		||||
      {
 | 
			
		||||
        float distance;         // mm
 | 
			
		||||
        float accelerate_until; // mm
 | 
			
		||||
        float decelerate_after; // mm
 | 
			
		||||
        FeedrateProfile feedrate;
 | 
			
		||||
 | 
			
		||||
        float acceleration_time(float acceleration) const;
 | 
			
		||||
        float cruise_time() const;
 | 
			
		||||
        float deceleration_time(float acceleration) const;
 | 
			
		||||
        float cruise_distance() const;
 | 
			
		||||
 | 
			
		||||
        // This function gives the time needed to accelerate from an initial speed to reach a final distance.
 | 
			
		||||
        static float acceleration_time_from_distance(float initial_feedrate, float distance, float acceleration);
 | 
			
		||||
 | 
			
		||||
        // This function gives the final speed while accelerating at the given constant acceleration from the given initial speed along the given distance.
 | 
			
		||||
        static float speed_from_distance(float initial_feedrate, float distance, float acceleration);
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      struct Flags
 | 
			
		||||
      {
 | 
			
		||||
        bool recalculate;
 | 
			
		||||
        bool nominal_length;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      Flags flags;
 | 
			
		||||
 | 
			
		||||
      float delta_pos[Num_Axis]; // mm
 | 
			
		||||
      float acceleration;        // mm/s^2
 | 
			
		||||
      float max_entry_speed;     // mm/s
 | 
			
		||||
      float safe_feedrate;       // mm/s
 | 
			
		||||
 | 
			
		||||
      FeedrateProfile feedrate;
 | 
			
		||||
      Trapezoid trapezoid;
 | 
			
		||||
 | 
			
		||||
      // Returns the length of the move covered by this block, in mm
 | 
			
		||||
      float move_length() const;
 | 
			
		||||
 | 
			
		||||
      // Returns true if this block is a retract/unretract move only
 | 
			
		||||
      float is_extruder_only_move() const;
 | 
			
		||||
 | 
			
		||||
      // Returns true if this block is a move with no extrusion
 | 
			
		||||
      float is_travel_move() const;
 | 
			
		||||
 | 
			
		||||
      // Returns the time spent accelerating toward cruise speed, in seconds
 | 
			
		||||
      float acceleration_time() const;
 | 
			
		||||
 | 
			
		||||
      // Returns the time spent at cruise speed, in seconds
 | 
			
		||||
      float cruise_time() const;
 | 
			
		||||
 | 
			
		||||
      // Returns the time spent decelerating from cruise speed, in seconds
 | 
			
		||||
      float deceleration_time() const;
 | 
			
		||||
 | 
			
		||||
      // Returns the distance covered at cruise speed, in mm
 | 
			
		||||
      float cruise_distance() const;
 | 
			
		||||
 | 
			
		||||
      // Calculates this block's trapezoid
 | 
			
		||||
      void calculate_trapezoid();
 | 
			
		||||
 | 
			
		||||
      // Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the 
 | 
			
		||||
      // acceleration within the allotted distance.
 | 
			
		||||
      static float max_allowable_speed(float acceleration, float target_velocity, float distance);
 | 
			
		||||
 | 
			
		||||
      // Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the given acceleration:
 | 
			
		||||
      static float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration);
 | 
			
		||||
 | 
			
		||||
      // This function gives you the point at which you must start braking (at the rate of -acceleration) if 
 | 
			
		||||
      // you started at speed initial_rate and accelerated until this point and want to end at the final_rate after
 | 
			
		||||
      // a total travel of distance. This can be used to compute the intersection point between acceleration and
 | 
			
		||||
      // deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed)
 | 
			
		||||
      static float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    typedef std::vector<Block> BlocksList;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    GCodeReader _parser;
 | 
			
		||||
    State _state;
 | 
			
		||||
    Feedrates _curr;
 | 
			
		||||
    Feedrates _prev;
 | 
			
		||||
    BlocksList _blocks;
 | 
			
		||||
    float _time; // s
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    GCodeTimeEstimator();
 | 
			
		||||
 | 
			
		||||
    // Calculates the time estimate from the given gcode in string format
 | 
			
		||||
    void calculate_time_from_text(const std::string& gcode);
 | 
			
		||||
 | 
			
		||||
    // Calculates the time estimate from the gcode contained in the file with the given filename
 | 
			
		||||
    void calculate_time_from_file(const std::string& file);
 | 
			
		||||
 | 
			
		||||
    // Calculates the time estimate from the gcode contained in given list of gcode lines
 | 
			
		||||
    void calculate_time_from_lines(const std::vector<std::string>& gcode_lines);
 | 
			
		||||
 | 
			
		||||
    // Adds the given gcode line
 | 
			
		||||
    void add_gcode_line(const std::string& gcode_line);
 | 
			
		||||
 | 
			
		||||
    // Calculates the time estimate from the gcode lines added using add_gcode_line()
 | 
			
		||||
    void calculate_time();
 | 
			
		||||
 | 
			
		||||
    // Set current position on the given axis with the given value
 | 
			
		||||
    void set_axis_position(EAxis axis, float position);
 | 
			
		||||
 | 
			
		||||
    void set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec);
 | 
			
		||||
    void set_axis_max_acceleration(EAxis axis, float acceleration);
 | 
			
		||||
    void set_axis_max_jerk(EAxis axis, float jerk);
 | 
			
		||||
 | 
			
		||||
    // Returns current position on the given axis
 | 
			
		||||
    float get_axis_position(EAxis axis) const;
 | 
			
		||||
 | 
			
		||||
    float get_axis_max_feedrate(EAxis axis) const;
 | 
			
		||||
    float get_axis_max_acceleration(EAxis axis) const;
 | 
			
		||||
    float get_axis_max_jerk(EAxis axis) const;
 | 
			
		||||
 | 
			
		||||
    void set_feedrate(float feedrate_mm_sec);
 | 
			
		||||
    float get_feedrate() const;
 | 
			
		||||
 | 
			
		||||
    void set_acceleration(float acceleration_mm_sec2);
 | 
			
		||||
    float get_acceleration() const;
 | 
			
		||||
 | 
			
		||||
    void set_retract_acceleration(float acceleration_mm_sec2);
 | 
			
		||||
    float get_retract_acceleration() const;
 | 
			
		||||
 | 
			
		||||
    void set_minimum_feedrate(float feedrate_mm_sec);
 | 
			
		||||
    float get_minimum_feedrate() const;
 | 
			
		||||
 | 
			
		||||
    void set_minimum_travel_feedrate(float feedrate_mm_sec);
 | 
			
		||||
    float get_minimum_travel_feedrate() const;
 | 
			
		||||
 | 
			
		||||
    void set_dialect(EDialect dialect);
 | 
			
		||||
    EDialect get_dialect() const;
 | 
			
		||||
 | 
			
		||||
    void set_units(EUnits units);
 | 
			
		||||
    EUnits get_units() const;
 | 
			
		||||
 | 
			
		||||
    void set_positioning_xyz_type(EPositioningType type);
 | 
			
		||||
    EPositioningType get_positioning_xyz_type() const;
 | 
			
		||||
 | 
			
		||||
    void set_positioning_e_type(EPositioningType type);
 | 
			
		||||
    EPositioningType get_positioning_e_type() const;
 | 
			
		||||
 | 
			
		||||
    void add_additional_time(float timeSec);
 | 
			
		||||
    void set_additional_time(float timeSec);
 | 
			
		||||
    float get_additional_time() const;
 | 
			
		||||
 | 
			
		||||
    void set_default();
 | 
			
		||||
 | 
			
		||||
    // Call this method before to start adding lines using add_gcode_line() when reusing an instance of GCodeTimeEstimator
 | 
			
		||||
    void reset();
 | 
			
		||||
 | 
			
		||||
    // Returns the estimated time, in seconds
 | 
			
		||||
    float get_time() const;
 | 
			
		||||
 | 
			
		||||
    // Returns the estimated time, in format HHh MMm SSs
 | 
			
		||||
    std::string get_time_hms() const;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    void _reset();
 | 
			
		||||
 | 
			
		||||
    // Calculates the time estimate
 | 
			
		||||
    void _calculate_time();
 | 
			
		||||
 | 
			
		||||
    // Processes the given gcode line
 | 
			
		||||
    void _process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Move
 | 
			
		||||
    void _processG1(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Dwell
 | 
			
		||||
    void _processG4(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set Units to Inches
 | 
			
		||||
    void _processG20(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set Units to Millimeters
 | 
			
		||||
    void _processG21(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Move to Origin (Home)
 | 
			
		||||
    void _processG28(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set to Absolute Positioning
 | 
			
		||||
    void _processG90(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set to Relative Positioning
 | 
			
		||||
    void _processG91(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set Position
 | 
			
		||||
    void _processG92(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set extruder to absolute mode
 | 
			
		||||
    void _processM82(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set extruder to relative mode
 | 
			
		||||
    void _processM83(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set Extruder Temperature and Wait
 | 
			
		||||
    void _processM109(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set max printing acceleration
 | 
			
		||||
    void _processM201(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set maximum feedrate
 | 
			
		||||
    void _processM203(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set default acceleration
 | 
			
		||||
    void _processM204(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Advanced settings
 | 
			
		||||
    void _processM205(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    // Set allowable instantaneous speed change
 | 
			
		||||
    void _processM566(const GCodeReader::GCodeLine& line);
 | 
			
		||||
 | 
			
		||||
    void _forward_pass();
 | 
			
		||||
    void _reverse_pass();
 | 
			
		||||
 | 
			
		||||
    void _planner_forward_pass_kernel(Block* prev, Block* curr);
 | 
			
		||||
    void _planner_reverse_pass_kernel(Block* curr, Block* next);
 | 
			
		||||
 | 
			
		||||
    void _recalculate_trapezoids();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
} /* namespace Slic3r */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -233,8 +233,9 @@ public:
 | 
			
		|||
    PrintRegionPtrs regions;
 | 
			
		||||
    PlaceholderParser placeholder_parser;
 | 
			
		||||
    // TODO: status_cb
 | 
			
		||||
    std::string                     estimated_print_time;
 | 
			
		||||
    double                          total_used_filament, total_extruded_volume, total_cost, total_weight;
 | 
			
		||||
    std::map<size_t,float>          filament_stats;
 | 
			
		||||
    std::map<size_t, float>         filament_stats;
 | 
			
		||||
    PrintState<PrintStep, psCount>  state;
 | 
			
		||||
 | 
			
		||||
    // ordered collections of extrusion paths to build skirt loops and brim
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -152,6 +152,8 @@ _constant()
 | 
			
		|||
        %code%{ RETVAL = &THIS->skirt; %};
 | 
			
		||||
    Ref<ExtrusionEntityCollection> brim()
 | 
			
		||||
        %code%{ RETVAL = &THIS->brim; %};
 | 
			
		||||
    std::string estimated_print_time()
 | 
			
		||||
        %code%{ RETVAL = THIS->estimated_print_time; %};
 | 
			
		||||
 | 
			
		||||
    PrintObjectPtrs* objects()
 | 
			
		||||
        %code%{ RETVAL = &THIS->objects; %};
 | 
			
		||||
| 
						 | 
				
			
			@ -280,7 +282,6 @@ Print::total_cost(...)
 | 
			
		|||
        }
 | 
			
		||||
        RETVAL = THIS->total_cost;
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        RETVAL
 | 
			
		||||
 | 
			
		||||
        RETVAL        
 | 
			
		||||
%}
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue