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:
Diffstat (limited to 'ArcWelderInverseProcessor/inverse_processor.cpp')
-rw-r--r--ArcWelderInverseProcessor/inverse_processor.cpp445
1 files changed, 0 insertions, 445 deletions
diff --git a/ArcWelderInverseProcessor/inverse_processor.cpp b/ArcWelderInverseProcessor/inverse_processor.cpp
deleted file mode 100644
index c2f0b4d..0000000
--- a/ArcWelderInverseProcessor/inverse_processor.cpp
+++ /dev/null
@@ -1,445 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Arc Welder: Inverse Processor Console Application
-//
-// Converts G2/G3(arc) commands back to G0/G1 commands. Intended to test firmware changes to improve arc support.
-// This reduces file size and the number of gcodes per second.
-//
-// Built using the 'Arc Welder: Anti Stutter' library
-//
-// Copyright(C) 2020 - Brad Hochgesang
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// This program is free software : you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published
-// by the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
-// GNU Affero General Public License for more details.
-//
-//
-// You can contact the author at the following email address:
-// FormerLurker@pm.me
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// This file includes portions from Marlin's motion_control.c file since it is intended to test some firmware modifications.
-// This file was included in the AntiStutter project for convenience, and will not be included within the final version.
-/*
- motion_control.c - high level interface for issuing motion commands
- Part of Grbl
-
- Copyright (c) 2009-2011 Simen Svale Skogsrud
- Copyright (c) 2011 Sungeun K. Jeon
- Copyright (c) 2020 Brad Hochgesang
-
- Grbl is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Grbl is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Grbl. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "inverse_processor.h"
-#include <cmath>
-
-//#include "Marlin.h"
-//#include "stepper.h"
-//#include "planner.h"
-
-inverse_processor::inverse_processor(std::string source_path, std::string target_path, bool g90_g91_influences_extruder, int buffer_size, ConfigurationStore cs)
-{
- source_path_ = source_path;
- target_path_ = target_path;
- p_source_position_ = new gcode_position(get_args_(g90_g91_influences_extruder, buffer_size));
- cs_ = cs;
- // ** Gloabal Variable Definition **
- // 20200417 - FormerLurker - Declare two globals and pre-calculate some values that will reduce the
- // amount of trig funcitons we need to call while printing. For the price of having two globals we
- // save one trig calc per G2/G3 for both MIN_ARC_SEGMENTS and MIN_MM_PER_ARC_SEGMENT. This is a good trade IMHO.
-
-
-#ifdef MIN_ARC_SEGMENTS
-// Determines the radius at which the transition from using MM_PER_ARC_SEGMENT to MIN_ARC_SEGMENTS
- arc_max_radius_threshold = MM_PER_ARC_SEGMENT / (2.0F * sin(M_PI / MIN_ARC_SEGMENTS));
-#endif
-/*
-#if defined(MIN_ARC_SEGMENTS) && defined(MIN_MM_PER_ARC_SEGMENT)
- // Determines the radius at which the transition from using MIN_ARC_SEGMENTS to MIN_MM_PER_ARC_SEGMENT.
- arc_min_radius_threshold = MIN_MM_PER_ARC_SEGMENT / (2.0F * sin(M_PI / MIN_ARC_SEGMENTS));
-#endif
-*/
-}
-
-gcode_position_args inverse_processor::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;
-}
-
-inverse_processor::~inverse_processor()
-{
- delete p_source_position_;
-}
-
-void inverse_processor::process()
-{
- // Create a stringstream we can use for messaging.
- std::stringstream stream;
-
- int read_lines_before_clock_check = 5000;
- //std::cout << "stabilization::process_file - Processing file.\r\n";
- stream << "Decompressing gcode file.";
- stream << "Source File: " << source_path_ << "\n";
- stream << "Target File: " << target_path_ << "\n";
- std::cout << stream.str();
- const clock_t start_clock = clock();
-
- // Create the source file read stream and target write stream
- std::ifstream gcode_file;
- gcode_file.open(source_path_.c_str());
- output_file_.open(target_path_.c_str());
- std::string line;
- int lines_with_no_commands = 0;
- gcode_file.sync_with_stdio(false);
- output_file_.sync_with_stdio(false);
- gcode_parser parser;
- int gcodes_processed = 0;
- if (gcode_file.is_open())
- {
- if (output_file_.is_open())
- {
- //stream.clear();
- //stream.str("");
- //stream << "Opened file for reading. File Size: " << file_size_ << "\n";
- //std::cout << stream.str();
- parsed_command cmd;
- // Communicate every second
- while (std::getline(gcode_file, line))
- {
- lines_processed_++;
-
- cmd.clear();
- parser.try_parse_gcode(line.c_str(), cmd);
- bool has_gcode = false;
- if (cmd.gcode.length() > 0)
- {
- has_gcode = true;
- gcodes_processed++;
- }
- else
- {
- lines_with_no_commands++;
- }
-
- p_source_position_->update(cmd, lines_processed_, gcodes_processed, -1);
-
- if (cmd.command == "G2" || cmd.command == "G3")
- {
- position* p_cur_pos = p_source_position_->get_current_position_ptr();
- position* p_pre_pos = p_source_position_->get_previous_position_ptr();
- float position[4];
- position[X_AXIS] = static_cast<float>(p_pre_pos->get_gcode_x());
- position[Y_AXIS] = static_cast<float>(p_pre_pos->get_gcode_y());
- position[Z_AXIS] = static_cast<float>(p_pre_pos->get_gcode_z());
- position[E_AXIS] = static_cast<float>(p_pre_pos->get_current_extruder().get_offset_e());
- float target[4];
- target[X_AXIS] = static_cast<float>(p_cur_pos->get_gcode_x());
- target[Y_AXIS] = static_cast<float>(p_cur_pos->get_gcode_y());
- target[Z_AXIS] = static_cast<float>(p_cur_pos->get_gcode_z());
- target[E_AXIS] = static_cast<float>(p_cur_pos->get_current_extruder().get_offset_e());
- float offset[2];
- offset[0] = 0.0;
- offset[1] = 0.0;
- for (unsigned int index = 0; index < cmd.parameters.size(); index++)
- {
- parsed_command_parameter p = cmd.parameters[index];
- if (p.name == "I")
- {
- offset[0] = static_cast<float>(p.double_value);
- }
- else if (p.name == "J")
- {
- offset[1] = static_cast<float>(p.double_value);
- }
- }
- float radius = hypot(offset[X_AXIS], offset[Y_AXIS]); // Compute arc radius for mc_arc
- uint8_t isclockwise = cmd.command == "G2" ? 1 : 0;
- output_relative_ = p_cur_pos->is_extruder_relative;
- mc_arc(position, target, offset, static_cast<float>(p_cur_pos->f), radius, isclockwise, 0);
- }
- else
- {
- output_file_ << line << "\n";
- }
-
- }
- output_file_.close();
- }
- else
- {
- std::cout << "Unable to open the output file for writing.\n";
- }
- std::cout << "Closing the input file.\n";
- gcode_file.close();
- }
- else
- {
- std::cout << "Unable to open the gcode file for processing.\n";
- }
-
- const clock_t end_clock = clock();
- const double total_seconds = (static_cast<double>(end_clock) - static_cast<double>(start_clock)) / CLOCKS_PER_SEC;
-
- stream.clear();
- stream.str("");
- stream << "Completed file processing\r\n";
- stream << "\tLines Processed : " << lines_processed_ << "\r\n";
- stream << "\tTotal Seconds : " << total_seconds << "\r\n";
- stream << "\tExtra Trig Count : " << trig_calc_count << "\r\n";
- stream << "\tTotal E Adjustment : " << total_e_adjustment << "\r\n";
- std::cout << stream.str();
-}
-
-// The arc is approximated by generating a huge number of tiny, linear segments. The length of each
-// segment is configured in settings.mm_per_arc_segment.
-void inverse_processor::mc_arc(float* position, float* target, float* offset, float feed_rate, float radius, uint8_t isclockwise, uint8_t extruder)
-{
- // Extract the position to reduce indexing at the cost of a few bytes of mem
- float p_x = position[X_AXIS];
- float p_y = position[Y_AXIS];
- float p_z = position[Z_AXIS];
- float p_e = position[E_AXIS];
-
- float t_x = target[X_AXIS];
- float t_y = target[Y_AXIS];
- float t_z = target[Z_AXIS];
- float t_e = target[E_AXIS];
-
- float r_axis_x = -offset[X_AXIS]; // Radius vector from center to current location
- float r_axis_y = -offset[Y_AXIS];
- float center_axis_x = p_x - r_axis_x;
- float center_axis_y = p_y - r_axis_y;
- float travel_z = t_z - p_z;
- float extruder_travel_total = t_e - p_e;
-
- float rt_x = t_x - center_axis_x;
- float rt_y = t_y - center_axis_y;
- // 20200419 - Add a variable that will be used to hold the arc segment length
- float mm_per_arc_segment = cs_.mm_per_arc_segment;
-
- // CCW angle between position and target from circle center. Only one atan2() trig computation required.
- float angular_travel_total = atan2(r_axis_x * rt_y - r_axis_y * rt_x, r_axis_x * rt_x + r_axis_y * rt_y);
- if (angular_travel_total < 0) { angular_travel_total += (float)(2 * M_PI); }
-
- bool check_mm_per_arc_segment_max = false;
- if (cs_.min_arc_segments > 0)
- {
- // 20200417 - FormerLurker - Implement MIN_ARC_SEGMENTS if it is defined - from Marlin 2.0 implementation
- // Do this before converting the angular travel for clockwise rotation
- mm_per_arc_segment = (float)(radius * ((2.0f * M_PI) / cs_.min_arc_segments));
- check_mm_per_arc_segment_max = true;
- }
-
- if (cs_.arc_segments_per_sec > 0)
- {
- // 20200417 - FormerLurker - Implement MIN_ARC_SEGMENTS if it is defined - from Marlin 2.0 implementation
- float mm_per_arc_segment_sec = (float)((feed_rate / 60.0f) * (1.0f / cs_.arc_segments_per_sec));
- if (mm_per_arc_segment_sec < mm_per_arc_segment)
- mm_per_arc_segment = mm_per_arc_segment_sec;
- check_mm_per_arc_segment_max = true;
- }
-
- if (cs_.min_mm_per_arc_segment > 0)
- {
- check_mm_per_arc_segment_max = true;
- // 20200417 - FormerLurker - Implement MIN_MM_PER_ARC_SEGMENT if it is defined
- // This prevents a very high number of segments from being generated for curves of a short radius
- if (mm_per_arc_segment < cs_.min_mm_per_arc_segment) mm_per_arc_segment = cs_.min_mm_per_arc_segment;
- }
-
- if (check_mm_per_arc_segment_max && mm_per_arc_segment > cs_.mm_per_arc_segment) mm_per_arc_segment = cs_.mm_per_arc_segment;
-
-
-
- // Adjust the angular travel if the direction is clockwise
- if (isclockwise) { angular_travel_total -= (float)(2 * M_PI); }
-
- //20141002:full circle for G03 did not work, e.g. G03 X80 Y80 I20 J0 F2000 is giving an Angle of zero so head is not moving
- //to compensate when start pos = target pos && angle is zero -> angle = 2Pi
- if (p_x == t_x && p_y == t_y && angular_travel_total == 0)
- {
- angular_travel_total += (float)(2 * M_PI);
- }
- //end fix G03
-
- // 20200417 - FormerLurker - rename millimeters_of_travel to millimeters_of_travel_arc to better describe what we are
- // calculating here
- float millimeters_of_travel_arc = hypot(angular_travel_total * radius, std::fabs(travel_z));
- if (millimeters_of_travel_arc < 0.001) { return; }
- // Calculate the total travel per segment
- // Calculate the number of arc segments
- uint16_t segments = static_cast<uint16_t>(ceil(millimeters_of_travel_arc / mm_per_arc_segment));
-
-
- // Calculate theta per segments and linear (z) travel per segment
- float theta_per_segment = angular_travel_total / segments;
- float linear_per_segment = travel_z / (segments);
- // Calculate the extrusion amount per segment
- float segment_extruder_travel = extruder_travel_total / (segments);
- /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
- and phi is the angle of rotation. Based on the solution approach by Jens Geisler.
- r_T = [cos(phi) -sin(phi);
- sin(phi) cos(phi] * r ;
-
- For arc generation, the center of the circle is the axis of rotation and the radius vector is
- defined from the circle center to the initial position. Each line segment is formed by successive
- vector rotations. This requires only two cos() and sin() computations to form the rotation
- matrix for the duration of the entire arc. Error may accumulate from numerical round-off, since
- all double numbers are single precision on the Arduino. (True double precision will not have
- round off issues for CNC applications.) Single precision error can accumulate to be greater than
- tool precision in some cases. Therefore, arc path correction is implemented.
-
- The small angle approximation was removed because of excessive errors for small circles (perhaps unique to
- 3d printing applications, causing significant path deviation and extrusion issues).
- Now there will be no corrections applied, but an accurate initial sin and cos will be calculated.
- This seems to work with a very high degree of accuracy and results in much simpler code.
-
- Finding a faster way to approximate sin, knowing that there can be substantial deviations from the true
- arc when using the previous approximation, would be beneficial.
- */
-
- // Don't bother calculating cot_T or sin_T if there is only 1 segment.
- if (segments > 1)
- {
- // Initialize the extruder axis
- float cos_T, sin_T, sin_Ti, cos_Ti;
- //float cos_T = cos(theta_per_segment);
- //float sin_T = sin(theta_per_segment);
- float sq_theta_per_segment = theta_per_segment * theta_per_segment;
- sin_T = theta_per_segment - sq_theta_per_segment * theta_per_segment / 6;
- cos_T = 1 - 0.5f * sq_theta_per_segment; // Small angle approximation
-
- //cos_T = 1 - 0.5 * theta_per_segment * theta_per_segment; // Small angle approximation
- //sin_T = theta_per_segment;
- float r_axisi;
- uint16_t i;
- int8_t count = 0;
- for (i = 1; i < segments; i++) { // Increment (segments-1)
-
- if (count < cs_.n_arc_correction) {
- // Apply vector rotation matrix
- r_axisi = r_axis_x * sin_T + r_axis_y * cos_T;
- r_axis_x = r_axis_x * cos_T - r_axis_y * sin_T;
- r_axis_y = r_axisi;
- count++;
- }
- else {
- // Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments.
- // Compute exact location by applying transformation matrix from initial radius vector(=-offset).
- cos_Ti = cos(i * theta_per_segment);
- sin_Ti = sin(i * theta_per_segment);
- r_axis_x = -offset[X_AXIS] * cos_Ti + offset[Y_AXIS] * sin_Ti;
- r_axis_y = -offset[X_AXIS] * sin_Ti - offset[Y_AXIS] * cos_Ti;
- count = 0;
-
- }
-
-
-
- // Update arc_target location
- p_x = center_axis_x + r_axis_x;
- p_y = center_axis_y + r_axis_y;
- p_z += linear_per_segment;
- p_e += segment_extruder_travel;
- // We can't clamp to the target because we are interpolating! We would need to update a position, clamp to it
- // after updating from calculated values.
- //clamp_to_software_endstops(position);
- plan_buffer_line(p_x, p_y, travel_z > 0, p_z, p_e, feed_rate, extruder);
- }
- }
- // Ensure last segment arrives at target location.
- // Here we could clamp, but why bother. We would need to update our current position, clamp to it
- //clamp_to_software_endstops(target);
- plan_buffer_line(t_x, t_y, travel_z> 0, t_z, t_e, feed_rate, extruder);
- position[X_AXIS] = t_x;
- position[Y_AXIS] = t_y;
- position[Z_AXIS] = t_z;
- position[E_AXIS] = t_e;
-}
-
-void inverse_processor::clamp_to_software_endstops(float* target)
-{
- // Do nothing, just added to keep mc_arc identical to the firmware version
- return;
-}
-
-void inverse_processor::plan_buffer_line(float x, float y, bool has_z, float z, const float& e, float feed_rate, uint8_t extruder, const float* gcode_target)
-{
- std::stringstream stream;
- stream << std::fixed;
-
- position * previous_pos = p_source_position_->get_previous_position_ptr();
- position* current_pos = p_source_position_->get_current_position_ptr();
-
- stream << "G1 X" << std::setprecision(3) << x << " Y" << y;
- if (has_z)
- {
- stream << " Z" << z;
- }
-
- double output_e = e;
- if (previous_pos->is_extruder_relative)
- {
- output_e = e - previous_pos->get_current_extruder().get_offset_e();
- }
-
- stream << std::setprecision(5) << " E" << output_e;
-
-
- if (feed_rate != previous_pos->f)
- {
- stream << std::setprecision(0) << " F" << feed_rate;
- }
-
- if (!current_pos->command.comment.empty())
- {
- stream << ";" << current_pos->command.comment;
- }
- stream << "\n";
- output_file_ << stream.str();
-}