Skip to content

Commit

Permalink
Rework arc handling
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkyhead committed Aug 21, 2021
1 parent cb40c3e commit 7d3b7d2
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 43 deletions.
25 changes: 14 additions & 11 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -2050,20 +2050,23 @@
//
// G2/G3 Arc Support
//
#define ARC_SUPPORT // Disable this feature to save ~3226 bytes
#define ARC_SUPPORT // Requires ~3226 bytes
#if ENABLED(ARC_SUPPORT)
#define MM_PER_ARC_SEGMENT 1 // (mm) Length (or minimum length) of each arc segment
//#define ARC_SEGMENTS_PER_R 1 // Max segment length, MM_PER = Min
#define MIN_ARC_SEGMENTS 24 // Minimum number of segments in a complete circle
//#define ARC_SEGMENTS_PER_SEC 50 // Use feedrate to choose segment length (with MM_PER_ARC_SEGMENT as the minimum)
#define N_ARC_CORRECTION 25 // Number of interpolated segments between corrections
//#define ARC_P_CIRCLES // Enable the 'P' parameter to specify complete circles
//#define CNC_WORKSPACE_PLANES // Allow G2/G3 to operate in XY, ZX, or YZ planes
//#define SF_ARC_FIX // Enable only if using SkeinForge with "Arc Point" fillet procedure
#define MIN_ARC_SEGMENT_MM 0.1 // (mm) Minimum length of each arc segment
#define MAX_ARC_SEGMENT_MM 1.0 // (mm) Maximum length of each arc segment
#define MIN_CIRCLE_SEGMENTS 24 // Minimum number of segments in a complete circle
//#define ARC_SEGMENTS_PER_SEC 50 // Use the feedrate to choose the segment length
#define N_ARC_CORRECTION 25 // Number of interpolated segments between corrections
//#define ARC_P_CIRCLES // Enable the 'P' parameter to specify complete circles
//#define SF_ARC_FIX // Enable only if using SkeinForge with "Arc Point" fillet procedure
#endif

// Support for G5 with XYZE destination and IJPQ offsets. Requires ~2666 bytes.
//#define BEZIER_CURVE_SUPPORT
// G5 Bézier Curve Support with XYZE destination and IJPQ offsets
//#define BEZIER_CURVE_SUPPORT // Requires ~2666 bytes

#if EITHER(ARC_SUPPORT, BEZIER_CURVE_SUPPORT)
//#define CNC_WORKSPACE_PLANES // Allow G2/G3/G5 to operate in XY, ZX, or YZ planes
#endif

