-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Add OvalBorder and BoxShape.oval #103833
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add OvalBorder and BoxShape.oval #103833
Conversation
|
Cc @gspencergoog. Kind of related to the PR you just reviewed 😅. This is a lot simpler. |
|
I think if we're going to add a new shape, then it should support lerping to all the other shapes. The problem is that adding a new shape is exponential in the number of lerps you need to implement, since not only does this have to lerp to, say, rectangle, rectangle needs to lerp to it. For three shapes, you need three lerp functions. For five, you need ten. If we had 32 shapes, we'd need 496 lerp functions. (x = n(n-1)/2). We don't have to support every lerp, but the more the better. I'd say at least oval->rectangle, oval->rounded rect, oval->circle, oval->stadium, and back again. It also makes the burden of adding a new shape exponentially harder, so we should consider if this is one of the shapes we really want to have. It seems reasonably likely that it is, but we should choose carefully. |
|
What do you think if I made it as an attribute to CircleBorder? Like |
|
I like that better, since it doesn't increase the cost of adding a new shape. What about making it more "lerpable": instead of a bool, make the parameter a double that goes from 0.0 to 1.0, where 0.0 is an oval, and 1.0 is a circle. The problem is the name of this parameter: circularity? rotundity? ovalness (reverse sense)? I think I'm leaning towards |
|
Having it be a continuum will make it more useful anyhow: I can imagine wanting a semi-oval circle even when I don't want to lerp it. |
|
I'll test that, probably tomorrow. There is already a property like you described used in lerping. It will be kind of weird, but let's see the result. Because we are not lerping from round to square, it is round with min bounds to round without bounds. |
|
Yes, but I think the rect to circle lerp parameter isn't the same as this. They probably need to be combined somehow, which might be complicated. Or it might just be a multiply in the right place, if you're lucky. :-) |
|
It should be simple, just the end result might be kind of weird. Let's see tomorrow. |
60f0a7f to
1a24251
Compare
|
I'll take a look. What about "eccentricity" for the name instead of "ovalness"? "The eccentricity of an ellipse is the ratio of the distance from the center to the foci and the distance from the center to the vertices." |
|
I replaced ovalness with your circularity idea. I think 0.0 = ellipse and 1.0 = circle makes slightly more sense codewise while at the same time being weird to introduce a parameter that by default is 1.0, not 0.0. Both have pros and cons. I changed from ovalness to circularity to make this change. If you go back two or three commits you can see how it was and which way you prefer. But eccentricity could also work. I guess first check the code, then we can discover the name we want. It could even be |
|
Now it is using eccentricity 👀 |
|
@bernaferrari are you still wanting to merge this? I think we're really close here. |
|
Yes, I just don't know what else we need. You never reviewed the code. |
gspencergoog
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So sorry, these comments were sitting here in GitHub, I just forgot to submit them, so I thought I was still waiting for your response!
Anyhow, here are my suggestions...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need to mention the "squared space", that is just a special case that should be expected: if the aspect ratio of the rectangle is 1:1, then the "oval" will be a circle, regardless of eccentricity.
Perhaps something like this?
| /// The [eccentricity] parameter allows the circle to be painted touching all | |
| /// the edges of a rectangle, becoming an oval. When applied to a | |
| /// squared space, [eccentricity] is ignored. | |
| /// The [eccentricity] parameter describes how much the circle will deform to | |
| /// fit the rectangle it is a border for. A value of zero implies no | |
| /// deformation (a circle touching at least two sides of the rectangle), a | |
| /// value of one implies full deformation (an oval touching all sides of the | |
| /// rectangle). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, but changed 'the circle' to 'a circle'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be a switch statement instead of an "if", just in case we add more shapes in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It got slightly weird (wish you could return a switch :( hopefully soon), but it is there, I agree with the more shapes in future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might split these into separate asserts for readability, and so when they fire they are more useful.
You can also print the offending value in the assert message so it's more helpful. Like so:
assert(eccentricity >= 0.0, 'The eccentricity argument $eccentricity is not greater than or equal to zero.');There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Just question, do we need eccentricity != null? I never understood side != null since null safety kills that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it does seem kind of silly, but there are people out there still running without strict null safety, which means that the asserts still do something.
We'll eventually make a pass and remove all such asserts with a tool once it's been long enough since we turned on null safety.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought even when null-safety was off when calling code that was null-safe it would be null-safe. Ok, adding eccentricity != null
gspencergoog
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use the same wording as above? "a squared box" doesn't make much sense to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to:
/// Defines the ratio (0.0-1.0) from which the border will deform
/// to fit a rectangle.
/// When 0.0, it draws a circle touching at least two sides of the rectangle.
/// When 1.0, it draws an oval touching all sides of the rectangle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting here is a little different from what we normally do:
| if ( | |
| borderRadius == null && | |
| (shape == BoxShape.oval || shape == BoxShape.oval) && | |
| !assertOval(shapeClipper, matchState) | |
| ) { | |
| if (borderRadius == null && | |
| (shape == BoxShape.oval || shape == BoxShape.oval) && | |
| !assertOval(shapeClipper, matchState)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, OK. Well, we don't usually format it like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed the others in 6cfd093
|
Don't worry, I'll forgive you if you can help give an end to the round outside StrokeAlign saga (either Flutter stays with the current one, we merge the other PR or we merge other and propose a new way for the one you and I like) before it is too late :P |



Fix #103829
Flutter supports rectangles and circles, but doesn't support ovals (circles that are not squared). I adjusted two things:
BoxShape.oval. There is a comment saying new shapes shouldn't go here. I believe oval could be an exception to that, sinceBoxShape.ovalis basicallyBoxShape.circlewithout constraints. It is easier to implement oval than circle. In most places, I just calldrawOvalinstead of calculating the smaller side and drawing a circle. If you disagree, I can remove it, no problem 😀.Demo:
