Skip to content

Commit

Permalink
Add Radius.clamp and Radius.clampValues (flutter#36106)
Browse files Browse the repository at this point in the history
  • Loading branch information
gspencergoog authored Sep 12, 2022
1 parent 3425e21 commit 0fce942
Show file tree
Hide file tree
Showing 11 changed files with 266 additions and 2 deletions.
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,7 @@ FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc
FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h
FILE: ../../../flutter/lib/ui/key.dart
FILE: ../../../flutter/lib/ui/lerp.dart
FILE: ../../../flutter/lib/ui/math.dart
FILE: ../../../flutter/lib/ui/natives.dart
FILE: ../../../flutter/lib/ui/painting.dart
FILE: ../../../flutter/lib/ui/painting/canvas.cc
Expand Down Expand Up @@ -1149,6 +1150,7 @@ FILE: ../../../flutter/lib/web_ui/lib/hash_codes.dart
FILE: ../../../flutter/lib/web_ui/lib/initialization.dart
FILE: ../../../flutter/lib/web_ui/lib/key.dart
FILE: ../../../flutter/lib/web_ui/lib/lerp.dart
FILE: ../../../flutter/lib/web_ui/lib/math.dart
FILE: ../../../flutter/lib/web_ui/lib/natives.dart
FILE: ../../../flutter/lib/web_ui/lib/painting.dart
FILE: ../../../flutter/lib/web_ui/lib/path.dart
Expand Down
31 changes: 31 additions & 0 deletions lib/ui/geometry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,37 @@ class Radius {
/// You can use [Radius.zero] with [RRect] to have right-angle corners.
static const Radius zero = Radius.circular(0.0);

/// Returns this [Radius], with values clamped to the given min and max
/// [Radius] values.
///
/// The `min` value defaults to `Radius.circular(-double.infinity)`, and
/// the `max` value defaults to `Radius.circular(double.infinity)`.
Radius clamp({Radius? minimum, Radius? maximum}) {
minimum ??= const Radius.circular(-double.infinity);
maximum ??= const Radius.circular(double.infinity);
return Radius.elliptical(
clampDouble(x, minimum.x, maximum.x),
clampDouble(y, minimum.y, maximum.y),
);
}

/// Returns this [Radius], with values clamped to the given min and max
/// values in each dimension
///
/// The `minimumX` and `minimumY` values default to `-double.infinity`, and
/// the `maximumX` and `maximumY` values default to `double.infinity`.
Radius clampValues({
double? minimumX,
double? minimumY,
double? maximumX,
double? maximumY,
}) {
return Radius.elliptical(
clampDouble(x, minimumX ?? -double.infinity, maximumX ?? double.infinity),
clampDouble(y, minimumY ?? -double.infinity, maximumY ?? double.infinity),
);
}

/// Unary negation operator.
///
/// Returns a Radius with the distances negated.
Expand Down
25 changes: 25 additions & 0 deletions lib/ui/math.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

part of dart.ui;

/// Same as [num.clamp] but optimized for a non-null [double].
///
/// This is faster because it avoids polymorphism, boxing, and special cases for
/// floating point numbers.
//
// See also: //dev/benchmarks/microbenchmarks/lib/foundation/clamp.dart
double clampDouble(double x, double min, double max) {
assert(min <= max && !max.isNaN && !min.isNaN);
if (x < min) {
return min;
}
if (x > max) {
return max;
}
if (x.isNaN) {
return max;
}
return x;
}
2 changes: 1 addition & 1 deletion lib/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ class Color {
/// The [opacity] value may not be null.
static int getAlphaFromOpacity(double opacity) {
assert(opacity != null);
return (opacity.clamp(0.0, 1.0) * 255).round();
return (clampDouble(opacity, 0.0, 1.0) * 255).round();
}

@override
Expand Down
1 change: 1 addition & 0 deletions lib/ui/ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ part 'hooks.dart';
part 'isolate_name_server.dart';
part 'key.dart';
part 'lerp.dart';
part 'math.dart';
part 'natives.dart';
part 'painting.dart';
part 'platform_dispatcher.dart';
Expand Down
19 changes: 19 additions & 0 deletions lib/web_ui/lib/geometry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,25 @@ class Radius {
final double x;
final double y;
static const Radius zero = Radius.circular(0.0);
Radius clamp({Radius? minimum, Radius? maximum}) {
minimum ??= const Radius.circular(-double.infinity);
maximum ??= const Radius.circular(double.infinity);
return Radius.elliptical(
clampDouble(x, minimum.x, maximum.x),
clampDouble(y, minimum.y, maximum.y),
);
}
Radius clampValues({
double? minimumX,
double? minimumY,
double? maximumX,
double? maximumY,
}) {
return Radius.elliptical(
clampDouble(x, minimumX ?? -double.infinity, maximumX ?? double.infinity),
clampDouble(y, minimumY ?? -double.infinity, maximumY ?? double.infinity),
);
}
Radius operator -() => Radius.elliptical(-x, -y);
Radius operator -(Radius other) => Radius.elliptical(x - other.x, y - other.y);
Radius operator +(Radius other) => Radius.elliptical(x + other.x, y + other.y);
Expand Down
19 changes: 19 additions & 0 deletions lib/web_ui/lib/math.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

part of ui;

double clampDouble(double x, double min, double max) {
assert(min <= max && !max.isNaN && !min.isNaN);
if (x < min) {
return min;
}
if (x > max) {
return max;
}
if (x.isNaN) {
return max;
}
return x;
}
2 changes: 1 addition & 1 deletion lib/web_ui/lib/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class Color {

static int getAlphaFromOpacity(double opacity) {
assert(opacity != null);
return (opacity.clamp(0.0, 1.0) * 255).round();
return (clampDouble(opacity, 0.0, 1.0) * 255).round();
}

@override
Expand Down
1 change: 1 addition & 0 deletions lib/web_ui/lib/ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ part 'hash_codes.dart';
part 'initialization.dart';
part 'key.dart';
part 'lerp.dart';
part 'math.dart';
part 'natives.dart';
part 'painting.dart';
part 'path.dart';
Expand Down
82 changes: 82 additions & 0 deletions lib/web_ui/test/geometry_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,86 @@ void testMain() {
expect(const Size(-1.0, -1.0).aspectRatio, 1.0);
expect(const Size(3.0, 4.0).aspectRatio, 3.0 / 4.0);
});
test('Radius.clamp() operates as expected', () {
final RRect rrectMin = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.circular(-100).clamp(minimum: Radius.zero));

expect(rrectMin.left, 1);
expect(rrectMin.top, 3);
expect(rrectMin.right, 5);
expect(rrectMin.bottom, 7);
expect(rrectMin.trRadius, equals(const Radius.circular(0)));
expect(rrectMin.blRadius, equals(const Radius.circular(0)));

final RRect rrectMax = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.circular(100).clamp(maximum: const Radius.circular(10)));

expect(rrectMax.left, 1);
expect(rrectMax.top, 3);
expect(rrectMax.right, 5);
expect(rrectMax.bottom, 7);
expect(rrectMax.trRadius, equals(const Radius.circular(10)));
expect(rrectMax.blRadius, equals(const Radius.circular(10)));

final RRect rrectMix = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.elliptical(-100, 100).clamp(minimum: Radius.zero, maximum: const Radius.circular(10)));

expect(rrectMix.left, 1);
expect(rrectMix.top, 3);
expect(rrectMix.right, 5);
expect(rrectMix.bottom, 7);
expect(rrectMix.trRadius, equals(const Radius.elliptical(0, 10)));
expect(rrectMix.blRadius, equals(const Radius.elliptical(0, 10)));

final RRect rrectMix1 = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.elliptical(100, -100).clamp(minimum: Radius.zero, maximum: const Radius.circular(10)));

expect(rrectMix1.left, 1);
expect(rrectMix1.top, 3);
expect(rrectMix1.right, 5);
expect(rrectMix1.bottom, 7);
expect(rrectMix1.trRadius, equals(const Radius.elliptical(10, 0)));
expect(rrectMix1.blRadius, equals(const Radius.elliptical(10, 0)));
});
test('Radius.clampValues() operates as expected', () {
final RRect rrectMin = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.circular(-100).clampValues(minimumX: 0, minimumY: 0));

expect(rrectMin.left, 1);
expect(rrectMin.top, 3);
expect(rrectMin.right, 5);
expect(rrectMin.bottom, 7);
expect(rrectMin.trRadius, equals(const Radius.circular(0)));
expect(rrectMin.blRadius, equals(const Radius.circular(0)));

final RRect rrectMax = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.circular(100).clampValues(maximumX: 10, maximumY: 20));

