refactor: changed docs and use angles in radians instead of degrees

pull/14/head
RuiAlonso 4 years ago
parent 5bff54f3d8
commit bbab659b85

@ -4,8 +4,9 @@ import 'package:flutter/material.dart';
import 'package:geometry/geometry.dart';
/// {@template pathway}
/// [Pathway] creates lines of various shapes that the ball can collide
/// with and move along.
/// [Pathway] creates lines of various shapes.
///
/// [BodyComponent]s such as a Ball can collide and move along a [Pathway].
/// {@endtemplate}
class Pathway extends BodyComponent {
Pathway._({
@ -20,20 +21,19 @@ class Pathway extends BodyComponent {
..style = PaintingStyle.stroke;
}
/// {@macro pathway}
/// [Pathway.straight] creates a straight pathway for the ball.
/// Creates a uniform unidirectional (straight) [Pathway].
///
/// given a [position] for the body, between a [start] and [end] points.
/// It creates two [ChainShape] separated by a [pathwayWidth].
/// If [singleWall] is true, just one [ChainShape] is created
/// (like a wall instead of a pathway)
/// The pathway could be rotated by [rotation] in degrees.
/// Does so with two [ChainShape] separated by a [width]. Placed
/// at a [position] between [start] and [end] points. Can
/// be rotated by a given [rotation] in radians.
///
/// If [singleWall] is true, just one [ChainShape] is created.
factory Pathway.straight({
Color? color,
required Vector2 position,
required Vector2 start,
required Vector2 end,
required double pathwayWidth,
required double width,
double rotation = 0,
bool singleWall = false,
}) {
@ -41,14 +41,14 @@ class Pathway extends BodyComponent {
final wall1 = [
start.clone(),
end.clone(),
].map((vector) => vector..rotate(radians(rotation))).toList();
].map((vector) => vector..rotate(rotation)).toList();
paths.add(wall1);
if (!singleWall) {
final wall2 = [
start + Vector2(pathwayWidth, 0),
end + Vector2(pathwayWidth, 0),
].map((vector) => vector..rotate(radians(rotation))).toList();
start + Vector2(width, 0),
end + Vector2(width, 0),
].map((vector) => vector..rotate(rotation)).toList();
paths.add(wall2);
}
@ -59,21 +59,24 @@ class Pathway extends BodyComponent {
);
}
/// {@macro pathway}
/// [Pathway.arc] creates an arc pathway for the ball.
/// Creates an arc [Pathway].
///
/// The [angle], in radians, specifies the size of the arc. For example, 2*pi
/// returns a complete circumference and minor angles a semi circumference.
///
/// The center of the arc is placed at [position].
///
/// Does so with two [ChainShape] separated by a [width]. Which can be
/// rotated by a given [rotation] in radians.
///
/// The outer radius is specified by [radius], whilst the inner one is
/// equivalent to [radius] - [width].
///
/// The arc is created given a [position] for the body, a [radius] for the
/// circumference and an [angle] to specify the size of it (360 will return
/// a completed circumference and minor angles a semi circumference ).
/// It creates two [ChainShape] separated by a [pathwayWidth], like a circular
/// crown. The specified [radius] is for the outer arc, the inner one will
/// have a radius of radius-pathwayWidth.
/// If [singleWall] is true, just one [ChainShape] is created.
/// The pathway could be rotated by [rotation] in degrees.
factory Pathway.arc({
Color? color,
required Vector2 position,
required double pathwayWidth,
required double width,
required double radius,
required double angle,
double rotation = 0,
@ -92,7 +95,7 @@ class Pathway extends BodyComponent {
if (!singleWall) {
final wall2 = calculateArc(
center: position,
radius: radius - pathwayWidth,
radius: radius - width,
angle: angle,
offsetAngle: rotation,
);
@ -106,38 +109,36 @@ class Pathway extends BodyComponent {
);
}
/// {@macro pathway}
/// [Pathway.bezierCurve] creates a bezier curve pathway for the ball.
/// Creates a bezier curve [Pathway].
///
/// The curve is created given a [position] for the body, and
/// with a list of control points specified by [controlPoints].
/// First and last points set the beginning and end of the curve, all the
/// inner points between them set the bezier curve final shape.
/// It creates two [ChainShape] separated by a [pathwayWidth].
/// If [singleWall] is true, just one [ChainShape] is created
/// (like a wall instead of a pathway)
/// The pathway could be rotated by [rotation] in degrees.
/// Does so with two [ChainShape] separated by a [width]. Which can be
/// rotated by a given [rotation] in radians.
///
/// First and last [controlPoints] set the beginning and end of the curve,
/// inner points between them set its final shape.
///
/// If [singleWall] is true, just one [ChainShape] is created.
factory Pathway.bezierCurve({
Color? color,
required Vector2 position,
required List<Vector2> controlPoints,
required double pathwayWidth,
required double width,
double rotation = 0,
bool singleWall = false,
}) {
final paths = <List<Vector2>>[];
final wall1 = calculateBezierCurve(controlPoints: controlPoints)
.map((vector) => vector..rotate(radians(rotation)))
.map((vector) => vector..rotate(rotation))
.toList();
paths.add(wall1);
if (!singleWall) {
final wall2 = calculateBezierCurve(
controlPoints: controlPoints
.map((vector) => vector + Vector2(pathwayWidth, -pathwayWidth))
.map((vector) => vector + Vector2(width, -width))
.toList(),
).map((vector) => vector..rotate(radians(rotation))).toList();
).map((vector) => vector..rotate(rotation)).toList();
paths.add(wall2);
}
@ -158,13 +159,13 @@ class Pathway extends BodyComponent {
..position = _position;
final body = world.createBody(bodyDef);
for (final path in _paths) {
final chain = ChainShape()
..createChain(
path.map(gameRef.screenToWorld).toList(),
);
final fixtureDef = FixtureDef(chain);
body.createFixture(fixtureDef);
}

@ -1,5 +1,4 @@
import 'dart:async';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/game/game.dart';

@ -4,7 +4,7 @@ import 'package:flame/extensions.dart';
/// Calculates all [Vector2]s of a circumference.
///
/// Circumference is created from a [center] and a [radius]
/// Also semi circumference could be created, specifying its [angle] in degrees
/// Also semi circumference could be created, specifying its [angle] in radians
/// and the offset start angle [offsetAngle] for this semi circumference.
/// The higher the [precision], the more [Vector2]s will be calculated,
/// achieving a more rounded arc.
@ -17,13 +17,12 @@ List<Vector2> calculateArc({
double offsetAngle = 0,
int precision = 100,
}) {
final stepAngle = radians(angle / (precision - 1));
final stepOffset = radians(offsetAngle);
final stepAngle = angle / (precision - 1);
final points = <Vector2>[];
for (var i = 0; i < precision; i++) {
final xCoord = center.x + radius * math.cos((stepAngle * i) + stepOffset);
final yCoord = center.y - radius * math.sin((stepAngle * i) + stepOffset);
final xCoord = center.x + radius * math.cos((stepAngle * i) + offsetAngle);
final yCoord = center.y - radius * math.sin((stepAngle * i) + offsetAngle);
final point = Vector2(xCoord, yCoord);
points.add(point);
@ -35,11 +34,14 @@ List<Vector2> calculateArc({
/// 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 in range 0<=step<=1 and indicates the precision to
/// calculate the curve.
///
/// 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<Vector2> calculateBezierCurve({
required List<Vector2> controlPoints,
@ -77,7 +79,7 @@ List<Vector2> calculateBezierCurve({
return points;
}
/// Method to calculate the binomial coefficient of 'n' and 'k'
/// 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');
@ -89,7 +91,7 @@ num binomial(num n, num k) {
}
}
/// Method to calculate the factorial of some number 'n'
/// 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)');

@ -1,4 +1,5 @@
// ignore_for_file: cascade_invocations, prefer_const_constructors
import 'dart:math' as math;
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter/material.dart';
@ -10,20 +11,21 @@ void main() {
final flameTester = FlameTester(PinballGame.new);
group('Pathway', () {
const pathwayWidth = 50.0;
const width = 50.0;
group('straight', () {
group('color', () {
flameTester.test(
'has transparent color by default if not specified',
'has transparent color by default when no color is specified',
(game) async {
final pathway = Pathway.straight(
position: Vector2.zero(),
start: Vector2(10, 10),
end: Vector2(20, 20),
pathwayWidth: pathwayWidth,
width: width,
);
await game.ensureAdd(pathway);
expect(game.contains(pathway), isTrue);
expect(pathway.paint, isNotNull);
expect(
@ -32,8 +34,9 @@ void main() {
);
},
);
flameTester.test(
'has a color if set',
'has a color when is specified',
(game) async {
const defaultColor = Colors.blue;
@ -42,9 +45,10 @@ void main() {
position: Vector2.zero(),
start: Vector2(10, 10),
end: Vector2(20, 20),
pathwayWidth: pathwayWidth,
width: width,
);
await game.ensureAdd(pathway);
expect(game.contains(pathway), isTrue);
expect(pathway.paint, isNotNull);
expect(pathway.paint.color.value, equals(defaultColor.value));
@ -59,9 +63,10 @@ void main() {
position: Vector2.zero(),
start: Vector2(10, 10),
end: Vector2(20, 20),
pathwayWidth: pathwayWidth,
width: width,
);
await game.ensureAdd(pathway);
expect(game.contains(pathway), isTrue);
},
);
@ -75,11 +80,11 @@ void main() {
position: position,
start: Vector2(10, 10),
end: Vector2(20, 20),
pathwayWidth: pathwayWidth,
width: width,
);
await game.ensureAdd(pathway);
game.contains(pathway);
game.contains(pathway);
expect(pathway.body.position, position);
},
);
@ -91,7 +96,7 @@ void main() {
position: Vector2.zero(),
start: Vector2(10, 10),
end: Vector2(20, 20),
pathwayWidth: pathwayWidth,
width: width,
);
await game.ensureAdd(pathway);
@ -102,13 +107,13 @@ void main() {
group('fixtures', () {
flameTester.test(
'exists only one ChainShape if just one wall',
'has only one ChainShape when singleWall is true',
(game) async {
final pathway = Pathway.straight(
position: Vector2.zero(),
start: Vector2(10, 10),
end: Vector2(20, 20),
pathwayWidth: pathwayWidth,
width: width,
singleWall: true,
);
await game.ensureAdd(pathway);
@ -121,13 +126,13 @@ void main() {
);
flameTester.test(
'exists two ChainShape if there is by default two walls',
'has two ChainShape when singleWall is false (default)',
(game) async {
final pathway = Pathway.straight(
position: Vector2.zero(),
start: Vector2(10, 10),
end: Vector2(20, 20),
pathwayWidth: pathwayWidth,
width: width,
);
await game.ensureAdd(pathway);
@ -147,11 +152,12 @@ void main() {
(game) async {
final pathway = Pathway.arc(
position: Vector2.zero(),
pathwayWidth: pathwayWidth,
radius: 100,
angle: 90,
width: width,
radius: math.pi / 2,
angle: math.pi / 2,
);
await game.ensureAdd(pathway);
expect(game.contains(pathway), isTrue);
},
);
@ -163,13 +169,13 @@ void main() {
final position = Vector2.all(10);
final pathway = Pathway.arc(
position: position,
pathwayWidth: pathwayWidth,
radius: 100,
angle: 90,
width: width,
radius: math.pi / 2,
angle: math.pi / 2,
);
await game.ensureAdd(pathway);
game.contains(pathway);
game.contains(pathway);
expect(pathway.body.position, position);
},
);
@ -179,9 +185,9 @@ void main() {
(game) async {
final pathway = Pathway.arc(
position: Vector2.zero(),
pathwayWidth: pathwayWidth,
radius: 100,
angle: 90,
width: width,
radius: math.pi / 2,
angle: math.pi / 2,
);
await game.ensureAdd(pathway);
@ -205,9 +211,10 @@ void main() {
final pathway = Pathway.bezierCurve(
position: Vector2.zero(),
controlPoints: controlPoints,
pathwayWidth: pathwayWidth,
width: width,
);
await game.ensureAdd(pathway);
expect(game.contains(pathway), isTrue);
},
);
@ -220,11 +227,11 @@ void main() {
final pathway = Pathway.bezierCurve(
position: position,
controlPoints: controlPoints,
pathwayWidth: pathwayWidth,
width: width,
);
await game.ensureAdd(pathway);
game.contains(pathway);
game.contains(pathway);
expect(pathway.body.position, position);
},
);
@ -235,7 +242,7 @@ void main() {
final pathway = Pathway.bezierCurve(
position: Vector2.zero(),
controlPoints: controlPoints,
pathwayWidth: pathwayWidth,
width: width,
);
await game.ensureAdd(pathway);

Loading…
Cancel
Save