Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 24 additions & 20 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:jump_app/screens/terms_and_services_screen.dart';
import 'package:provider/provider.dart';
Expand All @@ -11,9 +12,11 @@ import '/screens/jump_screen.dart';
void main() async {
// Turn off portrait mode
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
]);
if (!kIsWeb) {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
]);
}

runApp(const MyApp());
}
Expand All @@ -25,22 +28,23 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
final app = AppParameters();
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (ctx) => LocaleText(language: 'Fr')),
ChangeNotifierProvider(
create: (ctx) =>
Biomechanics(app.jumpDescription.bounds.initial)),
ChangeNotifierProvider(create: (ctx) => app),
],
child: MaterialApp(
title: 'Jump app',
debugShowCheckedModeBanner: false,
routes: {
TermsAndServicesScreen.route: (context) =>
const TermsAndServicesScreen(),
JumpScreen.route: (context) => const JumpScreen()
},
initialRoute: TermsAndServicesScreen.route,
));
providers: [
ChangeNotifierProvider(create: (ctx) => LocaleText(language: 'Fr')),
ChangeNotifierProvider(
create: (ctx) => Biomechanics(app.jumpDescription.bounds.initial),
),
ChangeNotifierProvider(create: (ctx) => app),
],
child: MaterialApp(
title: 'Jump app',
debugShowCheckedModeBanner: false,
routes: {
TermsAndServicesScreen.route: (context) =>
const TermsAndServicesScreen(),
JumpScreen.route: (context) => const JumpScreen(),
},
initialRoute: TermsAndServicesScreen.route,
),
);
}
}
2 changes: 1 addition & 1 deletion lib/providers/biomechanics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Biomechanics with ChangeNotifier {
notifyListeners();
}

late final double _finalInertia; // kg.m^2
late double _finalInertia; // kg.m^2
double get finalInertia => _finalInertia;
set finalInertia(double value) {
_finalInertia = MathHelper.roundToPrecision(value, 2);
Expand Down
6 changes: 5 additions & 1 deletion lib/widgets/background_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class BackgoundImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Positioned(
left: 0, right: 0, bottom: floor, child: Image.asset(imagePath));
left: 0,
right: 0,
bottom: floor,
child: Image.asset(imagePath, key: ValueKey(imagePath)),
);
}
}
48 changes: 32 additions & 16 deletions lib/widgets/skater_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ import 'pushoff_phase.dart';
import 'total_rotation.dart';

