getClip method

  1. @override
Path getClip(
  1. Size size
)
override

Returns a description of the clip given that the render object being clipped is of the given size.

Implementation

@override
Path getClip(Size size) {
  final centerX = size.width / 2;
  final centerY = size.height / 2;
  final center = Offset(centerX, centerY);

  // Use per-radius angular offsets so the gap is constant pixel-width
  // at both inner and outer edges (parallel-sided, not tapered).
  final halfGap = ((clipLinesWidth ?? 0) + padding) / 2;
  final halfGapOuter = outerRadius > 0 ? halfGap / outerRadius : 0.0;
  final halfGapInner = innerRadius > 0 ? halfGap / innerRadius : 0.0;

  final outerStart = -math.pi / 2 - sweepAngle / 2 + halfGapOuter;
  final outerSweep = sweepAngle - 2 * halfGapOuter;
  final innerEnd = -math.pi / 2 + sweepAngle / 2 - halfGapInner;
  final innerSweep = -(sweepAngle - 2 * halfGapInner);

  final outerRect = Rect.fromCircle(center: center, radius: outerRadius);
  final innerRect = Rect.fromCircle(center: center, radius: innerRadius);

  if (outerCornerRadius <= 0 && innerCornerRadius <= 0) {
    return Path()
      ..arcTo(outerRect, outerStart, outerSweep, true)
      ..arcTo(innerRect, innerEnd, innerSweep, false)
      ..close();
  }

  // Rounded corners via quadratic Bezier at each of the 4 junctions.
  final crO = outerCornerRadius;
  final crI = innerCornerRadius;
  final crOAngle = outerRadius > 0 ? crO / outerRadius : 0.0;
  final crIAngle = innerRadius > 0 ? crI / innerRadius : 0.0;

  Offset onCircle(double r, double a) =>
      Offset(centerX + r * math.cos(a), centerY + r * math.sin(a));

  // Corner angles
  final aAngle = outerStart; // outer-left
  final bAngle = outerStart + outerSweep; // outer-right
  final cAngle = innerEnd; // inner-right
  final dAngle = innerEnd + innerSweep; // inner-left

  // Corner points
  final a = onCircle(outerRadius, aAngle);
  final b = onCircle(outerRadius, bAngle);
  final c = onCircle(innerRadius, cAngle);
  final d = onCircle(innerRadius, dAngle);

  // Unit vectors along side edges
  final dirBC = (c - b) / (c - b).distance;
  final dirDA = (a - d) / (a - d).distance;

  // Points offset by corner radius along each edge from each corner
  final afterA = crO > 0 ? onCircle(outerRadius, aAngle + crOAngle) : a;
  final afterB = crO > 0 ? b + dirBC * crO : b;
  final beforeC = crI > 0 ? c - dirBC * crI : c;
  final afterC = crI > 0 ? onCircle(innerRadius, cAngle - crIAngle) : c;
  final afterD = crI > 0 ? d + dirDA * crI : d;
  final beforeA = crO > 0 ? a - dirDA * crO : a;

  final path = Path()..moveTo(afterA.dx, afterA.dy);

  // Outer arc
  path.arcTo(outerRect, aAngle + crOAngle, outerSweep - 2 * crOAngle, false);

  // Corner B (outer-right)
  if (crO > 0) {
    path.quadraticBezierTo(b.dx, b.dy, afterB.dx, afterB.dy);
  }

  // Right side edge
  path.lineTo(beforeC.dx, beforeC.dy);

  // Corner C (inner-right)
  if (crI > 0) {
    path.quadraticBezierTo(c.dx, c.dy, afterC.dx, afterC.dy);
  }

  // Inner arc
  path.arcTo(innerRect, cAngle - crIAngle, innerSweep + 2 * crIAngle, false);

  // Corner D (inner-left)
  if (crI > 0) {
    path.quadraticBezierTo(d.dx, d.dy, afterD.dx, afterD.dy);
  }

  // Left side edge
  path.lineTo(beforeA.dx, beforeA.dy);

  // Corner A (outer-left)
  if (crO > 0) {
    path.quadraticBezierTo(a.dx, a.dy, afterA.dx, afterA.dy);
  }

  path.close();
  return path;
}