expect(rrectMax.left, 1);
expect(rrectMax.top, 3);
expect(rrectMax.right, 5);
expect(rrectMax.bottom, 7);
expect(rrectMax.trRadius, equals(const Radius.elliptical(10, 20)));
expect(rrectMax.blRadius, equals(const Radius.elliptical(10, 20)));

final RRect rrectMix = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.elliptical(-100, 100).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20));

expect(rrectMix.left, 1);
expect(rrectMix.top, 3);
expect(rrectMix.right, 5);
expect(rrectMix.bottom, 7);
expect(rrectMix.trRadius, equals(const Radius.elliptical(5, 20)));
expect(rrectMix.blRadius, equals(const Radius.elliptical(5, 20)));

final RRect rrectMix2 = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.elliptical(100, -100).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20));

expect(rrectMix2.left, 1);
expect(rrectMix2.top, 3);
expect(rrectMix2.right, 5);
expect(rrectMix2.bottom, 7);
expect(rrectMix2.trRadius, equals(const Radius.elliptical(10, 6)));
expect(rrectMix2.blRadius, equals(const Radius.elliptical(10, 6)));
});
}
84 changes: 84 additions & 0 deletions testing/dart/geometry_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,88 @@ void main() {
expect(rrect.brRadiusX, 0.25);
expect(rrect.brRadiusY, 0.75);
});