class SkaterImage extends StatefulWidget {
const SkaterImage({
super.key,
this.width,
});
const SkaterImage({super.key, this.width});

final double? width;

Expand All @@ -43,20 +40,30 @@ class _SkaterImageState extends State<SkaterImage> {
final jumpHeigh = 0.100 * w;

final comStart = Offset(0.265 * w, -floor - 0.100 * w);
final comSliderStartPosition =
Offset(comStart.dx - 0.09 * w, floor + 0.045 * w);
final comSliderStartPosition = Offset(
comStart.dx - 0.09 * w,
floor + 0.045 * w,
);
final comFinal = Offset(0.791 * w, -floor - 0.115 * w);

final comSliderFinalPosition =
Offset(comFinal.dx + 0.03 * w, floor + 0.045 * w);
final comSliderFinalPosition = Offset(
comFinal.dx + 0.03 * w,
floor + 0.045 * w,
);
final comSliderHeight = 0.15 * w;
final comMid = Offset(
(comStart.dx + comFinal.dx) / 2, (comStart.dy + comFinal.dy) / 2);
(comStart.dx + comFinal.dx) / 2,
(comStart.dy + comFinal.dy) / 2,
);
final comSize = 0.011 * w;
final rotationPosition =
Offset(comFinal.dx - 0.05 * w, comFinal.dy - 0.1 * w);
final maximalRotationPosition =
Offset(comMid.dx - 0.07 * w, comFinal.dy - 0.02 * w);
final rotationPosition = Offset(
comFinal.dx - 0.05 * w,
comFinal.dy - 0.1 * w,
);
final maximalRotationPosition = Offset(
comMid.dx - 0.07 * w,
comFinal.dy - 0.02 * w,
);

final initialRotationSliderPosition = Offset(0.14 * w, 0.105 * w + floor);
final initialRotationSliderSize = 0.20 * w;
Expand Down Expand Up @@ -91,7 +98,11 @@ class _SkaterImageState extends State<SkaterImage> {
alignment: Alignment.bottomLeft,
children: [
const Header(),
BackgoundImage(floor: floor, imagePath: app.jumpDescription.path),
BackgoundImage(
key: ValueKey(app.jumpDescription.path),
floor: floor,
imagePath: app.jumpDescription.path,
),
if (isTranslation)
GroundReactionForce(
arrowHead: grfArrow,
Expand Down Expand Up @@ -126,7 +137,9 @@ class _SkaterImageState extends State<SkaterImage> {
MaximalRotationVelocity(position: maximalRotationPosition),
if (isTranslation)
FlightApex(
apex: Offset(comMid.dx, comMid.dy - jumpHeigh), floor: floor),
apex: Offset(comMid.dx, comMid.dy - jumpHeigh),
floor: floor,
),
TotalRotation(position: rotationPosition),
if (isRotation)
FlightInertia(
Expand All @@ -153,7 +166,10 @@ class _SkaterImageState extends State<SkaterImage> {
),
if (!isTranslation && !isRotation)
LandingPhase(
comFinal: comFinal, arrowsBelow: arrowsBelow, land: land),
comFinal: comFinal,
arrowsBelow: arrowsBelow,
land: land,
),
if (isRotation)
PreJumpRotation(
initialRotationSliderPosition: initialRotationSliderPosition,
Expand Down
32 changes: 32 additions & 0 deletions test/background_image_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jump_app/widgets/background_image.dart';

void main() {
Widget buildFrame(String imagePath) {
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(children: [BackgoundImage(floor: 10, imagePath: imagePath)]),
);
}

testWidgets('updates the displayed asset when the image path changes', (
tester,
) async {
const axelPath = 'assets/images/key_frames_axel5.png';
const lutzPath = 'assets/images/key_frames_lutz4.png';

await tester.pumpWidget(buildFrame(axelPath));

var image = tester.widget<Image>(find.byType(Image));
expect(image.key, const ValueKey(axelPath));
expect((image.image as AssetImage).assetName, axelPath);

await tester.pumpWidget(buildFrame(lutzPath));
await tester.pump();

image = tester.widget<Image>(find.byType(Image));
expect(image.key, const ValueKey(lutzPath));
expect((image.image as AssetImage).assetName, lutzPath);
});
}
12 changes: 12 additions & 0 deletions test/biomechanics_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ void main() {
biomechanics.finalHeight = 1.244;
biomechanics.initialInertia = 2.126;
biomechanics.minimumInertia = 0.756;
biomechanics.finalInertia = 2.126;
biomechanics.timeToMinimumInertia = 0.1806;
biomechanics.timeToFinalInertia = 0.0204;
biomechanics.initialRotation = 0.256 * 2 * pi;
Expand All @@ -42,6 +43,7 @@ void main() {
expect(biomechanics.finalHeight, 1.24);
expect(biomechanics.initialInertia, 2.13);
expect(biomechanics.minimumInertia, 0.76);
expect(biomechanics.finalInertia, 2.13);
expect(biomechanics.timeToMinimumInertia * 1000, 181);
expect(biomechanics.timeToFinalInertia * 1000, 20);
expect(biomechanics.initialRotation / 2 / pi, closeTo(0.26, 1e-12));
Expand All @@ -55,6 +57,16 @@ void main() {
});

group('Biomechanics calculations', () {
test('can reset values when the selected jump changes', () {
final biomechanics = Biomechanics(JumpDescription.axel.bounds.initial);

expect(
() => biomechanics.setValues(JumpDescription.somersault.bounds.initial),
returnsNormally,
);
expect(biomechanics.finalInertia, 10);
});

test('force in bodyweights uses the selected athlete mass', () {
final biomechanics = Biomechanics(JumpDescription.lutz.bounds.initial);

Expand Down
27 changes: 3 additions & 24 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@

<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<meta name="description" content="S2M Jump App">

<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="jump_app">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
Expand All @@ -31,29 +31,8 @@

<title>jump_app</title>
<link rel="manifest" href="manifest.json">

<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
});
});
</script>
<script src="flutter_bootstrap.js" async></script>
</body>
</html>