import 'dart:math' as math; import 'package:vector_math/vector_math_64.dart'; /// Calculates all [Vector2]s of a circumference. /// /// A circumference can be achieved by specifying a [center] and a [radius]. /// In addition, a semi-circle can be achieved by specifying its [angle] and an /// [offsetAngle] (both in radians). /// /// The higher the [precision], the more [Vector2]s will be calculated; /// achieving a more rounded arc. /// /// For more information read: https://en.wikipedia.org/wiki/Trigonometric_functions. List calculateArc({ required Vector2 center, required double radius, required double angle, double offsetAngle = 0, int precision = 100, }) { final stepAngle = angle / (precision - 1); final points = []; for (var i = 0; i < precision; i++) { final x = center.x + radius * math.cos((stepAngle * i) + offsetAngle); final y = center.y - radius * math.sin((stepAngle * i) + offsetAngle); final point = Vector2(x, y); points.add(point); } return points; } /// Calculates all [Vector2]s of an ellipse. /// /// An ellipse can be achieved by specifying a [center], a [majorRadius] and a /// [minorRadius]. /// /// The higher the [precision], the more [Vector2]s will be calculated; /// achieving a more rounded ellipse. /// /// For more information read: https://en.wikipedia.org/wiki/Ellipse. List calculateEllipse({ required Vector2 center, required double majorRadius, required double minorRadius, int precision = 100, }) { assert( 0 < minorRadius && minorRadius <= majorRadius, 'smallRadius ($minorRadius) and bigRadius ($majorRadius) must be in ' 'range 0 < smallRadius <= bigRadius', ); final stepAngle = 2 * math.pi / (precision - 1); final points = []; for (var i = 0; i < precision; i++) { final x = center.x + minorRadius * math.cos(stepAngle * i); final y = center.y - majorRadius * math.sin(stepAngle * i); final point = Vector2(x, y); points.add(point); } return points; } /// Calculates all [Vector2]s of a bezier curve. /// /// A bezier curve of [controlPoints] that say how to create this curve. /// /// First and last points specify the beginning and the end respectively /// of the curve. The inner points specify the shape of the curve and /// its turning points. /// /// The [step] must be between zero and one (inclusive), indicating the /// precision to calculate the curve. /// /// For more information read: https://en.wikipedia.org/wiki/B%C3%A9zier_curve List calculateBezierCurve({ required List controlPoints, double step = 0.01, }) { assert( 0 <= step && step <= 1, 'Step ($step) must be in range 0 <= step <= 1', ); assert( controlPoints.length >= 2, 'At least 2 control points needed to create a bezier curve', ); var t = 0.0; final n = controlPoints.length - 1; final points = []; do { var x = 0.0; var y = 0.0; for (var i = 0; i <= n; i++) { final point = controlPoints[i]; x += binomial(n, i) * math.pow(1 - t, n - i) * math.pow(t, i) * point.x; y += binomial(n, i) * math.pow(1 - t, n - i) * math.pow(t, i) * point.y; } points.add(Vector2(x, y)); t = t + step; } while (t <= 1); return points; } /// Calculates the binomial coefficient of 'n' and 'k'. /// /// For more information read: https://en.wikipedia.org/wiki/Binomial_coefficient num binomial(num n, num k) { assert(0 <= k && k <= n, 'k ($k) and n ($n) must be in range 0 <= k <= n'); if (k == 0 || n == k) { return 1; } else { return factorial(n) / (factorial(k) * factorial(n - k)); } } /// Calculate the factorial of 'n'. /// /// For more information read: https://en.wikipedia.org/wiki/Factorial num factorial(num n) { assert(n >= 0, 'Factorial is not defined for negative number n ($n)'); if (n == 0 || n == 1) { return 1; } else { return n * factorial(n - 1); } } /// Arithmetic mean position of all the [Vector2]s in a polygon. /// /// For more information read: https://en.wikipedia.org/wiki/Centroid Vector2 centroid(List vertices) { assert(vertices.isNotEmpty, 'Vertices must not be empty'); final sum = vertices.reduce((a, b) => a + b); return sum / vertices.length.toDouble(); }