test('Radius.clamp() operates as expected', () {
final RRect rrectMin = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.circular(-100).clamp(minimum: Radius.zero));

expect(rrectMin.left, 1);
expect(rrectMin.top, 3);
expect(rrectMin.right, 5);
expect(rrectMin.bottom, 7);
expect(rrectMin.trRadius, equals(const Radius.circular(0)));
expect(rrectMin.blRadius, equals(const Radius.circular(0)));

final RRect rrectMax = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.circular(100).clamp(maximum: const Radius.circular(10)));

expect(rrectMax.left, 1);
expect(rrectMax.top, 3);
expect(rrectMax.right, 5);
expect(rrectMax.bottom, 7);
expect(rrectMax.trRadius, equals(const Radius.circular(10)));
expect(rrectMax.blRadius, equals(const Radius.circular(10)));

final RRect rrectMix = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.elliptical(-100, 100).clamp(minimum: Radius.zero, maximum: const Radius.circular(10)));

expect(rrectMix.left, 1);
expect(rrectMix.top, 3);
expect(rrectMix.right, 5);
expect(rrectMix.bottom, 7);
expect(rrectMix.trRadius, equals(const Radius.elliptical(0, 10)));
expect(rrectMix.blRadius, equals(const Radius.elliptical(0, 10)));

final RRect rrectMix1 = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.elliptical(100, -100).clamp(minimum: Radius.zero, maximum: const Radius.circular(10)));

expect(rrectMix1.left, 1);
expect(rrectMix1.top, 3);
expect(rrectMix1.right, 5);
expect(rrectMix1.bottom, 7);
expect(rrectMix1.trRadius, equals(const Radius.elliptical(10, 0)));
expect(rrectMix1.blRadius, equals(const Radius.elliptical(10, 0)));
});

test('Radius.clampValues() operates as expected', () {
final RRect rrectMin = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.circular(-100).clampValues(minimumX: 0, minimumY: 0));

expect(rrectMin.left, 1);
expect(rrectMin.top, 3);
expect(rrectMin.right, 5);
expect(rrectMin.bottom, 7);
expect(rrectMin.trRadius, equals(const Radius.circular(0)));
expect(rrectMin.blRadius, equals(const Radius.circular(0)));

final RRect rrectMax = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.circular(100).clampValues(maximumX: 10, maximumY: 20));

expect(rrectMax.left, 1);
expect(rrectMax.top, 3);
expect(rrectMax.right, 5);
expect(rrectMax.bottom, 7);
expect(rrectMax.trRadius, equals(const Radius.elliptical(10, 20)));
expect(rrectMax.blRadius, equals(const Radius.elliptical(10, 20)));

final RRect rrectMix = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.elliptical(-100, 100).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20));

expect(rrectMix.left, 1);
expect(rrectMix.top, 3);
expect(rrectMix.right, 5);
expect(rrectMix.bottom, 7);
expect(rrectMix.trRadius, equals(const Radius.elliptical(5, 20)));
expect(rrectMix.blRadius, equals(const Radius.elliptical(5, 20)));

final RRect rrectMix2 = RRect.fromLTRBR(1, 3, 5, 7,
const Radius.elliptical(100, -100).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20));

expect(rrectMix2.left, 1);
expect(rrectMix2.top, 3);
expect(rrectMix2.right, 5);
expect(rrectMix2.bottom, 7);
expect(rrectMix2.trRadius, equals(const Radius.elliptical(10, 6)));
expect(rrectMix2.blRadius, equals(const Radius.elliptical(10, 6)));
});
}

0 comments on commit 0fce942

Please sign in to comment.