Add support for inverting CircularNotchedRectangle to optionally be drawn on the bottom of a path (#151386)
This PR allows for an optional argument [inverted[ to be passed to the [getOuterPath] method of a CircularNotchedRectangle object in order to invert the notch for situations where it is desired to draw the notch on the bottom of the path. This allows both of the below paths in the below screenshot to be drawn and changes no default behavior.  *List which issues are fixed by this PR. You must list at least one issue. An issue is not required if the PR fixes something trivial like a typo.* This add a feature similar to the one discussed in #49973, original feature proposal #151381 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*
This commit is contained in:
parent
a73c0f3abd
commit
30bf2888b9
@ -43,7 +43,11 @@ class CircularNotchedRectangle extends NotchedShape {
|
||||
/// Creates a [CircularNotchedRectangle].
|
||||
///
|
||||
/// The same object can be used to create multiple shapes.
|
||||
const CircularNotchedRectangle();
|
||||
const CircularNotchedRectangle({this.inverted = false});
|
||||
|
||||
/// If [inverted] is true, the notch is placed at the bottom of the
|
||||
/// rectangle.
|
||||
final bool inverted;
|
||||
|
||||
/// Creates a [Path] that describes a rectangle with a smooth circular notch.
|
||||
///
|
||||
@ -67,6 +71,10 @@ class CircularNotchedRectangle extends NotchedShape {
|
||||
// So the guest's radius is half the guest width.
|
||||
final double notchRadius = guest.width / 2.0;
|
||||
|
||||
// The variables [p2yA] and [p2yB] need to be inverted
|
||||
// when the notch is drawn on the bottom of a path.
|
||||
final double invertMultiplier = inverted ? -1.0 : 1.0;
|
||||
|
||||
// We build a path for the notch from 3 segments:
|
||||
// Segment A - a Bezier curve from the host's top edge to segment B.
|
||||
// Segment B - an arc with radius notchRadius.
|
||||
@ -80,13 +88,13 @@ class CircularNotchedRectangle extends NotchedShape {
|
||||
|
||||
final double r = notchRadius;
|
||||
final double a = -1.0 * r - s2;
|
||||
final double b = host.top - guest.center.dy;
|
||||
final double b = (inverted ? host.bottom : host.top) - guest.center.dy;
|
||||
|
||||
final double n2 = math.sqrt(b * b * r * r * (a * a + b * b - r * r));
|
||||
final double p2xA = ((a * r * r) - n2) / (a * a + b * b);
|
||||
final double p2xB = ((a * r * r) + n2) / (a * a + b * b);
|
||||
final double p2yA = math.sqrt(r * r - p2xA * p2xA);
|
||||
final double p2yB = math.sqrt(r * r - p2xB * p2xB);
|
||||
final double p2yA = math.sqrt(r * r - p2xA * p2xA) * invertMultiplier;
|
||||
final double p2yB = math.sqrt(r * r - p2xB * p2xB) * invertMultiplier;
|
||||
|
||||
final List<Offset?> p = List<Offset?>.filled(6, null);
|
||||
|
||||
@ -107,20 +115,38 @@ class CircularNotchedRectangle extends NotchedShape {
|
||||
p[i] = p[i]! + guest.center;
|
||||
}
|
||||
|
||||
return Path()
|
||||
..moveTo(host.left, host.top)
|
||||
..lineTo(p[0]!.dx, p[0]!.dy)
|
||||
..quadraticBezierTo(p[1]!.dx, p[1]!.dy, p[2]!.dx, p[2]!.dy)
|
||||
..arcToPoint(
|
||||
p[3]!,
|
||||
radius: Radius.circular(notchRadius),
|
||||
clockwise: false,
|
||||
)
|
||||
..quadraticBezierTo(p[4]!.dx, p[4]!.dy, p[5]!.dx, p[5]!.dy)
|
||||
..lineTo(host.right, host.top)
|
||||
..lineTo(host.right, host.bottom)
|
||||
// Use the calculated points to draw out a path object.
|
||||
final Path path = Path();
|
||||
path.moveTo(host.left, host.top);
|
||||
if (!inverted) {
|
||||
path
|
||||
..lineTo(p[0]!.dx, p[0]!.dy)
|
||||
..quadraticBezierTo(p[1]!.dx, p[1]!.dy, p[2]!.dx, p[2]!.dy)
|
||||
..arcToPoint(
|
||||
p[3]!,
|
||||
radius: Radius.circular(notchRadius),
|
||||
clockwise: false,
|
||||
)
|
||||
..quadraticBezierTo(p[4]!.dx, p[4]!.dy, p[5]!.dx, p[5]!.dy)
|
||||
..lineTo(host.right, host.top)
|
||||
..lineTo(host.right, host.bottom);
|
||||
} else {
|
||||
path
|
||||
..lineTo(host.right, host.top)
|
||||
..lineTo(host.right, host.bottom)
|
||||
..lineTo(p[5]!.dx, p[5]!.dy)
|
||||
..quadraticBezierTo(p[4]!.dx, p[4]!.dy, p[3]!.dx, p[3]!.dy)
|
||||
..arcToPoint(
|
||||
p[2]!,
|
||||
radius: Radius.circular(notchRadius),
|
||||
clockwise: false,
|
||||
)
|
||||
..quadraticBezierTo(p[1]!.dx, p[1]!.dy, p[0]!.dx, p[0]!.dy);
|
||||
}
|
||||
path
|
||||
..lineTo(host.left, host.bottom)
|
||||
..close();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,26 @@ void main() {
|
||||
expect(pathDoesNotContainCircle(actualPath, guest), isTrue);
|
||||
});
|
||||
|
||||
test('inverted guest center above host', () {
|
||||
const CircularNotchedRectangle shape = CircularNotchedRectangle(inverted: true);
|
||||
const Rect host = Rect.fromLTRB(0.0, 100.0, 300.0, 300.0);
|
||||
const Rect guest = Rect.fromLTRB(190.0, 285.0, 210.0, 305.0);
|
||||
|
||||
final Path actualPath = shape.getOuterPath(host, guest);
|
||||
|
||||
expect(pathDoesNotContainCircle(actualPath, guest), isTrue);
|
||||
});
|
||||
|
||||
test('inverted guest center below host', () {
|
||||
const CircularNotchedRectangle shape = CircularNotchedRectangle(inverted: true);
|
||||
const Rect host = Rect.fromLTRB(0.0, 100.0, 300.0, 300.0);
|
||||
const Rect guest = Rect.fromLTRB(190.0, 295.0, 210.0, 315.0);
|
||||
|
||||
final Path actualPath = shape.getOuterPath(host, guest);
|
||||
|
||||
expect(pathDoesNotContainCircle(actualPath, guest), isTrue);
|
||||
});
|
||||
|
||||
test('no guest is ok', () {
|
||||
const Rect host = Rect.fromLTRB(0.0, 100.0, 300.0, 300.0);
|
||||
expect(
|
||||
|
Loading…
x
Reference in New Issue
Block a user