/**
* Direct Stepping
Expand Down
72 changes: 40 additions & 32 deletions Marlin/src/gcode/motion/G2_G3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,18 @@
#undef N_ARC_CORRECTION
#define N_ARC_CORRECTION 1
#endif
#ifndef MIN_CIRCLE_SEGMENTS
#define MIN_CIRCLE_SEGMENTS 36 // 10° per segment
#endif
#if !defined(MAX_ARC_SEGMENT_MM) && defined(MIN_ARC_SEGMENT_MM)
#define MAX_ARC_SEGMENT_MM MIN_ARC_SEGMENT_MM
#elif !defined(MIN_ARC_SEGMENT_MM) && defined(MAX_ARC_SEGMENT_MM)
#define MIN_ARC_SEGMENT_MM MAX_ARC_SEGMENT_MM
#endif

/**
* Plan an arc in 2 dimensions, with optional linear motion in a 3rd dimension
*
* The arc is traced by generating many small linear segments, as configured by
* MM_PER_ARC_SEGMENT (Default 1mm). In the future we hope more slicers will include
* an option to generate G2/G3 arcs for curved surfaces, as this will allow faster
* boards to produce much smoother curved surfaces.
* Plan an arc in 2 dimensions, with linear motion in the other axes.
* The arc is traced with many small linear segments according to the configuration.
*/
void plan_arc(
const xyze_pos_t &cart, // Destination position
Expand Down Expand Up @@ -76,20 +80,18 @@ void plan_arc(
rt_Y = cart[q_axis] - center_Q
OPTARG(HAS_Z_AXIS, start_L = current_position[l_axis]);

#ifdef MIN_ARC_SEGMENTS
uint16_t min_segments = MIN_ARC_SEGMENTS;
#else
constexpr uint16_t min_segments = 1;
#endif

// Angle of rotation between position and target from the circle center.
float angular_travel, abs_angular_travel;

// Minimum number of segments in an arc move
uint16_t min_segments = 1;

// Do a full circle if starting and ending positions are "identical"
if (NEAR(current_position[p_axis], cart[p_axis]) && NEAR(current_position[q_axis], cart[q_axis])) {
// Preserve direction for circles
angular_travel = clockwise ? -RADIANS(360) : RADIANS(360);
abs_angular_travel = RADIANS(360);
min_segments = MIN_CIRCLE_SEGMENTS;
}
else {
// Calculate the angle
Expand All @@ -106,10 +108,9 @@ void plan_arc(

abs_angular_travel = ABS(angular_travel);

#ifdef MIN_ARC_SEGMENTS
min_segments = CEIL(min_segments * abs_angular_travel / RADIANS(360));
NOLESS(min_segments, 1U);
#endif
// Apply minimum segments to the arc
const float portion_of_circle = abs_angular_travel / RADIANS(360); // Portion of a complete circle (0 < N < 1)
min_segments = CEIL((MIN_CIRCLE_SEGMENTS) * portion_of_circle); // MinumSegments for the arc
}

#if HAS_Z_AXIS
Expand All @@ -119,7 +120,7 @@ void plan_arc(
float extruder_travel = cart.e - current_position.e;
#endif

// If circling around...
// If "P" specified circles, call plan_arc recursively then continue with the rest of the arc
if (TERN0(ARC_P_CIRCLES, circles)) {
const float total_angular = abs_angular_travel + circles * RADIANS(360), // Total rotation with all circles and remainder
part_per_circle = RADIANS(360) / total_angular; // Each circle's part of the total
Expand All @@ -141,30 +142,37 @@ void plan_arc(
TERN_(HAS_EXTRUDERS, extruder_travel = cart.e - current_position.e);
}

const float flat_mm = radius * abs_angular_travel,
mm_of_travel = TERN_(HAS_Z_AXIS, linear_travel ? HYPOT(flat_mm, linear_travel) :) flat_mm;
const float flat_mm = radius * abs_angular_travel, // Millimeters in the arc
mm_of_travel = TERN_(HAS_Z_AXIS, linear_travel ? HYPOT(flat_mm, linear_travel) :) flat_mm; // Real distance according to Pythagoras

// Return if the move is near zero
if (mm_of_travel < 0.001f) return;

// Feedrate for the move, scaled by the feedrate multiplier
const feedRate_t scaled_fr_mm_s = MMS_SCALED(feedrate_mm_s);

// Determine the segment length based on settings
const float seg_length = (
#ifdef ARC_SEGMENTS_PER_R
constrain(MM_PER_ARC_SEGMENT * radius, MM_PER_ARC_SEGMENT, ARC_SEGMENTS_PER_R)
#elif ARC_SEGMENTS_PER_SEC
_MAX(scaled_fr_mm_s * RECIPROCAL(ARC_SEGMENTS_PER_SEC), MM_PER_ARC_SEGMENT)
// Get the nominal segment length based on settings
float nominal_segment_mm = (
#if ARC_SEGMENTS_PER_SEC // Length based on segments per second and feedrate
constrain(scaled_fr_mm_s * RECIPROCAL(ARC_SEGMENTS_PER_SEC), MIN_ARC_SEGMENT_MM, MAX_ARC_SEGMENT_MM)
#else
MM_PER_ARC_SEGMENT
MAX_ARC_SEGMENT_MM // Length using the maximum segment size
#endif
);
// Divide total travel by segment length
uint16_t segments = FLOOR(mm_of_travel / seg_length);
NOLESS(segments, min_segments); // At least some segments

// Number of whole segments based on the nominal segment length
const float nominal_segments = _MAX(FLOOR(flat_mm / nominal_segment_mm), min_segments);

// A new segment length based on the required minimum
const float segment_mm = constrain(flat_mm / nominal_segments, MIN_ARC_SEGMENT_MM, MAX_ARC_SEGMENT_MM);

// The number of whole segments in the arc, ignoring the remainder
uint16_t segments = FLOOR(flat_mm / segment_mm);

// Are the segments now too few to reach the destination?
const float segmented_length = seg_length * segments;
const bool tooshort = segmented_length < mm_of_travel - 0.1f;
const float proportion = tooshort ? segmented_length / mm_of_travel : 1.0f;
const bool tooshort = segmented_length < flat_mm - 0.1f;
const float proportion = tooshort ? segmented_length / flat_mm : 1.0f;

/**
* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
Expand Down Expand Up @@ -216,7 +224,7 @@ void plan_arc(
TERN_(HAS_EXTRUDERS, raw.e = current_position.e);

#if ENABLED(SCARA_FEEDRATE_SCALING)
const float inv_duration = scaled_fr_mm_s * segments / mm_of_travel;
const float inv_duration = scaled_fr_mm_s / segment_mm;
#endif

millis_t next_idle_ms = millis() + 200UL;
Expand Down

0 comments on commit 7d3b7d2

Please sign in to comment.