// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:ui' as ui show lerpDouble; import 'package:flutter/foundation.dart'; import 'basic_types.dart'; /// An offset that's expressed as a fraction of a [Size]. /// /// `FractionalOffset(1.0, 0.0)` represents the top right of the [Size]. /// /// `FractionalOffset(0.0, 1.0)` represents the bottom left of the [Size]. /// /// `FractionalOffset(0.5, 2.0)` represents a point half way across the [Size], /// below the bottom of the rectangle by the height of the [Size]. /// /// A variety of widgets use [FractionalOffset] in their configuration, most /// notably: /// /// * [Align] positions a child according to a [FractionalOffset]. /// * [FractionalTranslation] moves a child according to a [FractionalOffset]. @immutable class FractionalOffset { /// Creates a fractional offset. /// /// The [dx] and [dy] arguments must not be null. const FractionalOffset(this.dx, this.dy) : assert(dx != null), assert(dy != null); /// Creates a fractional offset from a specific offset and size. /// /// The returned [FractionalOffset] describes the position of the /// [Offset] in the [Size], as a fraction of the [Size]. FractionalOffset.fromOffsetAndSize(Offset offset, Size size) : assert(size != null), assert(offset != null), dx = offset.dx / size.width, dy = offset.dy / size.height; /// Creates a fractional offset from a specific offset and rectangle. /// /// The offset is assumed to be relative to the same origin as the rectangle. /// /// If the offset is relative to the top left of the rectangle, use [new /// FractionalOffset.fromOffsetAndSize] instead, passing `rect.size`. /// /// The returned [FractionalOffset] describes the position of the /// [Offset] in the [Rect], as a fraction of the [Rect]. factory FractionalOffset.fromOffsetAndRect(Offset offset, Rect rect) { return new FractionalOffset.fromOffsetAndSize( offset - rect.topLeft, rect.size, ); } /// The distance fraction in the horizontal direction. /// /// A value of 0.0 corresponds to the leftmost edge. A value of 1.0 /// corresponds to the rightmost edge. Values are not limited to that range; /// negative values represent positions to the left of the left edge, and /// values greater than 1.0 represent positions to the right of the right /// edge. final double dx; /// The distance fraction in the vertical direction. /// /// A value of 0.0 corresponds to the topmost edge. A value of 1.0 corresponds /// to the bottommost edge. Values are not limited to that range; negative /// values represent positions above the top, and values greated than 1.0 /// represent positions below the bottom. final double dy; /// The top left corner. static const FractionalOffset topLeft = const FractionalOffset(0.0, 0.0); /// The center point along the top edge. static const FractionalOffset topCenter = const FractionalOffset(0.5, 0.0); /// The top right corner. static const FractionalOffset topRight = const FractionalOffset(1.0, 0.0); /// The bottom left corner. static const FractionalOffset bottomLeft = const FractionalOffset(0.0, 1.0); /// The center point along the bottom edge. static const FractionalOffset bottomCenter = const FractionalOffset(0.5, 1.0); /// The bottom right corner. static const FractionalOffset bottomRight = const FractionalOffset(1.0, 1.0); /// The center point along the left edge. static const FractionalOffset centerLeft = const FractionalOffset(0.0, 0.5); /// The center point along the right edge. static const FractionalOffset centerRight = const FractionalOffset(1.0, 0.5); /// The center point, both horizontally and vertically. static const FractionalOffset center = const FractionalOffset(0.5, 0.5); /// Returns the negation of the given fractional offset. FractionalOffset operator -() { return new FractionalOffset(-dx, -dy); } /// Returns the difference between two fractional offsets. FractionalOffset operator -(FractionalOffset other) { return new FractionalOffset(dx - other.dx, dy - other.dy); } /// Returns the sum of two fractional offsets. FractionalOffset operator +(FractionalOffset other) { return new FractionalOffset(dx + other.dx, dy + other.dy); } /// Scales the fractional offset in each dimension by the given factor. FractionalOffset operator *(double other) { return new FractionalOffset(dx * other, dy * other); } /// Divides the fractional offset in each dimension by the given factor. FractionalOffset operator /(double other) { return new FractionalOffset(dx / other, dy / other); } /// Integer divides the fractional offset in each dimension by the given factor. FractionalOffset operator ~/(double other) { return new FractionalOffset((dx ~/ other).toDouble(), (dy ~/ other).toDouble()); } /// Computes the remainder in each dimension by the given factor. FractionalOffset operator %(double other) { return new FractionalOffset(dx % other, dy % other); } /// Returns the offset that is this fraction in the direction of the given offset. Offset alongOffset(Offset other) { return new Offset(dx * other.dx, dy * other.dy); } /// Returns the offset that is this fraction within the given size. Offset alongSize(Size other) { return new Offset(dx * other.width, dy * other.height); } /// Returns the point that is this fraction within the given rect. Offset withinRect(Rect rect) { return new Offset(rect.left + dx * rect.width, rect.top + dy * rect.height); } /// Returns a rect of the given size, centered at this fraction of the given rect. /// /// For example, a 100×100 size inscribed on a 200×200 rect using /// [FractionalOffset.topLeft] would be the 100×100 rect at the top left of /// the 200×200 rect. Rect inscribe(Size size, Rect rect) { return new Rect.fromLTWH( rect.left + (rect.width - size.width) * dx, rect.top + (rect.height - size.height) * dy, size.width, size.height ); } @override bool operator ==(dynamic other) { if (other is! FractionalOffset) return false; final FractionalOffset typedOther = other; return dx == typedOther.dx && dy == typedOther.dy; } @override int get hashCode => hashValues(dx, dy); /// Linearly interpolate between two EdgeInsets. /// /// If either is null, this function interpolates from [FractionalOffset.topLeft]. // TODO(abarth): Consider interpolating from [FractionalOffset.center] instead // to remove upper-left bias. static FractionalOffset lerp(FractionalOffset a, FractionalOffset b, double t) { if (a == null && b == null) return null; if (a == null) return new FractionalOffset(b.dx * t, b.dy * t); if (b == null) return new FractionalOffset(a.dx * (1.0 - t), a.dy * (1.0 - t)); return new FractionalOffset(ui.lerpDouble(a.dx, b.dx, t), ui.lerpDouble(a.dy, b.dy, t)); } @override String toString() => '$runtimeType($dx, $dy)'; }