@@ -291,6 +291,10 @@ int bezier9Init(Bezier9 * const b,
291291 PmCartesian const * const u_end_abc,
292292 PmCartesian const * const u_start_uvw,
293293 PmCartesian const * const u_end_uvw,
294+ double kappa_start,
295+ PmCartesian const * const n_start,
296+ double kappa_end,
297+ PmCartesian const * const n_end,
294298 double alpha)
295299{
296300 // Validate inputs
@@ -314,14 +318,34 @@ int bezier9Init(Bezier9 * const b,
314318 PmCartesian start_uvw = {start->u , start->v , start->w };
315319 PmCartesian end_uvw = {end->u , end->v , end->w };
316320
317- // Set quintic control points for each subspace (G2 continuity )
321+ // Set quintic control points for each subspace (zero-curvature baseline )
318322 set_quintic_control_points (b->P , &start_xyz, &end_xyz,
319323 u_start_xyz, u_end_xyz, alpha);
320324 set_quintic_control_points (b->A , &start_abc, &end_abc,
321325 u_start_abc, u_end_abc, alpha);
322326 set_quintic_control_points (b->U , &start_uvw, &end_uvw,
323327 u_start_uvw, u_end_uvw, alpha);
324328
329+ // C2 curvature matching (XYZ only): offset P[2] and P[3] in the
330+ // curvature normal direction to match adjacent segment curvature.
331+ //
332+ // For a quintic Bezier with P0,P1,P2 collinear along tangent u:
333+ // B'(0) = 5α·u, B''(0) = 20·δ·n (after offset)
334+ // κ(0) = |B'×B''| / |B'|³ = 4δ / (5α²)
335+ // Solving for target curvature: δ = 5α²·κ / 4
336+ if (kappa_start > BEZIER9_CURVATURE_EPSILON && n_start) {
337+ double delta = 5.0 * alpha * alpha * kappa_start / 4.0 ;
338+ b->P [2 ].x += delta * n_start->x ;
339+ b->P [2 ].y += delta * n_start->y ;
340+ b->P [2 ].z += delta * n_start->z ;
341+ }
342+ if (kappa_end > BEZIER9_CURVATURE_EPSILON && n_end) {
343+ double delta = 5.0 * alpha * alpha * kappa_end / 4.0 ;
344+ b->P [3 ].x += delta * n_end->x ;
345+ b->P [3 ].y += delta * n_end->y ;
346+ b->P [3 ].z += delta * n_end->z ;
347+ }
348+
325349 // Build arc-length parameterization table
326350 build_arc_length_table (b);
327351
@@ -614,3 +638,84 @@ double bezier9Deviation(Bezier9 const * const b,
614638
615639 return sqrt (dx * dx + dy * dy + dz * dz);
616640}
641+
642+ double bezier9MaxDeviation (Bezier9 const * const b,
643+ PmCartesian const * const intersection_point,
644+ int n_samples)
645+ {
646+ if (!b || !intersection_point || n_samples < 1 ) {
647+ return 0.0 ;
648+ }
649+
650+ double max_dev = 0.0 ;
651+ for (int i = 1 ; i <= n_samples; i++) {
652+ double t = (double )i / (double )(n_samples + 1 );
653+ PmCartesian pt;
654+ bezier5_eval (b->P , t, &pt);
655+ double dx = pt.x - intersection_point->x ;
656+ double dy = pt.y - intersection_point->y ;
657+ double dz = pt.z - intersection_point->z ;
658+ double dev = sqrt (dx * dx + dy * dy + dz * dz);
659+ if (dev > max_dev) max_dev = dev;
660+ }
661+ return max_dev;
662+ }
663+
664+ /* *
665+ * Point-to-line-segment distance in 3D.
666+ * Returns the minimum Euclidean distance from point p to the
667+ * line segment from a to b.
668+ */
669+ static double point_to_segment_dist (PmCartesian const * const p,
670+ PmCartesian const * const a,
671+ PmCartesian const * const b)
672+ {
673+ double abx = b->x - a->x ;
674+ double aby = b->y - a->y ;
675+ double abz = b->z - a->z ;
676+ double apx = p->x - a->x ;
677+ double apy = p->y - a->y ;
678+ double apz = p->z - a->z ;
679+
680+ double ab_sq = abx * abx + aby * aby + abz * abz;
681+ if (ab_sq < 1e-30 ) {
682+ /* Degenerate segment (a == b): distance to the point */
683+ return sqrt (apx * apx + apy * apy + apz * apz);
684+ }
685+
686+ double t = (apx * abx + apy * aby + apz * abz) / ab_sq;
687+ if (t < 0.0 ) t = 0.0 ;
688+ if (t > 1.0 ) t = 1.0 ;
689+
690+ double cx = a->x + t * abx - p->x ;
691+ double cy = a->y + t * aby - p->y ;
692+ double cz = a->z + t * abz - p->z ;
693+
694+ return sqrt (cx * cx + cy * cy + cz * cz);
695+ }
696+
697+ double bezier9PathDeviation (Bezier9 const * const b,
698+ PmCartesian const * const seg1_start,
699+ PmCartesian const * const corner,
700+ PmCartesian const * const seg2_end,
701+ int n_samples)
702+ {
703+ if (!b || !seg1_start || !corner || !seg2_end || n_samples < 1 ) {
704+ return 0.0 ;
705+ }
706+
707+ double max_dev = 0.0 ;
708+ for (int i = 1 ; i <= n_samples; i++) {
709+ double t = (double )i / (double )(n_samples + 1 );
710+ PmCartesian pt;
711+ bezier5_eval (b->P , t, &pt);
712+
713+ /* Distance to programmed path = min of distances to both segments */
714+ double d1 = point_to_segment_dist (&pt, seg1_start, corner);
715+ double d2 = point_to_segment_dist (&pt, corner, seg2_end);
716+ double dev = fmin (d1, d2);
717+
718+ if (dev > max_dev) max_dev = dev;
719+ }
720+ return max_dev;
721+ }
0 commit comments