refactor: changed Pathway for Shapes

pull/87/head
alestiago 4 years ago
parent b3fc00a6bf
commit de0a0b1761

@ -1,209 +1,130 @@
// ignore_for_file: public_member_api_docs
import 'package:flame/extensions.dart'; import 'package:flame/extensions.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:geometry/geometry.dart'; import 'package:geometry/geometry.dart';
import 'package:pinball/game/game.dart';
/// {@template pathway} /// {@template arc_shape}
/// [Pathway] creates lines of various shapes. /// Creates an arc.
///
/// [BodyComponent]s such as a Ball can collide and move along a [Pathway].
/// {@endtemplate} /// {@endtemplate}
class Pathway extends BodyComponent with InitialPosition, Layered { class ArcShape extends ChainShape {
Pathway._({ /// {@macro arc_shape}
// TODO(ruialonso): remove color when assets added. ArcShape({
Color? color, required this.center,
required List<List<Vector2>> paths, required this.arcRadius,
}) : _paths = paths { required this.angle,
paint = Paint() required this.rotation,
..color = color ?? const Color.fromARGB(0, 0, 0, 0)
..style = PaintingStyle.stroke;
}
/// Creates a uniform unidirectional (straight) [Pathway].
///
/// Does so with two [ChainShape] separated by a [width]. 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 start,
required Vector2 end,
required double width,
double rotation = 0,
bool singleWall = false,
}) { }) {
final paths = <List<Vector2>>[]; createChain(
calculateArc(
// TODO(ruialonso): Refactor repetitive logic
final firstWall = [
start.clone(),
end.clone(),
].map((vector) => vector..rotate(rotation)).toList();
paths.add(firstWall);
if (!singleWall) {
final secondWall = [
start + Vector2(width, 0),
end + Vector2(width, 0),
].map((vector) => vector..rotate(rotation)).toList();
paths.add(secondWall);
}
return Pathway._(
color: color,
paths: paths,
);
}
/// Creates an arc [Pathway].
///
/// The [angle], in radians, specifies the size of the arc. For example, two
/// pi returns a complete circumference.
///
/// 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 the [radius] minus the [width].
///
/// If [singleWall] is true, just one [ChainShape] is created.
factory Pathway.arc({
Color? color,
required Vector2 center,
required double width,
required double radius,
required double angle,
double rotation = 0,
bool singleWall = false,
}) {
final paths = <List<Vector2>>[];
// TODO(ruialonso): Refactor repetitive logic
final outerWall = calculateArc(
center: center, center: center,
radius: radius, radius: radius,
angle: angle, angle: angle,
offsetAngle: rotation, offsetAngle: rotation,
),
); );
paths.add(outerWall); }
if (!singleWall) { /// The center of the arc.
final innerWall = calculateArc( final Vector2 center;
center: center,
radius: radius - width, /// The radius of the arc.
angle: angle, // TODO(alestiago): Check if modifying the parent radius makes sense.
offsetAngle: rotation, final double arcRadius;
/// Specifies the size of the arc, in radians.
///
/// For example, two pi returns a complete circumference.
final double angle;
/// Which can be rotated by a given [rotation] in radians.
final double rotation;
ArcShape copyWith({
Vector2? center,
double? arcRadius,
double? angle,
double? rotation,
}) =>
ArcShape(
center: center ?? this.center,
arcRadius: arcRadius ?? this.arcRadius,
angle: angle ?? this.angle,
rotation: rotation ?? this.rotation,
); );
paths.add(innerWall);
} }
return Pathway._( /// {@template bezier_curve_shape}
color: color, /// Creates a bezier curve.
paths: paths, /// {@endtemplate}
class BezierCurveShape extends ChainShape {
/// {@macro bezier_curve_shape}
BezierCurveShape({
required this.controlPoints,
}) {
createChain(
calculateBezierCurve(controlPoints: controlPoints),
); );
} }
/// Creates a bezier curve [Pathway]. /// Specifies the control points of the curve.
///
/// 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, /// First and last [controlPoints] set the beginning and end of the curve,
/// inner points between them set its final shape. /// inner points between them set its final shape.
/// final List<Vector2> controlPoints;
/// If [singleWall] is true, just one [ChainShape] is created.
factory Pathway.bezierCurve({
Color? color,
required List<Vector2> controlPoints,
required double width,
double rotation = 0,
bool singleWall = false,
}) {
final paths = <List<Vector2>>[];
// TODO(ruialonso): Refactor repetitive logic
final firstWall = calculateBezierCurve(controlPoints: controlPoints)
.map((vector) => vector..rotate(rotation))
.toList();
paths.add(firstWall);
if (!singleWall) {
final secondWall = calculateBezierCurve(
controlPoints: controlPoints
.map((vector) => vector + Vector2(width, -width))
.toList(),
).map((vector) => vector..rotate(rotation)).toList();
paths.add(secondWall);
}
return Pathway._( /// Rotates the curve by a given [angle] in radians.
color: color, void rotate(double angle) {
paths: paths, vertices.map((vector) => vector..rotate(angle)).toList();
); }
} }
/// Creates an ellipse [Pathway]. /// {@template ellipse_shape}
/// /// Creates an ellipse.
/// Does so with two [ChainShape]s separated by a [width]. Can /// {@endtemplate}
/// be rotated by a given [rotation] in radians. class EllipseShape extends ChainShape {
/// /// {@macro ellipse_shape}
/// If [singleWall] is true, just one [ChainShape] is created. EllipseShape({
factory Pathway.ellipse({ required this.center,
Color? color, required this.majorRadius,
required Vector2 center, required this.minorRadius,
required double width,
required double majorRadius,
required double minorRadius,
double rotation = 0,
bool singleWall = false,
}) { }) {
final paths = <List<Vector2>>[]; createChain(
calculateEllipse(
// TODO(ruialonso): Refactor repetitive logic
final outerWall = calculateEllipse(
center: center, center: center,
majorRadius: majorRadius, majorRadius: majorRadius,
minorRadius: minorRadius, minorRadius: minorRadius,
).map((vector) => vector..rotate(rotation)).toList(); ),
paths.add(outerWall);
if (!singleWall) {
final innerWall = calculateEllipse(
center: center,
majorRadius: majorRadius - width,
minorRadius: minorRadius - width,
).map((vector) => vector..rotate(rotation)).toList();
paths.add(innerWall);
}
return Pathway._(
color: color,
paths: paths,
); );
} }
final List<List<Vector2>> _paths; /// The top left corner of the ellipse.
///
/// Where the initial painting begines.
// TODO(ruialonso): Change to use appropiate center.
final Vector2 center;
/// Constructs different [ChainShape]s to form the [Pathway] shape. /// Major radius is specified by [majorRadius].
List<FixtureDef> createFixtureDefs() { final double majorRadius;
final fixturesDef = <FixtureDef>[];
for (final path in _paths) { /// Minor radius is specified by [minorRadius].
final chain = ChainShape()..createChain(path); final double minorRadius;
fixturesDef.add(FixtureDef(chain));
}
return fixturesDef; /// Rotates the ellipse by a given [angle] in radians.
void rotate(double angle) {
createChain(
vertices.map((vector) => vector..rotate(angle)).toList(),
);
} }
@override EllipseShape copyWith({
Body createBody() { Vector2? center,
final bodyDef = BodyDef()..position = initialPosition; double? majorRadius,
final body = world.createBody(bodyDef); double? minorRadius,
createFixtureDefs().forEach(body.createFixture); }) =>
EllipseShape(
return body; center: center ?? this.center,
} majorRadius: majorRadius ?? this.majorRadius,
minorRadius: minorRadius ?? this.minorRadius,
);
} }

Loading…
Cancel
Save