@@ -535,9 +535,14 @@ double findMaxBlendRegion9(TC_STRUCT const * const prev_tc,
535535 return 0.0 ;
536536 }
537537
538- /* Per-segment usable length */
539- double l_prev = fmin (prev_tc->target , prev_tc->nominal_length / 2.0 );
540- double l_tc = fmin (tc->target , tc->nominal_length / 2.0 );
538+ /* Per-segment usable length.
539+ * Each blend can claim up to BLEND9_MAX_SEGMENT_USE of the original
540+ * segment, so two blends on the same segment leave a guaranteed
541+ * remnant (≥10% at default 0.45). Unit-free, works in mm or inches. */
542+ double l_prev = fmin (prev_tc->target ,
543+ prev_tc->nominal_length * BLEND9_MAX_SEGMENT_USE);
544+ double l_tc = fmin (tc->target ,
545+ tc->nominal_length * BLEND9_MAX_SEGMENT_USE);
541546
542547 /* For circular arcs, limit to 60 degrees */
543548 if (prev_tc->motion_type == TC_CIRCULAR) {
@@ -706,6 +711,99 @@ int findBlendParameters9(BlendBoundary9 const * const boundary,
706711 return TP_ERR_OK;
707712}
708713
714+ /* *
715+ * Golden section search for optimal alpha (control point distance).
716+ *
717+ * bezier9AccLimit(alpha) = min(v_curvature, v_dkds) is unimodal in alpha:
718+ * - Small alpha → steep curvature ramp at endpoints → high dκ/ds → low v_dkds
719+ * - Large alpha → sharp peak curvature at midpoint → high κ_max → low v_curvature
720+ * The maximum is where the two limits cross.
721+ *
722+ * Returns the best velocity limit, stores the winning Bezier9 and alpha.
723+ */
724+ static double findOptimalAlpha9 (double Rb,
725+ BlendBoundary9 const * const boundary,
726+ double a_max,
727+ double j_max,
728+ double v_goal,
729+ Bezier9 * const best_bezier,
730+ double * const best_alpha)
731+ {
732+ double a_lo = Rb * BLEND9_ALPHA_MIN_RATIO;
733+ double a_hi = Rb * BLEND9_ALPHA_MAX_RATIO;
734+
735+ double gr = BEZIER9_GOLDEN_RATIO;
736+ double a1 = a_hi - gr * (a_hi - a_lo);
737+ double a2 = a_lo + gr * (a_hi - a_lo);
738+
739+ /* Evaluate at initial probe points */
740+ Bezier9 trial1, trial2;
741+ double v1 = 0.0 , v2 = 0.0 ;
742+
743+ if (bezier9Init (&trial1, &boundary->P_start , &boundary->P_end ,
744+ &boundary->u_start_xyz , &boundary->u_end_xyz ,
745+ &boundary->u_start_abc , &boundary->u_end_abc ,
746+ &boundary->u_start_uvw , &boundary->u_end_uvw ,
747+ a1) == TP_ERR_OK) {
748+ v1 = bezier9AccLimit (&trial1, v_goal, a_max, j_max);
749+ }
750+
751+ if (bezier9Init (&trial2, &boundary->P_start , &boundary->P_end ,
752+ &boundary->u_start_xyz , &boundary->u_end_xyz ,
753+ &boundary->u_start_abc , &boundary->u_end_abc ,
754+ &boundary->u_start_uvw , &boundary->u_end_uvw ,
755+ a2) == TP_ERR_OK) {
756+ v2 = bezier9AccLimit (&trial2, v_goal, a_max, j_max);
757+ }
758+
759+ for (int i = 0 ; i < BLEND9_ALPHA_SEARCH_ITERS; i++) {
760+ if (v1 < v2) {
761+ /* Maximum is in [a1, a_hi] */
762+ a_lo = a1;
763+ a1 = a2;
764+ v1 = v2;
765+ trial1 = trial2;
766+ a2 = a_lo + gr * (a_hi - a_lo);
767+ if (bezier9Init (&trial2, &boundary->P_start , &boundary->P_end ,
768+ &boundary->u_start_xyz , &boundary->u_end_xyz ,
769+ &boundary->u_start_abc , &boundary->u_end_abc ,
770+ &boundary->u_start_uvw , &boundary->u_end_uvw ,
771+ a2) == TP_ERR_OK) {
772+ v2 = bezier9AccLimit (&trial2, v_goal, a_max, j_max);
773+ } else {
774+ v2 = 0.0 ;
775+ }
776+ } else {
777+ /* Maximum is in [a_lo, a2] */
778+ a_hi = a2;
779+ a2 = a1;
780+ v2 = v1;
781+ trial2 = trial1;
782+ a1 = a_hi - gr * (a_hi - a_lo);
783+ if (bezier9Init (&trial1, &boundary->P_start , &boundary->P_end ,
784+ &boundary->u_start_xyz , &boundary->u_end_xyz ,
785+ &boundary->u_start_abc , &boundary->u_end_abc ,
786+ &boundary->u_start_uvw , &boundary->u_end_uvw ,
787+ a1) == TP_ERR_OK) {
788+ v1 = bezier9AccLimit (&trial1, v_goal, a_max, j_max);
789+ } else {
790+ v1 = 0.0 ;
791+ }
792+ }
793+ }
794+
795+ /* Pick the better of the two final probes */
796+ if (v1 >= v2) {
797+ *best_bezier = trial1;
798+ *best_alpha = a1;
799+ return v1;
800+ } else {
801+ *best_bezier = trial2;
802+ *best_alpha = a2;
803+ return v2;
804+ }
805+ }
806+
709807/* *
710808 * Binary search optimizer for blend region size.
711809 *
@@ -722,6 +820,7 @@ int optimizeBlendSize9(TC_STRUCT const * const prev_tc,
722820 double max_feed_scale,
723821 AxisBounds9 const * const vel_bounds,
724822 AxisBounds9 const * const acc_bounds,
823+ double j_max,
725824 BlendSolution9 * const result)
726825{
727826 if (!prev_tc || !tc || !result) {
@@ -757,6 +856,10 @@ int optimizeBlendSize9(TC_STRUCT const * const prev_tc,
757856 return res;
758857 }
759858
859+ /* Kinematic limits (constant across iterations) */
860+ double a_max_blend = fmin (prev_tc->maxaccel , tc->maxaccel );
861+ double j_max_blend = j_max;
862+
760863 /* Binary search between epsilon and Rb_max */
761864 double Rb_lo = TP_POS_EPSILON;
762865 double Rb_hi = Rb_max;
@@ -780,33 +883,20 @@ int optimizeBlendSize9(TC_STRUCT const * const prev_tc,
780883 continue ;
781884 }
782885
783- /* Construct trial Bezier9 */
784- double alpha = Rb * BLEND9_ALPHA_FACTOR;
886+ /* Find optimal alpha for this Rb via golden section search */
785887 Bezier9 trial;
786- res = bezier9Init (&trial,
787- &boundary.P_start ,
788- &boundary.P_end ,
789- &boundary.u_start_xyz ,
790- &boundary.u_end_xyz ,
791- &boundary.u_start_abc ,
792- &boundary.u_end_abc ,
793- &boundary.u_start_uvw ,
794- &boundary.u_end_uvw ,
795- alpha);
796- if (res != TP_ERR_OK) {
888+ double alpha;
889+ double v_limit = findOptimalAlpha9 (Rb, &boundary, a_max_blend,
890+ j_max_blend, params.v_goal ,
891+ &trial, &alpha);
892+ if (v_limit <= 0.0 ) {
797893 Rb_hi = Rb;
798894 continue ;
799895 }
800896
801897 /* Check deviation against tolerance */
802898 double deviation = bezier9Deviation (&trial, &boundary.intersection_point );
803899
804- /* Check velocity limit from curvature and curvature rate */
805- double a_max_blend = fmin (prev_tc->maxaccel , tc->maxaccel );
806- double j_max_blend = fmin (prev_tc->maxjerk , tc->maxjerk );
807- double v_limit = bezier9AccLimit (&trial, params.v_goal , a_max_blend,
808- j_max_blend);
809-
810900 tp_debug_print (" iter=%d Rb=%g alpha=%g dev=%g tol=%g v_limit=%g v_goal=%g len=%g\n " ,
811901 iter, Rb, alpha, deviation, tolerance, v_limit, params.v_goal ,
812902 bezier9Length (&trial));
@@ -858,25 +948,14 @@ int optimizeBlendSize9(TC_STRUCT const * const prev_tc,
858948 vel_bounds, acc_bounds, ¶ms);
859949 }
860950 if (res == TP_ERR_OK) {
861- double alpha = Rb * BLEND9_ALPHA_FACTOR;
862951 Bezier9 trial;
863- res = bezier9Init (&trial,
864- &boundary.P_start ,
865- &boundary.P_end ,
866- &boundary.u_start_xyz ,
867- &boundary.u_end_xyz ,
868- &boundary.u_start_abc ,
869- &boundary.u_end_abc ,
870- &boundary.u_start_uvw ,
871- &boundary.u_end_uvw ,
872- alpha);
873- if (res == TP_ERR_OK) {
952+ double alpha;
953+ double v_limit = findOptimalAlpha9 (Rb, &boundary, a_max_blend,
954+ j_max_blend, params.v_goal ,
955+ &trial, &alpha);
956+ if (v_limit > 0.0 ) {
874957 double deviation = bezier9Deviation (&trial, &boundary.intersection_point );
875958 if (deviation <= tolerance) {
876- double a_max_blend = fmin (prev_tc->maxaccel , tc->maxaccel );
877- double j_max_blend = fmin (prev_tc->maxjerk , tc->maxjerk );
878- double v_limit = bezier9AccLimit (&trial, params.v_goal ,
879- a_max_blend, j_max_blend);
880959 result->Rb = Rb;
881960 result->bezier = trial;
882961 result->boundary = boundary;
@@ -1043,7 +1122,9 @@ int trimSegment9(TC_STRUCT * const tc,
10431122 }
10441123
10451124 tc->target = new_target;
1046- tc->nominal_length = new_target;
1125+ /* Keep nominal_length as original untrimmed length so that
1126+ * findMaxBlendRegion9's "half the original segment" guard
1127+ * gives equal Rb budget to blends on both sides. */
10471128 break ;
10481129 }
10491130
@@ -1075,7 +1156,6 @@ int trimSegment9(TC_STRUCT * const tc,
10751156
10761157 /* Recompute target from new geometry */
10771158 tc->target = pmCircle9Target (&tc->coords .circle );
1078- tc->nominal_length = tc->target ;
10791159 break ;
10801160 }
10811161
@@ -1129,7 +1209,6 @@ int trimSegment9(TC_STRUCT * const tc,
11291209
11301210 /* Update arc target */
11311211 tc->target = new_angle * arc->radius + arc->line_length * ratio;
1132- tc->nominal_length = tc->target ;
11331212 break ;
11341213 }
11351214
0 commit comments