From d201085f565ed498d218ece1783a5c76bcfdb25d Mon Sep 17 00:00:00 2001 From: FormerLurker Date: Sat, 16 May 2020 17:17:15 -0500 Subject: Add max_radius_mm and switch to polar coordinates for arc generation. --- ArcWelder/arc_welder.cpp | 75 +++++++++++-------- ArcWelder/arc_welder.h | 28 ++++---- ArcWelder/segmented_arc.cpp | 83 +++++++++++----------- ArcWelder/segmented_arc.h | 12 ++-- ArcWelder/segmented_shape.cpp | 162 +++++++++++++++++++++++------------------- ArcWelder/segmented_shape.h | 36 +++++++--- 6 files changed, 221 insertions(+), 175 deletions(-) (limited to 'ArcWelder') diff --git a/ArcWelder/arc_welder.cpp b/ArcWelder/arc_welder.cpp index 0d3c021..81cc467 100644 --- a/ArcWelder/arc_welder.cpp +++ b/ArcWelder/arc_welder.cpp @@ -32,7 +32,7 @@ #include #include #include -arc_welder::arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, gcode_position_args args) : current_arc_(gcode_position_args_.position_buffer_size - 5, resolution_mm) +arc_welder::arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius, gcode_position_args args) : current_arc_(DEFAULT_MIN_SEGMENTS, gcode_position_args_.position_buffer_size - 5, resolution_mm, max_radius) { p_logger_ = log; debug_logging_enabled_ = false; @@ -70,14 +70,14 @@ arc_welder::arc_welder(std::string source_path, std::string target_path, logger p_source_position_ = new gcode_position(gcode_position_args_); } -arc_welder::arc_welder(std::string source_path, std::string target_path, logger* log, double resolution_mm, bool g90_g91_influences_extruder, int buffer_size) - : arc_welder(source_path, target_path, log, resolution_mm, arc_welder::get_args_(g90_g91_influences_extruder, buffer_size)) +arc_welder::arc_welder(std::string source_path, std::string target_path, logger* log, double resolution_mm, double max_radius, bool g90_g91_influences_extruder, int buffer_size) + : arc_welder(source_path, target_path, log, resolution_mm, max_radius, arc_welder::get_args_(g90_g91_influences_extruder, buffer_size)) { } -arc_welder::arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, bool g90_g91_influences_extruder, int buffer_size, progress_callback callback) - : arc_welder(source_path, target_path, log, resolution_mm, arc_welder::get_args_(g90_g91_influences_extruder, buffer_size)) +arc_welder::arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius, bool g90_g91_influences_extruder, int buffer_size, progress_callback callback) + : arc_welder(source_path, target_path, log, resolution_mm, max_radius, arc_welder::get_args_(g90_g91_influences_extruder, buffer_size)) { progress_callback_ = callback; } @@ -248,7 +248,7 @@ arc_welder_results arc_welder::process() process_gcode(cmd, 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 && (progress_callback_ != NULL || debug_logging_enabled_)) + if (has_gcode && (progress_callback_ != NULL || info_logging_enabled_)) { if ((lines_processed_ % read_lines_before_clock_check) == 0 && next_update_time < clock()) { @@ -256,20 +256,7 @@ arc_welder_results arc_welder::process() { p_logger_->log(logger_type_, VERBOSE, "Sending progress update."); } - 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.target_file_size = static_cast(output_file_.tellp()); - progress.source_file_size = static_cast(gcodeFile.tellg()); - // ToDo: tellg does not do what I think it does, but why? - long bytesRemaining = file_size_ - progress.source_file_size; - progress.percent_complete = static_cast(progress.source_file_size) / static_cast(file_size_) * 100.0; - progress.seconds_elapsed = get_time_elapsed(start_clock, clock()); - double bytesPerSecond = static_cast(progress.source_file_size) / progress.seconds_elapsed; - progress.seconds_remaining = bytesRemaining / bytesPerSecond; - continue_processing = on_progress_(progress); + continue_processing = on_progress_(get_progress_(static_cast(gcodeFile.tellg()), static_cast(start_clock))); next_update_time = get_next_update_time(); } } @@ -284,36 +271,62 @@ arc_welder_results arc_welder::process() write_unwritten_gcodes_to_file(); p_logger_->log(logger_type_, DEBUG, "Processing complete, closing source and target file."); + arc_welder_progress final_progress = get_progress_(static_cast(file_size_), static_cast(start_clock)); + if (progress_callback_ != NULL || info_logging_enabled_) + { + // Sending final progress update message + on_progress_(final_progress); + } + output_file_.close(); gcodeFile.close(); const clock_t end_clock = clock(); - const double total_seconds = static_cast(end_clock - start_clock) / CLOCKS_PER_SEC; + results.success = continue_processing; results.cancelled = !continue_processing; - results.progress.target_file_size = get_file_size(target_path_); - results.progress.seconds_elapsed = total_seconds; - results.progress.gcodes_processed = gcodes_processed_; - results.progress.lines_processed = lines_processed_; - results.progress.points_compressed = points_compressed_; - results.progress.arcs_created = arcs_created_; - results.progress.source_file_size = file_size_; + results.progress = final_progress; return results; } -bool arc_welder::on_progress_(arc_welder_progress progress) +bool arc_welder::on_progress_(const arc_welder_progress& progress) { if (progress_callback_ != NULL) { return progress_callback_(progress); } - else if (debug_logging_enabled_) + else if (info_logging_enabled_) { - p_logger_->log(logger_type_, DEBUG, progress.str()); + p_logger_->log(logger_type_, 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(output_file_.tellp()); + progress.source_file_size = file_size_; + long bytesRemaining = file_size_ - static_cast(source_file_position); + progress.percent_complete = static_cast(source_file_position) / static_cast(file_size_) * 100.0; + progress.seconds_elapsed = get_time_elapsed(start_clock, clock()); + double bytesPerSecond = static_cast(source_file_position) / progress.seconds_elapsed; + progress.seconds_remaining = bytesRemaining / bytesPerSecond; + + if (source_file_position > 0) { + progress.compression_ratio = (static_cast(source_file_position) / static_cast(progress.target_file_size)); + progress.compression_percent = (1.0 - (static_cast(progress.target_file_size) / static_cast(source_file_position))) * 100.0f; + } + + return progress; + +} + int arc_welder::process_gcode(parsed_command cmd, bool is_end) { // Update the position for the source gcode file diff --git a/ArcWelder/arc_welder.h b/ArcWelder/arc_welder.h index 208abf3..3e501af 100644 --- a/ArcWelder/arc_welder.h +++ b/ArcWelder/arc_welder.h @@ -37,6 +37,8 @@ #include "unwritten_command.h" #include "logger.h" +#define DEFAULT_G90_G91_INFLUENCES_EXTREUDER false + struct arc_welder_progress { arc_welder_progress() { percent_complete = 0.0; @@ -47,7 +49,10 @@ struct arc_welder_progress { points_compressed = 0; arcs_created = 0; source_file_size = 0; + source_file_position = 0; target_file_size = 0; + compression_ratio = 0; + compression_percent = 0; } double percent_complete; double seconds_elapsed; @@ -56,19 +61,15 @@ struct arc_welder_progress { int lines_processed; int points_compressed; int arcs_created; + double compression_ratio; + double compression_percent; + long source_file_position; long source_file_size; long target_file_size; - std::string str() { + std::string str() const { std::stringstream stream; stream << std::fixed << std::setprecision(2); - double compression_ratio = 0; - double target_compression_percent = 0; - - if (target_file_size > 0) { - compression_ratio = (static_cast(source_file_size) / static_cast(target_file_size)); - target_compression_percent = (1.0 - (static_cast(target_file_size) / static_cast(source_file_size))) * 100.0f; - } stream << percent_complete << "% complete in " << seconds_elapsed << " seconds with " << seconds_remaining << " seconds remaining."; stream << " Gcodes Processed: " << gcodes_processed; @@ -76,7 +77,7 @@ struct arc_welder_progress { stream << ", Points Compressed: " << points_compressed; stream << ", ArcsCreated: " << arcs_created; stream << ", Compression Ratio: " << compression_ratio; - stream << ", Size Reduction: " << target_compression_percent << "% "; + stream << ", Size Reduction: " << compression_percent << "% "; return stream.str(); } }; @@ -100,16 +101,17 @@ struct arc_welder_results { class arc_welder { public: - arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, gcode_position_args args); - arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, bool g90_g91_influences_extruder, int buffer_size); - arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, bool g90_g91_influences_extruder, int buffer_size, progress_callback callback); + arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius_mm, gcode_position_args args); + arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius_mm, bool g90_g91_influences_extruder, int buffer_size); + arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius_mm, bool g90_g91_influences_extruder, int buffer_size, progress_callback callback); void set_logger_type(int logger_type); virtual ~arc_welder(); arc_welder_results process(); double notification_period_seconds; protected: - virtual bool on_progress_(arc_welder_progress progress); + 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); diff --git a/ArcWelder/segmented_arc.cpp b/ArcWelder/segmented_arc.cpp index 2af6bc4..28145fd 100644 --- a/ArcWelder/segmented_arc.cpp +++ b/ArcWelder/segmented_arc.cpp @@ -32,16 +32,16 @@ #include #include -segmented_arc::segmented_arc() : segmented_shape() +segmented_arc::segmented_arc() : segmented_arc(DEFAULT_MIN_SEGMENTS, DEFAULT_MAX_SEGMENTS, DEFAULT_RESOLUTION_MM, DEFAULT_MAX_RADIUS_MM) { - min_segments_ = 3; - s_stream_ << std::fixed; } -segmented_arc::segmented_arc(int max_segments, double resolution_mm) : segmented_shape(3, max_segments, resolution_mm) +segmented_arc::segmented_arc(int min_segments, int max_segments, double resolution_mm, double max_radius_mm) : segmented_shape(min_segments, max_segments, resolution_mm) { - min_segments_ = 3; - s_stream_ << std::fixed; + gcode_buffer_[0] = '\0'; + + if (max_radius_mm > DEFAULT_MAX_RADIUS_MM) max_radius_mm_ = DEFAULT_MAX_RADIUS_MM; + else max_radius_mm_ = max_radius_mm; } segmented_arc::~segmented_arc() @@ -51,7 +51,7 @@ segmented_arc::~segmented_arc() point segmented_arc::pop_front(double e_relative) { e_relative_ -= e_relative; - if (points_.count() == min_segments_) + if (points_.count() == get_min_segments()) { set_is_shape(false); } @@ -61,7 +61,7 @@ point segmented_arc::pop_back(double e_relative) { e_relative_ -= e_relative; return points_.pop_back(); - if (points_.count() == min_segments_) + if (points_.count() == get_min_segments()) { set_is_shape(false); } @@ -115,9 +115,11 @@ bool segmented_arc::try_add_point(point p, double e_relative) return false; }*/ // Test - see what happens without a max segment length. } - if (points_.count() < min_segments_ - 1) + if (points_.count() < get_min_segments() - 1) { point_added = true; + points_.push_back(p); + original_shape_length_ += distance; } else { @@ -126,8 +128,7 @@ bool segmented_arc::try_add_point(point p, double e_relative) } if (point_added) { - points_.push_back(p); - original_shape_length_ += distance; + if (points_.count() > 1) { // Only add the relative distance to the second point on up. @@ -135,7 +136,7 @@ bool segmented_arc::try_add_point(point p, double e_relative) } //std::cout << " success - " << points_.count() << " points.\n"; } - else if (points_.count() < min_segments_ && points_.count() > 1) + else if (points_.count() < get_min_segments() && points_.count() > 1) { // If we haven't added a point, and we have exactly min_segments_, // pull off the initial arc point and try again @@ -155,7 +156,7 @@ bool segmented_arc::try_add_point(point p, double e_relative) bool segmented_arc::try_add_point_internal_(point p, double pd) { // If we don't have enough points (at least min_segments) return false - if (points_.count() < min_segments_ - 1) + if (points_.count() < get_min_segments() - 1) return false; // Create a test circle @@ -163,7 +164,7 @@ bool segmented_arc::try_add_point_internal_(point p, double pd) bool circle_created; // Find a point in the middle of our list for p2 int mid_point_index = ((points_.count() - 2) / 2)+1; - circle_created = circle::try_create_circle(points_[0], points_[mid_point_index], p, test_circle); + circle_created = circle::try_create_circle(points_[0], points_[mid_point_index], p, max_radius_mm_, test_circle); if (circle_created) { @@ -172,11 +173,20 @@ bool segmented_arc::try_add_point_internal_(point p, double pd) bool circle_fits_points; // the circle is new.. we have to test it now, which is expensive :( - circle_fits_points = does_circle_fit_points_(test_circle, p, pd); + points_.push_back(p); + double previous_shape_length = original_shape_length_; + original_shape_length_ += pd; + + circle_fits_points = does_circle_fit_points_(test_circle); if (circle_fits_points) { arc_circle_ = test_circle; } + else + { + points_.pop_back(); + original_shape_length_ = previous_shape_length; + } // Only set is_shape if it goes from false to true if (!is_shape()) @@ -190,7 +200,7 @@ bool segmented_arc::try_add_point_internal_(point p, double pd) } -bool segmented_arc::does_circle_fit_points_(circle c, point p, double pd) +bool segmented_arc::does_circle_fit_points_(const circle& c) { // We know point 1 must fit (we used it to create the circle). Check the other points // Note: We have not added the current point, but that's fine since it is guaranteed to fit too. @@ -229,36 +239,25 @@ bool segmented_arc::does_circle_fit_points_(circle c, point p, double pd) } } - - // Check the midpoint of the new point and the final point - point point_to_test; - if (segment::get_closest_perpendicular_point(points_[points_.count() - 1], p, c.center, point_to_test)) - { - distance_from_center = utilities::get_cartesian_distance(point_to_test.x, point_to_test.y, c.center.x, c.center.y); - difference_from_radius = abs(distance_from_center - c.radius); - // Test allowing more play for the midpoints. - if (utilities::greater_than(difference_from_radius, resolution_mm_)) - { - return false; - } - } // get the current arc and compare the total length to the original length arc a; - return try_get_arc_(c, p, pd, a ); + return try_get_arc_(c, a); } bool segmented_arc::try_get_arc(arc & target_arc) { - int mid_point_index = ((points_.count() - 2) / 2) + 1; - return arc::try_create_arc(arc_circle_, points_[0], points_[mid_point_index], points_[points_.count() - 1], original_shape_length_, resolution_mm_, target_arc); + //int mid_point_index = ((points_.count() - 2) / 2) + 1; + //return arc::try_create_arc(arc_circle_, points_[0], points_[mid_point_index], points_[points_.count() - 1], original_shape_length_, resolution_mm_, target_arc); + return arc::try_create_arc(arc_circle_ ,points_, original_shape_length_, resolution_mm_, target_arc); } -bool segmented_arc::try_get_arc_(circle& c, point endpoint, double additional_distance, arc &target_arc) +bool segmented_arc::try_get_arc_(const circle& c, arc &target_arc) { - int mid_point_index = ((points_.count() - 1) / 2) + 1; - return arc::try_create_arc(c, points_[0], points_[mid_point_index], endpoint, original_shape_length_ + additional_distance, resolution_mm_, target_arc); + //int mid_point_index = ((points_.count() - 1) / 2) + 1; + //return arc::try_create_arc(c, points_[0], points_[mid_point_index], endpoint, original_shape_length_ + additional_distance, resolution_mm_, target_arc); + return arc::try_create_arc(c, points_, original_shape_length_, resolution_mm_, target_arc); } std::string segmented_arc::get_shape_gcode_absolute(double e, double f) @@ -290,12 +289,12 @@ std::string segmented_arc::get_shape_gcode_(bool has_e, double e, double f) if (utilities::greater_than_or_equal(f, 1)) { // Add F param - snprintf(gcode_buffer_, sizeof(gcode_buffer_), "G2 X%.3f Y%.3f I%.3f J%.3f E%.5f F%.0f", c.end_point.x, c.end_point.y, i, j, e, f); + snprintf(gcode_buffer_, GCODE_CHAR_BUFFER_SIZE, "G2 X%.3f Y%.3f I%.3f J%.3f E%.5f F%.0f", c.end_point.x, c.end_point.y, i, j, e, f); } else { // No F param - snprintf(gcode_buffer_, sizeof(gcode_buffer_), "G2 X%.3f Y%.3f I%.3f J%.3f E%.5f", c.end_point.x, c.end_point.y, i, j, e); + snprintf(gcode_buffer_, GCODE_CHAR_BUFFER_SIZE, "G2 X%.3f Y%.3f I%.3f J%.3f E%.5f", c.end_point.x, c.end_point.y, i, j, e); } } else @@ -305,12 +304,12 @@ std::string segmented_arc::get_shape_gcode_(bool has_e, double e, double f) if (utilities::greater_than_or_equal(f, 1)) { // Add F param - snprintf(gcode_buffer_, sizeof(gcode_buffer_), "G2 X%.3f Y%.3f I%.3f J%.3f F%.0f", c.end_point.x, c.end_point.y, i, j, f); + snprintf(gcode_buffer_, GCODE_CHAR_BUFFER_SIZE, "G2 X%.3f Y%.3f I%.3f J%.3f F%.0f", c.end_point.x, c.end_point.y, i, j, f); } else { // No F param - snprintf(gcode_buffer_, sizeof(gcode_buffer_), "G2 X%.3f Y%.3f I%.3f J%.3f", c.end_point.x, c.end_point.y, i, j); + snprintf(gcode_buffer_, GCODE_CHAR_BUFFER_SIZE, "G2 X%.3f Y%.3f I%.3f J%.3f", c.end_point.x, c.end_point.y, i, j); } } } @@ -323,12 +322,12 @@ std::string segmented_arc::get_shape_gcode_(bool has_e, double e, double f) if (utilities::greater_than_or_equal(f, 1)) { // Add F param - snprintf(gcode_buffer_, sizeof(gcode_buffer_), "G3 X%.3f Y%.3f I%.3f J%.3f E%.5f F%.0f", c.end_point.x, c.end_point.y, i, j, e, f); + snprintf(gcode_buffer_, GCODE_CHAR_BUFFER_SIZE, "G3 X%.3f Y%.3f I%.3f J%.3f E%.5f F%.0f", c.end_point.x, c.end_point.y, i, j, e, f); } else { // No F param - snprintf(gcode_buffer_, sizeof(gcode_buffer_), "G3 X%.3f Y%.3f I%.3f J%.3f E%.5f", c.end_point.x, c.end_point.y, i, j, e); + snprintf(gcode_buffer_, GCODE_CHAR_BUFFER_SIZE, "G3 X%.3f Y%.3f I%.3f J%.3f E%.5f", c.end_point.x, c.end_point.y, i, j, e); } } else @@ -338,7 +337,7 @@ std::string segmented_arc::get_shape_gcode_(bool has_e, double e, double f) if (utilities::greater_than_or_equal(f, 1)) { // Add F param - snprintf(gcode_buffer_, sizeof(gcode_buffer_), "G3 X%.3f Y%.3f I%.3f J%.3f F%.0f", c.end_point.x, c.end_point.y, i, j, f); + snprintf(gcode_buffer_, GCODE_CHAR_BUFFER_SIZE, "G3 X%.3f Y%.3f I%.3f J%.3f F%.0f", c.end_point.x, c.end_point.y, i, j, f); } else { diff --git a/ArcWelder/segmented_arc.h b/ArcWelder/segmented_arc.h index 416fd3e..4a19b5c 100644 --- a/ArcWelder/segmented_arc.h +++ b/ArcWelder/segmented_arc.h @@ -29,12 +29,13 @@ #include #define GCODE_CHAR_BUFFER_SIZE 100 +#define DEFAULT_MAX_RADIUS_MM 1000000.0 // 1km class segmented_arc : public segmented_shape { public: segmented_arc(); - segmented_arc(int max_segments, double resolution_mm); + segmented_arc(int min_segments, int max_segments, double resolution_mm, double max_radius); virtual ~segmented_arc(); virtual bool try_add_point(point p, double e_relative); std::string get_shape_gcode_absolute(double e, double f); @@ -47,14 +48,13 @@ public: // static gcode buffer private: - char gcode_buffer_[GCODE_CHAR_BUFFER_SIZE]; + char gcode_buffer_[GCODE_CHAR_BUFFER_SIZE + 1]; bool try_add_point_internal_(point p, double pd); - bool does_circle_fit_points_(circle c, point p, double additional_distance); - bool try_get_arc_(circle& c, point endpoint, double additional_distance, arc & target_arc); + bool does_circle_fit_points_(const circle& c); + bool try_get_arc_(const circle& c, arc& target_arc); std::string get_shape_gcode_(bool has_e, double e, double f); - int min_segments_; circle arc_circle_; int test_count_ = 0; - std::ostringstream s_stream_; + double max_radius_mm_; }; diff --git a/ArcWelder/segmented_shape.cpp b/ArcWelder/segmented_shape.cpp index 21ca2b1..9cf0830 100644 --- a/ArcWelder/segmented_shape.cpp +++ b/ArcWelder/segmented_shape.cpp @@ -54,6 +54,14 @@ vector operator -(point& lhs, point& rhs) { ); } +vector operator -(const point& lhs, const point& rhs) { + return vector( + lhs.x - rhs.x, + lhs.y - rhs.y, + lhs.z - rhs.z + ); +} + vector operator *(vector lhs, const double& rhs) { return vector( lhs.x*rhs, @@ -88,7 +96,7 @@ bool segment::get_closest_perpendicular_point(point p1, point p2, point c, point double t = num / denom; // We're considering this a failure if t == 0 or t==1 within our tolerance. In that case we hit the endpoint, which is OK. - if (utilities::less_than_or_equal(t, 0, CIRCLE_FLOATING_POINT_TOLERANCE) || utilities::greater_than_or_equal(t, 1, CIRCLE_FLOATING_POINT_TOLERANCE)) + if (utilities::less_than_or_equal(t, 0, CIRCLE_GENERATION_A_ZERO_TOLERANCE) || utilities::greater_than_or_equal(t, 1, CIRCLE_GENERATION_A_ZERO_TOLERANCE)) return false; d.x = p1.x + t * (p2.x - p1.x); @@ -151,10 +159,10 @@ bool circle::is_point_on_circle(point p, double resolution_mm) { // get the difference between the point and the circle's center. double difference = std::abs(utilities::get_cartesian_distance(p.x, p.y, center.x, center.y) - radius); - return utilities::less_than(difference, resolution_mm, CIRCLE_FLOATING_POINT_TOLERANCE); + return utilities::less_than(difference, resolution_mm, CIRCLE_GENERATION_A_ZERO_TOLERANCE); } -bool circle::try_create_circle(point p1, point p2, point p3, circle& new_circle) +bool circle::try_create_circle(point p1, point p2, point p3, double max_radius, circle& new_circle) { double x1 = p1.x; double y1 = p1.y; @@ -165,12 +173,17 @@ bool circle::try_create_circle(point p1, point p2, point p3, circle& new_circle) double a = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2; - if (utilities::is_zero(a, CIRCLE_FLOATING_POINT_TOLERANCE)) + if (utilities::is_zero(a, CIRCLE_GENERATION_A_ZERO_TOLERANCE)) { +#if _DEBUG + if (!utilities::is_zero(a, 0.000001)) + { + std::cout << "This is an interesting point. Colinear"; + } +#endif return false; } - double b = (x1 * x1 + y1 * y1) * (y3 - y2) + (x2 * x2 + y2 * y2) * (y1 - y3) + (x3 * x3 + y3 * y3) * (y2 - y1); @@ -182,20 +195,31 @@ bool circle::try_create_circle(point p1, point p2, point p3, circle& new_circle) double x = -b / (2.0 * a); double y = -c / (2.0 * a); + double radius = utilities::get_cartesian_distance(x, y, x1, y1); + if (radius > max_radius) + return false; new_circle.center.x = x; new_circle.center.y = y; new_circle.center.z = p1.z; - new_circle.radius = utilities::get_cartesian_distance(x, y, x1, y1); + new_circle.radius = radius; return true; } -double circle::get_radians(point p1, point p2) +double circle::get_radians(const point& p1, const point& p2) const { double distance_sq = pow(utilities::get_cartesian_distance(p1.x, p1.y, p2.x, p2.y), 2.0); double two_r_sq = 2.0 * radius * radius; return acos((two_r_sq - distance_sq) / two_r_sq); } -point circle::get_closest_point(point p) +double circle::get_polar_radians(const point& p1) const +{ + double polar_radians = atan2(p1.y - center.y, p1.x - center.x); + if (polar_radians < 0) + polar_radians = (2.0 * PI_DOUBLE) + polar_radians; + return polar_radians; +} + +point circle::get_closest_point(const point& p) const { vector v = p - center; double mag = v.get_magnitude(); @@ -207,112 +231,102 @@ point circle::get_closest_point(point p) #pragma endregion Circle Functions #pragma region Arc Functions -bool arc::try_create_arc(circle c, point start_point, point mid_point, point end_point, double approximate_length, double resolution, arc& target_arc) +bool arc::try_create_arc(const circle& c, const point& start_point, const point& mid_point, const point& end_point, double approximate_length, double resolution, arc& target_arc) { point p1 = c.get_closest_point(start_point); point p2 = c.get_closest_point(mid_point); point p3 = c.get_closest_point(end_point); - // Get the radians between p1 and p2 (short angle) + /*// Get the radians between p1 and p2 (short angle) double p1_p2_rad = c.get_radians(p1, p2); double p2_p3_rad = c.get_radians(p2, p3); double p3_p1_rad = c.get_radians(p3, p1); + */ - bool found_angle = false; + double polar_start_theta = c.get_polar_radians(p1); + double polar_mid_theta = c.get_polar_radians(p2); + double polar_end_theta = c.get_polar_radians(p3); + + // variable to hold radians double angle_radians = 0; - double angle_1, angle_2; - if (utilities::is_equal(p1_p2_rad + p2_p3_rad + p3_p1_rad, 2 * PI_DOUBLE, CIRCLE_FLOATING_POINT_TOLERANCE)) + int direction = 0; // 1 = counter clockwise, 2 = clockwise, 3 = unknown. + // Determine the direction of the arc + if (polar_end_theta > polar_start_theta) { - found_angle = true; - angle_1 = p1_p2_rad; - angle_2 = p2_p3_rad; - } - else if (utilities::is_equal(p1_p2_rad + p2_p3_rad + (2 * PI_DOUBLE - p3_p1_rad), 2 * PI_DOUBLE, CIRCLE_FLOATING_POINT_TOLERANCE)) - { - found_angle = true; - angle_1 = p2_p3_rad; - angle_2 = p1_p2_rad; + if (polar_start_theta < polar_mid_theta && polar_mid_theta < polar_end_theta) { + direction = 1; + angle_radians = polar_end_theta - polar_start_theta; + } + else if ( + (0.0 <= polar_mid_theta && polar_mid_theta < polar_start_theta) || + (polar_end_theta < polar_mid_theta && polar_mid_theta < (2.0 * PI_DOUBLE)) + ) + { + direction = 2; + angle_radians = polar_start_theta + ((2.0 * PI_DOUBLE) - polar_end_theta); + } } - else + else if (polar_start_theta > polar_end_theta) { - double p1_p2_rad_lg = (2 * PI_DOUBLE - p1_p2_rad); - if (utilities::is_equal(p1_p2_rad_lg + p2_p3_rad + p3_p1_rad, 2 * PI_DOUBLE, CIRCLE_FLOATING_POINT_TOLERANCE)) + if ( + (polar_start_theta < polar_mid_theta && polar_mid_theta < (2.0 * PI_DOUBLE)) || + (0.0 < polar_mid_theta && polar_mid_theta < polar_end_theta) + ) { - found_angle = true; - angle_1 = p1_p2_rad_lg; - angle_2 = p2_p3_rad; + direction = 1; + angle_radians = polar_end_theta + ((2.0 * PI_DOUBLE) - polar_start_theta); } - else + else if (polar_end_theta < polar_mid_theta && polar_mid_theta < polar_start_theta) { - double p2_p3_rad_lg = (2 * PI_DOUBLE - p2_p3_rad); - if (utilities::is_equal(p1_p2_rad + p2_p3_rad_lg + p3_p1_rad, 2 * PI_DOUBLE, CIRCLE_FLOATING_POINT_TOLERANCE)) - { - found_angle = true; - angle_1 = p1_p2_rad; - angle_2 = p2_p3_rad_lg; - } + direction = 2; + angle_radians = polar_start_theta - polar_end_theta; } } - if (!found_angle) - return false; // No angle could be found, exit. - angle_radians = angle_1 + angle_2; - double length = angle_radians * c.radius; - if (!utilities::is_equal(length, approximate_length, resolution)) - return false; - - // Very small angles can't be relied upon to calculate the sign of the arc (clockwise vs anticlockwise) - if (angle_radians < MIN_ALLOWED_ARC_THETA) - { - return false; - } - // Calculate the sign of the angle. This should be accurate now that we have filtered out small angles and exited due to lengh mismatches - vector v1 = p1 - p2; - vector v2 = p3 - p2; - // Try to make a reasonable guess about the angle's direction. This works well unless the the angle is very small - double magnitude1 = vector::cross_product_magnitude(v1, v2); - // We can't use our utility compare (utility::greater_that) here, else we will lose - // very important resolution information - bool is_clockwise = false; - - if (magnitude1 > 0.0) - { - is_clockwise = true; - } - // If the calculated length isn't within the resolution, exit - if (is_clockwise) - angle_radians *= -1.0f; + if (direction == 0) return false; + double arc_length = c.radius * angle_radians; + if (!utilities::is_equal(arc_length, approximate_length, resolution)) + return false; + + if(direction == 2) + angle_radians *= -1.0; + target_arc.center.x = c.center.x; target_arc.center.y = c.center.y; target_arc.center.z = c.center.z; target_arc.radius = c.radius; target_arc.start_point = start_point; target_arc.end_point = end_point; - target_arc.length = length; + target_arc.length = arc_length; target_arc.angle_radians = angle_radians; + target_arc.polar_start_theta = polar_start_theta; + target_arc.polar_end_theta = polar_end_theta; return true; } +bool arc::try_create_arc(const circle& c, const array_list& points, double approximate_length, double resolution, arc& target_arc) +{ + int mid_point_index = ((points.count() - 2) / 2) + 1; + return arc::try_create_arc(c, points[0], points[mid_point_index], points[points.count() - 1], approximate_length, resolution, target_arc); +} #pragma endregion -segmented_shape::segmented_shape() : points_(50) +segmented_shape::segmented_shape() : segmented_shape(DEFAULT_MIN_SEGMENTS, DEFAULT_MAX_SEGMENTS, DEFAULT_RESOLUTION_MM ) { - max_segments_ = 50; - resolution_mm_ = 0.0250; - e_relative_ = 0; - is_shape_ = false; - min_segments_ = 3; - original_shape_length_ = 0; - is_extruding_ = true; } + segmented_shape::segmented_shape(int min_segments, int max_segments, double resolution_mm) : points_(max_segments) { + max_segments_ = max_segments; resolution_mm_ = resolution_mm / 2.0; // divide by 2 because it is + or - 1/2 of the desired resolution. e_relative_ = 0; is_shape_ = false; - min_segments_ = min_segments; + // min segments can never be lower than 3 (the default) else there could be no compression. + if (min_segments < DEFAULT_MIN_SEGMENTS) min_segments_ = DEFAULT_MIN_SEGMENTS; + else min_segments_ = min_segments; + original_shape_length_ = 0; is_extruding_ = true; } diff --git a/ArcWelder/segmented_shape.h b/ArcWelder/segmented_shape.h index 5a9b64c..53a30ee 100644 --- a/ArcWelder/segmented_shape.h +++ b/ArcWelder/segmented_shape.h @@ -26,14 +26,23 @@ #include #include #define PI_DOUBLE 3.14159265358979323846 -#define CIRCLE_FLOATING_POINT_TOLERANCE 0.0000000001 +//#define CIRCLE_GENERATION_A_ZERO_TOLERANCE 0.0000000001 // fail +//#define CIRCLE_GENERATION_A_ZERO_TOLERANCE 0.001 // pass +//#define CIRCLE_GENERATION_A_ZERO_TOLERANCE 0.0001 // pass +#define CIRCLE_GENERATION_A_ZERO_TOLERANCE 0.00001 // PASS +//#define CIRCLE_GENERATION_A_ZERO_TOLERANCE 0.000001 // pass +//#define CIRCLE_GENERATION_A_ZERO_TOLERANCE 0.0000001 // fail +//#define CIRCLE_GENERATION_A_ZERO_TOLERANCE 0.0000005 // fail +//#define CIRCLE_GENERATION_A_ZERO_TOLERANCE 0.00000075 // fail +//#define CIRCLE_GENERATION_A_ZERO_TOLERANCE 0.000000875 // fail + + #include #include "utilities.h" #include "array_list.h" // The minimum theta value allowed between any two arc in order for an arc to be // created. This prevents sign calculation issues for very small values of theta -#define MIN_ALLOWED_ARC_THETA 0.0001f // Safe value for full theta //#define MIN_ALLOWED_ARC_THETA 0.0000046875f // Lowest discovered value for full theta struct point @@ -117,11 +126,13 @@ struct circle { double radius; bool is_point_on_circle(point p, double resolution_mm); - static bool try_create_circle(point p1, point p2, point p3, circle& new_circle); + static bool try_create_circle(point p1, point p2, point p3, double max_radius, circle& new_circle); - double get_radians(point p1, point p2); + double get_radians(const point& p1, const point& p2) const; - point get_closest_point(point p); + double get_polar_radians(const point& p1) const; + + point get_closest_point(const point& p) const; }; struct arc : circle @@ -140,19 +151,27 @@ struct arc : circle end_point.y = 0; end_point.z = 0; is_arc = false; + polar_start_theta = 0; + polar_end_theta = 0; } bool is_arc; double length; double angle_radians; + double polar_start_theta; + double polar_end_theta; point start_point; point end_point; - static bool try_create_arc(circle c, point start_point, point mid_point, point end_point, double approximate_length, double resolution, arc& target_arc); - + bool is_point_in_arc_slice(const point& p); + static bool try_create_arc(const circle& c, const point& start_point, const point& mid_point, const point& end_point, double approximate_length, double resolution, arc& target_arc); + static bool try_create_arc(const circle& c, const array_list& points, double approximate_length, double resolution, arc& target_arc); }; double distance_from_segment(segment s, point p); +#define DEFAULT_MIN_SEGMENTS 3 +#define DEFAULT_MAX_SEGMENTS 50 +#define DEFAULT_RESOLUTION_MM 0.05 class segmented_shape { public: @@ -179,14 +198,13 @@ public: protected: array_list points_; void set_is_shape(bool value); - int min_segments_; double original_shape_length_; double e_relative_; bool is_extruding_; double resolution_mm_; bool is_shape_; private: - + int min_segments_; int max_segments_; }; -- cgit v1.2.3