提交 66b4b8da 编写于 作者: I Ian Hickson

Merge pull request #2147 from Hixie/RRect

Make RRect useful
...@@ -4,31 +4,34 @@ ...@@ -4,31 +4,34 @@
part of dart_ui; part of dart_ui;
/// An immutable 2D floating-point offset /// An immutable 2D floating-point offset.
/// ///
/// An Offset represents a vector from an unspecified point /// An Offset represents a vector from an unspecified point
class Offset extends OffsetBase { class Offset extends OffsetBase {
const Offset(double dx, double dy) : super(dx, dy); const Offset(double dx, double dy) : super(dx, dy);
/// The x component of the offset /// The x component of the offset.
double get dx => _dx; double get dx => _dx;
/// The y component of the offset /// The y component of the offset.
double get dy => _dy; double get dy => _dy;
/// The magnitude of the offset /// The magnitude of the offset.
double get distance => math.sqrt(_dx * _dx + _dy * _dy); double get distance => math.sqrt(_dx * _dx + _dy * _dy);
/// An offset with zero magnitude /// The square of the magnitude of the offset.
double get distanceSquared => _dx * _dx + _dy * _dy;
/// An offset with zero magnitude.
static const Offset zero = const Offset(0.0, 0.0); static const Offset zero = const Offset(0.0, 0.0);
/// An offset with infinite x and y components /// An offset with infinite x and y components.
static const Offset infinite = const Offset(double.INFINITY, double.INFINITY); static const Offset infinite = const Offset(double.INFINITY, double.INFINITY);
/// Returns a new offset with the x component scaled by scaleX and the y component scaled by scaleY /// Returns a new offset with the x component scaled by scaleX and the y component scaled by scaleY.
Offset scale(double scaleX, double scaleY) => new Offset(dx * scaleX, dy * scaleY); Offset scale(double scaleX, double scaleY) => new Offset(dx * scaleX, dy * scaleY);
/// Returns a new offset with translateX added to the x component and translateY added to the y component /// Returns a new offset with translateX added to the x component and translateY added to the y component.
Offset translate(double translateX, double translateY) => new Offset(dx + translateX, dy + translateY); Offset translate(double translateX, double translateY) => new Offset(dx + translateX, dy + translateY);
Offset operator -() => new Offset(-dx, -dy); Offset operator -() => new Offset(-dx, -dy);
...@@ -39,13 +42,13 @@ class Offset extends OffsetBase { ...@@ -39,13 +42,13 @@ class Offset extends OffsetBase {
Offset operator ~/(double operand) => new Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble()); Offset operator ~/(double operand) => new Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble());
Offset operator %(double operand) => new Offset(dx % operand, dy % operand); Offset operator %(double operand) => new Offset(dx % operand, dy % operand);
/// Returns a rect of the given size that starts at (0, 0) plus this offset /// Returns a rect of the given size that starts at (0, 0) plus this offset.
Rect operator &(Size other) => new Rect.fromLTWH(dx, dy, other.width, other.height); Rect operator &(Size other) => new Rect.fromLTWH(dx, dy, other.width, other.height);
/// Returns the point at (0, 0) plus this offset. /// Returns the point at (0, 0) plus this offset.
Point toPoint() => new Point(dx, dy); Point toPoint() => new Point(dx, dy);
/// Linearly interpolate between two offsets /// Linearly interpolate between two offsets.
/// ///
/// If either offset is null, this function interpolates from [Offset.zero]. /// If either offset is null, this function interpolates from [Offset.zero].
static Offset lerp(Offset a, Offset b, double t) { static Offset lerp(Offset a, Offset b, double t) {
......
part of dart_ui; part of dart_ui;
// A rounded rectangle. // A rounded rectangle with the same radii for all four corners.
class RRect { class RRect {
RRect(); RRect._();
/// Initialize with the same radii for all four corners. /// Construct a rounded rectangle from its left, top, right, and bottom edges,
RRect.fromRectXY(Rect rect, double xRadius, double yRadius) { /// and the radii along its horizontal axis and its vertical axis.
RRect.fromLTRBXY(double left, double top, double right, double bottom, double radiusX, double radiusY) {
_value
..[0] = left
..[1] = top
..[2] = right
..[3] = bottom
..[4] = radiusX
..[5] = radiusY;
}
/// Construct a rounded rectangle from its bounding box and the radii along
/// its horizontal axis and its vertical axis.
RRect.fromRectXY(Rect rect, double radiusX, double radiusY) {
_value _value
..[0] = rect.left ..[0] = rect.left
..[1] = rect.top ..[1] = rect.top
..[2] = rect.right ..[2] = rect.right
..[3] = rect.bottom ..[3] = rect.bottom
..[4] = xRadius ..[4] = radiusX
..[5] = yRadius; ..[5] = radiusY;
} }
final Float32List _value = new Float32List(6); static const int _kDataSize = 6;
final Float32List _value = new Float32List(_kDataSize);
/// The offset of the left edge of this rectangle from the x axis.
double get left => _value[0];
/// The offset of the top edge of this rectangle from the y axis.
double get top => _value[1];
/// The offset of the right edge of this rectangle from the x axis.
double get right => _value[2];
/// The offset of the bottom edge of this rectangle from the y axis.
double get bottom => _value[3];
/// The horizontal semi-axis of the corners.
double get radiusX => _value[4];
/// The vertical semi-axis of the corners.
double get radiusY => _value[5];
/// A rounded rectangle with all the values set to zero.
static final RRect zero = new RRect._();
/// Returns a new RRect translated by the given offset. /// Returns a new RRect translated by the given offset.
RRect shift(Offset offset) { RRect shift(Offset offset) {
RRect result = new RRect(); return new RRect.fromLTRBXY(
result._value _value[0] + offset.dx,
..[0] = _value[0] + offset.dx _value[1] + offset.dy,
..[1] = _value[1] + offset.dy _value[2] + offset.dx,
..[2] = _value[2] + offset.dx _value[3] + offset.dy,
..[3] = _value[3] + offset.dy _value[4],
..[4] = _value[4] _value[5]
..[5] = _value[5]; );
return result; }
/// Returns a new RRect with edges and radii moved outwards by the given delta.
RRect inflate(double delta) {
return new RRect.fromLTRBXY(
_value[0] - delta,
_value[1] - delta,
_value[2] + delta,
_value[3] + delta,
_value[4] + delta,
_value[5] + delta
);
}
/// Returns a new RRect with edges and radii moved inwards by the given delta.
RRect deflate(double delta) => inflate(-delta);
/// The distance between the left and right edges of this rectangle.
double get width => right - left;
/// The distance between the top and bottom edges of this rectangle.
double get height => bottom - top;
/// The bounding box of this rounded rectangle (the rectangle with no rounded corners).
Rect get outerRect => new Rect.fromLTRB(left, top, right, bottom);
/// The non-rounded rectangle that fits inside this rounded rectangle by
/// touching the middle of each curved corner.
Rect get safeInnerRect {
const double kInsetFactor = 0.29289321881; // 1-cos(pi/4)
return new Rect.fromLTRB(
left + radiusX * kInsetFactor,
top + radiusY * kInsetFactor,
right - radiusX * kInsetFactor,
bottom - radiusY * kInsetFactor
);
}
/// The rectangle that would be formed using only the straight sides of the
/// rounded rectangle, i.e., the rectangle formed from the centers of the
/// ellipses that form the corners. This is the intersection of the
/// [wideMiddleRect] and the [tallMiddleRect].
Rect get middleRect {
return new Rect.fromLTRB(
left + radiusX,
top + radiusY,
right - radiusX,
bottom - radiusY
);
} }
/// The biggest rectangle that is entirely inside the rounded rectangle and
/// has the full width of the rounded rectangle.
Rect get wideMiddleRect {
return new Rect.fromLTRB(
left,
top + radiusY,
right,
bottom - radiusY
);
}
/// The biggest rectangle that is entirely inside the rounded rectangle and
/// has the full height of the rounded rectangle.
Rect get tallMiddleRect {
return new Rect.fromLTRB(
left + radiusX,
top,
right - radiusX,
bottom
);
}
/// Whether this rounded rectangle encloses a non-zero area.
/// Negative areas are considered empty.
bool get isEmpty => left >= right || top >= bottom;
/// Whether this rounded rectangle has a side with no straight section.
bool get isStadium => width <= 2 * radiusX || height <= 2 * radiusY;
/// Whether this rounded rectangle has no side with a straight section.
bool get isEllipse => width <= 2 * radiusX && height <= 2 * radiusY;
/// Whether this rounded rectangle would draw as a circle.
bool get isCircle => width == height && isEllipse;
/// The lesser of the magnitudes of the width and the height of this rounded
/// rectangle.
double get shortestSide {
double w = width.abs();
double h = height.abs();
return w < h ? w : h;
}
/// The point halfway between the left and right and the top and bottom edges of this rectangle.
Point get center => new Point(left + width / 2.0, top + height / 2.0);
/// Whether the given point lies inside the rounded rectangle.
bool contains(Point point) {
if (point.x < left || point.x >= right || point.y < top || point.y >= bottom)
return false; // outside bounding box
double leftInner = left + radiusX;
double rightInner = right - radiusX;
double topInner = top + radiusY;
double bottomInner = bottom - radiusY;
if (point.x >= leftInner && point.x <= rightInner)
return true; // inside tallMiddleRect
if (point.y >= topInner && point.y <= bottomInner)
return true; // inside wideMiddleRect
// it is in one of the corners
// convert this to a test of the unit circle
double x, y;
if (point.x > leftInner) {
assert(point.x > rightInner);
x = point.x - (rightInner - leftInner);
} else {
x = point.x;
}
if (point.y > topInner) {
assert(point.y > bottomInner);
y = point.y - (bottomInner - topInner);
} else {
y = point.y;
}
x = x / (radiusX * 2) - 0.5;
y = y / (radiusY * 2) - 0.5;
// check if the point is outside the unit circle
if (x * x + y * y > 0.25)
return false;
return true;
}
/// Linearly interpolate between two rounded rectangles.
///
/// If either is null, this function substitutes [RRect.zero] instead.
static RRect lerp(RRect a, RRect b, double t) {
if (a == null && b == null)
return null;
if (a == null)
return new RRect.fromLTRBXY(b.left * t, b.top * t, b.right * t, b.bottom * t, b.radiusX * t, b.radiusY * t);
if (b == null) {
double k = 1.0 - t;
return new RRect.fromLTRBXY(a.left * k, a.top * k, a.right * k, a.bottom * k, a.radiusX * k, a.radiusY * k);
}
return new RRect.fromLTRBXY(
lerpDouble(a.left, b.left, t),
lerpDouble(a.top, b.top, t),
lerpDouble(a.right, b.right, t),
lerpDouble(a.bottom, b.bottom, t),
lerpDouble(a.radiusX, b.radiusX, t),
lerpDouble(a.radiusY, b.radiusY, t)
);
}
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! RRect)
return false;
final RRect typedOther = other;
for (int i = 0; i < _kDataSize; i += 1) {
if (_value[i] != typedOther._value[i])
return false;
}
return true;
}
int get hashCode => _value.fold(373, (value, item) => (37 * value + item.hashCode));
String toString() => "RRect.fromLTRBXY(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)}, ${radiusX.toStringAsFixed(1)}, ${radiusY.toStringAsFixed(1)})";
} }
...@@ -7,9 +7,9 @@ part of dart_ui; ...@@ -7,9 +7,9 @@ part of dart_ui;
/// An immutable 2D, axis-aligned, floating-point rectangle whose coordinates /// An immutable 2D, axis-aligned, floating-point rectangle whose coordinates
/// are relative to an origin point. /// are relative to an origin point.
class Rect { class Rect {
Rect(); Rect._();
/// Construct a rectangle from left, top, right, and bottom edges. /// Construct a rectangle from its left, top, right, and bottom edges.
Rect.fromLTRB(double left, double top, double right, double bottom) { Rect.fromLTRB(double left, double top, double right, double bottom) {
_value _value
..[0] = left ..[0] = left
...@@ -18,7 +18,7 @@ class Rect { ...@@ -18,7 +18,7 @@ class Rect {
..[3] = bottom; ..[3] = bottom;
} }
/// Construct a rectangle from left, top edges and a width and height. /// Construct a rectangle from its left and top edges, its width, and its height.
Rect.fromLTWH(double left, double top, double width, double height) { Rect.fromLTWH(double left, double top, double width, double height) {
_value _value
..[0] = left ..[0] = left
...@@ -27,7 +27,8 @@ class Rect { ...@@ -27,7 +27,8 @@ class Rect {
..[3] = top + height; ..[3] = top + height;
} }
final Float32List _value = new Float32List(4); static const int _kDataSize = 4;
final Float32List _value = new Float32List(_kDataSize);
/// The offset of the left edge of this rectangle from the x axis. /// The offset of the left edge of this rectangle from the x axis.
double get left => _value[0]; double get left => _value[0];
...@@ -42,7 +43,7 @@ class Rect { ...@@ -42,7 +43,7 @@ class Rect {
double get bottom => _value[3]; double get bottom => _value[3];
/// A rectangle with left, top, right, and bottom edges all at zero. /// A rectangle with left, top, right, and bottom edges all at zero.
static final Rect zero = new Rect(); static final Rect zero = new Rect._();
/// Returns a new rectangle translated by the given offset. /// Returns a new rectangle translated by the given offset.
Rect shift(Offset offset) { Rect shift(Offset offset) {
...@@ -79,7 +80,8 @@ class Rect { ...@@ -79,7 +80,8 @@ class Rect {
/// Negative areas are considered empty. /// Negative areas are considered empty.
bool get isEmpty => left >= right || top >= bottom; bool get isEmpty => left >= right || top >= bottom;
/// The lesser of the width and the height of this rectangle. /// The lesser of the magnitudes of the width and the height of this
/// rectangle.
double get shortestSide { double get shortestSide {
double w = width.abs(); double w = width.abs();
double h = height.abs(); double h = height.abs();
...@@ -110,7 +112,7 @@ class Rect { ...@@ -110,7 +112,7 @@ class Rect {
/// Linearly interpolate between two rectangles. /// Linearly interpolate between two rectangles.
/// ///
/// If either rect is null, this function interpolates from [Rect.zero]. /// If either rect is null, [Rect.zero] is used as a substitute.
static Rect lerp(Rect a, Rect b, double t) { static Rect lerp(Rect a, Rect b, double t) {
if (a == null && b == null) if (a == null && b == null)
return null; return null;
...@@ -118,7 +120,7 @@ class Rect { ...@@ -118,7 +120,7 @@ class Rect {
return new Rect.fromLTRB(b.left * t, b.top * t, b.right * t, b.bottom * t); return new Rect.fromLTRB(b.left * t, b.top * t, b.right * t, b.bottom * t);
if (b == null) { if (b == null) {
double k = 1.0 - t; double k = 1.0 - t;
return new Rect.fromLTRB(b.left * k, b.top * k, b.right * k, b.bottom * k); return new Rect.fromLTRB(a.left * k, a.top * k, a.right * k, a.bottom * k);
} }
return new Rect.fromLTRB( return new Rect.fromLTRB(
lerpDouble(a.left, b.left, t), lerpDouble(a.left, b.left, t),
...@@ -134,7 +136,7 @@ class Rect { ...@@ -134,7 +136,7 @@ class Rect {
if (other is! Rect) if (other is! Rect)
return false; return false;
final Rect typedOther = other; final Rect typedOther = other;
for (var i = 0; i < 4; ++i) { for (int i = 0; i < _kDataSize; i += 1) {
if (_value[i] != typedOther._value[i]) if (_value[i] != typedOther._value[i])
return false; return false;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册