@@ -217,7 +217,10 @@ class PieChartPainter extends BaseChartPainter<PieChartData> {
217217    final  endLineTo =  endLineFrom +  endLineDirection *  section.radius;
218218    final  endLine =  Line (endLineFrom, endLineTo);
219219
220-     var  sectionPath =  Path ()
220+     var  sectionPath =  Path ();
221+ 
222+     // First create the basic section path (without rounding) 
223+     sectionPath =  Path ()
221224      ..moveTo (startLine.from.dx, startLine.from.dy)
222225      ..lineTo (startLine.to.dx, startLine.to.dy)
223226      ..arcTo (sectionRadiusRect, startRadians, sweepRadians, false )
@@ -226,7 +229,7 @@ class PieChartPainter extends BaseChartPainter<PieChartData> {
226229      ..moveTo (startLine.from.dx, startLine.from.dy)
227230      ..close ();
228231
229-     /// Subtract  section space from  the sectionPath  
232+     /// First apply  section space separators to  the basic path  
230233if  (sectionSpace !=  0 ) {
231234      final  startLineSeparatorPath =  createRectPathAroundLine (
232235        Line (startLineFrom, startLineTo),
@@ -257,9 +260,279 @@ class PieChartPainter extends BaseChartPainter<PieChartData> {
257260
258261    }
259262
263+     // Then apply border radius to the resulting separated path 
264+     if  (section.cornerRadius >  0 ) {
265+       // Get the bounds of the separated path 
266+       final  pathBounds =  sectionPath.getBounds ();
267+       if  (! pathBounds.isEmpty) {
268+         // We need to calculate new angles for the separated section 
269+         // to apply rounding correctly to the actual shape we have 
270+ 
271+         // Calculate effective angles after separation 
272+         final  separatorAngleReduction =  sectionSpace !=  0 
273+             ?  math.atan2 (sectionSpace, centerRadius +  section.radius /  2 )
274+             :  0.0 ;
275+ 
276+         final  effectiveStartRadians =  startRadians +  separatorAngleReduction;
277+         final  effectiveSweepRadians = 
278+             sweepRadians -  (2  *  separatorAngleReduction);
279+ 
280+         if  (effectiveSweepRadians >  0 ) {
281+           // Create new rects for the adjusted geometry 
282+           final  effectiveSectionRadiusRect =  Rect .fromCircle (
283+             center:  center,
284+             radius:  centerRadius +  section.radius,
285+           );
286+ 
287+           final  effectiveCenterRadiusRect =  Rect .fromCircle (
288+             center:  center,
289+             radius:  centerRadius,
290+           );
291+ 
292+           // Generate rounded path with the effective angles 
293+           sectionPath =  generateRoundedSectionPath (
294+             section,
295+             effectiveStartRadians,
296+             effectiveSweepRadians,
297+             center,
298+             centerRadius,
299+             effectiveSectionRadiusRect,
300+             effectiveCenterRadiusRect,
301+           );
302+         }
303+       }
304+     }
305+ 
260306    return  sectionPath;
261307  }
262308
309+   /// Generates a Path for a pie-section with rounded corners. 
310+   /// 
311+   /// This method builds a path that rounds both the outer and inner 
312+   /// corners of a pie section (when `centerRadius > 0` ). It clamps the 
313+   /// requested `section.cornerRadius`  separately for the outer and inner 
314+   /// edges to avoid geometric overlap when the section is narrow or the 
315+   /// radii would be too large for the available arc length. 
316+   /// 
317+   /// Important behaviors / notes: 
318+   /// - If `cornerRadius <= 1`  the method returns a standard (non-rounded) 
319+   ///   section path for performance and to avoid tiny visual artifacts. 
320+   /// - Outer and inner corner radii are clamped independently (`clampedOuterRadius`  
321+   ///   and `clampedInnerRadius` ) to reasonable maxima based on section size 
322+   ///   and sweep angle. 
323+   /// - The code supports `centerRadius == 0`  (fully filled pie) and 
324+   ///   `centerRadius > 0`  (donut). When `centerRadius > 0`  the inner 
325+   ///   corners are rounded as well. 
326+   /// - `sectionsSpace`  trimming is applied later by subtracting separator 
327+   ///   rectangles from the resulting path (see `generateSectionPath` ). 
328+   /// - There are known platform/engine caveats when using `Path.combine`  on 
329+   ///   web-html renderer; the subtraction steps are guarded with try/catch 
330+   ///   where used. 
331+ @visibleForTesting 
332+   Path  generateRoundedSectionPath (
333+     PieChartSectionData  section,
334+     double  startRadians,
335+     double  sweepRadians,
336+     Offset  center,
337+     double  centerRadius,
338+     Rect  sectionRadiusRect,
339+     Rect  centerRadiusRect,
340+   ) {
341+     final  endRadians =  startRadians +  sweepRadians;
342+     final  outerRadius =  centerRadius +  section.radius;
343+     // User-provided corner radius (applies uniformly to this section). 
344+     final  cornerRadius =  section.cornerRadius;
345+ 
346+     final  path =  Path ();
347+ 
348+     if  (cornerRadius <=  1 ) {
349+       // Si el radio es muy pequeño, usar path normal 
350+       final  innerStart =  center + 
351+           Offset (math.cos (startRadians), math.sin (startRadians)) *  centerRadius;
352+       final  outerStart =  center + 
353+           Offset (math.cos (startRadians), math.sin (startRadians)) *  outerRadius;
354+       final  innerEnd =  center + 
355+           Offset (math.cos (endRadians), math.sin (endRadians)) *  centerRadius;
356+ 
357+       path
358+         ..moveTo (innerStart.dx, innerStart.dy)
359+         ..lineTo (outerStart.dx, outerStart.dy)
360+         ..arcTo (sectionRadiusRect, startRadians, sweepRadians, false )
361+         ..lineTo (innerEnd.dx, innerEnd.dy)
362+         ..arcTo (centerRadiusRect, endRadians, - sweepRadians, false )
363+         ..close ();
364+     } else  {
365+       // Clamp requested radii to avoid overlaps. We compute a separate 
366+       // maximum for the outer arc (based on section radius and sweep angle) 
367+       // and for the inner arc (based on centerRadius). This keeps rounding 
368+       // visually stable across different section sizes. 
369+       final  maxRadiusForSection = 
370+           math.min (section.radius *  0.3 , sweepRadians *  outerRadius *  0.15 );
371+       final  maxRadiusForCenter =  centerRadius >  0 
372+           ?  math.min (centerRadius *  0.3 , sweepRadians *  centerRadius *  0.15 )
373+           :  0.0 ;
374+       final  clampedOuterRadius =  math.min (cornerRadius, maxRadiusForSection);
375+       final  clampedInnerRadius =  math.min (cornerRadius, maxRadiusForCenter);
376+ 
377+       // Compute angular offsets that correspond to the linear corner radii. 
378+       // These are used to trim the sweep angles so the rounded joins fit 
379+       // cleanly along the arc. 
380+       final  outerAngleOffset = 
381+           outerRadius >  0  ?  clampedOuterRadius /  outerRadius :  0.0 ;
382+       final  innerAngleOffset = 
383+           centerRadius >  0  ?  clampedInnerRadius /  centerRadius :  0.0 ;
384+ 
385+       // Ángulos ajustados para esquinas exteriores 
386+       final  outerStartAngle =  startRadians +  outerAngleOffset;
387+       final  outerEndAngle =  endRadians -  outerAngleOffset;
388+       final  outerSweepAngle =  sweepRadians -  (2  *  outerAngleOffset);
389+ 
390+       // Ángulos ajustados para esquinas interiores 
391+       final  innerStartAngle =  startRadians +  innerAngleOffset;
392+       final  innerEndAngle =  endRadians -  innerAngleOffset;
393+       final  innerSweepAngle =  sweepRadians -  (2  *  innerAngleOffset);
394+ 
395+       // Puntos de las esquinas exteriores 
396+       final  outerStartPoint =  center + 
397+           Offset (math.cos (startRadians), math.sin (startRadians)) *  outerRadius;
398+       final  outerEndPoint =  center + 
399+           Offset (math.cos (endRadians), math.sin (endRadians)) *  outerRadius;
400+       final  outerStartRounded =  center + 
401+           Offset (math.cos (outerStartAngle), math.sin (outerStartAngle)) * 
402+               outerRadius;
403+       final  outerEndRounded =  center + 
404+           Offset (math.cos (outerEndAngle), math.sin (outerEndAngle)) * 
405+               outerRadius;
406+ 
407+       // Puntos de las esquinas interiores 
408+       final  innerStartPoint =  center + 
409+           Offset (math.cos (startRadians), math.sin (startRadians)) *  centerRadius;
410+       final  innerEndPoint =  center + 
411+           Offset (math.cos (endRadians), math.sin (endRadians)) *  centerRadius;
412+       final  innerStartRounded =  center + 
413+           Offset (math.cos (innerStartAngle), math.sin (innerStartAngle)) * 
414+               centerRadius;
415+       final  innerEndRounded =  center + 
416+           Offset (math.cos (innerEndAngle), math.sin (innerEndAngle)) * 
417+               centerRadius;
418+ 
419+       // Control points used to connect the rounded corner bezier segments to 
420+       // the inner/outer arcs. They lie along the original radial directions 
421+       // but offset inward/outward by the clamped radii. 
422+       final  startOuterControl =  center + 
423+           Offset (math.cos (startRadians), math.sin (startRadians)) * 
424+               (outerRadius -  clampedOuterRadius);
425+       final  endOuterControl =  center + 
426+           Offset (math.cos (endRadians), math.sin (endRadians)) * 
427+               (outerRadius -  clampedOuterRadius);
428+       final  startInnerControl =  center + 
429+           Offset (math.cos (startRadians), math.sin (startRadians)) * 
430+               (centerRadius +  clampedInnerRadius);
431+       final  endInnerControl =  center + 
432+           Offset (math.cos (endRadians), math.sin (endRadians)) * 
433+               (centerRadius +  clampedInnerRadius);
434+ 
435+       // Construir el path 
436+       if  (centerRadius >  0 ) {
437+         // Empezar desde la esquina interior redondeada 
438+         path.moveTo (innerStartRounded.dx, innerStartRounded.dy);
439+ 
440+         // Inner starting rounded corner (quadratic join). If the inner 
441+         // radius is small we fall back to a straight line to avoid tiny 
442+         // bezier segments. 
443+         if  (clampedInnerRadius >  1 ) {
444+           path.quadraticBezierTo (
445+             innerStartPoint.dx,
446+             innerStartPoint.dy,
447+             startInnerControl.dx,
448+             startInnerControl.dy,
449+           );
450+         } else  {
451+           path.lineTo (innerStartPoint.dx, innerStartPoint.dy);
452+         }
453+ 
454+         // Línea recta hacia el borde exterior 
455+         path.lineTo (startOuterControl.dx, startOuterControl.dy);
456+ 
457+         // Outer starting rounded corner (quadratic join). 
458+         if  (clampedOuterRadius >  1 ) {
459+           path.quadraticBezierTo (
460+             outerStartPoint.dx,
461+             outerStartPoint.dy,
462+             outerStartRounded.dx,
463+             outerStartRounded.dy,
464+           );
465+         } else  {
466+           path.lineTo (outerStartPoint.dx, outerStartPoint.dy);
467+         }
468+       } else  {
469+         // Si no hay centerRadius, empezar desde el centro 
470+         path
471+           ..moveTo (center.dx, center.dy)
472+           ..lineTo (startOuterControl.dx, startOuterControl.dy);
473+ 
474+         if  (clampedOuterRadius >  1 ) {
475+           path.quadraticBezierTo (
476+             outerStartPoint.dx,
477+             outerStartPoint.dy,
478+             outerStartRounded.dx,
479+             outerStartRounded.dy,
480+           );
481+         } else  {
482+           path.lineTo (outerStartPoint.dx, outerStartPoint.dy);
483+         }
484+       }
485+ 
486+       // Draw the outer arc between the two rounded outer corner points. 
487+       if  (outerSweepAngle >  0 ) {
488+         path.arcTo (sectionRadiusRect, outerStartAngle, outerSweepAngle, false );
489+       }
490+ 
491+       // Outer ending rounded corner (quadratic join). 
492+       if  (clampedOuterRadius >  1 ) {
493+         path
494+           ..lineTo (outerEndRounded.dx, outerEndRounded.dy)
495+           ..quadraticBezierTo (
496+             outerEndPoint.dx,
497+             outerEndPoint.dy,
498+             endOuterControl.dx,
499+             endOuterControl.dy,
500+           );
501+       } else  {
502+         path.lineTo (outerEndPoint.dx, outerEndPoint.dy);
503+       }
504+ 
505+       if  (centerRadius >  0 ) {
506+         // Línea hacia la esquina interior 
507+         path.lineTo (endInnerControl.dx, endInnerControl.dy);
508+ 
509+         // Inner ending rounded corner (quadratic join). 
510+         if  (clampedInnerRadius >  1 ) {
511+           path.quadraticBezierTo (
512+             innerEndPoint.dx,
513+             innerEndPoint.dy,
514+             innerEndRounded.dx,
515+             innerEndRounded.dy,
516+           );
517+         } else  {
518+           path.lineTo (innerEndPoint.dx, innerEndPoint.dy);
519+         }
520+ 
521+         // Arco interior 
522+         if  (innerSweepAngle >  0 ) {
523+           path.arcTo (centerRadiusRect, innerEndAngle, - innerSweepAngle, false );
524+         }
525+       } else  {
526+         // Si no hay centerRadius, cerrar hacia el centro 
527+         path.lineTo (center.dx, center.dy);
528+       }
529+ 
530+       path.close ();
531+     }
532+ 
533+     return  path;
534+   }
535+ 
263536  /// Creates a rect around a narrow line 
264537@visibleForTesting 
265538  Path  createRectPathAroundLine (Line  line, double  width) {
0 commit comments