Line data Source code
1 : import 'dart:math'; 2 : import 'dart:ui'; 3 : 4 : import '../../game.dart'; 5 : import '../../geometry.dart'; 6 : import '../extensions/vector2.dart'; 7 : import 'shape.dart'; 8 : 9 : class Circle extends Shape { 10 : /// The [normalizedRadius] is how many percentages of the shortest edge of 11 : /// [size] that the circle should cover. 12 : double normalizedRadius = 1; 13 : 14 : /// With this constructor you can create your [Circle] from a radius and 15 : /// a position. It will also calculate the bounding rectangle [size] for the 16 : /// [Circle]. 17 1 : Circle({ 18 : double? radius, 19 : Vector2? position, 20 : double angle = 0, 21 1 : }) : super( 22 : position: position, 23 2 : size: Vector2.all((radius ?? 0) * 2), 24 : angle: angle, 25 : ); 26 : 27 : /// This constructor is used by [HitboxCircle] 28 : /// definition is the percentages of the shortest edge of [size] that the 29 : /// circle should fill. 30 1 : Circle.fromDefinition({ 31 : this.normalizedRadius = 1.0, 32 : Vector2? position, 33 : Vector2? size, 34 : double? angle, 35 1 : }) : super(position: position, size: size, angle: angle ?? 0); 36 : 37 9 : double get radius => (min(size.x, size.y) / 2) * normalizedRadius; 38 : 39 : /// This render method doesn't rotate the canvas according to angle since a 40 : /// circle will look the same rotated as not rotated. 41 0 : @override 42 : void render(Canvas canvas, Paint paint) { 43 0 : canvas.drawCircle(localCenter.toOffset(), radius, paint); 44 : } 45 : 46 : /// Checks whether the represented circle contains the [point]. 47 0 : @override 48 : bool containsPoint(Vector2 point) { 49 0 : return absoluteCenter.distanceToSquared(point) < radius * radius; 50 : } 51 : 52 : /// Returns the locus of points in which the provided line segment intersect 53 : /// the circle. 54 : /// 55 : /// This can be an empty list (if they don't intersect), one point (if the 56 : /// line is tangent) or two points (if the line is secant). 57 1 : List<Vector2> lineSegmentIntersections( 58 : LineSegment line, { 59 : double epsilon = double.minPositive, 60 : }) { 61 3 : double sq(double x) => pow(x, 2).toDouble(); 62 : 63 2 : final cx = absoluteCenter.x; 64 2 : final cy = absoluteCenter.y; 65 : 66 1 : final point1 = line.from; 67 1 : final point2 = line.to; 68 : 69 1 : final delta = point2 - point1; 70 : 71 5 : final A = sq(delta.x) + sq(delta.y); 72 10 : final B = 2 * (delta.x * (point1.x - cx) + delta.y * (point1.y - cy)); 73 10 : final C = sq(point1.x - cx) + sq(point1.y - cy) - sq(radius); 74 : 75 4 : final det = B * B - 4 * A * C; 76 1 : final result = <Vector2>[]; 77 2 : if (A <= epsilon || det < 0) { 78 1 : return []; 79 1 : } else if (det == 0) { 80 0 : final t = -B / (2 * A); 81 0 : result.add(Vector2(point1.x + t * delta.x, point1.y + t * delta.y)); 82 : } else { 83 5 : final t1 = (-B + sqrt(det)) / (2 * A); 84 1 : final i1 = Vector2( 85 4 : point1.x + t1 * delta.x, 86 4 : point1.y + t1 * delta.y, 87 : ); 88 : 89 5 : final t2 = (-B - sqrt(det)) / (2 * A); 90 1 : final i2 = Vector2( 91 4 : point1.x + t2 * delta.x, 92 4 : point1.y + t2 * delta.y, 93 : ); 94 : 95 2 : result.addAll([i1, i2]); 96 : } 97 3 : result.removeWhere((v) => !line.containsPoint(v)); 98 : return result; 99 : } 100 : } 101 : 102 : class HitboxCircle extends Circle with HitboxShape { 103 0 : @override 104 : HitboxCircle({double definition = 1}) 105 0 : : super.fromDefinition( 106 : normalizedRadius: definition, 107 : ); 108 : }