LCOV - code coverage report
Current view: top level - lib/src/geometry - shape.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 38 51 74.5 %
Date: 2021-08-10 15:50:53 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:ui';
       2             : 
       3             : import '../../components.dart';
       4             : import '../../game.dart';
       5             : import '../../palette.dart';
       6             : import '../components/cache/value_cache.dart';
       7             : import '../extensions/vector2.dart';
       8             : import 'shape_intersections.dart' as intersection_system;
       9             : 
      10             : /// A shape can represent any geometrical shape with optionally a size, position
      11             : /// and angle. It can also have an anchor if it shouldn't be rotated around its
      12             : /// center.
      13             : /// A point can be determined to be within of outside of a shape.
      14             : abstract class Shape {
      15             :   final ValueCache<Vector2> _halfSizeCache = ValueCache();
      16             :   final ValueCache<Vector2> _localCenterCache = ValueCache();
      17             :   final ValueCache<Vector2> _absoluteCenterCache = ValueCache();
      18             : 
      19             :   /// Should be the center of that [offsetPosition] and [relativeOffset]
      20             :   /// should be calculated from, if they are not set this is the center of the
      21             :   /// shape
      22             :   Vector2 position = Vector2.zero();
      23             : 
      24             :   /// The size is the bounding box of the [Shape]
      25             :   Vector2 size;
      26             : 
      27           7 :   Vector2 get halfSize {
      28          28 :     if (!_halfSizeCache.isCacheValid([size])) {
      29          49 :       _halfSizeCache.updateCache(size / 2, [size.clone()]);
      30             :     }
      31          14 :     return _halfSizeCache.value!;
      32             :   }
      33             : 
      34             :   /// The angle of the shape from its initial definition
      35             :   double angle;
      36             : 
      37             :   /// The local position of your shape, so the diff from the [position] of the
      38             :   /// shape
      39             :   Vector2 offsetPosition = Vector2.zero();
      40             : 
      41             :   /// The position of your shape in relation to its size from (-1,-1) to (1,1)
      42             :   Vector2 relativeOffset = Vector2.zero();
      43             : 
      44             :   /// The [relativeOffset] converted to a length vector
      45           0 :   Vector2 get relativePosition => (size / 2)..multiply(relativeOffset);
      46             : 
      47             :   /// The angle of the parent that has to be taken into consideration for some
      48             :   /// applications of [Shape], for example [HitboxShape]
      49             :   double parentAngle;
      50             : 
      51             :   /// Whether the context that the shape is in has already prepared (rotated
      52             :   /// and translated) the canvas before coming to the shape's render method.
      53             :   bool isCanvasPrepared = false;
      54             : 
      55             :   /// The center position of the shape within itself, without rotation
      56           0 :   Vector2 get localCenter {
      57           0 :     final stateValues = [
      58           0 :       size,
      59           0 :       relativeOffset,
      60           0 :       offsetPosition,
      61             :     ];
      62           0 :     if (!_localCenterCache.isCacheValid(stateValues)) {
      63           0 :       final center = (size / 2)..add(relativePosition)..add(offsetPosition);
      64           0 :       _localCenterCache.updateCache(
      65             :         center,
      66           0 :         stateValues.map((e) => e.clone()).toList(growable: false),
      67             :       );
      68             :     }
      69           0 :     return _localCenterCache.value!;
      70             :   }
      71             : 
      72             :   /// The shape's absolute center with rotation taken into account
      73           7 :   Vector2 get absoluteCenter {
      74           7 :     final stateValues = [
      75           7 :       position,
      76           7 :       offsetPosition,
      77           7 :       relativeOffset,
      78           7 :       angle,
      79           7 :       parentAngle,
      80             :     ];
      81          14 :     if (!_absoluteCenterCache.isCacheValid(stateValues)) {
      82             :       /// The center of the shape, before any rotation
      83          21 :       final center = position + offsetPosition;
      84          14 :       if (!relativeOffset.isZero()) {
      85           0 :         center.add(relativePosition);
      86             :       }
      87          28 :       if (angle != 0 || parentAngle != 0) {
      88           5 :         center.rotate(parentAngle + angle, center: position);
      89             :       }
      90          21 :       _absoluteCenterCache.updateCache(center, [
      91          14 :         position.clone(),
      92          14 :         offsetPosition.clone(),
      93          14 :         relativeOffset.clone(),
      94           7 :         angle,
      95           7 :         parentAngle,
      96             :       ]);
      97             :     }
      98          14 :     return _absoluteCenterCache.value!;
      99             :   }
     100             : 
     101           9 :   Shape({
     102             :     Vector2? position,
     103             :     Vector2? size,
     104             :     this.angle = 0,
     105             :     this.parentAngle = 0,
     106           4 :   })  : position = position ?? Vector2.zero(),
     107           3 :         size = size ?? Vector2.zero();
     108             : 
     109             :   /// Whether the point [p] is within the shapes boundaries or not
     110             :   bool containsPoint(Vector2 p);
     111             : 
     112             :   void render(Canvas canvas, Paint paint);
     113             : 
     114             :   /// Where this [Shape] has intersection points with another shape
     115           2 :   Set<Vector2> intersections(Shape other) {
     116           2 :     return intersection_system.intersections(this, other);
     117             :   }
     118             : 
     119             :   /// Turns a [Shape] into a [ShapeComponent]
     120             :   ///
     121             :   /// Do note that while a [Shape] is defined from the center, a
     122             :   /// [ShapeComponent] like all other components default to an [Anchor] in the
     123             :   /// top left corner.
     124           1 :   ShapeComponent toComponent({Paint? paint, Anchor anchor = Anchor.topLeft}) {
     125           1 :     return ShapeComponent(
     126             :       this,
     127           1 :       paint ?? BasicPalette.white.paint(),
     128             :       anchor: anchor,
     129             :     );
     130             :   }
     131             : }
     132             : 
     133             : mixin HitboxShape on Shape {
     134             :   late PositionComponent component;
     135             : 
     136           4 :   @override
     137           8 :   Vector2 get size => component.scaledSize;
     138             : 
     139           4 :   @override
     140           8 :   double get parentAngle => component.angle;
     141             : 
     142           4 :   @override
     143           8 :   Vector2 get position => component.absoluteCenter;
     144             : 
     145             :   /// Assign your own [CollisionCallback] if you want a callback when this
     146             :   /// shape collides with another [HitboxShape]
     147             :   CollisionCallback onCollision = emptyCollisionCallback;
     148             : 
     149             :   /// Assign your own [CollisionEndCallback] if you want a callback when this
     150             :   /// shape stops colliding with another [HitboxShape]
     151             :   CollisionEndCallback onCollisionEnd = emptyCollisionEndCallback;
     152             : }
     153             : 
     154             : typedef CollisionCallback = void Function(
     155             :   Set<Vector2> intersectionPoints,
     156             :   HitboxShape other,
     157             : );
     158             : 
     159             : typedef CollisionEndCallback = void Function(HitboxShape other);
     160             : 
     161           1 : void emptyCollisionCallback(Set<Vector2> _, HitboxShape __) {}
     162           0 : void emptyCollisionEndCallback(HitboxShape _) {}

Generated by: LCOV version 1.15