Line data Source code
1 : import 'package:meta/meta.dart'; 2 : 3 : import 'extensions/vector2.dart'; 4 : 5 : /// Represents a relative position inside some 2D object with a rectangular 6 : /// size or bounding box. 7 : /// 8 : /// Think of it as the place where you "grab" or "hold" the object. 9 : /// In Components, the Anchor is where the component position is measured from. 10 : /// For example, if a component position is (100, 100) the anchor reflects what 11 : /// exact point of the component that is positioned at (100, 100), as a relative 12 : /// fraction of the size of the object. 13 : /// 14 : /// The "default" anchor in most cases is topLeft. 15 : /// 16 : /// The Anchor is represented by a fraction of the size (in each axis), 17 : /// where 0 in x-axis means left, 0 in y-axis means top, 1 in x-axis means right 18 : /// and 1 in y-axis means bottom. 19 : @immutable 20 : class Anchor { 21 : static const Anchor topLeft = Anchor(0.0, 0.0); 22 : static const Anchor topCenter = Anchor(0.5, 0.0); 23 : static const Anchor topRight = Anchor(1.0, 0.0); 24 : static const Anchor centerLeft = Anchor(0.0, 0.5); 25 : static const Anchor center = Anchor(0.5, 0.5); 26 : static const Anchor centerRight = Anchor(1.0, 0.5); 27 : static const Anchor bottomLeft = Anchor(0.0, 1.0); 28 : static const Anchor bottomCenter = Anchor(0.5, 1.0); 29 : static const Anchor bottomRight = Anchor(1.0, 1.0); 30 : 31 : /// The relative x position with respect to the object's width; 32 : /// 0 means totally to the left (beginning) and 1 means totally to the 33 : /// right (end). 34 : final double x; 35 : 36 : /// The relative y position with respect to the object's height; 37 : /// 0 means totally to the top (beginning) and 1 means totally to the 38 : /// bottom (end). 39 : final double y; 40 : 41 : /// Returns [x] and [y] as a Vector2. Note that this is still a relative 42 : /// fractional representation. 43 44 : Vector2 toVector2() => Vector2(x, y); 44 : 45 44 : const Anchor(this.x, this.y); 46 : 47 0 : Vector2 translate(Vector2 p, Vector2 size) { 48 0 : return p - (toVector2()..multiply(size)); 49 : } 50 : 51 : /// Take your position [position] that is on this anchor and give back what that 52 : /// position it would be on in anchor [otherAnchor] with a size of [size]. 53 9 : Vector2 toOtherAnchorPosition( 54 : Vector2 position, 55 : Anchor otherAnchor, 56 : Vector2 size, 57 : ) { 58 9 : if (this == otherAnchor) { 59 : return position; 60 : } else { 61 8 : return position + 62 32 : ((otherAnchor.toVector2() - toVector2())..multiply(size)); 63 : } 64 : } 65 : 66 : /// Returns a string representation of this Anchor. 67 : /// 68 : /// This should only be used for serialization purposes. 69 1 : String get name { 70 5 : return _valueNames[this] ?? 'Anchor($x, $y)'; 71 : } 72 : 73 : /// Returns a string representation of this Anchor. 74 : /// 75 : /// This is the same as `name` and should be used only for debugging or 76 : /// serialization. 77 1 : @override 78 1 : String toString() => name; 79 : 80 3 : static final Map<Anchor, String> _valueNames = { 81 : topLeft: 'topLeft', 82 : topCenter: 'topCenter', 83 : topRight: 'topRight', 84 : centerLeft: 'centerLeft', 85 : center: 'center', 86 : centerRight: 'centerRight', 87 : bottomLeft: 'bottomLeft', 88 : bottomCenter: 'bottomCenter', 89 : bottomRight: 'bottomRight', 90 : }; 91 : 92 : /// List of all predefined anchor values. 93 5 : static final List<Anchor> values = _valueNames.keys.toList(); 94 : 95 : /// This should only be used for de-serialization purposes. 96 : /// 97 : /// If you need to convert anchors to serializable data (like JSON), 98 : /// use the `toString()` and `valueOf` methods. 99 1 : static Anchor valueOf(String name) { 100 2 : if (_valueNames.containsValue(name)) { 101 7 : return _valueNames.entries.singleWhere((e) => e.value == name).key; 102 : } else { 103 1 : final regexp = RegExp(r'^\Anchor\(([^,]+), ([^\)]+)\)'); 104 3 : final matches = regexp.firstMatch(name)?.groups([1, 2]); 105 : assert( 106 3 : matches != null && matches.length == 2, 107 1 : 'Bad anchor format: $name', 108 : ); 109 5 : return Anchor(double.parse(matches![0]!), double.parse(matches[1]!)); 110 : } 111 : } 112 : 113 9 : @override 114 : bool operator ==(Object other) { 115 36 : return other is Anchor && hashCode == other.hashCode; 116 : } 117 : 118 9 : @override 119 54 : int get hashCode => x.hashCode * 31 + y.hashCode; 120 : }