Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/FormerLurker/ArcWelderLib.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFormerLurker <hochgebe@gmail.com>2021-11-21 22:40:04 +0300
committerFormerLurker <hochgebe@gmail.com>2021-11-21 22:40:04 +0300
commit33946985e505a5d72d47bcfbd573f0b4587c09f8 (patch)
treefebb4f56214b8a1d64ed78966a6bd6750e88e185 /ArcWelder
parentfe18f18b4c005a2c4e4b69a5866d0d065bde82aa (diff)
parent3bd3d54894cd49fb564396fd043c4b0c80f4b116 (diff)
Merge devel into master
Diffstat (limited to 'ArcWelder')
-rw-r--r--ArcWelder/CMakeLists.txt2
-rw-r--r--ArcWelder/arc_welder.cpp1606
-rw-r--r--ArcWelder/arc_welder.h374
-rw-r--r--ArcWelder/segmented_arc.cpp138
-rw-r--r--ArcWelder/segmented_arc.h13
-rw-r--r--ArcWelder/segmented_shape.cpp83
-rw-r--r--ArcWelder/segmented_shape.h24
-rw-r--r--ArcWelder/unwritten_command.h43
8 files changed, 1399 insertions, 884 deletions
diff --git a/ArcWelder/CMakeLists.txt b/ArcWelder/CMakeLists.txt
index 738983c..f84fd23 100644
--- a/ArcWelder/CMakeLists.txt
+++ b/ArcWelder/CMakeLists.txt
@@ -31,3 +31,5 @@ set(${PROJECT_NAME}_DEFINITIONS ${GcodeProcessorLib_DEFINITIONS}
set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/
${GcodeProcessorLib_INCLUDE_DIRS}
CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
+
+ \ No newline at end of file
diff --git a/ArcWelder/arc_welder.cpp b/ArcWelder/arc_welder.cpp
index c49f64d..883f66b 100644
--- a/ArcWelder/arc_welder.cpp
+++ b/ArcWelder/arc_welder.cpp
@@ -34,813 +34,957 @@
#include <fstream>
#include <iomanip>
#include <sstream>
-
-arc_welder::arc_welder(
- std::string source_path,
- std::string target_path,
- logger * log,
- double resolution_mm,
- double path_tolerance_percent,
- double max_radius,
- int min_arc_segments,
- double mm_per_arc_segment,
- bool g90_g91_influences_extruder,
- bool allow_3d_arcs,
- bool allow_dynamic_precision,
- unsigned char default_xyz_precision,
- unsigned char default_e_precision,
- int buffer_size,
- progress_callback callback) : current_arc_(
- DEFAULT_MIN_SEGMENTS,
- buffer_size - 5,
- resolution_mm,
- path_tolerance_percent,
- max_radius,
- min_arc_segments,
- mm_per_arc_segment,
- allow_3d_arcs,
- default_xyz_precision,
- default_e_precision
- ),
- segment_statistics_(
- segment_statistic_lengths,
- segment_statistic_lengths_count,
- log
- )
+#include <version.h>
+
+
+
+
+arc_welder::arc_welder(arc_welder_args args) : current_arc_(
+ DEFAULT_MIN_SEGMENTS,
+ args.buffer_size,
+ args.resolution_mm,
+ args.path_tolerance_percent,
+ args.max_radius_mm,
+ args.min_arc_segments,
+ args.mm_per_arc_segment,
+ args.allow_3d_arcs,
+ args.default_xyz_precision,
+ args.default_e_precision,
+ args.max_gcode_length
+ ),
+ segment_statistics_(
+ segment_statistic_lengths,
+ segment_statistic_lengths_count,
+ args.log
+ ),
+ segment_retraction_statistics_(
+ segment_statistic_lengths,
+ segment_statistic_lengths_count,
+ args.log
+ ),
+ travel_statistics_(
+ segment_statistic_lengths,
+ segment_statistic_lengths_count,
+ args.log
+ )
{
- p_logger_ = log;
- debug_logging_enabled_ = false;
- info_logging_enabled_ = false;
- error_logging_enabled_ = false;
- verbose_logging_enabled_ = false;
-
- logger_type_ = 0;
- resolution_mm_ = resolution_mm;
- progress_callback_ = callback;
- verbose_output_ = false;
- source_path_ = source_path;
- target_path_ = target_path;
- gcode_position_args_ = get_args_(g90_g91_influences_extruder, buffer_size);
- allow_3d_arcs_ = allow_3d_arcs;
- allow_dynamic_precision_ = allow_dynamic_precision;
- notification_period_seconds = 1;
- lines_processed_ = 0;
- gcodes_processed_ = 0;
- file_size_ = 0;
- last_gcode_line_written_ = 0;
- points_compressed_ = 0;
- arcs_created_ = 0;
- waiting_for_arc_ = false;
- previous_feedrate_ = -1;
- gcode_position_args_.set_num_extruders(8);
- for (int index = 0; index < 8; index++)
- {
- gcode_position_args_.retraction_lengths[0] = .0001;
- gcode_position_args_.z_lift_heights[0] = 0.001;
- gcode_position_args_.x_firmware_offsets[0] = 0.0;
- gcode_position_args_.y_firmware_offsets[0] = 0.0;
- }
-
- // We don't care about the printer settings, except for g91 influences extruder.
-
- p_source_position_ = new gcode_position(gcode_position_args_);
+ p_logger_ = args.log;
+ debug_logging_enabled_ = false;
+ info_logging_enabled_ = false;
+ error_logging_enabled_ = false;
+ verbose_logging_enabled_ = false;
+
+ logger_type_ = 0;
+ resolution_mm_ = args.resolution_mm;
+ progress_callback_ = args.callback;
+ verbose_output_ = false;
+ source_path_ = args.source_path;
+ target_path_ = args.target_path;
+ gcode_position_args_ = get_args_(args.g90_g91_influences_extruder, args.buffer_size);
+ allow_3d_arcs_ = args.allow_3d_arcs;
+ allow_travel_arcs_ = args.allow_travel_arcs;
+ allow_dynamic_precision_ = args.allow_dynamic_precision;
+ extrusion_rate_variance_percent_ = args.extrusion_rate_variance_percent;
+ lines_processed_ = 0;
+ gcodes_processed_ = 0;
+ file_size_ = 0;
+ notification_period_seconds_ = args.notification_period_seconds;
+ last_gcode_line_written_ = 0;
+ points_compressed_ = 0;
+ arcs_created_ = 0;
+ arcs_aborted_by_flow_rate_ = 0;
+ waiting_for_arc_ = false;
+ previous_feedrate_ = -1;
+ gcode_position_args_.set_num_extruders(8);
+ previous_extrusion_rate_ = 0;
+ box_encoding_ = args.box_encoding;
+ for (int index = 0; index < 8; index++)
+ {
+ gcode_position_args_.retraction_lengths[0] = .0001;
+ gcode_position_args_.z_lift_heights[0] = 0.001;
+ gcode_position_args_.x_firmware_offsets[0] = 0.0;
+ gcode_position_args_.y_firmware_offsets[0] = 0.0;
+ }
+
+ // We don't care about the printer settings, except for g91 influences extruder.
+
+ p_source_position_ = new gcode_position(gcode_position_args_);
}
gcode_position_args arc_welder::get_args_(bool g90_g91_influences_extruder, int buffer_size)
{
- gcode_position_args args;
- // Configure gcode_position_args
- args.g90_influences_extruder = g90_g91_influences_extruder;
- args.position_buffer_size = buffer_size;
- args.autodetect_position = true;
- args.home_x = 0;
- args.home_x_none = true;
- args.home_y = 0;
- args.home_y_none = true;
- args.home_z = 0;
- args.home_z_none = true;
- args.shared_extruder = true;
- args.zero_based_extruder = true;
-
-
- args.default_extruder = 0;
- args.xyz_axis_default_mode = "absolute";
- args.e_axis_default_mode = "absolute";
- args.units_default = "millimeters";
- args.location_detection_commands = std::vector<std::string>();
- args.is_bound_ = false;
- args.is_circular_bed = false;
- args.x_min = -9999;
- args.x_max = 9999;
- args.y_min = -9999;
- args.y_max = 9999;
- args.z_min = -9999;
- args.z_max = 9999;
- return args;
+ gcode_position_args args;
+ // Configure gcode_position_args
+ args.g90_influences_extruder = g90_g91_influences_extruder;
+ if (buffer_size < 2)
+ {
+ buffer_size = 2;
+ }
+ args.position_buffer_size = buffer_size;
+ args.autodetect_position = true;
+ args.home_x = 0;
+ args.home_x_none = true;
+ args.home_y = 0;
+ args.home_y_none = true;
+ args.home_z = 0;
+ args.home_z_none = true;
+ args.shared_extruder = true;
+ args.zero_based_extruder = true;
+
+
+ args.default_extruder = 0;
+ args.xyz_axis_default_mode = "absolute";
+ args.e_axis_default_mode = "absolute";
+ args.units_default = "millimeters";
+ args.location_detection_commands = std::vector<std::string>();
+ args.is_bound_ = false;
+ args.is_circular_bed = false;
+ args.x_min = -9999;
+ args.x_max = 9999;
+ args.y_min = -9999;
+ args.y_max = 9999;
+ args.z_min = -9999;
+ args.z_max = 9999;
+ return args;
}
arc_welder::~arc_welder()
{
- delete p_source_position_;
+ delete p_source_position_;
}
void arc_welder::set_logger_type(int logger_type)
{
- logger_type_ = logger_type;
+ logger_type_ = logger_type;
}
void arc_welder::reset()
{
- p_logger_->log(logger_type_, DEBUG, "Resetting all tracking variables.");
- lines_processed_ = 0;
- gcodes_processed_ = 0;
- last_gcode_line_written_ = 0;
- file_size_ = 0;
- points_compressed_ = 0;
- arcs_created_ = 0;
- waiting_for_arc_ = false;
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Resetting all tracking variables.");
+ lines_processed_ = 0;
+ gcodes_processed_ = 0;
+ last_gcode_line_written_ = 0;
+ file_size_ = 0;
+ points_compressed_ = 0;
+ arcs_created_ = 0;
+ waiting_for_arc_ = false;
}
long arc_welder::get_file_size(const std::string& file_path)
{
- // Todo: Fix this function. This is a pretty weak implementation :(
- std::ifstream file(file_path.c_str(), std::ios::in | std::ios::binary);
- const long l = (long)file.tellg();
- file.seekg(0, std::ios::end);
- const long m = (long)file.tellg();
- file.close();
- return (m - l);
+ // Todo: Fix this function. This is a pretty weak implementation :(
+ std::ifstream file(file_path.c_str(), std::ios::in | std::ios::binary);
+ const long l = (long)file.tellg();
+ file.seekg(0, std::ios::end);
+ const long m = (long)file.tellg();
+ file.close();
+ return (m - l);
}
double arc_welder::get_next_update_time() const
{
- return clock() + (notification_period_seconds * CLOCKS_PER_SEC);
+ return clock() + (notification_period_seconds_ * CLOCKS_PER_SEC);
}
double arc_welder::get_time_elapsed(double start_clock, double end_clock)
{
- return static_cast<double>(end_clock - start_clock) / CLOCKS_PER_SEC;
+ return static_cast<double>(end_clock - start_clock) / CLOCKS_PER_SEC;
}
arc_welder_results arc_welder::process()
{
-arc_welder_results results;
- p_logger_->log(logger_type_, DEBUG, "Configuring logging settings.");
- verbose_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, VERBOSE);
- debug_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, DEBUG);
- info_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, INFO);
- error_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, ERROR);
-
- std::stringstream stream;
- stream << std::fixed << std::setprecision(2);
- stream << "arc_welder::process - Parameters received: source_file_path: '" <<
- source_path_ << "', target_file_path:'" << target_path_ << "', resolution_mm:" <<
- resolution_mm_ << "mm (+-" << current_arc_.get_resolution_mm() << "mm), path_tolerance_percent: " << current_arc_.get_path_tolerance_percent()
- << ", max_radius_mm:" << current_arc_.get_max_radius()
- << ", min_arc_segments:" << std::setprecision(0) <<current_arc_.get_min_arc_segments()
- << ", mm_per_arc_segment:" << std::setprecision(0) << current_arc_.get_mm_per_arc_segment()
- << ", g90_91_influences_extruder: " << (p_source_position_->get_g90_91_influences_extruder() ? "True" : "False")
- << ", allow_3d_arcs: " << (allow_3d_arcs_ ? "True" : "False")
- << ", allow_dynamic_precision: " << (allow_dynamic_precision_ ? "True" : "False")
- << ", default_xyz_precision: " << std::setprecision(0) << static_cast<double>(current_arc_.get_xyz_precision())
- << ", default_e_precision: " << std::setprecision(0) << static_cast<double>(current_arc_.get_e_precision());
- p_logger_->log(logger_type_, INFO, stream.str());
-
-
- // reset tracking variables
- reset();
- // local variable to hold the progress update return. If it's false, we will exit.
- bool continue_processing = true;
-
- p_logger_->log(logger_type_, DEBUG, "Configuring progress updates.");
- int read_lines_before_clock_check = 1000;
- double next_update_time = get_next_update_time();
- const clock_t start_clock = clock();
- p_logger_->log(logger_type_, DEBUG, "Getting source file size.");
- file_size_ = get_file_size(source_path_);
- stream.clear();
- stream.str("");
- stream << "Source file size: " << file_size_;
- p_logger_->log(logger_type_, DEBUG, stream.str());
- // Create the source file read stream and target write stream
- std::ifstream gcodeFile;
- p_logger_->log(logger_type_, DEBUG, "Opening the source file for reading.");
- gcodeFile.open(source_path_.c_str(), std::ifstream::in);
- if (!gcodeFile.is_open())
- {
- results.success = false;
- results.message = "Unable to open the source file.";
- p_logger_->log_exception(logger_type_, results.message);
- return results;
- }
- p_logger_->log(logger_type_, DEBUG, "Source file opened successfully.");
-
- p_logger_->log(logger_type_, DEBUG, "Opening the target file for writing.");
-
- output_file_.open(target_path_.c_str(), std::ifstream::out);
- if (!output_file_.is_open())
- {
- results.success = false;
- results.message = "Unable to open the target file.";
- p_logger_->log_exception(logger_type_, results.message);
- gcodeFile.close();
- return results;
- }
-
- p_logger_->log(logger_type_, DEBUG, "Target file opened successfully.");
- std::string line;
- int lines_with_no_commands = 0;
- parsed_command cmd;
- // Communicate every second
- p_logger_->log(logger_type_, DEBUG, "Sending initial progress update.");
- continue_processing = on_progress_(get_progress_(static_cast<long>(gcodeFile.tellg()), static_cast<double>(start_clock)));
- p_logger_->log(logger_type_, DEBUG, "Processing source file.");
-
- while (std::getline(gcodeFile, line) && continue_processing)
- {
- lines_processed_++;
- // Check the first line of gcode and see if it = ;FLAVOR:UltiGCode
+ arc_welder_results results;
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Configuring logging settings.");
+ verbose_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, log_levels::VERBOSE);
+ debug_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, log_levels::DEBUG);
+ info_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, log_levels::INFO);
+ error_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, log_levels::ERROR);
+
+ std::stringstream stream;
+ // reset tracking variables
+ reset();
+ // local variable to hold the progress update return. If it's false, we will exit.
+ bool continue_processing = true;
+
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Configuring progress updates.");
+ int read_lines_before_clock_check = 1000;
+ double next_update_time = get_next_update_time();
+ const clock_t start_clock = clock();
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Getting source file size.");
+ file_size_ = get_file_size(source_path_);
+ stream.clear();
+ stream.str("");
+ stream << "Source file size: " << file_size_;
+ p_logger_->log(logger_type_, log_levels::DEBUG, stream.str());
+
+ // Determine if we need to overwrite the source file
+ bool overwrite_source_file = false;
+ std::string temp_file_path;
+ if (source_path_ == target_path_)
+ {
+ overwrite_source_file = true;
+ if (!utilities::get_temp_file_path_for_file(source_path_, temp_file_path))
+ {
+ results.success = false;
+ results.message = "The source and target path are the same, but a temporary file path could not be created. Are the paths empty?";
+ p_logger_->log_exception(logger_type_, results.message);
+ return results;
+ }
+
+ stream.clear();
+ stream.str("");
+ stream << "Source and target path are the same. The source file will be overwritten. Temporary file path: " << temp_file_path;
+ p_logger_->log(logger_type_, log_levels::DEBUG, stream.str());
+ target_path_ = temp_file_path;
+ }
+
+ // Create the source file read stream and target write stream
+ std::ifstream gcodeFile;
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Opening the source file for reading.");
+ gcodeFile.open(source_path_.c_str(), std::ifstream::in);
+ if (!gcodeFile.is_open())
+ {
+ results.success = false;
+ results.message = "Unable to open the source file.";
+ p_logger_->log_exception(logger_type_, results.message);
+ return results;
+ }
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Source file opened successfully.");
+
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Opening the target file for writing.");
+
+ output_file_.open(target_path_.c_str(), std::ios_base::binary | std::ios_base::out);
+ if (!output_file_.is_open())
+ {
+ results.success = false;
+ results.message = "Unable to open the target file.";
+ p_logger_->log_exception(logger_type_, results.message);
+ gcodeFile.close();
+ return results;
+ }
+
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Target file opened successfully.");
+ std::string line;
+ int lines_with_no_commands = 0;
+ parsed_command cmd;
+ // Communicate every second
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Sending initial progress update.");
+ continue_processing = on_progress_(get_progress_(static_cast<long>(gcodeFile.tellg()), static_cast<double>(start_clock)));
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Processing source file.");
+
+ bool arc_Welder_comment_added = false;
+ while (std::getline(gcodeFile, line) && continue_processing)
+ {
+ lines_processed_++;
+ // Check the first line of gcode and see if it = ;FLAVOR:UltiGCode
// This comment MUST be preserved as the first line for ultimakers, else things won't work
- if (lines_processed_ == 1)
- {
- bool isUltiGCode = line == ";FLAVOR:UltiGCode";
- if (isUltiGCode)
- {
- write_gcode_to_file(line);
- }
- add_arcwelder_comment_to_target();
- if (isUltiGCode)
- {
- lines_with_no_commands++;
- continue;
- }
- }
- cmd.clear();
- if (verbose_logging_enabled_)
- {
- stream.clear();
- stream.str("");
- stream << "Parsing: " << line;
- p_logger_->log(logger_type_, VERBOSE, stream.str());
- }
- parser_.try_parse_gcode(line.c_str(), cmd, true);
- bool has_gcode = false;
- if (cmd.gcode.length() > 0)
- {
- has_gcode = true;
- gcodes_processed_++;
- }
- else
- {
- lines_with_no_commands++;
- }
-
- // Always process the command through the printer, even if no command is found
- // This is important so that comments can be analyzed
- //std::cout << "stabilization::process_file - updating position...";
- process_gcode(cmd, false, false);
-
- // Only continue to process if we've found a command and either a progress_callback_ is supplied, or debug loggin is enabled.
- if (has_gcode)
- {
- if ((lines_processed_ % read_lines_before_clock_check) == 0 && next_update_time < clock())
- {
- if (verbose_logging_enabled_)
- {
- p_logger_->log(logger_type_, VERBOSE, "Sending progress update.");
- }
- continue_processing = on_progress_(get_progress_(static_cast<long>(gcodeFile.tellg()), static_cast<double>(start_clock)));
- next_update_time = get_next_update_time();
- }
- }
- }
-
- if (current_arc_.is_shape() && waiting_for_arc_)
- {
- p_logger_->log(logger_type_, DEBUG, "Processing the final line.");
- process_gcode(cmd, true, false);
- }
- p_logger_->log(logger_type_, DEBUG, "Writing all unwritten gcodes to the target file.");
- write_unwritten_gcodes_to_file();
- p_logger_->log(logger_type_, DEBUG, "Fetching the final progress struct.");
-
- arc_welder_progress final_progress = get_progress_(static_cast<long>(file_size_), static_cast<double>(start_clock));
- if (debug_logging_enabled_)
- {
- p_logger_->log(logger_type_, DEBUG, "Sending final progress update message.");
- }
- on_progress_(arc_welder_progress(final_progress));
-
- p_logger_->log(logger_type_, DEBUG, "Processing complete, closing source and target file.");
- output_file_.close();
- gcodeFile.close();
- const clock_t end_clock = clock();
-
- results.success = continue_processing;
- results.cancelled = !continue_processing;
- results.progress = final_progress;
- p_logger_->log(logger_type_, DEBUG, "Returning processing results.");
-
- return results;
+ if (lines_processed_ == 1)
+ {
+ bool isUltiGCode = line == ";FLAVOR:UltiGCode";
+ bool isPrusaSlicer = line.rfind("; generated by PrusaSlicer", 0) == 0;
+ if (isUltiGCode || isPrusaSlicer)
+ {
+ write_gcode_to_file(line);
+ }
+ add_arcwelder_comment_to_target();
+ if (isUltiGCode || isPrusaSlicer)
+ {
+ lines_with_no_commands++;
+ continue;
+ }
+ }
+
+
+ cmd.clear();
+ if (verbose_logging_enabled_)
+ {
+ stream.clear();
+ stream.str("");
+ stream << "Parsing: " << line;
+ p_logger_->log(logger_type_, log_levels::VERBOSE, stream.str());
+ }
+ parser_.try_parse_gcode(line.c_str(), cmd, true);
+ bool has_gcode = false;
+ if (cmd.gcode.length() > 0)
+ {
+ has_gcode = true;
+ gcodes_processed_++;
+ }
+ else
+ {
+ lines_with_no_commands++;
+ }
+
+ // Always process the command through the printer, even if no command is found
+ // This is important so that comments can be analyzed
+ //std::cout << "stabilization::process_file - updating position...";
+ process_gcode(cmd, false, false);
+
+ // Only continue to process if we've found a command and either a progress_callback_ is supplied, or debug loggin is enabled.
+ if (has_gcode)
+ {
+ if ((lines_processed_ % read_lines_before_clock_check) == 0 && next_update_time < clock())
+ {
+ if (verbose_logging_enabled_)
+ {
+ p_logger_->log(logger_type_, log_levels::VERBOSE, "Sending progress update.");
+ }
+ continue_processing = on_progress_(get_progress_(static_cast<long>(gcodeFile.tellg()), static_cast<double>(start_clock)));
+ next_update_time = get_next_update_time();
+ }
+ }
+ }
+
+ if (current_arc_.is_shape() && waiting_for_arc_)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Processing the final line.");
+ process_gcode(cmd, true, false);
+ }
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Writing all unwritten gcodes to the target file.");
+ write_unwritten_gcodes_to_file();
+
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Fetching the final progress struct.");
+
+ arc_welder_progress final_progress = get_progress_(static_cast<long>(file_size_), static_cast<double>(start_clock));
+ if (debug_logging_enabled_)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Sending final progress update message.");
+ }
+ on_progress_(final_progress);
+
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Closing source and target files.");
+ output_file_.close();
+ gcodeFile.close();
+
+ if (overwrite_source_file)
+ {
+ stream.clear();
+ stream.str("");
+ stream << "Deleting the original source file at '" << source_path_ << "'.";
+ p_logger_->log(logger_type_, log_levels::DEBUG, stream.str());
+ stream.clear();
+ stream.str("");
+ std::remove(source_path_.c_str());
+ stream << "Renaming temporary file at '" << target_path_ << "' to '" << source_path_ << "'.";
+ p_logger_->log(0, log_levels::DEBUG, stream.str());
+ std::rename(target_path_.c_str(), source_path_.c_str());
+ }
+
+ results.success = continue_processing;
+ results.cancelled = !continue_processing;
+ results.progress = final_progress;
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Returning processing results.");
+
+ return results;
}
bool arc_welder::on_progress_(const arc_welder_progress& progress)
{
- if (progress_callback_ != NULL)
- {
- return progress_callback_(progress, p_logger_, logger_type_);
- }
- else if (info_logging_enabled_)
- {
- p_logger_->log(logger_type_, INFO, progress.str());
- }
-
- return true;
+ if (progress_callback_ != NULL)
+ {
+ return progress_callback_(progress, p_logger_, logger_type_);
+ }
+ else if (info_logging_enabled_)
+ {
+ p_logger_->log(logger_type_, log_levels::INFO, progress.str());
+ }
+
+ return true;
}
arc_welder_progress arc_welder::get_progress_(long source_file_position, double start_clock)
{
- arc_welder_progress progress;
- progress.gcodes_processed = gcodes_processed_;
- progress.lines_processed = lines_processed_;
- progress.points_compressed = points_compressed_;
- progress.arcs_created = arcs_created_;
- progress.source_file_position = source_file_position;
- progress.target_file_size = static_cast<long>(output_file_.tellp());
- progress.source_file_size = file_size_;
- long bytesRemaining = file_size_ - static_cast<long>(source_file_position);
- progress.percent_complete = static_cast<double>(source_file_position) / static_cast<double>(file_size_) * 100.0;
- progress.seconds_elapsed = get_time_elapsed(start_clock, clock());
- double bytesPerSecond = static_cast<double>(source_file_position) / progress.seconds_elapsed;
- progress.seconds_remaining = bytesRemaining / bytesPerSecond;
-
- if (source_file_position > 0) {
- progress.compression_ratio = (static_cast<float>(source_file_position) / static_cast<float>(progress.target_file_size));
- progress.compression_percent = (1.0 - (static_cast<float>(progress.target_file_size) / static_cast<float>(source_file_position))) * 100.0f;
- }
- progress.num_firmware_compensations = current_arc_.get_num_firmware_compensations();
- progress.segment_statistics = segment_statistics_;
- return progress;
-
+ arc_welder_progress progress;
+ progress.gcodes_processed = gcodes_processed_;
+ progress.lines_processed = lines_processed_;
+ progress.points_compressed = points_compressed_;
+ progress.arcs_created = arcs_created_;
+ progress.arcs_aborted_by_flow_rate = arcs_aborted_by_flow_rate_;
+ progress.source_file_position = source_file_position;
+ progress.target_file_size = static_cast<long>(output_file_.tellp());
+ progress.source_file_size = file_size_;
+ long bytesRemaining = file_size_ - static_cast<long>(source_file_position);
+ progress.percent_complete = static_cast<double>(source_file_position) / static_cast<double>(file_size_) * 100.0;
+ progress.seconds_elapsed = get_time_elapsed(start_clock, clock());
+ double bytesPerSecond = static_cast<double>(source_file_position) / progress.seconds_elapsed;
+ progress.seconds_remaining = bytesRemaining / bytesPerSecond;
+
+ if (source_file_position > 0) {
+ progress.compression_ratio = (static_cast<float>(source_file_position) / static_cast<float>(progress.target_file_size));
+ progress.compression_percent = (1.0 - (static_cast<float>(progress.target_file_size) / static_cast<float>(source_file_position))) * 100.0f;
+ }
+ else {
+ progress.compression_ratio = 0;
+ progress.compression_percent = 0;
+ }
+ progress.num_firmware_compensations = current_arc_.get_num_firmware_compensations();
+ progress.num_gcode_length_exceptions = current_arc_.get_num_gcode_length_exceptions();
+ progress.segment_statistics = segment_statistics_;
+ progress.segment_retraction_statistics = segment_retraction_statistics_;
+ progress.travel_statistics = travel_statistics_;
+ progress.box_encoding = box_encoding_;
+ return progress;
+
}
int arc_welder::process_gcode(parsed_command cmd, bool is_end, bool is_reprocess)
{
- /* use to catch gcode for debugging since I can't set conditions equal to strings
- if (cmd.gcode == "G1 X118.762 Y104.054 E0.0163")
- {
- std::cout << "Found it!";
- }
- */
- // Update the position for the source gcode file
- p_source_position_->update(cmd, lines_processed_, gcodes_processed_, -1);
- position* p_cur_pos = p_source_position_->get_current_position_ptr();
- position* p_pre_pos = p_source_position_->get_previous_position_ptr();
- bool is_previous_extruder_relative = p_pre_pos->is_extruder_relative;
- extruder extruder_current = p_cur_pos->get_current_extruder();
- extruder previous_extruder = p_pre_pos->get_current_extruder();
- //std::cout << lines_processed_ << " - " << cmd.gcode << ", CurrentEAbsolute: " << cur_extruder.e <<", ExtrusionLength: " << cur_extruder.extrusion_length << ", Retraction Length: " << cur_extruder.retraction_length << ", IsExtruding: " << cur_extruder.is_extruding << ", IsRetracting: " << cur_extruder.is_retracting << ".\n";
-
- int lines_written = 0;
- // see if this point is an extrusion
-
- bool arc_added = false;
- bool clear_shapes = false;
- double movement_length_mm = 0;
- bool has_e_changed = extruder_current.is_extruding || extruder_current.is_retracting;
- // Update the source file statistics
- if (p_cur_pos->has_xy_position_changed && (has_e_changed))
- {
- if (allow_3d_arcs_) {
- movement_length_mm = utilities::get_cartesian_distance(p_pre_pos->x, p_pre_pos->y, p_pre_pos->z, p_cur_pos->x, p_cur_pos->y, p_cur_pos->z);
- }
- else {
- movement_length_mm = utilities::get_cartesian_distance(p_pre_pos->x, p_pre_pos->y, p_cur_pos->x, p_cur_pos->y);
- }
-
- if (movement_length_mm > 0)
- {
- if (!is_reprocess)
- segment_statistics_.update(movement_length_mm, true);
- }
- }
-
- // We need to make sure the printer is using absolute xyz, is extruding, and the extruder axis mode is the same as that of the previous position
- // TODO: Handle relative XYZ axis. This is possible, but maybe not so important.
- bool is_g1_g2 = cmd.command == "G0" || cmd.command == "G1";
- if (allow_dynamic_precision_ && is_g1_g2)
- {
- for (std::vector<parsed_command_parameter>::iterator it = cmd.parameters.begin(); it != cmd.parameters.end(); ++it)
- {
- switch ((*it).name[0])
- {
- case 'X':
- case 'Y':
- case 'Z':
- current_arc_.update_xyz_precision((*it).double_precision);
- break;
- case 'E':
- current_arc_.update_e_precision((*it).double_precision);
- break;
- }
- }
- }
-
- bool z_axis_ok = allow_3d_arcs_ ||
- utilities::is_equal(p_cur_pos->z, p_pre_pos->z);
-
- if (
- !is_end && cmd.is_known_command && !cmd.is_empty && (
- is_g1_g2 && z_axis_ok &&
- utilities::is_equal(p_cur_pos->x_offset, p_pre_pos->x_offset) &&
- utilities::is_equal(p_cur_pos->y_offset, p_pre_pos->y_offset) &&
- utilities::is_equal(p_cur_pos->z_offset, p_pre_pos->z_offset) &&
- utilities::is_equal(p_cur_pos->x_firmware_offset, p_pre_pos->x_firmware_offset) &&
- utilities::is_equal(p_cur_pos->y_firmware_offset, p_pre_pos->y_firmware_offset) &&
- utilities::is_equal(p_cur_pos->z_firmware_offset, p_pre_pos->z_firmware_offset) &&
- !p_cur_pos->is_relative &&
- (
- !waiting_for_arc_ ||
- extruder_current.is_extruding ||
- //(previous_extruder.is_extruding && extruder_current.is_extruding) || // Test to see if
- // we can get more arcs.
- (previous_extruder.is_retracting && extruder_current.is_retracting)
- ) &&
- p_cur_pos->is_extruder_relative == is_previous_extruder_relative &&
- (!waiting_for_arc_ || p_pre_pos->f == p_cur_pos->f) && // might need to skip the waiting for arc check...
- (!waiting_for_arc_ || p_pre_pos->feature_type_tag == p_cur_pos->feature_type_tag)
- )
- ) {
-
- printer_point p(p_cur_pos->get_gcode_x(), p_cur_pos->get_gcode_y(), p_cur_pos->get_gcode_z(), extruder_current.e_relative, movement_length_mm);
- if (!waiting_for_arc_)
- {
- if (debug_logging_enabled_)
- {
- p_logger_->log(logger_type_, DEBUG, "Starting new arc from Gcode:" + cmd.gcode);
- }
- write_unwritten_gcodes_to_file();
- // add the previous point as the starting point for the current arc
- printer_point previous_p(p_pre_pos->get_gcode_x(), p_pre_pos->get_gcode_y(), p_pre_pos->get_gcode_z(), previous_extruder.e_relative, 0);
- // Don't add any extrusion, or you will over extrude!
- //std::cout << "Trying to add first point (" << p.x << "," << p.y << "," << p.z << ")...";
-
- current_arc_.try_add_point(previous_p);
- }
-
- double e_relative = extruder_current.e_relative;
- int num_points = current_arc_.get_num_segments();
- arc_added = current_arc_.try_add_point(p);
- if (arc_added)
- {
- if (!waiting_for_arc_)
- {
- waiting_for_arc_ = true;
- previous_feedrate_ = p_pre_pos->f;
- }
- else
- {
- if (debug_logging_enabled_)
- {
- if (num_points+1 == current_arc_.get_num_segments())
- {
- p_logger_->log(logger_type_, DEBUG, "Adding point to arc from Gcode:" + cmd.gcode);
- }
-
- }
- }
- }
- }
- else if (debug_logging_enabled_ ){
- if (is_end)
- {
- p_logger_->log(logger_type_, DEBUG, "Procesing final shape, if one exists.");
- }
- else if (!cmd.is_empty)
- {
- if (!cmd.is_known_command)
- {
- p_logger_->log(logger_type_, DEBUG, "Command '" + cmd.command + "' is Unknown. Gcode:" + cmd.gcode);
- }
- else if (cmd.command != "G0" && cmd.command != "G1")
- {
- p_logger_->log(logger_type_, DEBUG, "Command '"+ cmd.command + "' is not G0/G1, skipping. Gcode:" + cmd.gcode);
- }
- else if (!allow_3d_arcs_ && !utilities::is_equal(p_cur_pos->z, p_pre_pos->z))
- {
- p_logger_->log(logger_type_, DEBUG, "Z axis position changed, cannot convert:" + cmd.gcode);
- }
- else if (p_cur_pos->is_relative)
- {
- p_logger_->log(logger_type_, DEBUG, "XYZ Axis is in relative mode, cannot convert:" + cmd.gcode);
- }
- else if (
- waiting_for_arc_ && !(
- (previous_extruder.is_extruding && extruder_current.is_extruding) ||
- (previous_extruder.is_retracting && extruder_current.is_retracting)
- )
- )
- {
- std::string message = "Extruding or retracting state changed, cannot add point to current arc: " + cmd.gcode;
- if (verbose_logging_enabled_)
- {
-
- message.append(
- " - Verbose Info\n\tCurrent Position Info - Absolute E:" + utilities::to_string(extruder_current.e) +
- ", Offset E:" + utilities::to_string(extruder_current.get_offset_e()) +
- ", Mode:" + (p_cur_pos->is_extruder_relative_null ? "NULL" : p_cur_pos->is_extruder_relative ? "relative" : "absolute") +
- ", Retraction: " + utilities::to_string(extruder_current.retraction_length) +
- ", Extrusion: " + utilities::to_string(extruder_current.extrusion_length) +
- ", Retracting: " + (extruder_current.is_retracting ? "True" : "False") +
- ", Extruding: " + (extruder_current.is_extruding ? "True" : "False")
- );
- message.append(
- "\n\tPrevious Position Info - Absolute E:" + utilities::to_string(previous_extruder.e) +
- ", Offset E:" + utilities::to_string(previous_extruder.get_offset_e()) +
- ", Mode:" + (p_pre_pos->is_extruder_relative_null ? "NULL" : p_pre_pos->is_extruder_relative ? "relative" : "absolute") +
- ", Retraction: " + utilities::to_string(previous_extruder.retraction_length) +
- ", Extrusion: " + utilities::to_string(previous_extruder.extrusion_length) +
- ", Retracting: " + (previous_extruder.is_retracting ? "True" : "False") +
- ", Extruding: " + (previous_extruder.is_extruding ? "True" : "False")
- );
- p_logger_->log(logger_type_, VERBOSE, message);
- }
- else
- {
- p_logger_->log(logger_type_, DEBUG, message);
- }
-
- }
- else if (p_cur_pos->is_extruder_relative != p_pre_pos->is_extruder_relative)
- {
- p_logger_->log(logger_type_, DEBUG, "Extruder axis mode changed, cannot add point to current arc: " + cmd.gcode);
- }
- else if (waiting_for_arc_ && p_pre_pos->f != p_cur_pos->f)
- {
- p_logger_->log(logger_type_, DEBUG, "Feedrate changed, cannot add point to current arc: " + cmd.gcode);
- }
- else if (waiting_for_arc_ && p_pre_pos->feature_type_tag != p_cur_pos->feature_type_tag)
- {
- p_logger_->log(logger_type_, DEBUG, "Feature type changed, cannot add point to current arc: " + cmd.gcode);
- }
- else
- {
- // Todo: Add all the relevant values
- p_logger_->log(logger_type_, DEBUG, "There was an unknown issue preventing the current point from being added to the arc: " + cmd.gcode);
- }
- }
- }
-
- if (!arc_added && !(cmd.is_empty && cmd.comment.length() == 0))
- {
- if (current_arc_.get_num_segments() < current_arc_.get_min_segments()) {
- if (debug_logging_enabled_ && !cmd.is_empty)
- {
- if (current_arc_.get_num_segments() != 0)
- {
- p_logger_->log(logger_type_, DEBUG, "Not enough segments, resetting. Gcode:" + cmd.gcode);
- }
-
- }
- waiting_for_arc_ = false;
- current_arc_.clear();
- }
- else if (waiting_for_arc_)
- {
-
- if (current_arc_.is_shape())
- {
- // update our statistics
- points_compressed_ += current_arc_.get_num_segments()-1;
- arcs_created_++; // increment the number of generated arcs
- write_arc_gcodes(p_pre_pos->is_extruder_relative, p_pre_pos->f);
- // Now clear the arc and flag the processor as not waiting for an arc
- waiting_for_arc_ = false;
- current_arc_.clear();
- p_cur_pos = NULL;
- p_pre_pos = NULL;
-
- // Reprocess this line
- if (!is_end)
- {
- return process_gcode(cmd, false, true);
- }
- else
- {
- if (debug_logging_enabled_)
- {
- p_logger_->log(logger_type_, DEBUG, "Final arc created, exiting.");
- }
- return 0;
- }
-
- }
- else
- {
- if (debug_logging_enabled_)
- {
- p_logger_->log(logger_type_, DEBUG, "The current arc is not a valid arc, resetting.");
- }
- current_arc_.clear();
- waiting_for_arc_ = false;
- }
- }
- else if (debug_logging_enabled_)
- {
- p_logger_->log(logger_type_, DEBUG, "Could not add point to arc from gcode:" + cmd.gcode);
- }
-
- }
-
- if (waiting_for_arc_ || !arc_added)
- {
- // This might not work....
- //position* cur_pos = p_source_position_->get_current_position_ptr();
-
- unwritten_commands_.push_back(unwritten_command(cmd, is_previous_extruder_relative, movement_length_mm));
-
- }
- else if (!waiting_for_arc_)
- {
- write_unwritten_gcodes_to_file();
- current_arc_.clear();
- }
- return lines_written;
+
+
+ // Update the position for the source gcode file
+ p_source_position_->update(cmd, lines_processed_, gcodes_processed_, -1);
+ position* p_cur_pos = p_source_position_->get_current_position_ptr();
+ position* p_pre_pos = p_source_position_->get_previous_position_ptr();
+ bool is_previous_extruder_relative = p_pre_pos->is_extruder_relative;
+ extruder extruder_current = p_cur_pos->get_current_extruder();
+ extruder previous_extruder = p_pre_pos->get_current_extruder();
+
+ // Determine if this is a G0, G1, G2 or G3
+ bool is_g0_g1 = cmd.command == "G0" || cmd.command == "G1";
+ bool is_g2_g3 = cmd.command == "G2" || cmd.command == "G3";
+ //std::cout << lines_processed_ << " - " << cmd.gcode << ", CurrentEAbsolute: " << cur_extruder.e <<", ExtrusionLength: " << cur_extruder.extrusion_length << ", Retraction Length: " << cur_extruder.retraction_length << ", IsExtruding: " << cur_extruder.is_extruding << ", IsRetracting: " << cur_extruder.is_retracting << ".\n";
+
+ int lines_written = 0;
+ // see if this point is an extrusion
+
+ bool arc_added = false;
+ bool clear_shapes = false;
+ double movement_length_mm = 0;
+ bool is_extrusion = extruder_current.e_relative > 0;
+ bool is_retraction = extruder_current.e_relative < 0;
+ bool is_travel = !(is_extrusion || is_retraction) && (is_g0_g1 || is_g2_g3);
+
+ // Update the source file statistics
+ if (p_cur_pos->has_xy_position_changed)
+ {
+ // If this is a g2/g3 command, we need to do a bit more to get the length of the arc.
+ // The movement_length_mm variable will contain the chord length, which we will need
+ if (is_g2_g3)
+ {
+ // Determine the radius of the arc, which is necessary to calculate the arc length from the chord length.
+ double i = 0;
+ double j = 0;
+ double r = 0;
+ // Iterate through the parameters and fill in I, J and R;
+ for (std::vector<parsed_command_parameter>::iterator it = cmd.parameters.begin(); it != cmd.parameters.end(); ++it)
+ {
+ switch ((*it).name[0])
+ {
+ case 'I':
+ i = (*it).double_precision;
+ break;
+ case 'J':
+ j = (*it).double_precision;
+ break;
+ // Note that the R form isn't fully implemented!
+ case 'R':
+ r = (*it).double_precision;
+ break;
+ }
+ }
+
+ // Calculate R
+ if (r == 0)
+ {
+ r = utilities::sqrt(i * i + j * j);
+ }
+ // Now we know the radius and the chord length;
+ movement_length_mm = utilities::get_arc_distance(p_pre_pos->x, p_pre_pos->y, p_pre_pos->z, p_cur_pos->x, p_cur_pos->y, p_cur_pos->z, i, j, r, p_cur_pos->command.command == "G2");
+
+ }
+ else if (allow_3d_arcs_) {
+ movement_length_mm = utilities::get_cartesian_distance(p_pre_pos->x, p_pre_pos->y, p_pre_pos->z, p_cur_pos->x, p_cur_pos->y, p_cur_pos->z);
+ }
+ else {
+ movement_length_mm = utilities::get_cartesian_distance(p_pre_pos->x, p_pre_pos->y, p_cur_pos->x, p_cur_pos->y);
+ }
+
+ if (movement_length_mm > 0)
+ {
+ if (!is_reprocess)
+ {
+ if (is_extrusion)
+ {
+ segment_statistics_.update(movement_length_mm, true);
+ }
+ else if (is_retraction)
+ {
+ segment_retraction_statistics_.update(movement_length_mm, true);
+ }
+ else if (allow_travel_arcs_ && is_travel)
+ {
+ travel_statistics_.update(movement_length_mm, true);
+ }
+
+ }
+ }
+ }
+
+ // calculate the extrusion rate (mm/mm) and see how much it changes
+ double mm_extruded_per_mm_travel = 0;
+ double extrusion_rate_change_percent = 0;
+ bool aborted_by_flow_rate = false;
+ if (extrusion_rate_variance_percent_ != 0)
+ {
+ // TODO: MAKE SURE THIS WORKS FOR TRANSITIONS FROM TRAVEL TO NON TRAVEL MOVES
+ if (movement_length_mm > 0 && (is_extrusion || is_retraction))
+ {
+ mm_extruded_per_mm_travel = extruder_current.e_relative / movement_length_mm;
+ if (previous_extrusion_rate_ > 0)
+ {
+ extrusion_rate_change_percent = utilities::abs(utilities::get_percent_change(previous_extrusion_rate_, mm_extruded_per_mm_travel));
+ }
+ }
+ if (previous_extrusion_rate_ != 0 && utilities::greater_than(extrusion_rate_change_percent, extrusion_rate_variance_percent_))
+ {
+ arcs_aborted_by_flow_rate_++;
+ aborted_by_flow_rate = true;
+ }
+ }
+
+
+ // We need to make sure the printer is using absolute xyz, is extruding, and the extruder axis mode is the same as that of the previous position
+ // TODO: Handle relative XYZ axis. This is possible, but maybe not so important.
+
+ if (allow_dynamic_precision_ && is_g0_g1)
+ {
+ for (std::vector<parsed_command_parameter>::iterator it = cmd.parameters.begin(); it != cmd.parameters.end(); ++it)
+ {
+ switch ((*it).name[0])
+ {
+ case 'X':
+ case 'Y':
+ case 'Z':
+ current_arc_.update_xyz_precision((*it).double_precision);
+ break;
+ case 'E':
+ current_arc_.update_e_precision((*it).double_precision);
+ break;
+ }
+ }
+ }
+
+ bool z_axis_ok = allow_3d_arcs_ ||
+ utilities::is_equal(p_cur_pos->z, p_pre_pos->z);
+
+ if (
+ !is_end && cmd.is_known_command && !cmd.is_empty && (
+ is_g0_g1 && z_axis_ok &&
+ utilities::is_equal(p_cur_pos->x_offset, p_pre_pos->x_offset) &&
+ utilities::is_equal(p_cur_pos->y_offset, p_pre_pos->y_offset) &&
+ utilities::is_equal(p_cur_pos->z_offset, p_pre_pos->z_offset) &&
+ utilities::is_equal(p_cur_pos->x_firmware_offset, p_pre_pos->x_firmware_offset) &&
+ utilities::is_equal(p_cur_pos->y_firmware_offset, p_pre_pos->y_firmware_offset) &&
+ utilities::is_equal(p_cur_pos->z_firmware_offset, p_pre_pos->z_firmware_offset) &&
+ (previous_extrusion_rate_ == 0 || utilities::less_than_or_equal(extrusion_rate_change_percent, extrusion_rate_variance_percent_)) &&
+ !p_cur_pos->is_relative &&
+ (
+ !waiting_for_arc_ ||
+ extruder_current.is_extruding ||
+ extruder_current.is_retracting ||
+ // Test for travel conversion
+ (allow_travel_arcs_ && p_cur_pos->is_travel())
+ //|| (previous_extruder.is_extruding && extruder_current.is_extruding) // Test to see if
+ // we can get more arcs.
+ // || (previous_extruder.is_retracting && extruder_current.is_retracting) // Test to see if
+ // we can get more arcs.
+ ) &&
+ p_cur_pos->is_extruder_relative == is_previous_extruder_relative &&
+ (!waiting_for_arc_ || p_pre_pos->f == p_cur_pos->f) && // might need to skip the waiting for arc check...
+ (!waiting_for_arc_ || p_pre_pos->feature_type_tag == p_cur_pos->feature_type_tag)
+ )
+ ) {
+
+ // Record the extrusion rate
+ previous_extrusion_rate_ = mm_extruded_per_mm_travel;
+ printer_point p(p_cur_pos->get_gcode_x(), p_cur_pos->get_gcode_y(), p_cur_pos->get_gcode_z(), extruder_current.get_offset_e(), extruder_current.e_relative, p_cur_pos->f, movement_length_mm, p_pre_pos->is_extruder_relative);
+ if (!waiting_for_arc_)
+ {
+ if (debug_logging_enabled_)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Starting new arc from Gcode:" + cmd.gcode);
+ }
+ write_unwritten_gcodes_to_file();
+ // add the previous point as the starting point for the current arc
+ printer_point previous_p(p_pre_pos->get_gcode_x(), p_pre_pos->get_gcode_y(), p_pre_pos->get_gcode_z(), previous_extruder.get_offset_e(), previous_extruder.e_relative, p_pre_pos->f, 0, p_pre_pos->is_extruder_relative);
+ // Don't add any extrusion, or you will over extrude!
+ //std::cout << "Trying to add first point (" << p.x << "," << p.y << "," << p.z << ")...";
+
+ current_arc_.try_add_point(previous_p);
+ }
+
+ double e_relative = extruder_current.e_relative;
+ int num_points = current_arc_.get_num_segments();
+ arc_added = current_arc_.try_add_point(p);
+ if (arc_added)
+ {
+ // Make sure our position list is large enough to handle all the segments
+ if (current_arc_.get_num_segments() + 2 > p_source_position_->get_max_positions())
+ {
+ p_source_position_->grow_max_positions(p_source_position_->get_max_positions() * 2);
+ }
+ if (!waiting_for_arc_)
+ {
+ waiting_for_arc_ = true;
+ previous_feedrate_ = p_pre_pos->f;
+ }
+ else
+ {
+ if (debug_logging_enabled_)
+ {
+ if (num_points + 1 == current_arc_.get_num_segments())
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Adding point to arc from Gcode:" + cmd.gcode);
+ }
+
+ }
+ }
+ }
+ }
+ else {
+
+ if (debug_logging_enabled_) {
+ if (is_end)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Procesing final shape, if one exists.");
+ }
+ else if (!cmd.is_empty)
+ {
+ if (!cmd.is_known_command)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Command '" + cmd.command + "' is Unknown. Gcode:" + cmd.gcode);
+ }
+ else if (cmd.command != "G0" && cmd.command != "G1")
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Command '" + cmd.command + "' is not G0/G1, skipping. Gcode:" + cmd.gcode);
+ }
+ else if (!allow_3d_arcs_ && !utilities::is_equal(p_cur_pos->z, p_pre_pos->z))
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Z axis position changed, cannot convert:" + cmd.gcode);
+ }
+ else if (p_cur_pos->is_relative)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "XYZ Axis is in relative mode, cannot convert:" + cmd.gcode);
+ }
+ else if (
+ waiting_for_arc_ && !(
+ (previous_extruder.is_extruding && extruder_current.is_extruding) ||
+ (previous_extruder.is_retracting && extruder_current.is_retracting)
+ )
+ )
+ {
+ std::string message = "Extruding or retracting state changed, cannot add point to current arc: " + cmd.gcode;
+ if (verbose_logging_enabled_)
+ {
+
+ message.append(
+ " - Verbose Info\n\tCurrent Position Info - Absolute E:" + utilities::to_string(extruder_current.e) +
+ ", Offset E:" + utilities::to_string(extruder_current.get_offset_e()) +
+ ", Mode:" + (p_cur_pos->is_extruder_relative_null ? "NULL" : p_cur_pos->is_extruder_relative ? "relative" : "absolute") +
+ ", Retraction: " + utilities::to_string(extruder_current.retraction_length) +
+ ", Extrusion: " + utilities::to_string(extruder_current.extrusion_length) +
+ ", Retracting: " + (extruder_current.is_retracting ? "True" : "False") +
+ ", Extruding: " + (extruder_current.is_extruding ? "True" : "False")
+ );
+ message.append(
+ "\n\tPrevious Position Info - Absolute E:" + utilities::to_string(previous_extruder.e) +
+ ", Offset E:" + utilities::to_string(previous_extruder.get_offset_e()) +
+ ", Mode:" + (p_pre_pos->is_extruder_relative_null ? "NULL" : p_pre_pos->is_extruder_relative ? "relative" : "absolute") +
+ ", Retraction: " + utilities::to_string(previous_extruder.retraction_length) +
+ ", Extrusion: " + utilities::to_string(previous_extruder.extrusion_length) +
+ ", Retracting: " + (previous_extruder.is_retracting ? "True" : "False") +
+ ", Extruding: " + (previous_extruder.is_extruding ? "True" : "False")
+ );
+ p_logger_->log(logger_type_, log_levels::VERBOSE, message);
+ }
+ else
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, message);
+ }
+
+ }
+ else if (p_cur_pos->is_extruder_relative != p_pre_pos->is_extruder_relative)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Extruder axis mode changed, cannot add point to current arc: " + cmd.gcode);
+ }
+ else if (waiting_for_arc_ && p_pre_pos->f != p_cur_pos->f)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Feedrate changed, cannot add point to current arc: " + cmd.gcode);
+ }
+ else if (waiting_for_arc_ && p_pre_pos->feature_type_tag != p_cur_pos->feature_type_tag)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Feature type changed, cannot add point to current arc: " + cmd.gcode);
+ }
+ else if (aborted_by_flow_rate)
+ {
+ std::stringstream stream;
+ stream << std::fixed << std::setprecision(5);
+ stream << "Arc Canceled - The extrusion rate variance of " << extrusion_rate_variance_percent_ << "% exceeded by " << extrusion_rate_change_percent - extrusion_rate_variance_percent_ << "% on line " << lines_processed_ << ". Extruded " << extruder_current.e_relative << "mm over " << movement_length_mm << "mm of travel (" << mm_extruded_per_mm_travel << "mm/mm). Previous rate: " << previous_extrusion_rate_ << "mm/mm.";
+ p_logger_->log(logger_type_, log_levels::DEBUG, stream.str());
+ }
+ else
+ {
+ // Todo: Add all the relevant values
+ p_logger_->log(logger_type_, log_levels::DEBUG, "There was an unknown issue preventing the current point from being added to the arc: " + cmd.gcode);
+ }
+ }
+ }
+
+ // Reset the previous extrusion rate
+ previous_extrusion_rate_ = 0;
+ }
+
+ if (!arc_added && !(cmd.is_empty && cmd.comment.length() == 0))
+ {
+ if (current_arc_.get_num_segments() < current_arc_.get_min_segments()) {
+ if (debug_logging_enabled_ && !cmd.is_empty)
+ {
+ if (current_arc_.get_num_segments() != 0)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Not enough segments, resetting. Gcode:" + cmd.gcode);
+ }
+
+ }
+ waiting_for_arc_ = false;
+ current_arc_.clear();
+ }
+ else if (waiting_for_arc_)
+ {
+
+ if (current_arc_.is_shape())
+ {
+ // update our statistics
+ points_compressed_ += current_arc_.get_num_segments() - 1;
+ arcs_created_++; // increment the number of generated arcs
+ write_arc_gcodes(p_pre_pos->f);
+ // Now clear the arc and flag the processor as not waiting for an arc
+ waiting_for_arc_ = false;
+ current_arc_.clear();
+ p_cur_pos = NULL;
+ p_pre_pos = NULL;
+
+ // Reprocess this line
+ if (!is_end)
+ {
+ return process_gcode(cmd, false, true);
+ }
+ else
+ {
+ if (debug_logging_enabled_)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Final arc created, exiting.");
+ }
+ return 0;
+ }
+
+ }
+ else
+ {
+ if (debug_logging_enabled_)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "The current arc is not a valid arc, resetting.");
+ }
+ current_arc_.clear();
+ waiting_for_arc_ = false;
+ }
+ }
+ else if (debug_logging_enabled_)
+ {
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Could not add point to arc from gcode:" + cmd.gcode);
+ }
+
+ }
+
+ if (waiting_for_arc_ || !arc_added)
+ {
+ // This might not work....
+ //position* cur_pos = p_source_position_->get_current_position_ptr();
+ unwritten_commands_.push_back(unwritten_command(cmd, is_previous_extruder_relative, is_extrusion, is_retraction, is_travel, movement_length_mm));
+
+ }
+ else if (!waiting_for_arc_)
+ {
+ write_unwritten_gcodes_to_file();
+ current_arc_.clear();
+ }
+ return lines_written;
}
-void arc_welder::write_arc_gcodes(bool is_extruder_relative, double current_feedrate)
+void arc_welder::write_arc_gcodes(double current_feedrate)
{
- std::string comment = get_comment_for_arc();
- // remove the same number of unwritten gcodes as there are arc segments, minus 1 for the start point
- // Which isn't a movement
- // note, skip the first point, it is the starting point
- int num_segments = current_arc_.get_num_segments() - 1;
- for (int index = 0; index < num_segments; index++)
- {
- while (!unwritten_commands_.pop_back().is_g1_g2);
- }
-
- // Undo the current command, since it isn't included in the arc
- p_source_position_->undo_update();
-
- // Set the current feedrate if it is different, else set to 0 to indicate that no feedrate should be included
- if (previous_feedrate_ > 0 && previous_feedrate_ == current_feedrate) {
- current_feedrate = 0;
- }
-
- // Craete the arc gcode
- std::string gcode;
- if (is_extruder_relative) {
- gcode = get_arc_gcode_relative(current_feedrate, comment);
- }
-
- else {
- gcode = get_arc_gcode_absolute(p_source_position_->get_current_position_ptr()->get_current_extruder().get_offset_e(), current_feedrate, comment);
- }
-
-
- if (debug_logging_enabled_)
- {
- char buffer[20];
- std::string message = "Arc created with ";
- sprintf(buffer, "%d", current_arc_.get_num_segments());
- message += buffer;
- message += " segments: ";
- message += gcode;
- p_logger_->log(logger_type_, DEBUG, message);
- }
-
- // Write everything that hasn't yet been written
- write_unwritten_gcodes_to_file();
-
- // Update the current extrusion statistics for the current arc gcode
- segment_statistics_.update(current_arc_.get_shape_length() , false);
- // now write the current arc to the file
- write_gcode_to_file(gcode);
-
-
+ std::string comment = get_comment_for_arc();
+ // remove the same number of unwritten gcodes as there are arc segments, minus 1 for the start point
+ // Which isn't a movement
+ // note, skip the first point, it is the starting point
+ int num_segments = current_arc_.get_num_segments() - 1;
+ for (int index = 0; index < num_segments; index++)
+ {
+ while (!unwritten_commands_.pop_back().is_g0_g1);
+ }
+
+ // Undo the current command, since it isn't included in the arc
+ p_source_position_->undo_update();
+
+ // Set the current feedrate if it is different, else set to 0 to indicate that no feedrate should be included
+ if (previous_feedrate_ > 0 && previous_feedrate_ == current_feedrate) {
+ current_feedrate = 0;
+ }
+
+ // Craete the arc gcode
+ std::string gcode = get_arc_gcode(comment);
+
+ if (debug_logging_enabled_)
+ {
+ char buffer[20];
+ std::string message = "Arc created with ";
+ sprintf(buffer, "%d", current_arc_.get_num_segments());
+ message += buffer;
+ message += " segments: ";
+ message += gcode;
+ p_logger_->log(logger_type_, log_levels::DEBUG, message);
+ }
+
+ // Write everything that hasn't yet been written
+ write_unwritten_gcodes_to_file();
+
+ // Update the current extrusion statistics for the current arc gcode
+ double shape_e_relative = current_arc_.get_shape_e_relative();
+ bool is_retraction = shape_e_relative < 0;
+ bool is_extrusion = shape_e_relative > 0;
+ if (is_extrusion)
+ {
+ segment_statistics_.update(current_arc_.get_shape_length(), false);
+
+ }
+ else if (is_retraction)
+ {
+ segment_retraction_statistics_.update(current_arc_.get_shape_length(), false);
+ }
+ else if (allow_travel_arcs_ ) {
+ travel_statistics_.update(current_arc_.get_shape_length(), false);
+ }
+ // now write the current arc to the file
+ write_gcode_to_file(gcode);
}
std::string arc_welder::get_comment_for_arc()
{
- // build a comment string from the commands making up the arc
- // We need to start with the first command entered.
- int comment_index = unwritten_commands_.count() - (current_arc_.get_num_segments() - 1);
- std::string comment;
- for (; comment_index < unwritten_commands_.count(); comment_index++)
- {
- std::string old_comment = unwritten_commands_[comment_index].comment;
- if (old_comment != comment && old_comment.length() > 0)
- {
- if (comment.length() > 0)
- {
- comment += " - ";
- }
- comment += old_comment;
- }
- }
- return comment;
+ // build a comment string from the commands making up the arc
+ // We need to start with the first command entered.
+ int comment_index = unwritten_commands_.count() - (current_arc_.get_num_segments() - 1);
+ std::string comment;
+ for (; comment_index < unwritten_commands_.count(); comment_index++)
+ {
+ std::string old_comment = unwritten_commands_[comment_index].comment;
+ if (old_comment != comment && old_comment.length() > 0)
+ {
+ if (comment.length() > 0)
+ {
+ comment += " - ";
+ }
+ comment += old_comment;
+ }
+ }
+ return comment;
}
std::string arc_welder::create_g92_e(double absolute_e)
{
- std::stringstream stream;
- stream << std::fixed << std::setprecision(5);
- stream << "G92 E" << absolute_e;
- return stream.str();
+ std::stringstream stream;
+ stream << std::fixed << std::setprecision(5);
+ stream << "G92 E" << absolute_e;
+ return stream.str();
}
int arc_welder::write_gcode_to_file(std::string gcode)
{
- output_file_ << gcode << "\n";
- return 1;
+ output_file_ << gcode << "\n";
+ return 1;
}
int arc_welder::write_unwritten_gcodes_to_file()
{
- int size = unwritten_commands_.count();
- std::string lines_to_write;
-
- for (int index = 0; index < size; index++)
- {
- // The the current unwritten position and remove it from the list
- unwritten_command p = unwritten_commands_.pop_front();
- if (p.extrusion_length > 0)
- {
- segment_statistics_.update(p.extrusion_length, false);
- }
- lines_to_write.append(p.to_string()).append("\n");
- }
-
- output_file_ << lines_to_write;
- return size;
-}
-
-std::string arc_welder::get_arc_gcode_relative(double f, const std::string comment)
-{
- // Write gcode to file
- std::string gcode;
-
- gcode = current_arc_.get_shape_gcode_relative(f);
-
- if (comment.length() > 0)
- {
- gcode += ";" + comment;
- }
- return gcode;
-
+ int size = unwritten_commands_.count();
+ std::string lines_to_write;
+
+ for (int index = 0; index < size; index++)
+ {
+ // The the current unwritten position and remove it from the list
+ unwritten_command p = unwritten_commands_.pop_front();
+ if ((p.is_g0_g1 || p.is_g2_g3) && p.length > 0)
+ {
+
+ if (p.is_extrusion)
+ {
+ segment_statistics_.update(p.length, false);
+ }
+ else if (p.is_retraction)
+ {
+ segment_retraction_statistics_.update(p.length, false);
+ }
+ else if (p.is_travel && allow_travel_arcs_) {
+ travel_statistics_.update(p.length, false);
+ }
+ }
+ lines_to_write.append(p.to_string()).append("\n");
+ }
+
+ output_file_ << lines_to_write;
+ return size;
}
-std::string arc_welder::get_arc_gcode_absolute(double e, double f, const std::string comment)
+std::string arc_welder::get_arc_gcode(const std::string comment)
{
- // Write gcode to file
- std::string gcode;
+ // Write gcode to file
+ std::string gcode;
- gcode = current_arc_.get_shape_gcode_absolute(e, f);
+ gcode = current_arc_.get_shape_gcode();
- if (comment.length() > 0)
- {
- gcode += ";" + comment;
- }
- return gcode;
+ if (comment.length() > 0)
+ {
+ gcode += ";" + comment;
+ }
+ return gcode;
}
void arc_welder::add_arcwelder_comment_to_target()
{
- p_logger_->log(logger_type_, DEBUG, "Adding ArcWelder comment to the target file.");
- std::stringstream stream;
- stream << std::fixed;
- stream << "; Postprocessed by [ArcWelder](https://github.com/FormerLurker/ArcWelderLib)\n";
- stream << "; Copyright(C) 2020 - Brad Hochgesang\n";
- stream << "; resolution=" << std::setprecision(2) << resolution_mm_ << "mm\n";
- stream << "; path_tolerance=" << std::setprecision(0) << (current_arc_.get_path_tolerance_percent() * 100.0) << "%\n";
- stream << "; max_radius=" << std::setprecision(2) << (current_arc_.get_max_radius()) << "mm\n";
- if (gcode_position_args_.g90_influences_extruder)
- {
- stream << "; g90_influences_extruder=True\n";
- }
- if (current_arc_.get_mm_per_arc_segment() > 0 && current_arc_.get_min_arc_segments() > 0)
- {
- stream << "; firmware_compensation=True\n";
- stream << "; mm_per_arc_segment="<< std::setprecision(2) << current_arc_.get_mm_per_arc_segment() << "mm\n";
- stream << "; min_arc_segments=" << std::setprecision(0) << current_arc_.get_min_arc_segments() << "\n";
- }
- if (allow_3d_arcs_)
- {
- stream << "; allow_3d_arcs=True\n";
-
- }
- if (allow_dynamic_precision_)
- {
- stream << "; allow_dynamic_precision=True\n";
- }
- stream << "; default_xyz_precision=" << std::setprecision(0) << static_cast<int>(current_arc_.get_xyz_precision()) << "\n";
- stream << "; default_e_precision=" << std::setprecision(0) << static_cast<int>(current_arc_.get_e_precision()) << "\n\n";
-
-
- output_file_ << stream.str();
+ p_logger_->log(logger_type_, log_levels::DEBUG, "Adding ArcWelder comment to the target file.");
+ std::stringstream stream;
+ stream << std::fixed;
+ stream << "; Postprocessed by [ArcWelder](https://github.com/FormerLurker/ArcWelderLib)\n";
+ stream << "; Copyright(C) 2020 - Brad Hochgesang\n";
+ stream << "; Version: " << GIT_TAGGED_VERSION << ", Branch: " << GIT_BRANCH << ", BuildDate: " << BUILD_DATE << "\n";
+ stream << "; resolution=" << std::setprecision(2) << resolution_mm_ << "mm\n";
+ stream << "; path_tolerance=" << std::setprecision(1) << (current_arc_.get_path_tolerance_percent() * 100.0) << "%\n";
+ stream << "; max_radius=" << std::setprecision(2) << (current_arc_.get_max_radius()) << "mm\n";
+ if (gcode_position_args_.g90_influences_extruder)
+ {
+ stream << "; g90_influences_extruder=True\n";
+ }
+ if (current_arc_.get_mm_per_arc_segment() > 0 && current_arc_.get_min_arc_segments() > 0)
+ {
+ stream << "; firmware_compensation=True\n";
+ stream << "; mm_per_arc_segment=" << std::setprecision(2) << current_arc_.get_mm_per_arc_segment() << "mm\n";
+ stream << "; min_arc_segments=" << std::setprecision(0) << current_arc_.get_min_arc_segments() << "\n";
+ }
+ if (allow_3d_arcs_)
+ {
+ stream << "; allow_3d_arcs=True\n";
+
+ }
+ if (allow_dynamic_precision_)
+ {
+ stream << "; allow_dynamic_precision=True\n";
+ }
+ stream << "; default_xyz_precision=" << std::setprecision(0) << static_cast<int>(current_arc_.get_xyz_precision()) << "\n";
+ stream << "; default_e_precision=" << std::setprecision(0) << static_cast<int>(current_arc_.get_e_precision()) << "\n";
+ stream << "; extrusion_rate_variance_percent=" << std::setprecision(1) << (extrusion_rate_variance_percent_ * 100.0) << "%\n\n";
+
+
+ output_file_ << stream.str();
}
diff --git a/ArcWelder/arc_welder.h b/ArcWelder/arc_welder.h
index 6d00ec4..c7d7465 100644
--- a/ArcWelder/arc_welder.h
+++ b/ArcWelder/arc_welder.h
@@ -44,9 +44,6 @@
#define _CRT_SECURE_NO_WARNINGS
#endif
-
-#define DEFAULT_G90_G91_INFLUENCES_EXTREUDER false
-
static const int segment_statistic_lengths_count = 12;
const double segment_statistic_lengths[] = { 0.002f, 0.005f, 0.01f, 0.05f, 0.1f, 0.5f, 1.0f, 5.0f, 10.0f, 20.0f, 50.0f, 100.0f };
@@ -64,7 +61,8 @@ struct segment_statistic {
};
struct source_target_segment_statistics {
- source_target_segment_statistics(const double segment_tracking_lengths[], const int num_lengths, logger* p_logger = NULL) {
+ source_target_segment_statistics(const double segment_tracking_lengths[], const int num_lengths, logger* p_logger = NULL)
+ {
total_length_source = 0;
total_length_target = 0;
total_count_source = 0;
@@ -76,6 +74,7 @@ struct source_target_segment_statistics {
for (int index = 0; index < num_lengths; index++)
{
double current_max = segment_tracking_lengths[index];
+ segment_statistic_lengths.push_back(segment_tracking_lengths[index]);
source_segments.push_back(segment_statistic(current_min, segment_tracking_lengths[index]));
target_segments.push_back(segment_statistic(current_min, segment_tracking_lengths[index]));
current_min = current_max;
@@ -86,7 +85,8 @@ struct source_target_segment_statistics {
p_logger_ = p_logger;
logger_type_ = 0;
}
-
+
+ std::vector<double> segment_statistic_lengths;
std::vector<segment_statistic> source_segments;
std::vector<segment_statistic> target_segments;
double total_length_source;
@@ -96,7 +96,7 @@ struct source_target_segment_statistics {
int total_count_source;
int total_count_target;
int num_segment_tracking_lengths;
-
+
double get_total_count_reduction_percent() const {
return utilities::get_percent_change(total_count_source, total_count_target);
}
@@ -130,10 +130,49 @@ struct source_target_segment_statistics {
}
}
+ static source_target_segment_statistics add(source_target_segment_statistics stats1, const source_target_segment_statistics stats2)
+ {
+
+ double * lengths = &stats1.segment_statistic_lengths[0];
+ std::copy(stats1.segment_statistic_lengths.begin(), stats1.segment_statistic_lengths.end(), lengths);
+ source_target_segment_statistics combined_stats(lengths, segment_statistic_lengths_count, stats1.p_logger_);
+ if (stats1.num_segment_tracking_lengths != stats2.num_segment_tracking_lengths)
+ {
+ // Todo: throw a reasonable exception
+ throw std::exception();
+ }
+
+ // Copy the segment statistics
+ for (int index = 0; index <= stats1.num_segment_tracking_lengths; index++)
+ {
+ // Verify the stats are the same
+ if (
+ stats1.source_segments[index].min_mm != stats2.source_segments[index].min_mm
+ || stats1.source_segments[index].max_mm != stats2.source_segments[index].max_mm
+ )
+ {
+ // Todo: throw a reasonable exception
+ throw std::exception();
+ }
+ combined_stats.source_segments[index].count = stats1.source_segments[index].count + stats2.source_segments[index].count;
+ combined_stats.target_segments[index].count = stats1.target_segments[index].count + stats2.target_segments[index].count;
+ }
+
+ combined_stats.total_length_source = stats1.total_length_source + stats2.total_length_source;
+ combined_stats.total_length_target = stats1.total_length_target + stats2.total_length_target;
+ combined_stats.total_count_source = stats1.total_count_source + stats2.total_count_source;
+ combined_stats.total_count_target = stats1.total_count_target + stats2.total_count_target;
+
+ return combined_stats;
+ }
std::string str() const {
+ return str("", utilities::box_drawing::BoxEncodingEnum::ASCII);
+ }
+
+ std::string str(std::string title, utilities::box_drawing::BoxEncodingEnum box_encoding) const {
//if (p_logger_ != NULL) p_logger_->log(logger_type_, VERBOSE, "Building Segment Statistics.");
-
+
std::stringstream output_stream;
std::stringstream format_stream;
const int min_column_size = 8;
@@ -210,21 +249,51 @@ struct source_target_segment_statistics {
}
// Get the table width
int table_width = mm_col_size + min_max_label_col_size + mm_col_size + source_col_size + target_col_size + percent_col_size;
- // Add a separator for the statistics
- //output_stream << std::setw(table_width) << std::setfill('-') << "-" << "\n" << std::setfill(' ') ;
- // Output the column headers
- // Center the min and max column.
- output_stream << utilities::center("Min", mm_col_size);
+ int table_left_padding = 0;
+ int table_right_padding = 0;
+ if (table_width < (int)title.length())
+ {
+ table_left_padding = ((int)title.length() - table_width) / 2;
+ table_right_padding = ((int)title.length() - table_width - table_left_padding);
+ table_width = (int)title.length();
+
+ }
+ utilities::box_drawing box(box_encoding, table_width);
+ // Draw the top border
+ box.top(output_stream);
+
+ if (title != "")
+ {
+ // Draw the title
+ box.row(output_stream, utilities::center(title, table_width));
+ // Draw the title separator
+ box.middle(output_stream);
+ }
+
+ // Output the centered column headers
+ // start the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL);
+ // add the left padding for the table
+ output_stream << std::string(table_left_padding, ' ');
+ output_stream << std::setfill(' ') << utilities::center("Min", mm_col_size);
output_stream << std::setw(min_max_label_col_size) << "";
output_stream << utilities::center("Max", mm_col_size);
// right align the source, target and change columns
output_stream << std::setw(source_col_size) << std::right << "Source";
output_stream << std::setw(target_col_size) << std::right << "Target";
output_stream << std::setw(percent_col_size) << std::right << "Change";
- output_stream << "\n";
- output_stream << std::setw(table_width) << std::setfill('-') << "" << std::setfill(' ') << "\n";
+ // Add the right padding for the table
+ output_stream << std::string(table_right_padding, ' ');
+ // end the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL) << "\n";
output_stream << std::fixed << std::setprecision(max_precision);
+ // Add the separator
+ box.middle(output_stream);
for (int index = 0; index < source_segments.size(); index++) {
+ // start the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL);
+ // add the left padding for the table
+ output_stream << std::string(table_left_padding, ' ');
//extract the necessary variables from the source and target segments
double min_mm = source_segments[index].min_mm;
double max_mm = source_segments[index].max_mm;
@@ -287,24 +356,34 @@ struct source_target_segment_statistics {
output_stream << std::setw(target_col_size) << target_count_string;
// Add the percent change string
output_stream << std::setw(percent_col_size) << percent_change_string;
- // End the line
- output_stream << "\n";
+ // Add the right padding for the table
+ output_stream << std::string(table_right_padding, ' ');
+ // end the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL) << "\n";
}
- // Add the total rows separator
- output_stream << std::setw(table_width) << std::setfill('-') << "" << std::setfill(' ') << "\n";
+
+
// Add the total rows;
+ // Draw the totals separator
+ box.middle(output_stream);
if (utilities::is_equal(total_length_source, total_length_target, 0.001))
{
+ // start the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL);
std::string total_distance_string;
format_stream.str(std::string());
format_stream << std::fixed << std::setprecision(max_precision) << total_length_source << "mm";
total_distance_string = format_stream.str();
output_stream << std::setw(totals_row_label_size) << std::right << "Total distance:";
- output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_distance_string << "\n" << std::setfill(' ');
+ output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_distance_string << std::setfill(' ');
+ // end the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL) << "\n";
}
else
{
- // We need to output two different distances (this probably should never happen)
+ // We need to output two different distances
+ // start the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL);
// Format the total source distance string
std::string total_source_distance_string;
format_stream.str(std::string());
@@ -312,8 +391,12 @@ struct source_target_segment_statistics {
total_source_distance_string = format_stream.str();
// Add the total source distance row
output_stream << std::setw(totals_row_label_size) << std::right << "Total distance source:";
- output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_source_distance_string << "\n" << std::setfill(' ');
+ output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_source_distance_string << std::setfill(' ');
+ // end the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL) << "\n";
+ // start the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL);
// Format the total target distance string
std::string total_target_distance_string;
format_stream.str(std::string());
@@ -321,32 +404,55 @@ struct source_target_segment_statistics {
total_target_distance_string = format_stream.str();
// Add the total target distance row
output_stream << std::setw(totals_row_label_size) << std::right << "Total distance target:";
- output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_target_distance_string << "\n" << std::setfill(' ');
+ output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_target_distance_string << std::setfill(' ');
+ // end the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL) << "\n";
}
// Add the total count rows
+
+ // start the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL);
// Add the source count
output_stream << std::setprecision(0) << std::setw(totals_row_label_size) << std::right << "Total count source:";
- output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_count_source << "\n" << std::setfill(' ');
+ output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_count_source << std::setfill(' ');
+ // end the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL) << "\n";
+
+ // start the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL);
// Add the target count
output_stream << std::setw(totals_row_label_size) << std::right << "Total count target:";
- output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_count_target << "\n" << std::setfill(' ');
+ output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_count_target << std::setfill(' ');
+ // end the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL) << "\n";
+
+ // start the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL);
// Add the total percent change row
std::string total_percent_change_string = utilities::get_percent_change_string(total_count_source, total_count_target, 1);
output_stream << std::setw(totals_row_label_size) << std::right << "Total percent change:";
output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_percent_change_string << std::setfill(' ');
+ // end the row
+ output_stream << box.get_box_replacement_element(utilities::box_drawing::BoxElementEnum::VERTICAL) << "\n";
+
+ // Add the final separator
+ box.bottom(output_stream);
+
std::string output_string = output_stream.str();
+ box.make_replacements(output_string);
return output_string;
}
private:
+
logger* p_logger_;
int logger_type_;
};
// Struct to hold the progress, statistics, and return values
struct arc_welder_progress {
- arc_welder_progress() : segment_statistics(segment_statistic_lengths, segment_statistic_lengths_count, NULL) {
+ arc_welder_progress() : segment_statistics(segment_statistic_lengths, segment_statistic_lengths_count, NULL), segment_retraction_statistics(segment_statistic_lengths, segment_statistic_lengths_count, NULL), travel_statistics(segment_statistic_lengths, segment_statistic_lengths_count, NULL) {
percent_complete = 0.0;
seconds_elapsed = 0.0;
seconds_remaining = 0.0;
@@ -354,12 +460,16 @@ struct arc_welder_progress {
lines_processed = 0;
points_compressed = 0;
arcs_created = 0;
+ arcs_aborted_by_flow_rate = 0;
num_firmware_compensations = 0;
+ num_gcode_length_exceptions = 0;
source_file_size = 0;
source_file_position = 0;
target_file_size = 0;
compression_ratio = 0;
compression_percent = 0;
+ combine_extrusion_and_retraction = true;
+ box_encoding = utilities::box_drawing::BoxEncodingEnum::ASCII;
}
double percent_complete;
double seconds_elapsed;
@@ -368,15 +478,39 @@ struct arc_welder_progress {
int lines_processed;
int points_compressed;
int arcs_created;
+ int arcs_aborted_by_flow_rate;
int num_firmware_compensations;
+ int num_gcode_length_exceptions;
double compression_ratio;
double compression_percent;
long source_file_position;
long source_file_size;
long target_file_size;
+ bool combine_extrusion_and_retraction;
+ utilities::box_drawing::BoxEncodingEnum box_encoding;
+
source_target_segment_statistics segment_statistics;
+ source_target_segment_statistics segment_retraction_statistics;
+ source_target_segment_statistics travel_statistics;
+
+ std::string simple_progress_str() const {
+ std::stringstream stream;
+ if (percent_complete == 0) {
+ stream << " 00.0% complete - Estimating remaining time.";
+ }
+ else if (percent_complete == 100)
+ {
+ stream << "100.0% complete - " << seconds_elapsed << " seconds total.";
+ }
+ else {
+ stream << " " << std::fixed << std::setprecision(1) << std::setfill('0') << std::setw(4) << percent_complete << "% complete - Estimated " << std::setprecision(0) << std::setw(-1) << seconds_remaining << " of " << seconds_elapsed + seconds_remaining << " seconds remaing.";
+ }
+
+ return stream.str();
+ }
std::string str() const {
+
std::stringstream stream;
stream << std::fixed << std::setprecision(2);
@@ -385,20 +519,159 @@ struct arc_welder_progress {
stream << ", current_file_line: " << lines_processed;
stream << ", points_compressed: " << points_compressed;
stream << ", arcs_created: " << arcs_created;
+ stream << ", arcs_aborted_by_flowrate: " << arcs_aborted_by_flow_rate;
stream << ", num_firmware_compensations: " << num_firmware_compensations;
+ stream << ", num_gcode_length_exceptions: " << num_gcode_length_exceptions;
stream << ", compression_ratio: " << compression_ratio;
- stream << ", size_reduction: " << compression_percent << "% ";
+ stream << ", size_reduction: " << compression_percent << "% " ;
return stream.str();
}
std::string detail_str() const {
- std::stringstream stream;
- stream << "\n" << "Extrusion/Retraction Counts" << "\n" << segment_statistics.str() << "\n";
- return stream.str();
+ std::stringstream wstream;
+ wstream << "\n";
+
+ if (travel_statistics.total_count_source > 0)
+ {
+ wstream << travel_statistics.str("Target File Travel Statistics", box_encoding) << "\n";
+ }
+
+ if (combine_extrusion_and_retraction)
+ {
+ source_target_segment_statistics combined_stats = source_target_segment_statistics::add(segment_statistics, segment_retraction_statistics);
+ wstream << combined_stats.str("Target File Extrusion/Retraction Statistics", box_encoding) << "\n";
+ }
+ else
+ {
+
+ if (segment_retraction_statistics.total_count_source > 0)
+ {
+ wstream << segment_retraction_statistics.str("Target File Retraction Statistics", box_encoding) << "\n";
+ }
+
+ wstream << segment_statistics.str("Target File Extrusion Statistics", box_encoding) << "\n";
+ }
+ return wstream.str();
}
+
};
-
// define the progress callback type
typedef bool(*progress_callback)(arc_welder_progress, logger* p_logger, int logger_type);
+// LOGGER_NAME
+#define ARC_WELDER_LOGGER_NAME "arc_welder.gcode_conversion"
+// Default argument values
+#define DEFAULT_G90_G91_INFLUENCES_EXTRUDER false
+#define DEFAULT_GCODE_BUFFER_SIZE 10
+#define DEFAULT_G90_G91_INFLUENCES_EXTRUDER false
+#define DEFAULT_ALLOW_DYNAMIC_PRECISION false
+#define DEFAULT_ALLOW_TRAVEL_ARCS true
+#define DEFAULT_EXTRUSION_RATE_VARIANCE_PERCENT 0.05
+#define DEFAULT_NOTIFICATION_PERIOD_SECONDS 0.5
+
+struct arc_welder_args
+{
+ arc_welder_args() {
+ set_defaults();
+ };
+
+
+ arc_welder_args(std::string source, std::string target, logger* ptr_log)
+ {
+ set_defaults();
+ source_path = source;
+ target_path = target;
+ log = ptr_log;
+ }
+ std::string source_path;
+ std::string target_path;
+ logger* log;
+ double resolution_mm;
+ double path_tolerance_percent;
+ double max_radius_mm;
+ int min_arc_segments;
+ double mm_per_arc_segment;
+ bool g90_g91_influences_extruder;
+ bool allow_3d_arcs;
+ bool allow_travel_arcs;
+ bool allow_dynamic_precision;
+ unsigned char default_xyz_precision;
+ unsigned char default_e_precision;
+ double extrusion_rate_variance_percent;
+ int buffer_size;
+ int max_gcode_length;
+ double notification_period_seconds;
+ utilities::box_drawing::BoxEncodingEnum box_encoding;
+
+ progress_callback callback;
+
+ std::string str() const {
+ std::string log_level_name = "NO_LOGGING";
+ if (log != NULL)
+ {
+ log_level_name = log->get_log_level_name(ARC_WELDER_LOGGER_NAME);
+ }
+ std::stringstream stream;
+ stream << "Arc Welder Arguments\n";
+ stream << std::fixed << std::setprecision(2);
+ stream << "\tSource File Path : " << source_path << "\n";
+ if (source_path == target_path)
+ {
+ stream << "\tTarget File Path (overwrite) : " << target_path << "\n";
+ }
+ else
+ {
+ stream << "\tTarget File Path : " << target_path << "\n";
+ }
+ stream << "\tResolution : " << resolution_mm << "mm (+-" << std::setprecision(5) << resolution_mm / 2.0 << "mm)\n";
+ stream << "\tPath Tolerance : " << std::setprecision(3) << path_tolerance_percent * 100.0 << "%\n";
+ stream << "\tMaximum Arc Radius : " << std::setprecision(0) << max_radius_mm << "mm\n";
+ stream << "\tMin Arc Segments : " << std::setprecision(0) << min_arc_segments << "\n";
+ stream << "\tMM Per Arc Segment : " << std::setprecision(3) << mm_per_arc_segment << "\n";
+ stream << "\tAllow 3D Arcs : " << (allow_3d_arcs ? "True" : "False") << "\n";
+ stream << "\tAllow Travel Arcs : " << (allow_travel_arcs ? "True" : "False") << "\n";
+ stream << "\tAllow Dynamic Precision : " << (allow_dynamic_precision ? "True" : "False") << "\n";
+ stream << "\tDefault XYZ Precision : " << std::setprecision(0) << static_cast<int>(default_xyz_precision) << "\n";
+ stream << "\tDefault E Precision : " << std::setprecision(0) << static_cast<int>(default_e_precision) << "\n";
+ stream << "\tExtrusion Rate Variance % : " << std::setprecision(3) << extrusion_rate_variance_percent * 100.0 << "%\n";
+ stream << "\tG90/G91 Influences Extruder : " << (g90_g91_influences_extruder ? "True" : "False") << "\n";
+ if (max_gcode_length == 0)
+ {
+ stream << "\tMax Gcode Length : Unlimited\n";
+ }
+ else {
+ stream << "\tMax Gcode Length : " << std::setprecision(0) << max_gcode_length << " characters\n";
+ }
+ stream << "\tLog Level : " << log_level_name << "\n";
+ stream << "\tHide Progress Updates : " << (callback == NULL ? "True" : "False") << "\n";
+ stream << "\tProgress Notification Period : " << std::setprecision(2) << notification_period_seconds << " seconds";
+ return stream.str();
+ };
+
+private:
+ void set_defaults()
+ {
+ source_path = "",
+ target_path = "",
+ log = NULL,
+ resolution_mm = DEFAULT_RESOLUTION_MM,
+ path_tolerance_percent = ARC_LENGTH_PERCENT_TOLERANCE_DEFAULT,
+ max_radius_mm = DEFAULT_MAX_RADIUS_MM,
+ min_arc_segments = DEFAULT_MIN_ARC_SEGMENTS,
+ mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT,
+ g90_g91_influences_extruder = DEFAULT_G90_G91_INFLUENCES_EXTRUDER,
+ allow_3d_arcs = DEFAULT_ALLOW_3D_ARCS,
+ allow_travel_arcs = DEFAULT_ALLOW_TRAVEL_ARCS,
+ allow_dynamic_precision = DEFAULT_ALLOW_DYNAMIC_PRECISION,
+ default_xyz_precision = DEFAULT_XYZ_PRECISION,
+ default_e_precision = DEFAULT_E_PRECISION,
+ extrusion_rate_variance_percent = DEFAULT_EXTRUSION_RATE_VARIANCE_PERCENT,
+ max_gcode_length = DEFAULT_MAX_GCODE_LENGTH,
+ buffer_size = DEFAULT_GCODE_BUFFER_SIZE,
+ notification_period_seconds = DEFAULT_NOTIFICATION_PERIOD_SECONDS,
+ callback = NULL;
+ box_encoding = utilities::box_drawing::BoxEncodingEnum::ASCII;
+ }
+
+};
struct arc_welder_results {
arc_welder_results() : progress()
@@ -412,45 +685,31 @@ struct arc_welder_results {
std::string message;
arc_welder_progress progress;
};
-#define DEFAULT_GCODE_BUFFER_SIZE 1000
-#define DEFAULT_G90_G91_INFLUENCES_EXTRUDER false
-#define DEFAULT_ALLOW_DYNAMIC_PRECISION false
+
class arc_welder
{
public:
- arc_welder(
- std::string source_path,
- std::string target_path,
- logger* log,
- double resolution_mm,
- double path_tolerance_percent,
- double max_radius,
- int min_arc_segments,
- double mm_per_arc_segment,
- bool g90_g91_influences_extruder = DEFAULT_G90_G91_INFLUENCES_EXTRUDER,
- bool allow_3d_arcs = DEFAULT_ALLOW_3D_ARCS,
- bool allow_dynamic_precision = DEFAULT_ALLOW_DYNAMIC_PRECISION,
- unsigned char default_xyz_precision = DEFAULT_XYZ_PRECISION,
- unsigned char default_e_precision = DEFAULT_E_PRECISION,
- int buffer_size = DEFAULT_GCODE_BUFFER_SIZE,
- progress_callback callback = NULL);
+
+ arc_welder(arc_welder_args args);
+
+
void set_logger_type(int logger_type);
virtual ~arc_welder();
arc_welder_results process();
- double notification_period_seconds;
+
protected:
virtual bool on_progress_(const arc_welder_progress& progress);
private:
+
arc_welder_progress get_progress_(long source_file_position, double start_clock);
void add_arcwelder_comment_to_target();
void reset();
static gcode_position_args get_args_(bool g90_g91_influences_extruder, int buffer_size);
progress_callback progress_callback_;
int process_gcode(parsed_command cmd, bool is_end, bool is_reprocess);
- void write_arc_gcodes(bool is_extruder_relative, double current_feedrate);
+ void write_arc_gcodes(double current_feedrate);
int write_gcode_to_file(std::string gcode);
- std::string get_arc_gcode_relative(double f, const std::string comment);
- std::string get_arc_gcode_absolute(double e, double f, const std::string comment);
+ std::string get_arc_gcode(const std::string comment);
std::string get_comment_for_arc();
int write_unwritten_gcodes_to_file();
std::string create_g92_e(double absolute_e);
@@ -460,13 +719,18 @@ private:
gcode_position_args gcode_position_args_;
bool allow_dynamic_precision_;
bool allow_3d_arcs_;
+ bool allow_travel_arcs_;
long file_size_;
int lines_processed_;
int gcodes_processed_;
int last_gcode_line_written_;
int points_compressed_;
int arcs_created_;
+ int arcs_aborted_by_flow_rate_;
+ double notification_period_seconds_;
source_target_segment_statistics segment_statistics_;
+ source_target_segment_statistics segment_retraction_statistics_;
+ source_target_segment_statistics travel_statistics_;
long get_file_size(const std::string& file_path);
double get_time_elapsed(double start_clock, double end_clock);
double get_next_update_time() const;
@@ -478,6 +742,8 @@ private:
// We don't care about the printer settings, except for g91 influences extruder.
gcode_position* p_source_position_;
double previous_feedrate_;
+ double previous_extrusion_rate_;
+ double extrusion_rate_variance_percent_;
gcode_parser parser_;
bool verbose_output_;
int logger_type_;
@@ -486,5 +752,5 @@ private:
bool info_logging_enabled_;
bool verbose_logging_enabled_;
bool error_logging_enabled_;
-
+ utilities::box_drawing::BoxEncodingEnum box_encoding_;
};
diff --git a/ArcWelder/segmented_arc.cpp b/ArcWelder/segmented_arc.cpp
index 4da8f8e..f6cfff1 100644
--- a/ArcWelder/segmented_arc.cpp
+++ b/ArcWelder/segmented_arc.cpp
@@ -27,8 +27,6 @@
#include "utilities.h"
#include "segmented_shape.h"
#include <iostream>
-//#include <iomanip>
-//#include <sstream>
#include <stdio.h>
#include <cmath>
@@ -38,7 +36,10 @@ segmented_arc::segmented_arc() : segmented_shape(DEFAULT_MIN_SEGMENTS, DEFAULT_M
min_arc_segments_ = DEFAULT_MIN_ARC_SEGMENTS,
mm_per_arc_segment_ = DEFAULT_MM_PER_ARC_SEGMENT;
allow_3d_arcs_ = DEFAULT_ALLOW_3D_ARCS;
+ max_gcode_length_ = DEFAULT_MAX_GCODE_LENGTH;
+ num_gcode_length_exceptions_ = 0;
num_firmware_compensations_ = 0;
+
}
segmented_arc::segmented_arc(
@@ -51,7 +52,8 @@ segmented_arc::segmented_arc(
double mm_per_arc_segment,
bool allow_3d_arcs,
unsigned char default_xyz_precision,
- unsigned char default_e_precision
+ unsigned char default_e_precision,
+ int max_gcode_length
) : segmented_shape(min_segments, max_segments, resolution_mm, path_tolerance_percent, default_xyz_precision, default_e_precision)
{
max_radius_mm_ = max_radius_mm;
@@ -69,7 +71,15 @@ segmented_arc::segmented_arc(
min_arc_segments_ = 0;
}
allow_3d_arcs_ = allow_3d_arcs;
+ max_gcode_length_ = max_gcode_length;
+ if (max_gcode_length_ < 1)
+ {
+ max_gcode_length_ = 0;
+ }
num_firmware_compensations_ = 0;
+ num_gcode_length_exceptions_ = 0;
+
+
}
segmented_arc::~segmented_arc()
@@ -108,7 +118,10 @@ int segmented_arc::get_num_firmware_compensations() const
{
return num_firmware_compensations_;
}
-
+int segmented_arc::get_num_gcode_length_exceptions() const
+{
+ return num_gcode_length_exceptions_;
+}
double segmented_arc::get_mm_per_arc_segment() const
{
return mm_per_arc_segment_;
@@ -127,11 +140,12 @@ bool segmented_arc::try_add_point(printer_point p)
bool point_added = false;
// if we don't have enough segnemts to check the shape, just add
- if (points_.count() > get_max_segments() - 1)
+
+ if (points_.count() == points_.get_max_size())
{
// Too many points, we can't add more
- return false;
- }
+ points_.resize(points_.get_max_size()*2);
+ }
if (points_.count() > 0)
{
printer_point p1 = points_[points_.count() - 1];
@@ -141,6 +155,23 @@ bool segmented_arc::try_add_point(printer_point p)
return false;
}
+ // If we have more than 2 points, we need to make sure the current and previous moves are all of the same type.
+ if (points_.count() > 2)
+ {
+ // TODO: Do we need this?
+ // We already have at least an initial point and a second point. Make cure the current point and the previous are either both
+ // travel moves, or both extrusion
+ if (!(
+ (p1.e_relative > 0 && p.e_relative > 0) // Extrusions
+ || (p1.e_relative < 0 && p.e_relative < 0) // Retractions
+ || (p1.e_relative == 0 && p.e_relative == 0) // Travel
+ )
+ )
+ {
+ return false;
+ }
+ }
+
if (utilities::is_zero(p.distance))
{
// there must be some distance between the points
@@ -193,9 +224,6 @@ bool segmented_arc::try_add_point_internal_(printer_point p)
if (points_.count() < get_min_segments() - 1)
return false;
- // Create a test circle
- circle target_circle;
-
// the circle is new.. we have to test it now, which is expensive :(
points_.push_back(p);
double previous_shape_length = original_shape_length_;
@@ -204,15 +232,22 @@ bool segmented_arc::try_add_point_internal_(printer_point p)
if (arc::try_create_arc(points_, current_arc_, original_shape_length_, max_radius_mm_, resolution_mm_, path_tolerance_percent_, min_arc_segments_, mm_per_arc_segment_, get_xyz_tolerance(), allow_3d_arcs_))
{
bool abort_arc = false;
+ if (max_gcode_length_ > 0 && get_shape_gcode_length() > max_gcode_length_)
+ {
+ abort_arc = true;
+ num_gcode_length_exceptions_++;
+ }
if (min_arc_segments_ > 0 && mm_per_arc_segment_ > 0)
{
// Apply firmware compensation
// See how many arcs will be interpolated
double circumference = 2.0 * PI_DOUBLE * current_arc_.radius;
- int num_segments = (int)std::floor(circumference / min_arc_segments_);
+ // TODO: Should this be ceil?
+ int num_segments = (int)utilities::floor(circumference / min_arc_segments_);
if (num_segments < min_arc_segments_) {
//num_segments = (int)std::ceil(circumference/approximate_length) * (int)std::ceil(approximate_length / mm_per_arc_segment);
- num_segments = (int)std::floor(circumference / original_shape_length_);
+ // TODO: Should this be ceil?
+ num_segments = (int)utilities::floor(circumference / original_shape_length_);
if (num_segments < min_arc_segments_) {
abort_arc = true;
num_firmware_compensations_++;
@@ -239,7 +274,7 @@ bool segmented_arc::try_add_point_internal_(printer_point p)
// or because both I and J == 0
current_arc_ = original_arc;
}
- else if (!abort_arc)
+ else
{
if (!is_shape())
{
@@ -254,22 +289,12 @@ bool segmented_arc::try_add_point_internal_(printer_point p)
return false;
}
-std::string segmented_arc::get_shape_gcode_absolute(double e, double f)
-{
- bool has_e = e_relative_ != 0;
- return get_shape_gcode_(has_e, e, f);
-}
-
-std::string segmented_arc::get_shape_gcode_relative(double f)
-{
- bool has_e = e_relative_ != 0;
- return get_shape_gcode_(has_e, e_relative_, f);
-}
-
-std::string segmented_arc::get_shape_gcode_(bool has_e, double e, double f) const
+std::string segmented_arc::get_shape_gcode() const
{
std::string gcode;
- // Calculate gcode size
+ double e = current_arc_.end_point.is_extruder_relative ? e_relative_ : current_arc_.end_point.e_offset;
+ double f = current_arc_.start_point.f == current_arc_.end_point.f ? 0 : current_arc_.end_point.f;
+ bool has_e = e_relative_ != 0;
bool has_f = utilities::greater_than_or_equal(f, 1);
bool has_z = allow_3d_arcs_ && !utilities::is_equal(
current_arc_.start_point.z, current_arc_.end_point.z, get_xyz_tolerance()
@@ -285,6 +310,9 @@ std::string segmented_arc::get_shape_gcode_(bool has_e, double e, double f) cons
gcode += "G3";
}
+ // TODO: Limit Gcode Precision based on max_gcode_length
+
+
// Add X, Y, I and J
gcode += " X";
gcode += utilities::dtos(current_arc_.end_point.x, get_xyz_precision());
@@ -327,6 +355,62 @@ std::string segmented_arc::get_shape_gcode_(bool has_e, double e, double f) cons
}
+int segmented_arc::get_shape_gcode_length()
+{
+ double e = current_arc_.end_point.is_extruder_relative ? e_relative_ : current_arc_.end_point.e_offset;
+ double f = current_arc_.start_point.f == current_arc_.end_point.f ? 0 : current_arc_.end_point.f;
+ bool has_e = e_relative_ != 0;
+ bool has_f = utilities::greater_than_or_equal(f, 1);
+ bool has_z = allow_3d_arcs_ && !utilities::is_equal(
+ current_arc_.start_point.z, current_arc_.end_point.z, get_xyz_tolerance()
+ );
+
+ int xyz_precision = get_xyz_precision();
+ int e_precision = get_e_precision();
+
+ double i = current_arc_.get_i();
+ double j = current_arc_.get_j();
+
+
+ int num_spaces = 4 + (has_z ? 1 : 0) + (has_e ? 1 : 0) + (has_f ? 1 : 0);
+ int num_decimal_points = 4 + (has_z ? 1 : 0) + (has_e ? 1 : 0); // note f has no decimal point
+ int num_decimals = xyz_precision * (4 + (has_z ? 1 : 0)) + e_precision * (has_e ? 1 : 0); // Note f is an int
+ int num_digits = (
+ utilities::get_num_digits(current_arc_.end_point.x, xyz_precision) +
+ utilities::get_num_digits(current_arc_.end_point.y, xyz_precision) +
+ (has_z ? utilities::get_num_digits(current_arc_.end_point.z, xyz_precision) : 0) +
+ (has_e ? utilities::get_num_digits(e, e_precision) : 0) +
+ utilities::get_num_digits(i, xyz_precision) +
+ utilities::get_num_digits(j, xyz_precision) +
+ (has_f ? utilities::get_num_digits(f,0) : 0)
+ );
+ int num_minus_signs = (
+ (current_arc_.end_point.x < 0 ? 1 : 0) +
+ (current_arc_.end_point.y < 0 ? 1 : 0) +
+ (i < 0 ? 1 : 0) +
+ (j < 0 ? 1 : 0) +
+ (has_e && e < 0 ? 1 : 0) +
+ (has_z && current_arc_.end_point.z < 0 ? 1 : 0)
+ );
+
+ int num_parameters = 4 + (has_e ? 1 : 0) + (has_z ? 1: 0) + (has_f ? 1: 0);
+ // Return the length of the gcode.
+ int gcode_length = 2 + num_spaces + num_decimal_points + num_digits + num_minus_signs + num_decimals + num_parameters;
+
+ // Keep this around in case we have any future issues with the gcode length calculation
+ #ifdef Debug
+ std::string gcode = get_shape_gcode();
+ if (gcode.length() != gcode_length)
+ {
+ return 9999999;
+ }
+ #endif
+ return gcode_length;
+
+
+
+}
+
/*
* This is an older implementation using ostringstream. It is substantially slower.
* Keep this around in case there are problems with the custom dtos function
diff --git a/ArcWelder/segmented_arc.h b/ArcWelder/segmented_arc.h
index c67c071..2c7b240 100644
--- a/ArcWelder/segmented_arc.h
+++ b/ArcWelder/segmented_arc.h
@@ -42,14 +42,14 @@ public:
double mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT,
bool allow_3d_arcs = DEFAULT_ALLOW_3D_ARCS,
unsigned char default_xyz_precision = DEFAULT_XYZ_PRECISION,
- unsigned char default_e_precision = DEFAULT_E_PRECISION
+ unsigned char default_e_precision = DEFAULT_E_PRECISION,
+ int max_gcode_length = DEFAULT_MAX_GCODE_LENGTH
);
virtual ~segmented_arc();
virtual bool try_add_point(printer_point p);
virtual double get_shape_length();
- std::string get_shape_gcode_absolute(double e, double f);
- std::string get_shape_gcode_relative(double f);
-
+ std::string get_shape_gcode() const;
+ int get_shape_gcode_length();
virtual bool is_shape() const;
printer_point pop_front(double e_relative);
printer_point pop_back(double e_relative);
@@ -57,15 +57,16 @@ public:
int get_min_arc_segments() const;
double get_mm_per_arc_segment() const;
int get_num_firmware_compensations() const;
-
+ int get_num_gcode_length_exceptions() const;
private:
bool try_add_point_internal_(printer_point p);
- std::string get_shape_gcode_(bool has_e, double e, double f) const;
arc current_arc_;
double max_radius_mm_;
int min_arc_segments_;
double mm_per_arc_segment_;
int num_firmware_compensations_;
bool allow_3d_arcs_;
+ int max_gcode_length_;
+ int num_gcode_length_exceptions_;
};
diff --git a/ArcWelder/segmented_shape.cpp b/ArcWelder/segmented_shape.cpp
index 42aadcb..d50e5b5 100644
--- a/ArcWelder/segmented_shape.cpp
+++ b/ArcWelder/segmented_shape.cpp
@@ -83,7 +83,12 @@ point point::get_midpoint(point p1, point p2)
bool point::is_near_collinear(const point& p1, const point& p2, const point& p3, double tolerance)
{
- return fabs((p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x)) <= 1e-9;
+ return utilities::abs((p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x)) <= 1e-9;
+}
+
+double point::cartesian_distance(const point& p1, const point& p2)
+{
+ return utilities::get_cartesian_distance(p1.x, p1.y, p2.x, p2.y);
}
#pragma endregion Point Functions
@@ -119,7 +124,7 @@ bool segment::get_closest_perpendicular_point(const point& p1, const point& p2,
#pragma region Vector Functions
double vector::get_magnitude()
{
- return sqrt(x * x + y * y + z * z);
+ return utilities::sqrt(x * x + y * y + z * z);
}
double vector::cross_product_magnitude(vector v1, vector v2)
@@ -139,24 +144,21 @@ double vector::cross_product_magnitude(vector v1, vector v2)
// Users of this code must verify correctness for their application.
// dot product (3D) which allows vector operations in arguments
#define dot(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
-#define dotxy(u,v) ((u).x * (v).x + (u).y * (v).y)
-#define norm(v) sqrt(dot(v,v)) // norm = length of vector
-#define d(u,v) norm(u-v) // distance = norm of difference
+//#define dotxy(u,v) ((u).x * (v).x + (u).y * (v).y)
+//#define norm(v) utilities::sqrt(dot(v,v)) // norm = length of vector
+//#define d(u,v) norm(u-v) // distance = norm of difference
#pragma endregion Distance Calculation Source
#pragma region Circle Functions
-
bool circle::try_create_circle(const point& p1, const point& p2, const point& p3, const double max_radius, circle& new_circle)
{
if (point::is_near_collinear(p1,p2,p3, 0.001))
{
return false;
}
-
-
double x1 = p1.x;
double y1 = p1.y;
double x2 = p2.x;
@@ -196,22 +198,40 @@ bool circle::try_create_circle(const point& p1, const point& p2, const point& p3
bool circle::try_create_circle(const array_list<printer_point>& points, const double max_radius, const double resolution_mm, const double xyz_tolerance, bool allow_3d_arcs, circle& new_circle)
{
int count = points.count();
-
int middle_index = count / 2;
+ int end_index = count - 1;
+
- // The middle point will almost always produce the best arcs.
- if (circle::try_create_circle(points[0], points[middle_index], points[count - 1], max_radius, new_circle) && !new_circle.is_over_deviation(points, resolution_mm, xyz_tolerance, allow_3d_arcs))
+
+ if (circle::try_create_circle(points[0], points[middle_index], points[end_index], max_radius, new_circle) && !new_circle.is_over_deviation(points, resolution_mm, xyz_tolerance, allow_3d_arcs))
{
return true;
}
-
+
+ /*
+ // This could be a near complete circle. In that case, the endpoints might be too close together to generate an accurate circle with the
+ // precision we have to work with. Let's adjust our circle into thirds and test those points as a last ditch effort.
+ if (count > 5)
+ {
+ middle_index = count / 3;
+ end_index = middle_index + middle_index;
+ if (circle::try_create_circle(points[0], points[middle_index], points[end_index], max_radius, test_circle) && !test_circle.is_over_deviation(points, resolution_mm, xyz_tolerance, allow_3d_arcs))
+ {
+ new_circle = test_circle;
+ return true;
+ }
+ }
+ return false;
+ */
+
// Find the circle with the least deviation, if one exists.
// Note, this could possibly take a LONG time in the worst case, but it's a pretty unlikely.
// However, if the midpoint check doesn't pass, it's worth it to spend a bit more time
// finding the best fit for the circle (least squares deviation)
- circle test_circle;
+
double least_deviation;
bool found_circle=false;
+
for (int index = 1; index < count - 1; index++)
{
@@ -220,7 +240,7 @@ bool circle::try_create_circle(const array_list<printer_point>& points, const do
// We already checked this one, and it failed, continue.
continue;
}
-
+ circle test_circle;
double current_deviation;
if (circle::try_create_circle(points[0], points[index], points[count - 1], max_radius, test_circle) && test_circle.get_deviation_sum_squared(points, resolution_mm, xyz_tolerance, allow_3d_arcs, current_deviation))
{
@@ -234,11 +254,12 @@ bool circle::try_create_circle(const array_list<printer_point>& points, const do
}
}
return found_circle;
+
}
double circle::get_polar_radians(const point& p1) const
{
- double polar_radians = atan2(p1.y - center.y, p1.x - center.x);
+ double polar_radians = utilities::atan2(p1.y - center.y, p1.x - center.x);
if (polar_radians < 0)
polar_radians = (2.0 * PI_DOUBLE) + polar_radians;
return polar_radians;
@@ -269,7 +290,7 @@ bool circle::get_deviation_sum_squared(const array_list<printer_point>& points,
return false;
}
}
- double deviation = std::fabs(distance_from_center - radius);
+ double deviation = utilities::abs(distance_from_center - radius);
total_deviation += deviation * deviation;
if (deviation > resolution_mm)
{
@@ -284,7 +305,7 @@ bool circle::get_deviation_sum_squared(const array_list<printer_point>& points,
if (segment::get_closest_perpendicular_point(points[index], points[index + 1], center, point_to_test))
{
double distance = utilities::get_cartesian_distance(point_to_test.x, point_to_test.y, center.x, center.y);
- double deviation = std::fabs(distance - radius);
+ double deviation = utilities::abs(distance - radius);
total_deviation += deviation * deviation;
if (deviation > resolution_mm)
{
@@ -325,19 +346,20 @@ bool circle::is_over_deviation(const array_list<printer_point>& points, const do
return true;
}
}
- if (std::fabs(distance_from_center - radius) > resolution_mm)
+ if (utilities::abs(distance_from_center - radius) > resolution_mm)
{
return true;
}
}
// Check the point perpendicular from the segment to the circle's center, if any such point exists
+
if (segment::get_closest_perpendicular_point(current_point, points[index + 1], center, point_to_test))
{
double distance = utilities::get_cartesian_distance(point_to_test.x, point_to_test.y, center.x, center.y);
- if (std::fabs(distance - radius) > resolution_mm)
+ if (utilities::abs(distance - radius) > resolution_mm)
{
- return true;
+ return true;
}
}
}
@@ -358,9 +380,9 @@ double arc::get_j() const
bool arc::try_create_arc(
const circle& c,
- const point& start_point,
- const point& mid_point,
- const point& end_point,
+ const printer_point& start_point,
+ const printer_point& mid_point,
+ const printer_point& end_point,
arc& target_arc,
double approximate_length,
double resolution,
@@ -425,8 +447,8 @@ bool arc::try_create_arc(
}
}
// Calculate the percent difference of the original path
- double difference = (arc_length - approximate_length) / approximate_length;
- if (!utilities::is_zero(difference, path_tolerance_percent))
+ double path_difference_percent = utilities::get_percent_change(arc_length, approximate_length);
+ if (!utilities::is_zero(path_difference_percent, path_tolerance_percent))
{
// So it's possible our vector calculation above got the direction wrong.
// This can happen if there is a crazy arrangement of points
@@ -435,7 +457,7 @@ bool arc::try_create_arc(
// see if an arc moving in the opposite direction had the correct length.
// Find the rest of the angle across the circle
- double test_radians = std::fabs(angle_radians - 2 * PI_DOUBLE);
+ double test_radians = utilities::abs(angle_radians - 2 * PI_DOUBLE);
// Calculate the length of that arc
double test_arc_length = c.radius * test_radians;
if (allow_3d_arcs)
@@ -446,8 +468,8 @@ bool arc::try_create_arc(
test_arc_length = utilities::hypot(test_arc_length, end_point.z - start_point.z);
}
}
- difference = (test_arc_length - approximate_length) / approximate_length;
- if (!utilities::is_zero(difference, path_tolerance_percent))
+ path_difference_percent = utilities::get_percent_change(test_arc_length,approximate_length);
+ if (!utilities::is_zero(path_difference_percent, path_tolerance_percent))
{
return false;
}
@@ -499,7 +521,6 @@ bool arc::try_create_arc(
{
circle test_circle = (circle)target_arc;
-
if (!circle::try_create_circle(points, max_radius_mm, resolution_mm, xyz_tolerance, allow_3d_arcs, test_circle))
{
return false;
@@ -645,7 +666,7 @@ bool arc::ray_intersects_segment(const point rayOrigin, const point rayDirection
vector v3 = vector(-rayDirection.y, rayDirection.x, 0);
double dot = dot(v2, v3);
- if (std::fabs(dot) < 0.000001)
+ if (utilities::abs(dot) < 0.000001)
return false;
double t1 = vector::cross_product_magnitude(v2, v1) / dot;
@@ -705,7 +726,7 @@ void segmented_shape::set_xyz_precision(unsigned char precision)
void segmented_shape::set_xyz_tolerance_from_precision()
{
- xyz_tolerance_ = std::pow(10.0, -1.0 * static_cast<double>(xyz_precision_));
+ xyz_tolerance_ = utilities::pow(10, -1.0 * static_cast<double>(xyz_precision_));
}
void segmented_shape::reset_precision()
diff --git a/ArcWelder/segmented_shape.h b/ArcWelder/segmented_shape.h
index 8b014bc..d27a568 100644
--- a/ArcWelder/segmented_shape.h
+++ b/ArcWelder/segmented_shape.h
@@ -25,7 +25,6 @@
#pragma once
#include <string>
#include <limits>
-#define PI_DOUBLE 3.14159265358979323846264338327950288
#include <list>
#include "utilities.h"
@@ -36,7 +35,7 @@
#define DEFAULT_XYZ_TOLERANCE 0.001
#define DEFAULT_E_PRECISION 5
#define ARC_LENGTH_PERCENT_TOLERANCE_DEFAULT 0.05 // one percent
-
+#define DEFAULT_MAX_GCODE_LENGTH 0 // the maximum gcode length ( < 1 = unlimited)
struct point
{
public:
@@ -47,14 +46,19 @@ public:
double z;
static point get_midpoint(point p1, point p2);
static bool is_near_collinear(const point& p1, const point& p2, const point& p3, double percent_tolerance);
+ static double cartesian_distance(const point& p1, const point& p2);
};
struct printer_point : point
{
public:
- printer_point() :point(0, 0, 0), e_relative(0), distance(0) {}
- printer_point(double x, double y, double z, double e_relative, double distance) :point(x,y,z), e_relative(e_relative), distance(distance) {}
+ printer_point() :point(0, 0, 0), e_relative(0), distance(0), is_extruder_relative(false), e_offset(0), f(0) {}
+ printer_point(double x, double y, double z, double e_offset, double e_relative, double f, double distance, bool is_extruder_relative)
+ : point(x,y,z), e_offset(e_offset), e_relative(e_relative), f(f), distance(distance), is_extruder_relative(is_extruder_relative) {}
+ bool is_extruder_relative;
+ double e_offset;
double e_relative;
+ double f;
double distance;
};
@@ -99,7 +103,7 @@ struct vector : point
};
-#define DEFAULT_MAX_RADIUS_MM 1000000.0 // 1km
+#define DEFAULT_MAX_RADIUS_MM 9999.0 // 9.999m
struct circle {
circle() {
center.x = 0;
@@ -163,8 +167,8 @@ struct arc : circle
double polar_start_theta;
double polar_end_theta;
double max_deviation;
- point start_point;
- point end_point;
+ printer_point start_point;
+ printer_point end_point;
DirectionEnum direction;
double get_i() const;
double get_j() const;
@@ -184,9 +188,9 @@ struct arc : circle
private:
static bool try_create_arc(
const circle& c,
- const point& start_point,
- const point& mid_point,
- const point& end_point,
+ const printer_point& start_point,
+ const printer_point& mid_point,
+ const printer_point& end_point,
arc& target_arc,
double approximate_length,
double resolution = DEFAULT_RESOLUTION_MM,
diff --git a/ArcWelder/unwritten_command.h b/ArcWelder/unwritten_command.h
index 8f85a50..bfca84f 100644
--- a/ArcWelder/unwritten_command.h
+++ b/ArcWelder/unwritten_command.h
@@ -29,34 +29,27 @@ struct unwritten_command
{
unwritten_command() {
is_extruder_relative = false;
- e_relative = 0;
- offset_e = 0;
- extrusion_length = 0;
- is_g1_g2 = false;
+ length = 0;
+ is_g0_g1 = false;
+ is_g2_g3 = false;
+ is_travel = false;
+ is_extrusion = false;
+ is_retraction = false;
+ gcode = "";
+ comment = "";
}
- unwritten_command(parsed_command &cmd, bool is_relative, double command_length) {
- is_extruder_relative = is_relative;
- is_g1_g2 = cmd.command == "G0" || cmd.command == "G1";
- gcode = cmd.gcode;
- comment = cmd.comment;
- extrusion_length = command_length;
+ unwritten_command(parsed_command &cmd, bool is_relative, bool is_extrusion, bool is_retraction, bool is_travel, double command_length)
+ : is_extruder_relative(is_relative), is_extrusion(is_extrusion), is_retraction(is_retraction), is_travel(is_travel), is_g0_g1(cmd.command == "G0" || cmd.command == "G1"), is_g2_g3(cmd.command == "G2" || cmd.command == "G3"), gcode(cmd.gcode), comment(cmd.comment), length(command_length)
+ {
+
}
- /*
- unwritten_command(position* p, double command_length) {
-
- e_relative = p->get_current_extruder().e_relative;
- offset_e = p->get_current_extruder().get_offset_e();
- is_extruder_relative = p->is_extruder_relative;
- is_g1_g2 = p->command.command == "G0" || p->command.command == "G1";
- gcode = p->command.gcode;
- comment = p->command.comment;
- extrusion_length = command_length;
- } */
- bool is_g1_g2;
+ bool is_g0_g1;
+ bool is_g2_g3;
bool is_extruder_relative;
- double e_relative;
- double offset_e;
- double extrusion_length;
+ bool is_travel;
+ bool is_extrusion;
+ bool is_retraction;
+ double length;
std::string gcode;
std::string comment;