Line data Source code
1 : import '../../extensions.dart'; 2 : 3 : /// A simple interface to mark a class that can perform projection operations 4 : /// from one 2D euclidian coordinate space to another. 5 : /// 6 : /// This can be a Viewport, a Camera or anything else that exposes such 7 : /// operations to the user. 8 : abstract class Projector { 9 : /// Converts a vector in the screen space to the world space. 10 : /// 11 : /// This considers both the translation and scaling transformations. 12 : Vector2 unprojectVector(Vector2 screenCoordinates); 13 : 14 : /// Converts a vector in the world space to the screen space. 15 : /// 16 : /// This considers both the translation and scaling transformations. 17 : Vector2 projectVector(Vector2 worldCoordinates); 18 : 19 : /// Converts a vector representing a delta in the screen space to the world 20 : /// space. 21 : /// 22 : /// This considers only the scaling transformation, as the translations are 23 : /// cancelled in a delta transformation. 24 : /// A delta can be a displacement (difference between two position 25 : /// vectors), a velocity (displacement over time), etc. 26 : Vector2 unscaleVector(Vector2 screenCoordinates); 27 : 28 : /// Converts a vector representing a delta in the world space to the screen 29 : /// space. 30 : /// 31 : /// This considers only the scaling transformation, as the translations are 32 : /// cancelled in a delta transformation. 33 : /// A delta can be a displacement (difference between two position 34 : /// vectors), a velocity (displacement over time), etc. 35 : Vector2 scaleVector(Vector2 worldCoordinates); 36 : 37 : /// Creates a [ComposedProjector] that will apply the provided projectors 38 : /// in order. 39 : /// 40 : /// Use when dealing with multiple coordinate transformations in succession. 41 24 : static Projector compose(List<Projector> projectors) { 42 24 : return ComposedProjector(projectors); 43 : } 44 : } 45 : 46 : /// This is a [Projector] implementation that composes a list of projectors, 47 : /// in the order provided. 48 : /// 49 : /// It will call the `project*` functions in the order in the array and the 50 : /// `unproject*` in the reversed order. 51 : /// For a list of projectors p1, p2, ..., pn, this is equivalent of the 52 : /// projector p = pn ∘ ... ∘ p2 ∘ p1. 53 : class ComposedProjector extends Projector { 54 : final List<Projector> _components; 55 24 : ComposedProjector(this._components); 56 : 57 1 : @override 58 : Vector2 scaleVector(Vector2 worldCoordinates) { 59 2 : return _components.fold( 60 : worldCoordinates, 61 2 : (previousValue, element) => element.scaleVector(previousValue), 62 : ); 63 : } 64 : 65 1 : @override 66 : Vector2 projectVector(Vector2 worldCoordinates) { 67 2 : return _components.fold( 68 : worldCoordinates, 69 2 : (previousValue, element) => element.projectVector(previousValue), 70 : ); 71 : } 72 : 73 1 : @override 74 : Vector2 unscaleVector(Vector2 screenCoordinates) { 75 3 : return _components.reversed.fold( 76 : screenCoordinates, 77 2 : (previousValue, element) => element.unscaleVector(previousValue), 78 : ); 79 : } 80 : 81 5 : @override 82 : Vector2 unprojectVector(Vector2 screenCoordinates) { 83 15 : return _components.reversed.fold( 84 : screenCoordinates, 85 10 : (previousValue, element) => element.unprojectVector(previousValue), 86 : ); 87 : } 88 : }