27 Angajamente 1bf18cb614 ... 3319cc9f33

Autor SHA1 Permisiunea de a trimite mesaje. Dacă este dezactivată, utilizatorul nu va putea trimite nici un fel de mesaj Data
  Marcin Jaborski 3319cc9f33 Fix generating new location for weekly and remove address from register form 3 ani în urmă
  Marcin Jaborski 13d964db6f Merge remote-tracking branch 'origin/main' into push-notifications 3 ani în urmă
  Léo Salé d428486ffb fix: handle big font sizes 3 ani în urmă
  Léo Salé 1f1a4309d5 feat: add back button navigation 3 ani în urmă
  Léo Salé 9051190f44 feat: improve color contrast 3 ani în urmă
  Léo Salé e324ce191c feat: update score when doing a repetition 3 ani în urmă
  Léo Salé 70c08900b4 better fix 3 ani în urmă
  Léo Salé 9439a6199c fix 3 ani în urmă
  Léo Salé 8f7f8d9378 fix: walking 3 ani în urmă
  Léo Salé b4a5078379 to reset 3 ani în urmă
  Léo Salé 510c9b5bcc feat: add exercises 3 ani în urmă
  Léo Salé ea9ec9d225 feat: more ui 3 ani în urmă
  Léo Salé cc3495d538 feat: challenge pages 3 ani în urmă
  Léo Salé 61ced74e1b fix: coordinate to double 3 ani în urmă
  Léo Salé a242d97d01 feat: store user data on log in 3 ani în urmă
  Léo Salé 1cf139132c feat: friends page 3 ani în urmă
  Léo Salé b3c3440dfa feat: navigation menu 3 ani în urmă
  Leo Sale 12b8f9194a Merge branch 'marcin-local' of iwa-13/PhysiGo into main 3 ani în urmă
  Marcin Jaborski bcda7ec4e1 Merge remote-tracking branch 'origin/marcin-local' into marcin-local 3 ani în urmă
  Marcin Jaborski d34255232f Add default names of places 3 ani în urmă
  Marcin Jaborski 01cf4e60f3 Find place for challenge with geoflutterfire 3 ani în urmă
  Marcin Jaborski ec3e7a65cb Challenge widget 3 ani în urmă
  Marcin Jaborski 0980634dbc Generating challenges 3 ani în urmă
  Marcin Jaborski 1b2b0fea18 Merge remote-tracking branch 'origin/marcin-local' into marcin-local 3 ani în urmă
  Marcin Jaborski f5e6962dac Add default names of places 3 ani în urmă
  Marcin Jaborski 9988132e7f Merge branch 'main' into marcin-local 3 ani în urmă
  Marcin Jaborski f30f5f78f1 Add default names of places 3 ani în urmă
44 a modificat fișierele cu 3893 adăugiri și 895 ștergeri
  1. 26 12
      app/lib/Services/AuthService.dart
  2. 1 1
      app/lib/Services/DatabaseManager.dart
  3. 30 0
      app/lib/Services/logged_in_user.dart
  4. 2 13
      app/lib/SignupPage.dart
  5. 34 0
      app/lib/challenge/daily_challenge_page.dart
  6. 22 0
      app/lib/challenge/models/challenge.dart
  7. 31 0
      app/lib/challenge/services/challenge_service.dart
  8. 93 0
      app/lib/challenge/weekly_challenge_page.dart
  9. 75 0
      app/lib/challenge/widgets/challenge_exercises.dart
  10. 29 29
      app/lib/challenges/challenge.dart
  11. 33 33
      app/lib/challenges/challenge_page.dart
  12. 17 21
      app/lib/challenges/challenges_utils.dart
  13. 20 23
      app/lib/currentUser.dart
  14. 160 27
      app/lib/exercises/exercises_page.dart
  15. 0 115
      app/lib/exercises/exercises_validation/meanFilteredData.csv
  16. 185 15
      app/lib/exercises/exercises_validation/models/exercise.dart
  17. 0 22
      app/lib/exercises/exercises_validation/models/push_up.dart
  18. 0 22
      app/lib/exercises/exercises_validation/models/squat.dart
  19. 0 76
      app/lib/exercises/exercises_validation/plot.py
  20. 89 0
      app/lib/exercises/exercises_validation/widgets/exercise_indicator.dart
  21. 74 70
      app/lib/exercises/exercises_validation/widgets/pose_detector.dart
  22. 35 0
      app/lib/exercises/services/exercises_service.dart
  23. 19 21
      app/lib/friends/friends_page.dart
  24. 6 3
      app/lib/friends/widgets/friends_list.dart
  25. 93 0
      app/lib/home/home_page.dart
  26. 1 1
      app/lib/logIn.dart
  27. 27 20
      app/lib/main.dart
  28. 0 37
      app/lib/mainPage.dart
  29. 52 0
      app/lib/menu/main_page.dart
  30. 1 1
      app/lib/navigation/models/directions.dart
  31. 3 2
      app/lib/navigation/navigation_page.dart
  32. 3 1
      app/lib/push_notifications_initializer.dart
  33. 49 0
      app/lib/ranking/ranking_page.dart
  34. 22 0
      app/lib/ranking/widgets/ranking.dart
  35. 0 136
      app/lib/walking/walking_counter.dart
  36. 4 6
      app/lib/walking/walking_page.dart
  37. 104 0
      app/lib/walking/widgets/walking.dart
  38. 72 92
      app/lib/widgets/datatable.dart
  39. 0 77
      app/lib/widgets/rankingNavigation.dart
  40. 21 0
      app/pubspec.lock
  41. 1 0
      app/pubspec.yaml
  42. 11 5
      scripts/fetch_places.py
  43. 13 13
      scripts/map features.txt
  44. 2435 1
      serverless_functions/package-lock.json

+ 26 - 12
app/lib/Services/AuthService.dart

@@ -1,17 +1,22 @@
 import 'package:cloud_firestore/cloud_firestore.dart';
 import 'package:firebase_auth/firebase_auth.dart';
 import 'package:physigo/Services/DatabaseManager.dart';
+import 'package:physigo/Services/logged_in_user.dart';
+import 'package:location/location.dart';
 
 class AuthenticationServices {
-  final FirebaseAuth _auth = FirebaseAuth.instance;
+  static final FirebaseAuth _auth = FirebaseAuth.instance;
+  static LoggedInUser? user;
 
   //Register a user
-  Future createNewUser(String address, bool anonymous, String birth, String name, String email, String password,
+  static Future createNewUser(bool anonymous, String birth, String name, String email, String password,
       String phone, String sharedID, String surname) async {
     try {
       UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
       User? user = result.user;
-      await DatabaseManager("total_points").createUserData(address, anonymous, birth, DateTime.now(), DateTime.now(),
+      LocationData userLocation = await Location.instance.getLocation();
+      GeoPoint userGeoPoint = GeoPoint(userLocation.latitude!, userLocation.longitude!);
+      await DatabaseManager("total_points").createUserData(userGeoPoint, anonymous, birth, DateTime.now(), DateTime.utc(1970, 1, 1),
           email, name, phone, sharedID, surname, 0, DateTime.now(), 'null', user!.uid);
 
       return user;
@@ -21,27 +26,36 @@ class AuthenticationServices {
   }
 
   //LogIn with user
-  Future loginUser(String email, String password) async {
+  static Future loginUser(String email, String password) async {
     try {
       UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
+      await getCurrentUser();
       return result.user;
     } catch (e) {
       print(e.toString());
     }
   }
 
-  Future<dynamic> getCurrentUser() async {
-    User? user = _auth.currentUser;
-    var querySnapshot =
-        await FirebaseFirestore.instance.collection('profileInfo').where('mail', isEqualTo: user?.email).get();
-    return querySnapshot;
+  static Future<LoggedInUser?> getCurrentUser() async {
+    if (user != null) {
+      return user;
+    }
+    if (_auth.currentUser == null) {
+      return null;
+    }
+    var querySnapshot = await FirebaseFirestore.instance
+        .collection('profileInfo')
+        .where('mail', isEqualTo: _auth.currentUser?.email)
+        .get();
+    user = LoggedInUser.fromMap(querySnapshot.docs.first.data());
+    return user;
   }
 
-  Stream<int> getPlace(dynamic user) async* {
+  static Future<int> getPlace() async {
     var users = await FirebaseFirestore.instance
         .collection('Users')
-        .where("total_points", isGreaterThan: user["total_points"])
+        .where("total_points", isGreaterThan: user!.totalPoints)
         .get();
-    yield users.docs.length + 1;
+    return users.docs.length + 1;
   }
 }

+ 1 - 1
app/lib/Services/DatabaseManager.dart

@@ -10,7 +10,7 @@ class DatabaseManager {
 
   DatabaseManager(this.points);
 
-  Future<void> createUserData(String address,
+  Future<void> createUserData(GeoPoint address,
       bool anonymous,
       String birth_date,
       DateTime create_date,

+ 30 - 0
app/lib/Services/logged_in_user.dart

@@ -0,0 +1,30 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+
+class LoggedInUser {
+  final String name;
+  final num totalPoints;
+  dynamic weeklyPlace;
+  final String sharedId;
+  final Timestamp lastChallengeDate;
+  final GeoPoint address;
+
+  LoggedInUser({
+    required this.name,
+    required this.totalPoints,
+    required this.weeklyPlace,
+    required this.sharedId,
+    required this.lastChallengeDate,
+    required this.address,
+  });
+
+  factory LoggedInUser.fromMap(Map<String, dynamic> map) {
+    return LoggedInUser(
+      name: map["name"],
+      totalPoints: map["total_points"],
+      weeklyPlace: map["weekly_place"],
+      sharedId: map["shared_id"],
+      lastChallengeDate: map["last_challenge_date"],
+      address: map["address"],
+    );
+  }
+}

+ 2 - 13
app/lib/SignupPage.dart

@@ -7,7 +7,6 @@ TextEditingController _name = TextEditingController();
 TextEditingController _surname = TextEditingController();
 TextEditingController _mail = TextEditingController();
 TextEditingController _phoneNumber = TextEditingController();
-TextEditingController _address = TextEditingController();
 TextEditingController _username = TextEditingController();
 TextEditingController _passwd = TextEditingController();
 TextEditingController _passwd2 = TextEditingController();
@@ -15,7 +14,6 @@ TextEditingController _passwd2 = TextEditingController();
 bool anonymous = false;
 String? _dateString;
 
-final AuthenticationServices _auth = AuthenticationServices();
 
 
 class SignupPage extends StatefulWidget {
@@ -199,13 +197,6 @@ class _SignupForm2 extends State<SignupForm2> {
             ),
           ),
           const SizedBox(height: 10),
-          TextFormField(
-            controller: _address,
-            decoration: InputDecoration(
-              hintText: 'Home Adress..',
-            ),
-          ),
-          const SizedBox(height: 10),
           Row(children: <Widget>[
             Text('Anonymous'),
             Checkbox(
@@ -305,7 +296,7 @@ class _SignupForm3 extends State<SignupForm3> {
 
   void createUser() async {
     dynamic result =
-        await _auth.createNewUser(_address.text, anonymous, _dateString.toString(), _name.text, _mail.text, _passwd.text,
+        await AuthenticationServices.createNewUser(anonymous, _dateString.toString(), _name.text, _mail.text, _passwd.text,
             _phoneNumber.text, _username.text, _surname.text);
     if (result == null) {
       print('mail not valid');
@@ -313,8 +304,7 @@ class _SignupForm3 extends State<SignupForm3> {
       //print(result.toString());
       //[51.787378° N, 19.449455° E]
 
-      print('*****  address: ' + _address.text +
-          ' anonymous: ' + anonymous.toString() +
+      print(' anonymous: ' + anonymous.toString() +
           ' birthdate: ' + _dateString.toString() +
           ' createDate: ' + DateTime.now().toString()+
           ' last_challenge_data: ' + 'null'+
@@ -332,7 +322,6 @@ class _SignupForm3 extends State<SignupForm3> {
       _surname.clear();
       _mail.clear();
       _phoneNumber.clear();
-      _address.clear();
       _username.clear();
       _passwd.clear();
       _passwd2.clear();

+ 34 - 0
app/lib/challenge/daily_challenge_page.dart

@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/challenge/services/challenge_service.dart';
+import 'package:physigo/challenge/widgets/challenge_exercises.dart';
+
+class DailyChallengePage extends StatelessWidget {
+  const DailyChallengePage({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(iconTheme: const IconThemeData(color: Colors.white, size: 36), ),
+      body: Padding(
+        padding: const EdgeInsets.all(16.0),
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: const [
+            Center(
+              child: Text(
+                "Daily Challenge",
+                textAlign: TextAlign.center,
+                style: TextStyle(
+                  fontSize: 28,
+                  fontWeight: FontWeight.bold,
+                ),
+              ),
+            ),
+            SizedBox(height: 48),
+            ChallengeExercises(getChallenge: ChallengeService.getDailyChallenge),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 22 - 0
app/lib/challenge/models/challenge.dart

@@ -0,0 +1,22 @@
+import '../../exercises/exercises_validation/models/exercise.dart';
+
+class Challenge {
+  final List<Exercise> exercises;
+  final int bonusExercise;
+
+  const Challenge({
+    required this.exercises,
+    required this.bonusExercise,
+  });
+
+  factory Challenge.fromMap(Map<String, dynamic> data) {
+    return Challenge(
+      exercises: _getExercise(data["list_exercises"]),
+      bonusExercise: data["bonus_exercise"],
+    );
+  }
+
+  static List<Exercise> _getExercise(List<Map<String, dynamic>> exercises) {
+    return exercises.map((exercise) => Exercise.fromMap(exercise["id"], exercise)).toList();
+  }
+}

+ 31 - 0
app/lib/challenge/services/challenge_service.dart

@@ -0,0 +1,31 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+
+import '../models/challenge.dart';
+
+class ChallengeService {
+  static final _db = FirebaseFirestore.instance;
+  static final _dailyChallenges = _db.collection("DailyChallenges");
+  static final _weeklyChallenges = _db.collection("WeeklyChallenges");
+
+  static Future<Challenge> getDailyChallenge() async {
+    final challenges = await _dailyChallenges.orderBy("date", descending: true).limit(1).get();
+    final challenge = challenges.docs.first.data();
+    challenge["list_exercises"] = await _getExercisesFromChallenge(challenge);
+    return Challenge.fromMap(challenge);
+  }
+
+  static Future<Challenge> getWeeklyChallenge() async {
+    final challenges = await _weeklyChallenges.orderBy("date", descending: true).limit(1).get();
+    final challenge = challenges.docs.first.data();
+    challenge["list_exercises"] = await _getExercisesFromChallenge(challenge);
+    return Challenge.fromMap(challenge);
+  }
+
+  static Future<List<Map<String, dynamic>>> _getExercisesFromChallenge(Map<String, dynamic> challenge) async {
+    return await Future.wait(
+        List<DocumentReference<Map<String, dynamic>>>.from(challenge["list_exercises"]).map((exerciseRef) async {
+      final exercise = await exerciseRef.get();
+      return {"id": exercise.id,...exercise.data()!};
+    }));
+  }
+}

+ 93 - 0
app/lib/challenge/weekly_challenge_page.dart

@@ -0,0 +1,93 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/Services/AuthService.dart';
+import 'package:physigo/challenge/services/challenge_service.dart';
+import 'package:physigo/challenge/widgets/challenge_exercises.dart';
+import 'package:physigo/navigation/navigation_page.dart';
+
+class WeeklyChallengePage extends StatelessWidget {
+  const WeeklyChallengePage({Key? key}) : super(key: key);
+  Future<dynamic> get weeklyPlaceFuture => AuthenticationServices.user!.weeklyPlace.get();
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder<dynamic>(
+        future: weeklyPlaceFuture,
+        builder: (context, snapshot) {
+          if (snapshot.connectionState == ConnectionState.waiting) {
+            return const Center(child: CircularProgressIndicator());
+          }
+          final weeklyPlace = snapshot.data.data();
+          final weeklyPlaceName = weeklyPlace["name"];
+          final weeklyPlaceLocation = weeklyPlace["location"]["geopoint"];
+          return Scaffold(
+            appBar: AppBar(
+              iconTheme: const IconThemeData(color: Colors.white, size: 36),
+            ),
+            body: Padding(
+              padding: const EdgeInsets.all(16.0),
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  const Center(
+                    child: Text(
+                      "Weekly Challenge",
+                      textAlign: TextAlign.center,
+                      style: TextStyle(
+                        fontSize: 28,
+                        fontWeight: FontWeight.bold,
+                      ),
+                    ),
+                  ),
+                  const SizedBox(height: 12),
+                  Center(
+                    child: Text(
+                      "Location: $weeklyPlaceName",
+                      textAlign: TextAlign.center,
+                      maxLines: 2,
+                      overflow: TextOverflow.ellipsis,
+                      style: const TextStyle(fontSize: 18),
+                    ),
+                  ),
+                  const SizedBox(height: 48),
+                  const ChallengeExercises(getChallenge: ChallengeService.getWeeklyChallenge),
+                  const SizedBox(height: 48),
+                  NavigationButton(weeklyPlaceLocation: weeklyPlaceLocation),
+                ],
+              ),
+            ),
+          );
+        });
+  }
+}
+
+class NavigationButton extends StatelessWidget {
+  const NavigationButton({
+    Key? key,
+    required this.weeklyPlaceLocation,
+  }) : super(key: key);
+
+  final dynamic weeklyPlaceLocation;
+
+  @override
+  Widget build(BuildContext context) {
+    return ElevatedButton(
+      style: ElevatedButton.styleFrom(primary: Colors.teal),
+      onPressed: () => Navigator.push(
+        context,
+        MaterialPageRoute(
+          builder: (_) => NavigationPage(destination: weeklyPlaceLocation),
+        ),
+      ),
+      child: Padding(
+        padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+          children: const [
+            Flexible(child: Text("Show me the way", style: TextStyle(fontSize: 24), maxLines: 2)),
+            Icon(Icons.map, size: 32),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 75 - 0
app/lib/challenge/widgets/challenge_exercises.dart

@@ -0,0 +1,75 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/challenge/models/challenge.dart';
+import 'package:physigo/exercises/exercises_validation/exercise_validation_page.dart';
+
+import '../../exercises/exercises_validation/models/exercise.dart';
+
+class ChallengeExercises extends StatelessWidget {
+  final Future<Challenge> Function() getChallenge;
+  const ChallengeExercises({required this.getChallenge, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder<Challenge>(
+      future: getChallenge(),
+      builder: (context, snapshot) {
+        if (snapshot.connectionState == ConnectionState.waiting) {
+          return const CircularProgressIndicator();
+        }
+        final challenge = snapshot.data!;
+        return Column(
+          children: challenge.exercises
+              .asMap()
+              .entries
+              .map((e) => ExerciseTile(
+                    exercise: e.value,
+                    isBonusExercise: e.key == challenge.bonusExercise,
+                  ))
+              .toList(),
+        );
+      },
+    );
+  }
+}
+
+class ExerciseTile extends StatelessWidget {
+  final Exercise exercise;
+  final bool isBonusExercise;
+  const ExerciseTile({required this.exercise, required this.isBonusExercise, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+      elevation: 3,
+      color: isBonusExercise ? Colors.orange : Colors.blueGrey,
+      child: ListTile(
+        iconColor: Colors.white,
+        textColor: Colors.white,
+        title: Text(
+          exercise.name.toUpperCase(),
+          textAlign: TextAlign.center,
+          style: const TextStyle(
+            fontSize: 18,
+            fontWeight: FontWeight.bold,
+            letterSpacing: 1.8,
+          ),
+        ),
+        minLeadingWidth: 24,
+        leading: isBonusExercise ? const Icon(Icons.star, size: 24) : const SizedBox(width: 24),
+        subtitle: isBonusExercise
+            ? const Text(
+                "Bonus exercise, earn 2x points!",
+                textAlign: TextAlign.center,
+                style: TextStyle(fontSize: 16),
+              )
+            : null,
+        trailing: const Icon(Icons.arrow_forward),
+        onTap: exercise.startMovement != null
+            ? () {
+                Navigator.push(context, MaterialPageRoute(builder: (_) => ExerciseValidationPage(exercise: exercise)));
+              }
+            : null,
+      ),
+    );
+  }
+}

+ 29 - 29
app/lib/challenges/challenge.dart

@@ -1,31 +1,31 @@
-import 'package:cloud_firestore/cloud_firestore.dart';
-import 'package:flutter/material.dart';
-import 'package:latlong2/latlong.dart';
+// import 'package:cloud_firestore/cloud_firestore.dart';
+// import 'package:flutter/material.dart';
+// import 'package:latlong2/latlong.dart';
 
-import '../navigation/navigation_page.dart';
+// import '../navigation/navigation_page.dart';
 
-Widget buildChallenge(DocumentSnapshot<Map<String, dynamic>> data, BuildContext context) {
-  final LatLng coordinates =
-      LatLng(data.get('location')['geopoint'].latitude, data.get('location')['geopoint'].longitude);
-  return Center(
-    child: Column(
-      mainAxisAlignment: MainAxisAlignment.center,
-      children: [
-        const Text('Your challenge for this week:'),
-        Text(data.get('name')),
-        TextButton(
-            onPressed: () {
-              Navigator.push(
-                context,
-                MaterialPageRoute(
-                  builder: (context) => NavigationPage(
-                    destination: coordinates,
-                  ),
-                ),
-              );
-            },
-            child: const Text('Navigate me'))
-      ],
-    ),
-  );
-}
+// Widget buildChallenge(DocumentSnapshot<Map<String, dynamic>> data, BuildContext context) {
+//   final LatLng coordinates =
+//       LatLng(data.get('location')['geopoint'].latitude, data.get('location')['geopoint'].longitude);
+//   return Center(
+//     child: Column(
+//       mainAxisAlignment: MainAxisAlignment.center,
+//       children: [
+//         const Text('Your challenge for this week:'),
+//         Text(data.get('name')),
+//         TextButton(
+//             onPressed: () {
+//               Navigator.push(
+//                 context,
+//                 MaterialPageRoute(
+//                   builder: (context) => NavigationPage(
+//                     destination: coordinates,
+//                   ),
+//                 ),
+//               );
+//             },
+//             child: const Text('Navigate me'))
+//       ],
+//     ),
+//   );
+// }

+ 33 - 33
app/lib/challenges/challenge_page.dart

@@ -1,35 +1,35 @@
-import 'package:cloud_firestore/cloud_firestore.dart';
-import 'package:flutter/material.dart';
-import 'package:physigo/challenges/challenges_utils.dart';
-import 'package:physigo/challenges/challenge.dart';
+// import 'package:cloud_firestore/cloud_firestore.dart';
+// import 'package:flutter/material.dart';
+// import 'package:physigo/challenges/challenges_utils.dart';
+// import 'package:physigo/challenges/challenge.dart';
 
-class ChallengePage extends StatelessWidget {
-  const ChallengePage({Key? key}) : super(key: key);
+// class ChallengePage extends StatelessWidget {
+//   const ChallengePage({Key? key}) : super(key: key);
 
-  @override
-  Widget build(BuildContext context) {
-    return Scaffold(
-      body: FutureBuilder<DocumentSnapshot<Map<String, dynamic>>>(
-        future: ChallengesUtils.getChallenge(),
-        builder: (context, snapshot) {
-          switch (snapshot.connectionState) {
-            case ConnectionState.waiting:
-              return const Center(
-                child: Text('Getting challenges...'),
-              );
-            case ConnectionState.done:
-              if (snapshot.hasError) {
-                return const Text('Error occurred');
-              }
-              if (snapshot.hasData) {
-                return buildChallenge(snapshot.data!, context);
-              }
-              return const Text('No data');
-            default:
-              return const Text('Dunno');
-          }
-        },
-      ),
-    );
-  }
-}
+//   @override
+//   Widget build(BuildContext context) {
+//     return Scaffold(
+//       body: FutureBuilder<DocumentSnapshot<Map<String, dynamic>>>(
+//         future: ChallengesUtils.getChallenge(),
+//         builder: (context, snapshot) {
+//           switch (snapshot.connectionState) {
+//             case ConnectionState.waiting:
+//               return const Center(
+//                 child: Text('Getting challenges...'),
+//               );
+//             case ConnectionState.done:
+//               if (snapshot.hasError) {
+//                 return const Text('Error occurred');
+//               }
+//               if (snapshot.hasData) {
+//                 return buildChallenge(snapshot.data!, context);
+//               }
+//               return const Text('No data');
+//             default:
+//               return const Text('Dunno');
+//           }
+//         },
+//       ),
+//     );
+//   }
+// }

+ 17 - 21
app/lib/challenges/challenges_utils.dart

@@ -1,50 +1,46 @@
 import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:firebase_auth/firebase_auth.dart';
 import 'package:geoflutterfire/geoflutterfire.dart';
 import 'dart:math';
 
+import '../Services/AuthService.dart';
+import '../Services/logged_in_user.dart';
+
 class ChallengesUtils {
   static final geo = Geoflutterfire();
   static final _firestore = FirebaseFirestore.instance;
 
-  static Future<DocumentSnapshot<Map<String, dynamic>>> getChallenge() async {
-    Future<DocumentSnapshot<Map<String, dynamic>>> challenge;
+  static generateChallengeIfNeeded() async {
     if (await shouldGenerateChallenge()) {
-      challenge = generateChallenge();
-    } else {
-      final user = _firestore.collection('Users').doc('tlmysIvwTBaoZKWqBofx');
-      challenge = user.get().then((value) => value.get('weekly_place').get());
+      generateChallenge();
     }
-    return challenge;
   }
 
   static Future<bool> shouldGenerateChallenge() async {
-    final user = _firestore.collection('Users').doc('tlmysIvwTBaoZKWqBofx');
+    LoggedInUser? user = await AuthenticationServices.getCurrentUser();
     DateTime now = DateTime.now();
     DateTime today = DateTime(now.year, now.month, now.day);
     DateTime lastMonday = today.subtract(Duration(days: today.weekday - 1));
     int secondsSinceEpoch = lastMonday.millisecondsSinceEpoch ~/ 1000;
-    bool shouldGenerate = await user.get().then((value) {
-      final dif = secondsSinceEpoch - value.get('last_challenge_date').seconds;
-      return dif / 3600 / 24 > 7;
-    });
-    return Future.value(shouldGenerate);
+    final dif = secondsSinceEpoch - user!.lastChallengeDate.seconds;
+    return Future.value(dif / 3600 / 24 > 7);
   }
 
-  static Future<DocumentSnapshot<Map<String, dynamic>>> generateChallenge() async {
-    final user = await _firestore.collection('Users').doc('tlmysIvwTBaoZKWqBofx').get();
+  static void generateChallenge() async {
+    LoggedInUser? user = await AuthenticationServices.getCurrentUser();
     Query<Map<String, dynamic>> places = _firestore.collection('Places');
-    double innerRadius = 3;
-    double outerRadius = 5;
+    double innerRadius = 1;
+    double outerRadius = 2.5;
     String randomPlaceID = '';
 
     Stream<List<DocumentSnapshot>> possiblePlaces = geo.collection(collectionRef: places).within(
-        center: geo.point(latitude: user.get('address').latitude, longitude: user.get('address').longitude),
+        center: geo.point(latitude: user!.address.latitude, longitude: user.address.longitude),
         radius: outerRadius,
         field: 'location',
         strictMode: true);
 
     Stream<List<DocumentSnapshot>> placesTooClose = geo.collection(collectionRef: places).within(
-        center: geo.point(latitude: user.get('address').latitude, longitude: user.get('address').longitude),
+        center: geo.point(latitude: user.address.latitude, longitude: user.address.longitude),
         radius: innerRadius,
         field: 'location',
         strictMode: true);
@@ -58,10 +54,10 @@ class ChallengesUtils {
     possiblePlacesList.retainWhere((element) => !ids.contains(element.id));
     int numOfPlaces = possiblePlacesList.length;
     randomPlaceID = possiblePlacesList[Random().nextInt(numOfPlaces)].id;
-    _firestore.collection('Users').doc('tlmysIvwTBaoZKWqBofx').update({
+    AuthenticationServices.user!.weeklyPlace = _firestore.collection('Places').doc(randomPlaceID);
+    _firestore.collection("profileInfo").doc(FirebaseAuth.instance.currentUser?.uid).update({
       'weekly_place': _firestore.collection('Places').doc(randomPlaceID),
       'last_challenge_date': Timestamp.now(),
     });
-    return _firestore.collection('Places').doc(randomPlaceID).get();
   }
 }

+ 20 - 23
app/lib/currentUser.dart

@@ -1,11 +1,11 @@
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
+import 'package:physigo/Services/logged_in_user.dart';
 import 'Services/AuthService.dart';
 
 class CurrentUser extends StatefulWidget {
-  const CurrentUser( {
-    required this.name, Key? key}):super(key: key);
+  const CurrentUser({required this.name, Key? key}) : super(key: key);
   final String name;
   @override
   State<CurrentUser> createState() => _CurrentUserState();
@@ -13,20 +13,18 @@ class CurrentUser extends StatefulWidget {
 
 class _CurrentUserState extends State<CurrentUser> {
   final AuthenticationServices _auth = AuthenticationServices();
-  late dynamic user = _auth.getCurrentUser();
+  late Future<LoggedInUser?> user = AuthenticationServices.getCurrentUser();
 
   @override
   Widget build(BuildContext context) {
-    return FutureBuilder<dynamic>(
+    return FutureBuilder<LoggedInUser?>(
       future: user,
       builder: (context, snapshot) {
-        if(snapshot.connectionState == ConnectionState.waiting){
+        if (snapshot.connectionState == ConnectionState.waiting) {
           return const Text('Wait a moment');
-        }
-        else if(snapshot.connectionState == ConnectionState.done) {
-          dynamic user2 = snapshot.data.docs[0];
+        } else if (snapshot.connectionState == ConnectionState.done) {
+          LoggedInUser user2 = snapshot.data!;
           // DatabaseManager manager = DatabaseManager(widget.name);
-           Stream<int> place = _auth.getPlace(user2);
           return Row(
             children: [
               SizedBox(
@@ -36,29 +34,28 @@ class _CurrentUserState extends State<CurrentUser> {
               SizedBox(
                 height: 100,
                 width: 50,
-              child: StreamBuilder(
-                stream: place,
-                builder: (context, snapshot){
-                  return Text(
-                      "${snapshot.data}");
-                },
-              ),
+                child: FutureBuilder(
+                  future: AuthenticationServices.getPlace(),
+                  builder: (context, snapshot) {
+                    return Text("${snapshot.data}");
+                  },
+                ),
               ),
               SizedBox(
                 height: 100,
                 width: 135,
-                child: Text(user2["shared_id"]),
+                child: Text(user2.sharedId),
               ),
               SizedBox(
                 height: 100,
                 width: 60,
-                child: Text(user2["name"]),
-              ),
-              SizedBox(
-                height: 100,
-                width: 50,
-                child: Text("${user2[widget.name]}"),
+                child: Text(user2.name),
               ),
+              // SizedBox(
+              //   height: 100,
+              //   width: 50,
+              //   child: Text("${user2[widget.name]}"),
+              // ),
             ],
           );
         }

+ 160 - 27
app/lib/exercises/exercises_page.dart

@@ -1,8 +1,9 @@
 import 'package:flutter/material.dart';
-import 'package:physigo/exercises/exercises_validation/models/squat.dart';
 
+import '../walking/walking_page.dart';
 import 'exercises_validation/exercise_validation_page.dart';
-import 'exercises_validation/models/push_up.dart';
+import 'exercises_validation/models/exercise.dart';
+import 'services/exercises_service.dart';
 
 class ExercisesPage extends StatelessWidget {
   const ExercisesPage({Key? key}) : super(key: key);
@@ -10,40 +11,172 @@ class ExercisesPage extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return Scaffold(
-      body: Column(
-        mainAxisAlignment: MainAxisAlignment.center,
-        children: [
-          Center(
-            child: ElevatedButton(
-              onPressed: () {
-                Navigator.push(
-                  context,
-                  MaterialPageRoute(
-                    builder: (context) => const ExerciseValidationPage(
-                      exercise: pushUp,
+      body: Padding(
+        padding: const EdgeInsets.all(16.0),
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            const Center(
+              child: Text(
+                "Exercises",
+                textAlign: TextAlign.center,
+                style: TextStyle(
+                  fontSize: 28,
+                  fontWeight: FontWeight.bold,
+                  // color: Colors.white,
+                ),
+              ),
+            ),
+            const SizedBox(height: 24),
+            const TextField(
+              decoration: InputDecoration(
+                border: OutlineInputBorder(),
+                hintText: "Search exercise...",
+              ),
+            ),
+            const SizedBox(height: 24),
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+              children: [
+                FilterChip(
+                  label: const Text("Easy", style: TextStyle(color: Colors.black, fontSize: 18)),
+                  selected: true,
+                  backgroundColor: Colors.green.shade300,
+                  selectedColor: Colors.green.shade300,
+                  checkmarkColor: Colors.black,
+                  onSelected: (_) {},
+                ),
+                FilterChip(
+                  label: const Text("Normal", style: TextStyle(color: Colors.black, fontSize: 18)),
+                  selected: true,
+                  backgroundColor: Colors.orange.shade300,
+                  selectedColor: Colors.orange.shade300,
+                  checkmarkColor: Colors.black,
+                  onSelected: (_) {},
+                ),
+                FilterChip(
+                  label: const Text("Hard", style: TextStyle(color: Colors.black, fontSize: 18)),
+                  selected: true,
+                  backgroundColor: Colors.red.shade300,
+                  selectedColor: Colors.red.shade300,
+                  checkmarkColor: Colors.black,
+                  onSelected: (_) {},
+                ),
+              ],
+            ),
+            const SizedBox(height: 48),
+            FutureBuilder<List<Exercise>>(
+              future: ExercisesService.getExercises(),
+              builder: (context, snapshot) {
+                if (snapshot.connectionState == ConnectionState.waiting) {
+                  return const Center(child: CircularProgressIndicator());
+                }
+                final exercises = snapshot.data!;
+                exercises.sort((a, b) => a.difficulty.compareTo(b.difficulty));
+                return Flexible(
+                  child: Scrollbar(
+                    isAlwaysShown: true,
+                    thickness: 8,
+                    child: ListView(
+                      shrinkWrap: true,
+                      children: [
+                        const WalkExerciseTile(),
+                        ...exercises.map((exercise) => ExerciseTile(exercise: exercise))
+                      ],
                     ),
                   ),
                 );
               },
-              child: const Text('Push up'),
+            )
+          ],
+        ),
+      ),
+    );
+  }
+}
+
+class ExerciseTile extends StatelessWidget {
+  final Exercise exercise;
+  const ExerciseTile({
+    required this.exercise,
+    Key? key,
+  }) : super(key: key);
+
+  Color get cardColor {
+    if (exercise.difficulty == 0) return Colors.green.shade300;
+    if (exercise.difficulty == 1) return Colors.orange.shade300;
+    if (exercise.difficulty == 2) return Colors.red.shade300;
+    return Colors.blueGrey;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Center(
+      child: Card(
+        color: cardColor,
+        elevation: 3,
+        child: ListTile(
+          iconColor: Colors.black,
+          textColor: Colors.black,
+          title: Text(
+            exercise.name.toUpperCase(),
+            textAlign: TextAlign.center,
+            style: const TextStyle(
+              fontSize: 18,
+              fontWeight: FontWeight.bold,
+              letterSpacing: 1.8,
             ),
           ),
-          Center(
-            child: ElevatedButton(
-              onPressed: () {
-                Navigator.push(
-                  context,
-                  MaterialPageRoute(
-                    builder: (context) => const ExerciseValidationPage(
-                      exercise: squat,
+          trailing: const Icon(Icons.arrow_forward),
+          onTap: exercise.startMovement != null // not all exercises have validation right now
+              ? () {
+                  Navigator.push(
+                    context,
+                    MaterialPageRoute(
+                      builder: (_) => ExerciseValidationPage(exercise: exercise),
                     ),
-                  ),
-                );
-              },
-              child: const Text('Squat'),
+                  );
+                }
+              : null,
+        ),
+      ),
+    );
+  }
+}
+
+class WalkExerciseTile extends StatelessWidget {
+  const WalkExerciseTile({
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Center(
+      child: Card(
+        color: Colors.green.shade300,
+        elevation: 3,
+        child: ListTile(
+          iconColor: Colors.black,
+          textColor: Colors.black,
+          title: Text(
+            "walk".toUpperCase(),
+            textAlign: TextAlign.center,
+            style: const TextStyle(
+              fontSize: 18,
+              fontWeight: FontWeight.bold,
+              letterSpacing: 1.8,
             ),
           ),
-        ],
+          trailing: const Icon(Icons.arrow_forward),
+          onTap: () {
+            Navigator.push(
+              context,
+              MaterialPageRoute(
+                builder: (_) => WalkingPage(),
+              ),
+            );
+          },
+        ),
       ),
     );
   }

+ 0 - 115
app/lib/exercises/exercises_validation/meanFilteredData.csv

@@ -1,115 +0,0 @@
-287.4857345581055, 568.4196289062501, -99.20217094421388;223.77404327392577, 479.2088012695313, 31.810911178588867;367.1575958251953, 481.2058288574219, 68.1833122253418;159.5632080078125, 384.4332794189453, 60.535033035278325;418.0308776855469, 385.0218505859375, 116.76266021728517;179.71376647949216, 288.20873413085934, 3.3081211328506464;408.63691711425776, 292.77028808593747, 47.37070541381836;245.55656890869142, 250.27068481445315, -12.099936819076538;340.81452636718745, 249.5531799316406, 12.540831995010377;244.34209136962892, 67.37737541198732, -55.24716453552246;358.6746704101562, 64.52450599670411, 1.1409255623817423;235.17531890869142, -71.9123016357422, 126.79845809936522;358.4949005126953, -75.59934043884277, 138.83372573852537;
-277.3113388061524, 558.4363098144531, -87.60459785461427;215.1403854370117, 471.14206237792973, 39.08097190856933;354.3775238037109, 473.64607543945317, 70.3926357269287;155.54925079345702, 377.0209503173828, 63.491790008544925;405.51905212402346, 377.90625, 108.59338684082032;172.52390747070314, 282.4958404541016, 5.522794270515441;397.98669128417964, 286.9440063476562, 34.351969909667964;236.8184753417969, 247.01797637939453, -10.6782555103302;329.8189239501953, 246.3216369628906, 11.136384057998656;234.7391159057617, 68.43863182067872, -42.47158069610595;348.3825988769531, 65.97445755004883, 7.185424196720123;224.64052734375002, -70.80169982910157, 117.21453781127929;348.4453491210937, -74.60537528991699, 124.09577789306641;
-270.56074981689454, 551.4118408203125, -79.11850509643554;208.6164535522461, 465.57167053222656, 40.47891502380371;344.9586669921875, 468.69081420898436, 70.25329399108887;153.16446685791016, 370.7834350585938, 62.25489196777343;395.46295166015625, 372.0100830078125, 103.390128326416;165.56335601806643, 277.3509979248047, 5.95127251148224;390.47259826660155, 282.22953491210933, 27.658829116821288;230.16194610595704, 245.8289535522461, -11.378282022476196;321.72080383300784, 245.29962005615235, 11.843807458877562;230.64291687011718, 69.88106765747071, -38.707248497009275;340.03643493652345, 67.41449394226073, 13.10458618402481;219.41356353759767, -74.263232421875, 103.04113311767577;340.4396514892578, -76.36397514343261, 117.5176498413086;
-266.6096908569336, 545.8109497070313, -77.02203598022462;204.44555053710937, 461.1707672119141, 38.03351097106933;338.885708618164, 464.14857482910156, 71.70675010681151;151.46490936279295, 366.63067016601565, 55.79946784973144;389.3224029541015, 368.3259185791016, 103.69652404785155;160.32393493652347, 274.0745422363281, -3.194315791130066;387.30827636718755, 278.4133270263672, 25.137400913238523;226.12735748291013, 243.58104705810547, -12.929290342330932;316.69667358398436, 243.0184158325195, 13.40792908668518;228.86105346679685, 69.74331741333009, -38.76639385223388;334.35886840820314, 67.56181945800782, 16.328343451023105;217.48383331298828, -77.07601280212403, 93.99613647460939;334.0723114013672, -78.53955993652343, 118.29548721313475;
-264.66703033447266, 540.1386352539063, -80.95198974609374;202.5697784423828, 456.9370880126953, 34.58341479301452;335.3580596923828, 459.425048828125, 70.30816574096681;150.6500030517578, 362.6568908691407, 49.76026525497436;386.0727264404297, 365.15423278808595, 99.7264518737793;157.17592010498046, 271.44948120117186, -13.48891532421112;386.16557006835933, 275.8453643798828, 16.315802478790282;223.97686157226562, 241.32455902099608, -14.149851322174072;313.07753295898436, 240.13409118652342, 14.630369758605955;227.32814331054684, 69.10256690979004, -38.93003597259521;330.1996307373047, 67.52256622314454, 18.61171146631241;217.05272674560544, -77.28567314147949, 89.82571029663086;328.2778259277344, -78.07855148315429, 121.39094390869138;
-264.73583221435547, 533.0442871093751, -92.6622589111328;202.05039520263674, 451.1524230957031, 25.38663439750671;334.2736358642578, 453.1440948486328, 60.66773672103882;151.41332702636717, 356.5593963623047, 40.02575044631958;385.15076293945316, 359.5098327636719, 89.89716634750364;157.9189437866211, 267.4340789794922, -24.673466086387634;384.2586334228515, 271.1579391479492, 6.9007961273193335;223.74398193359374, 237.23011779785153, -14.531653308868409;311.89331970214846, 235.7453430175781, 14.99657402038574;226.89084320068358, 68.46742172241211, -37.96389255523682;328.46007995605464, 66.90191307067872, 22.714137232303624;217.15863647460935, -76.62870750427246, 84.71155319213868;325.05088500976564, -77.39109344482421, 123.40727386474607;
-266.10665130615234, 523.4503112792969, -117.06093139648438;202.8447387695313, 443.6923889160156, 9.12624621391296;334.3296844482422, 444.91777954101565, 43.79725828170776;153.9302764892578, 347.8884613037109, 24.734152698516855;384.9868560791016, 351.2160278320313, 73.35601873397826;161.83801269531253, 261.06553344726564, -42.79363877773285;380.04510803222655, 264.3978515625, -10.819694423675537;224.13969268798826, 231.77234649658203, -14.687121868133545;311.2411804199219, 229.9619216918945, 15.14339666366577;226.40938720703122, 66.88898963928223, -40.282026863098146;327.9676666259765, 65.2501434326172, 22.599271798133852;218.87009735107418, -76.23566169738768, 83.78991928100587;322.51763305664065, -77.24138946533203, 121.36520233154296;
-268.5617141723633, 511.18163146972654, -138.64885787963868;204.35702362060547, 433.417333984375, -4.64301419258118;335.58936157226555, 433.83427734375, 28.16013708114624;157.046647644043, 336.0240753173828, 10.484040927886966;385.4136413574219, 338.858317565918, 55.97606191635131;167.65057830810548, 250.73875732421877, -59.790585899353026;374.26060791015624, 253.48223114013672, -28.953573608398436;225.0568130493164, 224.31769714355468, -14.65532341003418;311.21693725585936, 222.44542083740234, 15.108127689361572;225.9222839355469, 64.54133415222168, -54.75895748138428;329.5943176269531, 61.62868156433105, 21.28745129108429;218.05341949462888, -77.9265110015869, 68.02713470458986;323.1315002441406, -81.24031524658204, 114.59312896728515;
-271.2935760498047, 495.03612060546874, -161.53784942626953;205.13586578369143, 419.6654846191406, -21.289092683792113;337.064126586914, 419.3113677978516, 8.084802913665765;159.12593536376954, 319.7926849365234, -7.091223049163819;386.03964538574223, 322.3650451660157, 34.38874959945679;175.20391387939455, 234.71271209716798, -77.49582405090331;367.18086853027353, 237.6621337890625, -48.63957996368408;225.75240936279295, 214.29067077636722, -14.419386196136477;311.43986816406243, 212.8745162963867, 14.858409690856933;226.32476959228518, 63.41018562316894, -81.15828838348389;332.0859893798828, 60.15633354187012, -0.8652149915695198;218.62671508789063, -79.39407081604003, 38.315204620361314;326.11077270507815, -83.75069427490234, 84.87701263427735;
-272.8022003173828, 472.9469604492188, -200.60229568481446;205.35006713867188, 400.7804595947266, -46.652089738845824;338.81529235839844, 399.6794738769531, -24.763113880157476;159.07821350097655, 298.59651641845704, -35.95417699813843;387.5467864990234, 300.3165710449219, -1.6603989601135183;181.5700866699219, 213.32415237426758, -113.27393035888672;360.63518066406255, 215.97117156982424, -84.66475172042847;225.908674621582, 200.7406921386719, -12.278464889526369;311.63895874023433, 198.88566131591796, 12.713560104370115;219.7982391357422, 68.38233528137206, -109.73913803100587;335.5895751953125, 61.96379051208496, -39.13256232738495;217.5519058227539, -77.61423416137696, 10.380181121826165;323.47137451171875, -82.87647399902343, 47.444531250000004;
-274.43214721679686, 447.46189880371094, -250.770308303833;204.94277954101562, 377.72022094726566, -85.53323044776917;340.1271850585938, 375.68553771972654, -68.57910661697389;157.9073059082031, 272.3909523010254, -78.41914854049682;389.963217163086, 273.58653335571285, -50.493013095855716;187.48779754638673, 185.7294183731079, -158.38073682785034;357.3703765869141, 187.91047592163088, -133.14701833724973;225.67228393554686, 185.14996337890625, -10.742250975966456;310.88742980957034, 183.22049102783205, 11.163913697004316;211.09527435302735, 64.89311599731445, -138.02209224700928;342.2860137939453, 59.76905860900879, -69.7820057630539;216.52602844238282, -71.0908935546875, -4.647319030761727;322.81459045410156, -79.26514282226562, 24.46071166992189;
-275.661538696289, 420.903549194336, -301.80523338317875;204.37822265625002, 354.1493469238281, -125.40532898902893;341.5220428466797, 350.80985870361326, -112.78724946975709;155.8485824584961, 245.63965759277343, -121.4789553642273;393.04375000000005, 245.94982986450194, -98.80665445327759;193.42507476806642, 156.97421550750732, -204.15149593353271;354.5415985107422, 159.23581829071048, -180.4799826622009;225.00794067382813, 168.6492126464844, -10.046298488974575;310.0585174560547, 166.53289871215821, 10.451811116933822;201.7537048339844, 59.94391021728515, -164.35886077880858;350.4841979980469, 55.89859580993652, -96.78710963726044;215.4383041381836, -65.26029472351075, -18.594358825683607;323.6451416015625, -77.59712600708008, 4.122683715820317;
-276.0577056884765, 395.43804321289065, -356.138257598877;203.69982910156253, 331.54541015625, -166.50532574653624;342.64251098632815, 326.6679153442383, -158.5031247138977;153.9435592651367, 220.320157623291, -166.48226041793822;395.9168334960938, 219.05235061645507, -149.09567804336547;199.9059280395508, 129.438703918457, -252.36886024475098;351.654296875, 130.90121097564696, -229.44255170822146;224.7939208984375, 153.4327415466309, -8.480759677290916;309.3646606445312, 151.4687423706055, 8.868641811609269;195.26841278076168, 58.142460632324216, -194.2128231048584;358.392709350586, 53.65113868713379, -132.16746890544894;214.40555267333986, -59.17976455688477, -33.18518524169922;325.49583740234374, -75.29558334350587, -22.392959594726552;
-276.31683959960935, 374.6691101074219, -405.3802124023438;202.96453247070315, 312.6256134033203, -204.71111416816711;343.3779388427734, 306.885188293457, -202.97843980789185;153.1218475341797, 198.79574203491208, -208.40179433822632;397.9415466308594, 196.99911422729494, -198.00366525650023;208.2699234008789, 106.51925468444824, -297.0717567443848;345.30714111328126, 107.95287799835205, -274.8793556213379;224.58267974853516, 140.05314559936525, -6.773751291632652;309.01288146972655, 138.03405151367187, 7.134163385629654;189.35256042480466, 57.93724822998047, -220.51740226745605;363.1110290527344, 52.268834304809566, -162.764057803154;212.0590316772461, -55.12388877868653, -48.76272354125977;325.2996795654297, -73.60910568237304, -46.08432388305665;
-276.6160064697266, 359.51419372558587, -419.5576843261719;202.50654907226564, 298.8090026855469, -217.23180236816404;343.9735229492187, 291.7163436889649, -220.017015171051;152.73030853271484, 182.89792709350584, -221.5732912063599;399.94840698242194, 180.36938400268556, -217.33454294204714;213.92107238769532, 89.84946479797362, -307.94188766479493;340.78601074218756, 90.54554080963135, -289.2058120727539;224.10164794921874, 129.66523818969728, -6.685651811957359;308.22344360351565, 128.2494323730469, 7.030212122201919;187.8348571777344, 58.16511535644531, -245.23003959655762;365.8098937988281, 51.90864181518555, -192.52649438381195;212.81734466552734, -56.90348167419434, -76.77899169921875;323.63617553710947, -74.68491210937499, -80.4407341003418;
-276.9616333007813, 351.3489349365234, -438.0920959472657;202.20060119628906, 290.8628997802734, -229.34954299926758;344.54413757324215, 283.10961151123047, -231.21811294555664;152.50616607666018, 174.247811126709, -233.8281234741211;401.13641967773447, 171.14459609985352, -229.25078964233396;215.77513275146487, 79.97909889221191, -321.8514801025391;339.6022247314454, 80.13513431549072, -302.06895294189445;223.430322265625, 123.8657600402832, -6.502463182806968;307.7491119384766, 122.34998016357423, 6.837392717599869;186.50602722167972, 57.0569206237793, -273.2493133544922;368.2932403564453, 51.93113784790039, -224.59593446254732;209.77868957519533, -56.7716640472412, -90.28619461059571;325.73877258300786, -75.52279434204102, -103.92281036376953;
-276.895834350586, 352.1434143066406, -419.7665298461914;202.3224685668945, 290.7073547363281, -222.864421081543;344.5167053222656, 282.51147003173827, -220.3844041824341;152.8046325683594, 174.47075424194333, -228.71437854766847;401.8572662353516, 170.80849380493166, -220.86904144287107;213.84443969726564, 78.36529731750488, -312.79872131347656;343.35885925292973, 78.36799411773681, -292.67435760498046;223.2277130126953, 124.24626388549805, -6.451042780280114;307.27753295898435, 122.06636199951171, 6.778293997049332;185.9207717895508, 57.99945907592774, -277.0326301574707;368.208023071289, 52.823069381713864, -228.02542352676394;208.27258911132816, -54.47020072937011, -84.1263298034668;325.34572143554686, -73.59813613891602, -100.10126419067385;
-276.98328857421876, 359.72886352539064, -402.01704559326174;202.30025634765622, 296.0353637695313, -218.59932098388668;344.3440307617187, 287.802619934082, -210.15382857322695;153.3636444091797, 180.56065597534177, -224.7767370223999;402.3752807617187, 177.34152755737304, -209.65011215209958;209.99935607910157, 82.66475105285645, -305.7025375366211;349.8205230712891, 83.75294132232666, -279.9335952758789;223.41380310058594, 128.46216201782227, -7.199012026190758;307.4318908691406, 125.87056274414061, 7.512735754251481;186.2165740966797, 58.5466594696045, -265.7979385375977;366.51697082519524, 54.74261627197265, -225.98029956817626;209.35838623046877, -51.67662391662598, -65.49196777343751;324.5137237548828, -69.07867736816408, -88.55230026245118;
-276.90852661132817, 373.1135772705078, -382.55449218750005;202.52147216796874, 306.7624542236328, -210.614826965332;343.83233032226565, 298.3739547729492, -194.55519900321963;154.58147735595702, 192.5948661804199, -216.32475776672362;402.25030822753905, 189.4878242492676, -192.56127166748044;204.04236297607423, 94.15147132873534, -295.69668350219723;357.90555419921867, 95.64685764312745, -263.9404742240906;223.79149780273437, 136.04426803588865, -8.096655496954916;307.8499267578125, 132.93836669921873, 8.402155405282974;186.34990692138675, 58.269983291625984, -242.4947082519531;363.90957336425777, 55.47829360961913, -202.9139585494995;210.6731369018555, -48.056800842285156, -32.152783203125;321.5910278320312, -65.13783264160156, -51.943428802490246;
-276.8480194091797, 393.867593383789, -345.0464775085449;203.0123794555664, 324.3203186035156, -190.80710906982424;342.7695617675781, 316.25351104736325, -162.6207389354706;157.55944213867187, 211.38360671997071, -194.72596702575683;400.33110046386713, 209.4336219787598, -157.00876312255855;199.16381835937503, 112.84535026550292, -267.9794387817383;365.22826538085934, 115.43580570220946, -228.28678359985352;224.3436279296875, 148.83251266479493, -11.14123271405697;308.0583160400391, 146.2526626586914, 11.432714277505873;193.2141555786133, 53.41955223083496, -215.88194847106934;360.69983215332024, 53.99425506591797, -165.18207836151123;213.1269332885742, -47.25642204284668, -1.1427291870117244;325.4936920166015, -63.04011535644531, -12.173372650146494;
-276.3808563232422, 418.6430419921875, -296.2329551696777;203.68675384521484, 345.8899871826172, -155.9006967544556;341.53086242675784, 338.6631820678711, -119.87207598686219;160.78670959472655, 234.85076446533205, -158.25018310546875;396.79118041992183, 234.00903930664066, -110.601016998291;195.46231384277348, 137.24932498931884, -229.5858657836914;368.0283569335938, 141.0611207962036, -184.032873237133;224.97954711914065, 162.82168807983396, -13.603994631767275;308.97490844726565, 160.5692367553711, 13.8958319067955;202.21636962890625, 55.646480560302734, -189.6577522277832;354.1637329101563, 55.2578842163086, -134.84372177124024;214.5642868041992, -54.03940773010254, 14.703366851806644;326.8310943603515, -66.91991958618165, 12.423677825927728;
-275.5086242675781, 444.7543029785156, -247.8886474609375;204.28768157958984, 368.2941955566406, -121.32676362991332;340.036849975586, 362.4129150390625, -77.6250792980194;164.47181091308593, 259.33641281127933, -121.48281536102296;392.33891296386713, 259.631430053711, -64.72349548339845;191.9673950195313, 162.52416286468505, -189.78146743774414;369.20115051269534, 166.9048412322998, -139.80189765691756;225.52572937011718, 178.67644119262695, -15.755912256240844;309.4800262451172, 176.71342849731445, 16.0545596241951;211.54768829345704, 60.19440612792968, -166.6889923095703;345.72064208984375, 58.62655410766602, -106.73696193695069;215.65725708007812, -60.1860237121582, 26.721843719482422;326.41600646972654, -68.55529022216797, 36.007293701171875;
-274.88686218261716, 469.82363586425777, -195.77679824829102;204.67098846435545, 390.13661804199216, -84.29141206741333;338.5494415283203, 385.41333618164066, -33.76847796440124;168.29491577148437, 283.06379547119144, -81.54885444641114;387.60043640136723, 284.5082611083985, -16.353233337402347;188.51942138671876, 187.21465644836425, -146.57979583740232;369.0809814453125, 192.3429458618164, -92.59249309301377;225.57328338623046, 193.50013122558593, -18.25932013988495;309.45155334472656, 191.59172821044922, 18.568233537673947;217.75196685791016, 61.88147888183593, -138.20123825073244;337.3494842529297, 60.819131469726564, -71.53486843109131;216.322705078125, -66.57967758178711, 39.9392723083496;324.924560546875, -70.93613891601562, 62.09580078124999;
-274.17259521484374, 490.1719696044922, -146.49245223999026;205.19876708984373, 408.7313262939453, -50.06295738220215;337.3714813232422, 404.6866638183594, 7.505475282669064;171.49213104248045, 303.4745208740234, -43.90767364501953;383.3867156982422, 305.63480377197266, 28.873957824707034;182.8270263671875, 207.93327713012695, -103.32786636352539;372.5542419433594, 213.3298286437988, -47.91863719224929;225.91240234375, 207.3523910522461, -20.44262342453003;309.2680633544922, 205.38656158447267, 20.77176961898803;223.62451782226563, 63.29291763305664, -110.94840545654297;331.66075744628904, 63.13165740966797, -39.00703372955322;218.2306655883789, -68.54185485839844, 55.41950683593749;324.6243682861328, -70.73126907348633, 86.49731979370117;
-273.5837005615234, 506.4345703125, -130.01496505737305;205.1947296142578, 423.27053222656247, -39.65018768310547;336.34876708984376, 420.7006530761719, 23.08816418647766;174.1315994262695, 318.66127014160156, -34.132200431823726;379.2305999755859, 322.35193634033203, 46.311997985839845;179.19090270996094, 223.1788543701172, -93.24065017700194;374.5492919921875, 229.53181152343748, -32.28017913103104;226.31330718994138, 218.83506469726564, -20.75859022140503;309.9670989990235, 216.63692626953122, 21.09692888259887;225.279052734375, 64.73819770812989, -85.83743782043457;327.80151062011714, 64.7750228881836, -8.578125762939447;217.0787078857422, -65.39226989746093, 81.08972625732422;325.71510314941406, -68.34960784912109, 118.9557258605957;
-273.13440551757816, 517.2892578125, -105.7281135559082;205.57984161376956, 433.5788024902343, -25.66855926513672;335.58740844726566, 431.9703857421875, 38.772475481033325;176.0865295410156, 328.82188720703124, -21.82516212463379;376.224282836914, 332.9009765625, 62.026728820800784;177.39338836669924, 233.28728790283205, -78.48937377929688;374.930029296875, 240.75166168212888, -15.22513839006424;226.84394989013668, 226.3972579956055, -21.396819972991942;310.1717529296875, 224.1958511352539, 21.74955682754516;226.82677307128907, 66.87435417175293, -57.017859268188474;324.0012268066406, 65.46504745483398, 22.824426078796385;219.71090087890622, -64.5783805847168, 98.29771041870117;322.2713317871094, -66.89602966308593, 143.91033477783202;
-273.08452758789065, 520.9372528076171, -107.7616569519043;205.38891754150393, 436.5212493896484, -24.794904708862312;335.3762725830078, 435.65049743652344, 38.84675936698914;176.85577392578125, 332.08368225097655, -22.07466468811035;374.46394348144526, 336.0370391845703, 63.03029651641846;177.9532196044922, 236.808154296875, -81.1359130859375;372.8362609863281, 244.71187591552732, -15.24767028093338;226.9984588623047, 227.97670288085936, -21.92478094100952;310.59413757324216, 226.41567993164062, 22.291225910186764;227.9090789794922, 67.60503540039062, -50.518698120117186;323.00178833007806, 65.87607536315917, 27.668680381774905;220.8442260742187, -64.22820816040038, 97.80823974609375;321.9235961914062, -67.4186782836914, 143.78400344848635;
-272.8898010253906, 519.3416687011719, -118.31216506958009;205.59425964355472, 435.19562072753905, -26.15892448425293;335.4081390380859, 434.1520294189453, 32.620006084442146;177.06858215332034, 330.8093292236328, -22.270546722412114;374.0488677978515, 334.1072265625, 57.701562118530276;179.58002929687498, 236.61027984619142, -84.15228881835938;370.24011840820316, 243.7377212524414, -21.795775902271274;227.22551727294922, 227.01272735595703, -21.11161518096924;310.7124298095703, 225.5170120239258, 21.48340892791748;228.16789855957032, 67.15872344970703, -49.71000213623047;323.24248657226565, 65.50970344543457, 26.42308578491211;221.82584381103513, -66.01225814819335, 93.80355377197264;322.4244415283203, -69.21880111694335, 138.4268699645996;
-273.176107788086, 513.1102111816407, -141.89920578002932;205.97846832275394, 429.6343627929687, -35.238551712036134;335.6961273193359, 428.4250854492187, 18.41816911697388;176.60186004638672, 325.03734741210945, -29.334619331359864;374.2858612060547, 328.3089263916015, 44.66434288024903;181.84066009521482, 232.8419158935547, -95.44860916137694;366.9846466064453, 238.9982116699219, -37.831006634235386;227.32151336669924, 223.0707992553711, -20.6491641998291;310.57423400878906, 221.2077163696289, 21.029433250427246;228.05877075195312, 65.51707344055175, -51.77270431518555;325.9986541748047, 63.79824256896973, 21.52823886871338;222.20875244140623, -68.97404060363769, 92.02714691162109;324.0642486572265, -71.10045166015625, 131.49990921020506;
-273.45439758300785, 502.0054016113282, -164.87386474609372;206.2506332397461, 419.4944732666016, -44.11263542175293;336.37321472167974, 418.22548217773436, 3.5995247840881426;175.25391387939453, 315.08530883789064, -36.99448566436767;375.66135864257814, 317.8533599853515, 28.475033950805667;184.73971252441402, 225.4853958129883, -107.25126953125;363.0811920166016, 229.98232879638675, -56.76687499284745;227.86393127441409, 216.09319610595702, -20.235345649719235;310.82017211914064, 213.89508514404298, 20.621668815612797;227.71429138183595, 64.70348243713379, -71.76867179870605;329.95733337402345, 61.32641944885253, 11.228271675109864;220.64842987060547, -73.5692096710205, 66.88134155273437;326.0154479980469, -77.26773223876954, 115.56512665748596;
-273.64365844726564, 487.55057983398444, -190.14034118652347;206.1996185302734, 406.4213073730469, -55.559705543518064;337.3261932373047, 404.76675109863277, -14.754525279998779;173.64132690429685, 301.8507904052734, -46.16242580413818;378.2690643310547, 304.0293563842773, 9.88104972839356;188.9411895751953, 214.22832489013675, -119.33196411132812;358.7364532470703, 218.6857574462891, -75.68389570713043;228.00730285644536, 207.49868621826172, -19.107516288757324;310.85852966308596, 205.32908477783204, 19.49649257659912;227.3940841674805, 65.21187019348145, -94.25803833007812;333.68255615234375, 59.10879173278808, -10.28310737609863;220.79617462158205, -74.84365043640136, 43.92236862182617;329.212826538086, -79.92433090209961, 87.96004548072816;
-274.0092407226563, 470.1526489257813, -206.5619743347168;206.02707519531248, 390.8431823730469, -59.85007963180542;338.25633544921874, 388.6798706054687, -27.022386455535887;171.2814910888672, 285.45447845458983, -51.218712615966794;381.6766387939454, 287.55647430419924, -3.9509273529052713;194.71013183593746, 199.61683044433596, -128.34335556030274;353.80174255371094, 204.31547546386722, -89.72688300609587;228.13834686279301, 194.70370178222657, -18.00870208740234;311.6188293457031, 192.94635009765625, 18.407764911651608;227.41556701660153, 64.01413917541504, -120.40892105102539;338.0463439941406, 57.01344413757324, -43.07615566253662;222.13680419921877, -79.25542259216309, 11.286032867431636;332.25406799316414, -84.7556541442871, 43.9338125705719;
-274.37304077148434, 450.67155761718743, -223.0858337402344;205.58184356689452, 372.99497985839844, -62.842697048187254;339.27764282226565, 370.57250366210934, -38.908095836639404;168.15781707763674, 266.7324630737304, -56.63006000518798;385.696810913086, 268.78732757568366, -19.19512138366699;200.5716552734375, 182.85092697143557, -138.97903289794922;349.89524230957034, 187.88790283203124, -106.67014071941375;228.29105987548832, 179.43477478027344, -16.124720668792722;312.82929077148435, 177.7280242919922, 16.54736108779907;226.5344436645508, 63.631432723999026, -150.28117828369142;342.406021118164, 53.541613769531246, -78.0873830795288;226.32254486083986, -81.44999885559082, -23.806214904785158;334.8869598388672, -87.69150619506836, -3.841280508041379;
-274.75658264160154, 430.0736572265625, -250.0786331176758;204.84504699707034, 353.70498046874997, -74.10045928955077;340.3063140869141, 351.03173370361327, -58.99656629562378;164.7710235595703, 246.1334732055664, -69.03622951507568;389.68177185058596, 248.06938323974612, -41.21444969177246;205.65924072265625, 163.72976646423342, -156.29033432006838;346.0151519775391, 168.70633888244626, -130.76233158111572;228.1917221069336, 162.35852966308593, -14.48487539291382;313.7344116210937, 161.10168914794923, 14.935592365264895;226.44848022460937, 55.66221446990967, -174.28141403198242;345.43800354003906, 45.8503210067749, -111.69487781524658;230.2423797607422, -91.14191513061523, -51.70393676757813;336.091635131836, -98.0618034362793, -49.18863921165466;
-274.8900146484375, 408.63478698730466, -284.6161483764649;204.40891571044924, 334.177912902832, -93.16013450622557;341.31731262207035, 330.89106903076174, -84.90796918869017;161.81920013427734, 225.66854400634762, -88.78463439941405;393.5652893066407, 226.41556396484378, -68.47249794006348;210.63618927001954, 142.94901542663575, -180.2580451965332;342.24257202148436, 148.12100830078123, -159.03037490844724;228.41753692626952, 146.62859115600588, -13.434825801849366;314.5908630371094, 144.72442474365235, 13.89146661758423;223.00847778320312, 46.98939437866211, -191.35649757385252;348.3967376708984, 36.0271957397461, -134.60502815246582;230.39679565429688, -99.6991081237793, -68.89612884521485;335.69421081542964, -108.70207061767579, -74.600222158432;
-274.8992492675781, 388.13041381835933, -310.74265747070314;203.61346282958985, 315.5151824951172, -107.88861656188965;342.05958251953126, 311.42239532470705, -104.03583974838259;159.46303100585936, 205.56113739013674, -104.90358219146728;396.7675720214844, 205.4814994812012, -88.69644584655762;215.3769302368164, 122.96909599304199, -200.2559356689453;338.7575286865235, 127.25965614318847, -181.02799530029296;228.3637954711914, 130.49254837036133, -13.48495969772339;315.09291076660156, 129.0067321777344, 13.954258060455322;221.32706909179686, 35.55944175720215, -207.18808937072754;349.6273468017579, 24.91319427490234, -157.12181797027588;231.73166198730468, -110.85690002441406, -84.97937316894532;335.07780456542963, -120.79104766845704, -102.38234114646912;
-275.0199066162109, 369.88392639160156, -332.5174255371093;203.18260650634767, 299.45626068115234, -121.03521270751953;342.9432342529296, 294.6545822143555, -122.18915176391602;157.65710906982423, 187.2630569458008, -120.8712562561035;399.2730499267579, 186.82105484008792, -109.19023113250734;220.7414962768555, 104.8056468963623, -219.1854949951172;335.48597412109376, 108.43609657287597, -201.6003791809082;228.24874877929685, 117.33140945434572, -12.736135435104371;315.28785705566406, 115.8057113647461, 13.216421365737915;218.40412597656248, 28.282577228546145, -223.34274139404297;350.4398559570313, 16.75448913574219, -181.55145893096923;231.38545532226564, -119.65544128417969, -105.52602767944336;333.93934936523436, -131.10690002441407, -134.80037398338317;
-275.1322937011719, 355.89110717773434, -334.99268341064453;202.7371887207031, 287.03390960693355, -122.88262100219727;343.56086120605465, 281.9972213745117, -125.70453567504883;156.3915237426758, 173.53482360839843, -125.84360885620117;400.858154296875, 172.47016983032228, -116.16786499023438;225.06436920166016, 90.7840045928955, -224.77398529052738;332.2542358398438, 93.83124351501465, -208.82359466552737;227.7451705932617, 106.8909248352051, -13.130406045913697;315.0856048583985, 105.54723587036133, 13.630994939804078;216.70687408447264, 27.681942844390868, -245.9135871887207;352.0816131591797, 15.975075912475587, -210.62090167999267;234.7446044921875, -121.82146987915038, -132.54173812866213;332.8934234619141, -134.61236114501952, -173.9646363735199;
-274.8678283691406, 347.81107788085933, -326.4492630004883;202.16096496582028, 279.59949188232423, -119.06079025268555;343.9741729736328, 274.0299575805664, -122.44465103149415;155.37473602294924, 165.69298706054687, -123.56013717651368;402.06699829101564, 164.25877609252927, -115.61587028503418;226.8038314819336, 82.6560245513916, -220.56448669433593;331.1183563232422, 84.99624824523926, -206.510319519043;227.1296844482422, 101.02466125488283, -13.274563455581665;315.03484191894523, 99.89430847167968, 13.773807096481322;216.40609588623047, 29.3945969581604, -266.52339324951174;353.36094360351564, 17.014189910888675, -236.25517501831052;233.780419921875, -123.67990188598633, -161.50579757690429;334.31440734863276, -136.66071929931638, -207.01100783348085;
-274.6800262451172, 346.4619323730468, -328.39636077880857;201.79088897705077, 277.8001419067383, -120.73548126220702;343.8186920166016, 271.86963348388673, -123.57929153442382;154.29312438964845, 165.33932342529297, -123.55630645751953;402.9794036865234, 162.96637191772462, -115.37828407287597;225.42818298339844, 81.55485267639159, -219.9745620727539;333.40263671875005, 83.19333229064941, -206.19922637939456;226.1827606201172, 99.38858032226562, -12.605706167221069;314.4601348876953, 98.47051544189453, 13.10225863456726;215.1078674316406, 29.285453128814694, -270.67431640625;353.1082336425781, 18.143356323242188, -250.2915084838867;231.82519683837893, -122.3179084777832, -159.04972457885742;334.87963562011714, -134.3644958496094, -215.63099060058596;
-274.5242919921875, 351.1741516113281, -326.8349899291992;201.57172851562498, 281.6358413696289, -120.11480941772462;343.0549560546875, 275.41955718994143, -120.35306091308593;153.27848358154296, 171.02472076416015, -120.9973762512207;403.4468658447265, 168.9029640197754, -110.19539489746094;220.9105270385742, 87.3917179107666, -217.02259063720703;339.11076354980474, 88.15101509094238, -201.56974029541018;225.28612213134767, 101.9617889404297, -12.944985723495483;313.5183227539062, 100.6341583251953, 13.434527349472045;213.87059936523437, 26.021572971343993, -254.04115447998046;352.24730224609374, 19.109725952148438, -238.24913368225097;230.95210876464844, -123.23507995605469, -134.3327323913574;333.88561401367184, -131.9480262756348, -193.01262435913088;
-274.3047729492188, 361.6009033203125, -314.1918670654297;201.59216156005857, 290.722428894043, -117.48634014129638;342.36437988281256, 284.07360687255857, -112.18028621673584;152.3884307861328, 182.63148498535156, -115.53017520904541;403.0070068359375, 180.23937759399413, -100.35080947875977;213.11702728271482, 99.15351142883301, -208.4713928222656;347.1754486083985, 99.45722389221191, -191.91434135437015;224.64671020507814, 109.64988250732422, -12.893744611740114;312.3920684814453, 107.6899200439453, 13.369215250015257;212.71382904052734, 23.11377878189087, -232.42679443359373;350.71087646484375, 17.891978073120118, -209.6055464744568;229.62344970703126, -120.9709213256836, -98.46688232421876;332.51544799804685, -128.65670547485354, -147.99667892456057;
-273.9580505371094, 376.7634582519531, -300.56835556030273;201.76659545898434, 304.2053756713867, -115.62269077301025;341.2498443603516, 297.5129959106445, -102.74437751770019;152.4275924682617, 198.40407104492186, -109.98865356445312;401.9528564453125, 196.3841651916504, -88.21505241394043;204.43941955566405, 114.69336051940918, -199.40579299926756;355.0881866455078, 115.37685890197754, -179.77723541259763;224.43247680664066, 121.23423919677734, -13.780031633377076;311.30882873535154, 119.12151794433593, 14.232684564590452;213.20228271484373, 19.528753757476807, -208.08427429199216;348.32774963378904, 18.46935157775879, -179.04054789543153;226.5187957763672, -120.45310134887697, -58.08229370117187;330.23753662109374, -127.04521865844727, -96.16578598022461;
-273.5975219726563, 395.60632934570316, -274.5530609130859;201.92817230224608, 320.76114349365236, -105.01875743865966;339.81636352539067, 314.34131774902346, -83.92341384887696;153.55223388671874, 217.22398376464844, -96.93381805419921;399.85625, 215.8234046936035, -67.4202121734619;197.19348602294923, 133.01523361206054, -182.30994338989257;362.1684387207031, 135.26911621093748, -157.85339241027833;224.18248748779297, 136.10803070068357, -14.480455446243289;310.1573211669922, 133.8556091308594, 14.899150705337524;213.00497741699218, 25.2007887840271, -185.9253067016602;346.1765533447266, 24.458415031433105, -147.39935717582705;223.12757415771486, -111.3067497253418, -25.553636169433595;329.39953308105464, -117.57822418212889, -49.3940574645996;
-273.3166931152344, 416.61922912597663, -238.38209228515626;201.78175659179686, 339.5598724365234, -86.50982799530031;338.31040344238284, 333.60159301757807, -58.09361076354978;155.5198272705078, 237.7814971923828, -76.34205131530761;396.9156616210937, 237.66533737182618, -39.62223091125489;190.41997070312502, 153.59758377075192, -156.50541305541992;368.23805847167966, 157.07254524230956, -128.10982890129088;223.58538208007815, 151.48952102661133, -15.142080354690552;308.694775390625, 150.1164123535156, 15.546692895889281;216.4152648925781, 33.106335926055905, -167.00550060272218;343.0896240234375, 33.93047523498535, -123.4172236919403;222.9488510131836, -102.35361251831054, -6.630001068115238;329.17316589355465, -106.46601028442382, -22.50429916381835;
-273.1985778808594, 438.5118347167969, -206.4369239807129;201.84215545654297, 359.192658996582, -70.87483940124513;336.91613159179684, 353.99261779785155, -34.87204856872558;157.62524108886717, 259.18501281738276, -59.44666910171509;393.1194854736328, 260.02385864257815, -14.224303817749025;184.8126480102539, 174.49225845336915, -134.2281021118164;373.19586486816405, 178.99451751708983, -99.9732196331024;223.32163696289066, 168.73081893920897, -15.488959169387817;307.7834716796875, 167.39325714111328, 15.872727632522583;218.05286102294917, 44.91746377944946, -150.4276477813721;341.31776733398436, 45.62144355773926, -101.08541598320006;221.56227264404296, -90.9185203552246, 8.598658752441406;329.4160522460937, -94.17377853393555, 2.127724456787117;
-272.69894714355473, 459.8518402099609, -175.29353637695314;202.00482788085935, 378.58497924804686, -55.28979830741882;335.54027709960934, 374.16779785156245, -9.848519515991205;160.11004791259765, 280.1017425537109, -42.20835094451904;388.70365905761713, 281.6991073608398, 13.519864273071288;178.47567901611328, 194.7631523132324, -111.26690368652343;376.8802459716797, 199.43478240966795, -70.80695729255676;223.15642242431642, 184.92100448608397, -17.272215938568117;307.0815155029297, 183.53446197509766, 17.639713287353516;221.1984451293945, 54.04638328552246, -133.68653602600097;338.8168304443359, 55.50665817260742, -74.31288752555847;220.67073669433594, -81.37589187622069, 24.432763671875;329.33567199707034, -82.45485305786133, 34.203759765624994;
-272.2863067626953, 479.49605712890633, -157.44512252807615;202.1847930908203, 396.4677398681641, -47.56228013038635;334.3399475097656, 392.70966796875, 5.318503570556638;162.70412139892576, 298.8900772094726, -35.109722709655756;384.2542388916016, 301.0940933227539, 30.68992042541504;173.2822250366211, 212.69947509765623, -100.7887535095215;380.1013214111328, 217.1620964050293, -52.42052545547485;223.16675262451173, 199.9647834777832, -18.151406383514406;306.70870971679693, 198.5151985168457, 18.49959545135498;223.17371826171873, 58.1074161529541, -108.58496322631835;334.8691680908203, 59.32054557800292, -44.66950163841248;215.77411804199218, -76.33453521728515, 51.55250091552735;328.1487213134766, -76.32310104370117, 71.77663116455078;
-271.85560607910156, 496.0182800292969, -140.0351364135742;202.55311126708983, 411.8466491699219, -39.464627313613896;333.444790649414, 408.9454071044922, 18.133676147460932;165.46324310302734, 314.30287322998043, -29.61180629730224;380.03328857421883, 316.85651245117185, 43.80734634399414;170.00928649902343, 226.4869384765625, -93.8805000305176;381.8744232177735, 231.20492553710938, -38.77087621688843;223.48167572021484, 212.09046936035156, -18.482547283172607;306.3109130859375, 210.50209503173826, 18.83245162963867;223.77666015625002, 60.13424453735352, -82.12084045410155;329.7156768798828, 61.49304656982421, -14.792818975448611;215.36186981201172, -71.4074520111084, 79.67799835205079;324.23896179199215, -72.36907043457032, 104.84115295410156;
-271.6350067138672, 508.7346435546875, -113.9892852783203;202.69263458251953, 424.1322052001953, -24.65651326179504;332.80340576171875, 421.9977111816406, 34.42954940795899;167.72747192382812, 324.94269714355465, -19.25436873435974;376.4422515869141, 328.79726867675777, 57.93274269104004;168.07826385498046, 235.49573211669923, -81.59859848022461;381.98605651855473, 241.67465515136718, -23.772207927703857;223.54923400878906, 220.98355102539062, -18.563250637054445;306.34340209960936, 219.26046142578124, 18.922510147094727;225.14497222900394, 61.33575744628906, -56.0645429611206;325.37347412109375, 63.10750885009765, 6.386153548955917;217.5830368041992, -70.12326049804688, 99.80601654052734;320.8646545410156, -71.23974914550782, 121.8034133911133;
-271.69816284179683, 517.6291931152343, -92.43470764160156;202.78318023681638, 432.488363647461, -13.297478818893433;332.54737548828126, 430.8699584960938, 46.65660133361816;169.50796966552736, 331.73869323730474, -11.253535199165345;373.6494293212891, 335.4522430419922, 68.22985572814942;167.61309967041015, 240.45880279541018, -71.14820632934571;380.8130737304688, 246.39032897949218, -12.433283901214601;224.00608062744138, 226.00554809570312, -18.151371479034424;306.94493713378904, 224.42952880859374, 18.524369907379153;226.31553955078127, 63.959045791625975, -47.643208503723145;322.6265869140625, 64.48389511108398, 15.037996429204938;218.39523162841795, -67.7166633605957, 100.31085052490235;318.96718139648436, -71.32318572998047, 124.54054870605471;
-271.66477661132814, 521.5285186767578, -93.17966461181642;202.68718109130856, 436.1877075195313, -11.541902685165404;332.4751037597656, 434.95724182128913, 46.654809379577635;171.03509521484375, 334.3567291259766, -10.808475518226622;371.9480438232422, 337.9224884033203, 68.51551589965821;168.13531646728515, 241.62929840087895, -71.92019653320312;379.31831359863287, 246.86806182861326, -12.158343029022214;224.23391418457027, 228.2193283081055, -17.759852600097656;307.21783752441405, 226.85555114746091, 18.14463329315186;227.10181274414066, 66.30446662902833, -40.11826267242432;321.00014038085936, 66.55123596191405, 16.217942231893538;219.33055877685544, -66.01436614990234, 104.39857406616213;318.4247680664063, -71.44173736572266, 119.83023147583009;
-271.82362365722656, 520.6818695068359, -99.61277542114259;202.87580871582028, 435.43735961914064, -13.59412589073181;332.7763122558594, 433.9645202636719, 43.373291647434236;171.7265853881836, 332.69306335449215, -12.909424805641173;371.23912353515624, 336.40745849609374, 66.90505104064941;168.81170959472655, 239.46462249755862, -76.46270065307617;377.92664489746096, 244.6257629394531, -13.90538511276245;224.16084289550778, 227.23520202636718, -17.878296470642088;307.170083618164, 225.98307189941406, 18.268991661071777;227.10582275390627, 66.71600303649902, -37.35237369537354;320.9420043945313, 66.67835311889647, 18.077156490087507;219.94203338623043, -65.93801422119141, 105.79411926269532;319.1231323242187, -71.94291687011719, 118.42398605346682;
-271.9148223876953, 515.0200714111328, -111.41542510986329;203.24472503662108, 430.56274719238286, -18.494430685043334;333.2110504150391, 428.68182067871095, 34.69762445688248;171.17259674072267, 327.4322509765625, -15.988918137550355;371.5932525634766, 331.43914184570315, 59.81302013397217;169.9150634765625, 234.5352798461914, -80.30698699951171;376.7725036621094, 239.07501068115232, -20.458000087738036;224.35664215087888, 223.12459869384764, -17.518403911590575;307.5004760742187, 221.8631866455078, 17.913904094696044;226.72133026123046, 65.10820960998535, -40.83831272125245;322.5302795410156, 65.40822677612304, 15.988703912496566;220.421711730957, -69.36925048828125, 100.15477905273438;320.4563385009765, -73.89406051635743, 114.47607345581056;
-272.1722290039063, 504.8205230712891, -132.9432846069336;203.48607788085937, 421.3327819824219, -27.63673644065857;333.88759460449216, 418.9073822021485, 20.12612062692642;170.28257751464844, 318.1262084960937, -23.389617371559144;372.7665344238281, 321.88195190429684, 43.202470207214354;172.3679214477539, 226.33453979492188, -89.92910461425781;375.0556579589844, 230.64759979248043, -40.1730571269989;224.23406066894532, 216.2834976196289, -16.61221046447754;307.9363586425781, 214.67563018798828, 17.02053689956665;225.5005889892578, 63.34183616638184, -61.92581443786621;325.00893249511716, 64.80499649047852, -7.901301580667498;220.14764404296875, -71.68224639892578, 84.24242362976075;322.9448944091797, -75.9386199951172, 89.21978530883788;
-272.2662384033203, 490.8153045654297, -155.1628761291504;203.63152618408202, 408.86947631835943, -37.21667895317078;335.0726593017578, 405.80607604980474, 2.5829054474830606;169.3111785888672, 305.1350784301757, -30.031223797798155;374.7035736083985, 308.7203704833984, 24.751100730895992;176.60581970214847, 214.99259490966796, -99.04193954467775;371.99015502929683, 219.692561340332, -58.62075800895691;224.74219512939456, 207.652978515625, -15.06442766189575;308.627880859375, 205.45617065429687, 15.470719337463379;224.53967895507813, 64.78298988342286, -87.02335262298584;328.4780334472656, 61.99370498657227, -27.829677587747575;218.73218078613283, -71.06087265014648, 61.15699882507325;325.7562774658203, -78.41094055175782, 66.3028450012207;
-272.6936401367188, 474.21949768066406, -171.74289093017575;203.47733764648436, 393.6698364257813, -42.2023494720459;336.0624176025391, 390.1502685546875, -10.054739511013036;167.93419189453124, 289.3416549682617, -35.147792840003966;377.86646728515626, 292.74587097167966, 10.199405860900878;181.9778533935547, 200.8217704772949, -107.62375259399415;368.2709991455078, 205.9321350097656, -73.57685952186584;224.7860107421875, 196.43355331420898, -13.921579456329345;308.9855072021485, 194.13666610717772, 14.335273361206054;224.19387817382812, 64.67640266418458, -112.59901943206786;331.47213439941413, 60.72233963012696, -58.86482811570168;220.1676742553711, -72.87317810058593, 32.169503402709964;327.146322631836, -80.3627426147461, 29.258056640624993;
-273.1523712158203, 455.2780303955078, -188.67798690795897;203.1151580810547, 376.3398071289063, -46.68927087783814;337.24126892089845, 372.3898895263672, -22.632037866115567;165.91282196044918, 270.60758209228516, -41.50808489322662;381.5384338378907, 273.77010955810545, -5.524738121032715;187.90041656494142, 182.84446945190427, -118.93384552001953;364.3465881347657, 189.17622222900388, -91.53985233306885;224.86876220703124, 182.32308044433594, -12.460005569458009;309.89748535156247, 179.8191970825195, 12.891114330291748;223.4341262817383, 64.19739494323731, -141.75097675323488;334.4725555419922, 57.93362045288086, -92.86278248429299;224.57935943603516, -75.29446334838866, -5.532416152954092;326.50269470214846, -81.07813720703125, -15.56399841308594;
-273.7197509765625, 434.5737213134766, -214.3080535888672;202.639306640625, 357.1932678222656, -61.32937211990357;338.41876220703125, 352.8621215820312, -44.760413110256195;163.85230255126953, 250.24257812499997, -58.08144762516022;385.47742004394536, 252.90002288818354, -31.08613109588623;193.95304107666016, 162.08524894714355, -138.89876251220704;361.3137390136719, 169.81388549804686, -117.46746940612793;224.89358520507812, 167.27167053222658, -10.671089363098144;310.42885131835936, 164.7130561828613, 11.107642602920532;219.0073471069336, 63.5969913482666, -170.20366458892823;338.83812561035154, 55.79658660888671, -129.44719830155373;226.08156738281252, -74.85765571594239, -35.56351661682129;321.9760467529297, -80.77754898071288, -54.899330139160156;
-274.1385375976563, 412.71768188476574, -259.17320098876957;202.20217742919922, 337.0996170043945, -94.21551198959351;339.77162475585936, 331.85384368896484, -82.33317083120346;162.10792694091796, 228.4692687988281, -92.7841702222824;389.28107910156257, 229.8397064208984, -70.17764835357667;200.36644287109377, 139.86590156555178, -176.75187225341796;358.6251861572266, 147.55755100250244, -155.06147155761718;224.76268157958984, 153.0658226013184, -9.771553993225098;310.47600097656243, 150.10592346191407, 10.192142820358278;213.07660980224614, 61.72941703796386, -195.49646720886233;343.88776855468745, 52.820516586303704, -158.6393681526184;222.13917999267582, -73.9247257232666, -56.92239265441894;320.9600219726563, -80.32058601379394, -75.63041534423829;
-274.52173461914066, 391.098797607422, -297.5061660766602;202.11046295166017, 317.12636413574216, -121.17818107604981;341.2476623535156, 311.27479705810543, -114.8535455107689;161.4383056640625, 206.5756980895996, -121.97231607437134;392.39813842773435, 207.22774353027341, -105.2902479171753;206.97635040283203, 117.36664838790894, -209.03745727539064;355.5147796630859, 124.6692144393921, -188.8309070587158;225.47156066894533, 139.2211486816406, -8.351180791854858;311.1208953857422, 136.18718719482422, 8.759752655029299;208.66067810058593, 60.59167022705078, -220.05949325561525;350.4838287353516, 50.207490539550776, -187.15202150344848;220.22038421630862, -72.68761367797852, -81.3078067779541;317.87703247070317, -78.15870590209961, -100.07483291625978;
-274.6957427978516, 370.74375915527344, -334.41760711669923;201.89389343261718, 298.9966186523437, -149.37396545410155;342.2972076416015, 292.5983947753906, -147.01072248220441;161.32182922363282, 185.83007049560547, -152.60096473693847;394.9700653076172, 185.79686126708987, -140.62464084625242;214.4636199951172, 95.72680158615114, -241.59049453735352;351.7661041259765, 102.20839748382569, -222.35309257507328;226.1134735107422, 126.22204360961915, -7.820369529724121;311.6979278564453, 123.22539291381835, 8.206295108795166;204.24759063720703, 59.77453994750976, -246.05164642333986;356.25355224609376, 48.21241149902344, -212.14481568336487;218.66004486083986, -68.07164974212647, -110.47118415832519;313.7182647705078, -73.81654891967773, -120.50889434814454;
-275.20704040527346, 353.6091888427734, -369.10059356689453;201.39352264404297, 283.662435913086, -178.06561355590821;343.3255615234375, 277.06004333496094, -179.2283851623535;161.43208160400388, 168.5143005371094, -185.61378746032716;396.7264526367187, 167.59544677734374, -178.41056232452394;221.87360382080078, 76.89326105117799, -275.4248786926269;347.98376464843744, 81.90154943466187, -258.39445037841796;226.39710388183596, 116.11425094604492, -6.7936282634735115;312.0276733398438, 113.32973098754881, 7.152642488479615;200.03011627197262, 62.12996368408202, -267.65467224121096;361.1822784423828, 49.16597137451172, -237.3832039356232;216.81263122558593, -58.983342266082765, -130.77020301818848;309.2166381835937, -63.778475570678715, -145.80048675537108;
-275.6837890625, 341.73609924316406, -389.08671875;201.3938217163086, 272.9915740966797, -194.23160781860352;344.42711486816404, 266.27221069335934, -193.75557403564454;161.86337890624998, 156.1400939941406, -206.23478851318362;398.28005065917966, 154.22830047607422, -196.84442634582518;227.33966522216795, 63.54981698989867, -297.99646148681643;345.23792724609376, 67.88458700180054, -278.7202407836914;226.62542724609378, 108.72501373291016, -8.144442987442018;311.81675109863284, 106.35483551025389, 8.484285306930541;198.48647918701167, 60.89790306091309, -284.84046630859376;364.2195983886719, 48.49730949401855, -262.46994323730473;220.2942367553711, -57.12659330368042, -148.17776222229003;298.4649353027344, -56.13138999938965, -168.20690002441407;
-276.2052856445313, 335.9998809814453, -382.4642196655273;201.81423187255862, 267.89781188964844, -194.24099349975586;345.2837890625, 260.9219085693359, -193.258740234375;161.42962036132812, 150.2523162841797, -209.02239303588868;400.04225463867186, 147.41888275146485, -197.04011039733885;229.73176116943358, 56.78384866714477, -299.52598114013676;345.0063171386719, 59.54444875717164, -275.35546417236327;227.33695373535156, 104.83646240234377, -8.572585248947144;311.8005798339844, 102.93632202148437, 8.891079664230347;199.77052001953123, 58.72439270019531, -281.98776092529295;365.73609008789066, 48.115990829467776, -264.66967926025393;222.4937942504883, -57.412977123260504, -150.69589080810545;298.96286926269534, -55.61707038879395, -170.13183898925783;
-276.6719024658203, 336.63768615722654, -390.79381866455077;202.26526336669926, 268.1657470703125, -200.77125930786136;345.3921081542968, 261.06395874023434, -196.8907569885254;160.8553009033203, 151.53338775634768, -214.86973266601558;401.6402191162109, 148.8450149536133, -199.32045478820802;226.9416748046875, 56.595765781402584, -304.5970626831055;349.38657531738284, 58.72404165267944, -278.21044006347654;226.94503021240237, 104.90078582763674, -9.308594465255739;310.9694915771484, 103.49763641357421, 9.624613428115845;200.0192932128906, 54.099960708618156, -278.07618713378906;366.2238342285156, 50.75446891784668, -275.8329299926758;223.03390350341797, -60.74054136276246, -143.14433250427246;299.89047851562503, -53.904164505004886, -172.49997100830078;
-277.06220092773435, 343.49341125488286, -392.13782196044923;202.92165679931642, 273.8647155761719, -205.57412719726562;345.37315368652344, 266.7061706542969, -198.72660522460936;160.6100082397461, 159.15007476806642, -216.19578094482418;402.14904174804684, 156.72206573486326, -199.66573600769044;221.45603179931638, 63.01061487197876, -302.5103851318359;356.379946899414, 65.18633108139038, -277.8876083374023;227.20181427001955, 109.69122543334962, -9.00432915687561;310.5730255126953, 108.34407577514648, 9.308994913101197;199.48701019287108, 51.727530288696286, -262.2175018310547;366.92699890136714, 52.03908424377442, -266.99549102783203;222.1417236328125, -59.4247675895691, -115.05642814636231;301.549008178711, -50.95238838195801, -151.36958045959472;
-277.29550781250003, 355.53626403808596, -378.7313522338867;203.84395141601564, 284.8285949707031, -203.73921813964844;345.2373992919922, 277.47105712890624, -191.77618236541747;160.4805877685547, 172.59508819580077, -209.24112606048584;402.1901580810547, 170.28695068359377, -189.38721694946287;213.74018554687498, 76.32727136611939, -290.0566215515137;364.5927459716797, 77.80270376205445, -265.10938262939453;227.62763977050784, 118.93612670898438, -9.178642129898073;310.0371978759766, 117.52230834960938, 9.46007513999939;199.87816314697264, 48.860483169555664, -237.28289108276368;366.9032897949219, 51.445932388305664, -235.1098371744156;218.5805847167969, -57.60966291427612, -74.14378471374512;304.9336669921875, -50.856697463989256, -103.59482612609864;
-277.36048278808596, 372.1964569091797, -355.5046630859375;204.61340484619143, 299.867431640625, -191.74028930664062;344.5521423339844, 292.5519348144531, -172.8777723312378;160.4081039428711, 189.9808380126953, -193.09884033203124;401.4077545166015, 188.15293884277347, -166.77087898254393;205.80053100585934, 94.38661088943482, -269.9605888366699;372.3116729736328, 95.57225179672241, -242.24755554199214;228.11804504394533, 130.88343963623046, -10.19881920814514;309.9095306396485, 129.40279006958008, 10.468403911590576;204.18371582031247, 46.755706024169925, -213.16698760986327;364.4270782470703, 51.67611198425293, -199.53322212696077;217.50462036132814, -58.78810529708862, -42.10770835876465;310.33982543945314, -51.52759971618652, -59.77569160461425;
-277.62302551269534, 392.4596923828125, -312.0922233581543;205.156364440918, 317.97179107666017, -163.29698104858397;343.66412353515625, 310.94357452392575, -137.47674713134765;161.20454864501954, 210.55121765136715, -160.68178596496583;400.2735504150391, 209.43889007568362, -128.41655120849612;198.77416381835937, 115.34044923782349, -232.37844696044922;378.293701171875, 117.23222398757935, -204.3806400299072;228.65527801513673, 143.41540145874023, -11.783046865463257;310.0786865234375, 142.4642105102539, 12.061411476135254;210.44716949462887, 47.15983772277832, -190.22604637145994;360.4021392822266, 53.15514030456543, -166.5529101610184;221.9012680053711, -60.143686580657956, -19.746397781372067;311.6567810058594, -52.34325103759766, -31.18992958068847;
-277.5917510986328, 414.54436035156255, -273.8203704833984;205.30751495361332, 338.5987976074219, -138.64727134704586;342.574868774414, 331.738996887207, -104.77723922729494;162.02380981445313, 233.1250190734863, -133.12399892807005;398.50989990234376, 233.15700531005862, -92.60683784484863;192.15026550292973, 137.94794216156006, -200.54446258544922;383.3848846435547, 141.98353815078735, -169.55504951477047;227.9212203979492, 158.39473876953124, -13.731052780151366;309.381185913086, 157.8086395263672, 14.009935426712035;215.2028671264648, 48.92197227478027, -165.70211696624756;353.7825103759766, 56.064657592773436, -136.70853798389436;223.7394760131836, -60.12817792892456, 5.710377120971678;314.46030883789064, -53.45442848205567, -4.7677417755127;
-277.68682250976565, 438.0354431152344, -232.45235595703124;205.57816314697266, 359.9308868408203, -111.09184684753417;341.4470733642578, 353.6122375488281, -68.09790496826173;163.25633239746094, 256.1852371215821, -103.96865663528445;395.73047790527346, 257.7247772216797, -52.73306999206544;185.68940734863284, 161.20570735931398, -167.83024291992186;387.4002014160156, 167.39007501602174, -131.12262630462646;227.60321197509765, 173.84409561157227, -15.825759363174438;308.8353698730469, 173.44368515014648, 16.118410921096803;220.0902191162109, 50.52593574523926, -139.99893016815187;347.0819305419922, 59.13133277893066, -109.24165565967557;224.82651824951174, -64.91660432815551, 30.718888473510745;317.4105560302734, -57.20845069885254, 16.198365402221672;
-277.2017303466797, 461.38278198242193, -190.12889175415037;206.01909790039062, 380.8557800292969, -81.03077049255371;340.3648712158203, 374.9579925537109, -30.15228843688965;164.39512176513674, 278.48888244628904, -70.92119150161743;392.2787231445313, 280.48355560302736, -9.292373275756841;179.5193771362305, 183.758992767334, -130.91461944580078;390.2019897460937, 191.05900039672852, -87.89914321899415;227.34570922851563, 188.1419776916504, -18.308464527130127;308.4017608642578, 187.5489799499512, 18.61804685592651;224.84796295166012, 51.2169002532959, -116.28154048919681;340.3576293945313, 60.7434440612793, -80.25932991504668;225.26931610107422, -72.12988166809082, 48.42535514831543;320.77886962890625, -65.20468978881836, 43.6297420501709;
-276.7798583984375, 482.0998474121094, -157.89441452026367;205.93691711425782, 399.70343322753905, -59.898102951049815;339.1428253173828, 394.37191772460943, -5.058089828491207;165.5726745605469, 298.1641571044922, -48.07260904312134;388.7642272949219, 300.6690216064453, 18.225915145874026;174.3788070678711, 203.19905853271484, -103.97556610107422;391.58439331054683, 210.66126403808596, -58.68870611190796;227.06593017578126, 201.71981506347657, -18.24762392044067;308.39618225097655, 200.83228454589846, 18.574537181854247;226.97633056640623, 55.90753021240234, -93.1847990036011;334.66254882812495, 64.24889259338379, -50.53380119800566;220.3071029663086, -70.1772491455078, 68.06951713562012;329.70650939941413, -70.01281433105468, 69.80003776550292;
-276.2374603271484, 500.0679077148438, -142.84160766601562;205.77928466796877, 415.99114990234375, -50.058283615112316;338.0046661376953, 411.3104675292969, 10.283519363403322;166.9533966064453, 314.90411682128905, -37.72294607162475;384.9031616210937, 317.39540405273436, 36.38892936706543;170.08986358642574, 219.46944274902344, -92.09209136962892;391.76801452636715, 227.21815338134766, -41.15623626708984;226.497705078125, 213.45098876953125, -19.29915761947632;308.13490295410156, 212.08131256103516, 19.640328121185306;226.7907989501953, 60.497898483276366, -76.53361988067627;330.2879364013672, 65.4376232147217, -23.57625801563262;217.76274719238282, -67.42859840393066, 85.58713569641114;326.4544311523438, -68.24694061279297, 99.31009635925292;
-275.81255493164065, 513.7147705078125, -113.19468612670899;205.52160949707033, 429.2604675292969, -33.072528839111335;337.0923645019531, 424.8331146240235, 31.12943000793457;168.19638214111325, 328.0683288574219, -23.618763923645023;381.6026824951172, 329.7380004882813, 57.13061408996582;167.98140563964841, 232.98430633544922, -76.04701728820801;390.4175323486328, 239.99911346435547, -19.738299179077142;226.34888305664063, 222.13745117187497, -19.8505428314209;308.43604736328126, 220.72227325439454, 20.20140256881714;227.41820678710937, 63.983160400390624, -55.35862293243409;326.5781555175781, 65.67951316833496, 7.613368105888366;218.31346130371094, -64.1851634979248, 101.86073150634766;322.9089782714844, -67.10517120361328, 126.42118797302244;
-275.3538299560547, 522.6946350097656, -95.82394714355469;205.31533813476565, 438.5534393310547, -20.180926132202153;336.35101318359375, 434.22379760742194, 47.57172813415528;168.40559082031248, 337.377880859375, -12.543873786926271;379.83204345703126, 338.7819152832032, 74.73757781982422;167.6349685668945, 243.60842590332032, -65.38396759033203;388.81410522460936, 250.2185272216797, -3.6103775024414078;226.36124267578126, 228.04888610839842, -20.673760509490968;309.0539276123047, 226.723127746582, 21.036976909637453;227.89336853027348, 65.31950225830079, -45.632480430603025;323.97201843261723, 64.90513725280762, 27.89287784099579;218.43269042968748, -64.76723365783691, 104.19232406616212;320.31113281250003, -69.18925018310547, 141.0196830749512;
-275.05584411621095, 527.1231903076172, -92.70361099243163;204.9978973388672, 442.34179077148434, -10.407228088378908;335.3823394775391, 438.5528686523438, 57.521791267395024;168.25370025634766, 343.2537506103516, -11.65296220779419;378.98612976074213, 344.19800109863286, 75.4279541015625;168.9646209716797, 253.39537353515627, -76.97320175170898;385.7050262451172, 259.32161102294924, -17.033580017089843;226.1837020874023, 230.29002838134764, -20.62260341644287;309.08680725097656, 229.39811553955076, 21.00424470901489;227.78222961425786, 66.13436355590821, -43.07072620391846;323.034945678711, 66.73985214233399, 23.923884677886964;218.38993835449216, -66.9919589996338, 104.81471023559571;320.0492858886719, -71.87404708862304, 132.2259422302246;
-274.6952239990235, 526.3954376220703, -100.18109817504883;205.13088836669922, 442.66835937499997, -3.0719306945800793;334.9224517822266, 438.97773437499995, 59.15802974700927;168.41923828125, 345.53114318847656, -18.434604167938232;379.3992218017578, 345.99994506835935, 61.09381217956543;172.250505065918, 262.7798126220703, -105.81107406616212;380.14845275878906, 268.4077087402344, -49.994805908203126;226.13589782714843, 229.74176635742188, -19.646514320373537;309.1221130371094, 229.09166412353514, 20.030732679367066;227.38008728027347, 65.68653144836426, -41.6796895980835;323.49761962890625, 66.40595664978028, 21.71093921661377;217.97995300292965, -68.98120918273926, 110.05002212524414;321.3577514648438, -73.83998336791991, 133.62441940307616;
-274.2252532958984, 521.1261047363281, -112.7505126953125;205.24591217041015, 439.44578857421874, 1.499440956115723;334.97221984863285, 435.74258422851557, 56.23599328994751;167.32695312500002, 344.6080200195313, -30.45393056869507;379.88301391601556, 344.5856384277344, 38.71743240356445;176.75674438476562, 272.46138000488287, -141.41751403808593;375.0647735595703, 278.27443542480466, -91.27962913513183;226.0056045532227, 226.4964538574219, -17.944737112522127;309.17297363281244, 225.93661956787108, 18.323836863040924;226.60243377685552, 63.01213111877441, -42.68343296051025;325.1159698486328, 64.53749313354493, 16.766487884521485;218.32571258544922, -69.92150535583497, 126.15602874755857;323.14985046386727, -74.87028732299805, 140.73473892211913;
-274.04233703613284, 511.79140014648436, -131.51215744018555;205.75548248291014, 432.50067443847655, 1.3498140335083009;334.90893859863286, 428.4882263183593, 48.44518747329711;166.29506683349612, 339.99954223632807, -48.99431800842285;380.95592041015624, 339.4821411132813, 10.38847007751464;182.16280822753905, 281.6740417480469, -183.2188934326172;368.49460754394534, 286.2496826171875, -138.07392997741698;226.41094818115235, 220.5505523681641, -16.51687239408493;309.5078582763672, 220.12791900634764, 16.883613216876984;225.35393981933598, 58.552740478515624, -47.03041229248047;328.1114532470703, 61.659872436523436, 7.763538360595703;218.84728698730467, -72.98220100402833, 141.04400939941405;325.46142272949226, -77.35680084228517, 144.59796066284179;
-273.94627685546874, 498.9536163330078, -158.47351303100584;206.43272247314454, 422.0789764404297, -5.034181022644043;335.07003784179693, 417.9860198974609, 31.87273950576782;164.77311553955076, 331.7228851318359, -69.95689449310302;383.2975708007812, 330.8933227539063, -25.606830978393553;188.2766174316406, 287.5492416381836, -224.24105072021484;361.59010009765626, 291.68483276367186, -190.26762027740477;226.80471649169922, 213.0107833862305, -14.541977679729461;309.72519836425784, 212.45122375488282, 14.885063946247103;224.48231353759772, 54.11868667602539, -51.333925628662115;331.8443817138672, 58.073798370361324, 0.9099761962890636;219.33889770507812, -75.24340476989747, 151.6816223144531;328.4451599121093, -78.74099578857422, 150.12456436157228;
-274.08356933593757, 483.0145294189453, -192.65619049072265;207.15799407958986, 409.27199401855466, -18.457244682312016;335.18197937011723, 404.66894836425774, 9.043567371368407;163.38594360351559, 320.6826400756836, -99.08289966583251;386.5916961669921, 319.8596649169922, -69.85743370056151;194.91814422607422, 289.9499435424804, -273.4333770751953;356.0829864501953, 294.26531066894535, -253.45378341674805;226.99909515380858, 204.40302124023438, -12.384038054943085;309.7939056396484, 203.94134063720702, 12.700414478778839;222.77589721679692, 49.19926109313965, -64.139892578125;336.4605865478516, 54.615473937988284, -23.522767639160158;219.8558181762695, -78.79017486572266, 149.19073486328125;330.9639221191406, -81.72521591186523, 131.4732427597046;
-274.4586517333985, 464.8487274169922, -244.37472534179688;207.89578399658205, 393.9736907958984, -48.136446952819824;335.65810546875, 388.7603363037109, -28.426890087127685;161.98740234375, 307.18528137207034, -147.4229127883911;390.5745513916015, 306.2366989135742, -130.82852516174316;201.10740051269534, 288.3115463256836, -344.00945434570315;351.63515014648436, 292.62675781250005, -334.0304436683655;227.06984710693357, 194.26975860595704, -10.838849055767058;309.8267761230469, 193.70419311523435, 11.113889610767364;221.12794799804692, 43.62614002227783, -88.99682693481446;341.7445892333985, 50.930474090576176, -57.198465728759764;220.29071655273435, -81.85641822814942, 141.83164491653443;334.41396484374997, -84.51120681762696, 110.93384151458741;
-274.7016632080078, 444.55633239746095, -297.99699783325195;208.39980621337895, 376.50073242187494, -80.08502254486083;336.2239654541016, 370.68947448730466, -66.28429059982301;160.67932586669926, 290.68191223144527, -196.34252738952637;394.3816223144531, 289.61174468994136, -191.09899406433107;207.87772674560551, 282.56669311523433, -412.55214233398436;347.5379028320313, 286.9954010009766, -414.6292556762695;227.57982330322264, 182.5787971496582, -9.783156287670137;309.77085266113284, 182.19337234497067, 10.029823410511018;219.63409118652345, 37.46674633026123, -122.58576087951661;347.18934020996096, 46.70092754364014, -96.15432281494141;220.5243209838867, -86.5059295654297, 122.05117387771604;338.2319244384766, -88.67154998779297, 82.85782222747804;
-274.9712188720703, 423.5902435302734, -349.2897384643555;209.14425659179688, 357.7957214355468, -110.770188331604;336.96656494140626, 351.3609283447266, -102.09201765060426;159.69287109375, 271.8026901245117, -243.03766565322874;397.6799072265625, 270.81287078857423, -248.67662925720217;215.27250061035159, 272.8981002807617, -478.33013420104976;341.7801574707032, 278.28720397949223, -493.4562503814698;228.1707015991211, 169.9442352294922, -9.243460929393768;309.66829223632817, 169.51120986938474, 9.472507393360138;218.4179412841797, 34.687967491149905, -168.4671314239502;352.3948699951171, 43.561082267761236, -141.80639705657958;219.64142456054685, -89.46824264526367, 86.96315507888794;341.5969879150391, -91.89214248657227, 45.51842098236084;
-275.50517578125, 402.46470336914064, -378.23438186645507;209.5290740966797, 338.6407119750976, -124.78885860443116;337.7661071777344, 331.68076782226564, -121.12145261764526;159.36658630371093, 251.63277740478514, -268.9488694190979;399.8797332763672, 250.01236419677736, -284.38190040588375;222.2142013549805, 259.953157043457, -518.5363708496094;335.3256744384766, 266.3152389526367, -544.6078323364258;228.38294982910156, 155.58835449218748, -9.024991118907929;309.43414001464845, 155.24205627441404, 9.251156532764433;217.1502868652344, 34.765334129333496, -218.33377685546876;357.6141937255859, 40.303614997863775, -182.28483562469484;218.9399642944336, -91.73911666870117, 39.3347409248352;344.4396087646485, -94.65138778686524, 7.960239982604971;
-275.8689208984375, 382.0414154052734, -422.0703491210938;209.69412536621095, 321.01636810302733, -153.44945621490479;339.021533203125, 313.0554794311524, -154.145463848114;159.38186950683593, 230.8332977294922, -304.93726730346674;401.5556610107422, 229.027099609375, -327.5938278198242;228.17011871337894, 243.51002655029293, -561.3229705810547;330.4432739257812, 251.19596252441409, -595.298175048828;228.7305236816406, 142.2355812072754, -8.94364937543869;309.4641876220703, 142.15528717041016, 9.161036121845244;215.66478881835937, 35.57451457977295, -270.48346519470215;361.8292541503906, 37.8099142074585, -224.64253330230713;217.69003448486328, -93.58861618041992, -11.766310405731218;346.5359527587891, -96.36470260620118, -28.9187868118286;
-276.50424804687503, 363.8492492675781, -442.80353851318364;209.7738250732422, 304.57239837646483, -170.35631275177002;339.76442565917966, 295.9556381225586, -170.5106268882751;158.927001953125, 211.64464263916017, -320.6909255981445;403.00612487792966, 209.34235076904298, -344.65824737548826;232.72359771728514, 225.68385620117186, -570.3198120117188;327.9936553955078, 233.9936294555664, -610.8907836914062;229.16747283935544, 130.14085006713864, -9.423851573467255;309.6286437988282, 130.10301666259767, 9.645142996311186;214.40880584716797, 38.01949825286865, -319.01875305175787;365.63299560546875, 35.68125171661377, -261.66770496368406;216.76668243408204, -95.3510612487793, -69.43774747848511;349.0718872070313, -98.4698860168457, -70.26154346466066;
-276.83883666992193, 349.266763305664, -476.7921081542969;210.17535552978518, 290.9295364379883, -200.2427059173584;340.32136535644526, 281.852229309082, -198.55966873168944;158.85809020996095, 195.2349365234375, -351.48020935058594;403.92588500976564, 192.8288772583008, -374.53648223876957;236.15482635498046, 208.5978012084961, -596.6901641845702;325.5327239990234, 216.02781524658204, -642.5347869873048;229.61584167480467, 121.40609512329102, -10.022777676582336;309.70716552734376, 121.17759323120117, 10.234400939941406;213.53554077148436, 40.02298831939697, -366.7870269775391;368.84458618164064, 34.41604442596436, -300.5797832489013;215.06846618652347, -97.58146209716796, -128.2046206474304;350.42278137207035, -100.66526031494142, -113.22313861846924;
-276.86276550292973, 339.29767761230465, -511.3378204345703;210.2715805053711, 281.1293319702149, -232.38817749023437;341.08518676757814, 271.70821380615234, -228.60842208862303;158.6686767578125, 184.0528350830078, -384.0013961791992;405.3330200195312, 180.6093551635742, -407.4340454101563;238.47687530517578, 194.41150970458986, -625.0284759521484;325.51550903320316, 201.8568923950195, -676.3210571289063;229.5746154785156, 116.59128189086915, -10.124010515213012;309.4640350341797, 115.77395706176758, 10.317021179199218;212.8557647705078, 40.27119255065918, -401.11376037597654;370.92234191894534, 33.639072227478025, -334.4580772399902;213.06036224365235, -97.1292121887207, -162.45778532028197;350.86592102050787, -100.72491989135743, -142.6138109207153;
-277.22263488769534, 334.9425628662109, -534.0167694091797;210.29459075927736, 276.3920364379883, -255.81436767578128;341.7778991699219, 266.43409881591793, -249.36316604614257;158.43817291259765, 179.3922088623047, -411.06571655273433;406.3571990966797, 174.79567260742184, -432.2854766845703;238.93805694580078, 186.75057678222655, -650.7278167724609;326.59766845703126, 194.432551574707, -702.8375915527345;229.45830535888672, 114.26269912719728, -10.8472158908844;309.4547454833984, 113.4839698791504, 11.02360372543335;212.3928436279297, 39.46775436401367, -424.4573272705078;372.00353088378915, 32.60270977020264, -353.5817108154297;211.54181365966798, -97.84268646240234, -180.38696088790894;350.3024536132813, -101.82051239013673, -153.09461803436275;
-277.3193115234375, 336.05519104003906, -518.1565582275391;210.4026016235352, 276.7992202758789, -244.5548984527588;342.2009948730469, 267.2284042358398, -238.35933990478514;158.46649017333985, 181.06595916748046, -401.67127685546876;407.0001556396484, 175.97741394042967, -422.15000305175784;238.90960235595705, 187.02447204589842, -641.4346466064453;326.9339050292969, 195.11829986572263, -690.4948516845702;229.72826538085937, 115.14129867553713, -10.928567361831664;309.6164276123047, 114.24106674194336, 11.116728782653809;212.72220458984376, 39.23552322387695, -430.7096801757812;372.0037689208985, 31.62158470153809, -347.6539321899414;211.29981231689453, -97.62108840942382, -180.97138471603392;349.96987304687497, -102.16253051757812, -140.13057518005368;
-277.31580200195316, 342.9017364501953, -484.4997039794922;210.63511962890627, 282.66211395263673, -216.67940635681154;342.16834716796876, 273.4260787963867, -212.01476974487304;158.452961730957, 188.42903747558594, -374.0022506713867;406.8512329101563, 182.69625396728517, -392.58328704833986;238.73782958984376, 194.14942016601563, -614.0082946777345;326.74067993164067, 203.15318756103514, -657.0329589843749;230.62529907226562, 118.3942268371582, -10.415868520736694;310.1509338378906, 117.52511825561524, 10.63454728126526;213.8510452270508, 40.06746292114258, -411.76473617553705;370.7322143554687, 31.63141212463379, -322.93122520446775;212.76657257080078, -97.1983573913574, -160.80443649291993;349.030615234375, -102.56151809692382, -115.98732261657713;
-277.3141815185547, 355.1031005859375, -445.4486923217774;210.6269943237305, 293.49756622314453, -183.8899238586426;341.9876037597656, 284.62611236572263, -181.09747533798216;158.4796447753906, 200.6049957275391, -339.5026000976563;406.3333221435547, 195.12032318115234, -356.4958724975586;237.09089508056644, 205.96999511718752, -581.9444213867188;326.80091552734376, 216.3441390991211, -616.4548553466797;230.8401962280273, 124.70050659179688, -9.739004039764405;310.5325592041016, 123.88327331542968, 9.983664369583131;215.17004241943363, 42.65933113098144, -381.18839950561517;368.19060363769523, 32.74184017181397, -290.4010770797729;213.79580993652345, -94.33050384521482, -126.97979736328125;347.50469665527345, -100.97119750976562, -84.98573570251466;
-277.47898864746094, 371.87514648437497, -405.9595046997071;210.1780548095703, 307.77030487060546, -153.219877910614;341.5786712646484, 299.54606781005856, -150.9892847061157;157.96821441650394, 216.7834426879883, -302.1553924560547;405.84562988281255, 211.69928741455078, -320.15497360229494;234.66531372070315, 220.02246856689453, -540.5686187744142;327.974398803711, 231.68167877197266, -573.4819763183594;230.98735351562496, 134.48532485961914, -8.86241569519043;310.8639831542969, 133.80470581054686, 9.115681171417236;216.2126678466797, 43.16315994262695, -339.9554336547851;364.5677307128906, 34.25487957000732, -249.23441371917724;215.4422317504883, -91.35621643066405, -80.29077606201172;345.0564361572265, -98.24494247436523, -41.31896934509277;
-276.79346923828126, 392.34649963378905, -387.8300903320313;209.5899215698242, 325.73969116210935, -141.04419479370117;340.72748413085935, 318.1720932006836, -138.54010238647462;157.6037612915039, 236.3645248413086, -281.64864082336425;404.8627807617188, 231.62956695556642, -300.23465156555176;230.21804504394532, 235.35186004638672, -515.7077194213867;329.9944244384766, 248.11356811523436, -549.2408843994141;230.79688720703123, 148.64960403442382, -8.045243263244629;310.63171997070316, 148.15188369750976, 8.287572097778321;217.97667083740234, 43.49113388061523, -292.7696071624756;359.56179199218747, 37.903410530090326, -212.15241146087646;216.9996780395508, -87.60575103759766, -27.301376342773434;342.3625305175781, -94.20822219848634, -0.9292690277099656;
-275.59800109863284, 415.48694763183596, -347.1577537536622;208.6483917236328, 346.00816345214844, -119.34057779312135;339.3156829833984, 339.33185729980465, -113.39184207916259;157.42330322265622, 257.2326141357422, -243.38529081344603;402.58342590332035, 253.60624389648436, -256.28905029296874;224.76923370361328, 248.56962738037112, -459.4951126098633;331.9983642578125, 263.57033233642574, -485.99151382446297;229.78967742919923, 164.87765960693358, -8.626795959472657;309.8831939697266, 164.17427139282228, 8.857018566131591;220.27451629638674, 45.172367095947266, -236.45915451049805;353.8860229492187, 41.68191394805908, -163.46091270446777;218.17988281249998, -83.25956268310547, 19.687696838378915;338.6263214111328, -89.62914276123048, 39.12698173522949;
-273.7368682861328, 440.1298950195312, -317.5546592712403;206.9314178466797, 367.021450805664, -107.3287133216858;337.79694824218745, 361.27003784179686, -95.74773578643799;157.95826873779296, 277.7581527709961, -219.43020410537721;398.64503784179686, 275.5452651977539, -222.10587081909176;217.4214340209961, 259.83776702880857, -421.5480178833008;334.7483215332031, 276.5343353271484, -434.79728460311895;228.037272644043, 180.9304962158203, -9.958395862579346;308.5275817871094, 180.13055801391602, 10.187872982025148;221.79778747558598, 45.265998077392574, -183.99444160461425;347.8132843017578, 45.980252265930176, -121.82908287048339;218.80945129394533, -79.9411102294922, 70.28453674316405;334.0500030517578, -85.78609619140626, 75.60158500671386;
-271.6616806030273, 464.2118286132812, -271.84610366821295;204.35611572265626, 387.40318603515624, -81.11545152664185;335.56000976562495, 382.43284301757814, -64.02296619415281;158.841650390625, 297.0031372070313, -177.53624086380003;394.0940521240234, 296.09327545166013, -169.18718490600583;208.69451446533202, 268.1706268310547, -363.15219345092777;338.3894958496094, 286.6406616210937, -362.120691394806;225.59069061279297, 195.2006423950195, -11.155994701385497;306.64033203125007, 194.5262435913086, 11.405396604537964;222.58686981201174, 47.1027214050293, -133.12135963439945;341.51634521484374, 50.44917964935303, -78.82337951660158;219.40452270507814, -76.79395751953126, 114.1111587524414;329.9364868164062, -82.66456451416016, 108.82093467712403;
-269.485073852539, 487.1506591796875, -219.16152801513672;201.08412475585936, 406.40018920898433, -48.09354276657105;332.9390594482422, 402.535922241211, -24.803767204284668;158.82679901123046, 313.584994506836, -127.83281259536744;388.2648651123046, 314.63730926513676, -106.06915969848633;199.41274871826172, 273.0933364868164, -296.9054664611817;341.93485717773433, 293.0433349609375, -280.64897270202636;223.29687194824217, 207.29951477050778, -12.811952447891235;304.9844268798828, 207.0865921020508, 13.095714473724366;223.20805206298832, 51.34850559234619, -96.64146385192872;335.6850341796875, 54.96089916229248, -38.41550207138061;219.91060485839844, -75.51093215942383, 133.5455123901367;326.7292327880859, -81.33266525268556, 132.81500816345215;
-267.0468688964844, 507.85597839355466, -173.507283782959;197.45227355957033, 423.68256530761715, -19.65516538619995;330.546484375, 420.40308532714846, 8.949400901794434;158.23721771240233, 327.2332427978516, -80.85777006149293;382.1717987060547, 329.3189331054688, -47.71647109985352;190.8933807373047, 274.3907379150391, -232.40217208862305;345.71901245117186, 294.439176940918, -204.7851561546326;220.93141937255857, 218.29351348876952, -13.432850694656372;303.38679199218745, 218.30909576416016, 13.754265785217287;223.2265457153321, 56.94884395599365, -71.18448181152343;330.47718200683596, 60.252316856384276, -13.77828903198242;219.54386749267576, -72.80507202148436, 141.69992675781248;324.0190307617187, -79.18117370605468, 139.4488525390625;
-264.8533432006836, 526.2186614990235, -154.3885238647461;193.61995544433594, 438.8573059082031, -15.793895053863526;328.9511627197266, 435.53897399902354, 19.131173133850098;157.04551544189454, 337.25143127441413, -57.55520312786103;376.43962402343743, 340.0510223388672, -15.086544799804688;181.9179977416992, 270.94802246093747, -187.39674530029293;349.6884643554688, 290.851058959961, -155.56173355877402;218.1272445678711, 227.61229248046874, -14.334715032577513;301.987222290039, 227.9179412841797, 14.673775386810306;223.207975769043, 61.44118061065674, -54.18365478515625;325.41852111816405, 64.08714141845704, 1.4418376922607425;218.46016845703124, -71.22988433837891, 143.725528717041;323.0143249511719, -78.17418975830078, 140.02989196777344;
-263.9078353881836, 542.8815399169922, -138.37603378295898;190.22329101562502, 452.12770996093747, -16.311803913116456;328.5805847167969, 448.32979736328133, 28.61686840057373;155.74947509765624, 344.50213317871095, -37.544515299797055;371.5529449462891, 347.6076385498046, 16.994739532470703;173.14025726318357, 264.5315612792969, -145.14951019287108;354.8264587402344, 283.1017456054688, -108.64488766491414;215.06181335449216, 234.6946838378906, -16.762832593917846;301.01427307128904, 235.49667358398438, 17.110518980026246;223.22685089111332, 63.99233474731445, -52.96658020019531;321.85979614257815, 65.80534400939942, 8.453463935852053;217.44212341308594, -71.80245361328124, 137.55358657836913;321.99468994140625, -78.50340957641602, 139.30716094970703;
-264.104899597168, 558.6470336914062, -124.86178131103514;188.1620071411133, 464.425537109375, -18.83066568374634;330.03472290039065, 459.54752197265634, 35.01444902420044;154.9279357910156, 351.6328063964844, -23.377093291282655;368.79258117675784, 353.0053253173828, 43.684166717529294;165.72606506347657, 258.2023529052734, -110.32861404418945;361.44410095214846, 273.5834335327148, -67.53573715984821;213.10809173583982, 240.47216644287107, -18.921908664703366;301.7562652587891, 241.53790435791015, 19.27373175621033;223.58267211914065, 64.13400611877441, -58.8579719543457;320.2956359863282, 66.59828491210938, 11.963425445556641;216.74446716308591, -75.64801788330078, 130.53167495727538;320.8273712158203, -81.22392578124999, 144.39018707275392;
-265.61543731689454, 575.688397216797, -115.70973205566406;187.43847045898437, 478.4484069824219, -22.396488380432128;333.8136688232422, 471.9460906982422, 41.67964401245117;155.7706024169922, 360.0853332519531, -15.551304268836976;368.5843505859375, 358.5835418701172, 71.64771575927735;160.40091400146483, 255.59422302246094, -88.90388107299806;369.6477905273438, 266.08044281005857, -25.752213600277905;212.57898406982423, 245.91634826660155, -21.199300003051754;304.2166442871094, 246.93442230224608, 21.552068424224856;224.24891204833983, 63.14879570007324, -67.43618469238281;320.8991424560547, 65.9992286682129, 11.248178482055664;216.6666259765625, -80.54235458374023, 134.2162178039551;322.3832122802734, -85.74519424438476, 153.16710357666017;
-270.68997650146486, 594.4899230957033, -108.93278274536132;188.3563705444336, 492.87153930664067, -30.704147815704346;340.83515014648435, 484.8150299072266, 45.18772220611572;157.19037780761718, 370.1802947998047, -18.656637978553768;373.0266052246094, 365.4700561523438, 90.10989494323731;159.01814727783199, 257.43010864257815, -83.1715187072754;382.2722991943359, 262.0637893676758, 4.087173530459404;213.61260986328128, 251.50477294921873, -23.748246002197263;308.65237731933587, 251.8558151245117, 24.093741416931156;224.38858489990233, 61.77403373718261, -74.1504077911377;324.45806579589845, 65.14920921325684, 15.117552185058596;215.97874755859377, -84.7130355834961, 139.75006332397462;325.48109741210936, -89.86889877319337, 166.1930709838867;
-279.2024917602539, 615.0131469726563, -121.11412506103515;191.37048797607423, 508.28200683593747, -40.96777076721192;350.7067291259765, 498.58581237792964, 35.94929389953613;159.23350372314454, 381.7314880371094, -39.23296864032746;383.3139801025391, 374.9955291748047, 80.60153427124024;161.21296234130858, 266.41674194335934, -120.09757766723634;397.63926696777344, 266.448078918457, -10.797479942440987;216.0937911987305, 258.82404479980465, -22.159273147583004;315.2731018066406, 257.9984909057617, 22.49279091358185;224.64305572509764, 60.446360397338864, -82.83605194091797;330.1363830566406, 64.54488945007324, 8.0961332321167;216.10019989013674, -89.28667297363282, 165.24182510375977;329.0704315185547, -96.15277175903321, 185.92286758422853;
-288.3306961059571, 637.3307556152345, -146.24036712646483;195.54656219482425, 524.6480651855469, -48.77995491027831;363.22160034179683, 514.4160583496093, 20.969806289672857;160.96313781738283, 394.61023864746096, -55.213438344001766;398.6371612548828, 387.72698669433595, 60.193716430664075;166.91009521484372, 281.03455352783203, -158.61372756958008;414.6208923339844, 276.759455871582, -49.14090044796467;220.66915130615237, 265.5147750854492, -19.33625521659851;324.8347198486328, 264.12306976318354, 19.660058760643004;227.82259826660157, 57.52080764770508, -95.47657508850098;337.2206115722656, 60.978445053100586, 2.8365473747253427;219.43626861572267, -98.69652557373047, 189.87381515502932;334.49436950683594, -106.89979629516603, 217.33962631225586;
-300.66419372558596, 657.5575622558594, -166.31023254394535;201.28206481933594, 544.2929382324219, -54.62727279663086;376.8823028564453, 531.554409790039, 17.250327825546268;163.67941589355468, 409.84595947265626, -67.49099533557892;408.1751342773438, 400.33619384765626, 56.2503553390503;174.6297668457031, 300.27884979248046, -183.3282897949219;419.9309967041016, 290.7769241333007, -65.95699866116047;226.44039306640627, 272.9795394897461, -19.134065961837763;335.7613983154297, 270.8986145019531, 19.46355130672455;232.8532699584961, 53.406349945068364, -117.49577369689942;344.9397064208984, 56.86313819885254, -0.36507601737976114;224.81797943115237, -109.90934143066406, 201.08336868286133;340.6738830566406, -118.71515502929688, 250.29677658081053;
-306.9510208129883, 649.2613800048828, -121.40352096557616;207.02471466064455, 552.6133422851561, -22.54203433990478;384.0454223632812, 530.4880767822266, 50.56908659934998;166.69674987792968, 423.65771789550786, -56.03006813526153;413.78649597167964, 399.4047424316406, 77.42693767547607;184.34758911132812, 318.83939819335933, -188.73473815917967;419.2441223144531, 297.2740005493164, -57.54034531414509;232.44573059082032, 281.06217193603516, -18.126725244522095;345.95397644042964, 276.78236999511716, 18.458814978599552;238.30863342285159, 53.25687255859375, -129.26318626403807;351.6938598632812, 52.994519805908205, -11.35006833076477;230.10085906982425, -117.4859031677246, 183.50281867980956;344.89044799804685, -128.61833953857422, 235.64871978759766;
-309.3451858520508, 638.9988006591797, -76.28489227294924;211.4042922973633, 553.0147338867187, 17.014220237731926;390.27358093261716, 524.9891387939454, 86.09729666709899;168.0235580444336, 433.5857788085938, -35.6648183107376;420.3218597412109, 398.2371063232422, 88.29072551727295;192.76692810058594, 337.5005249023438, -186.21887817382813;420.1390045166015, 308.8668182373047, -70.5866569787264;237.8225555419922, 287.84147796630856, -17.358267927169802;353.1414520263672, 280.8607833862304, 17.644829249382017;242.24007873535157, 66.38754577636719, -138.43779220581052;356.2677520751953, 55.96683883666992, -25.432229948043823;231.59131774902346, -108.97702827453612, 158.61382255554196;345.13026428222656, -128.0656936645508, 202.15793991088867;
-307.88951568603517, 623.584799194336, -34.49641647338869;215.79942016601564, 546.2046905517578, 57.58489608764648;390.4589935302734, 516.4019287109375, 110.91824584007263;171.43051147460938, 438.1020263671875, -2.4264306545257597;418.6112640380859, 399.4753692626953, 89.5176336288452;196.6395446777344, 358.0207336425781, -152.4113838195801;414.5710632324218, 323.70924530029293, -83.31949806213379;242.49162902832032, 286.8635177612304, -12.66747794151306;356.7176208496094, 278.0332290649414, 12.889944911003111;247.56694946289062, 75.16429901123047, -136.62766113281248;361.04843750000003, 55.085872650146484, -36.07718272209167;235.91021270751952, -97.17835884094238, 154.97960586547848;346.5560852050781, -123.20044555664063, 185.76988449096683;
-299.4108642578125, 602.2431610107423, 10.04231414794922;218.8025161743164, 537.1103271484374, 97.39566001892089;383.7326354980468, 507.26394348144527, 121.88862738609313;176.8287826538086, 440.11822204589845, 40.76747899055482;406.3258392333984, 401.49574890136716, 88.50781078338623;197.6518768310547, 374.69416809082037, -98.51670608520506;394.55041503906244, 338.3762283325195, -73.49308071136474;245.3271545410156, 281.06148223876954, -4.42409644126892;353.5048736572266, 271.91927337646484, 4.625339007377622;248.81118164062497, 71.43806266784668, -128.15385153293607;356.5042266845703, 49.531904220581055, -44.646887350082395;237.81639556884767, -102.11655006408691, 140.89278850555414;341.21680908203126, -130.04814834594728, 154.26142501831055;
-271.57916259765625, 540.1275054931641, 19.96632614135742;199.85926208496093, 485.67324218749997, 100.38012962341307;348.3740020751953, 457.3983245849609, 116.72315726280212;161.77796783447266, 402.1006805419922, 42.2166295528412;368.9958587646484, 365.30788269042966, 77.6116147994995;182.92660675048828, 349.3964782714844, -90.60080413818362;354.77441406249994, 314.58263244628904, -75.3139591217041;224.20704650878903, 256.41258392333987, -1.7413342952728268;321.65003051757816, 247.02481689453126, 1.902957797050476;225.93663787841797, 67.34001541137695, -115.42379400730132;323.6118530273438, 44.55546569824219, -45.444305944442746;216.02361450195315, -90.55762062072753, 124.34650707244873;308.6442016601563, -118.51894302368164, 131.49630813598634;

+ 185 - 15
app/lib/exercises/exercises_validation/models/exercise.dart

@@ -19,25 +19,121 @@ enum AuthorizedTypeIndex {
   rightAnkle,
 }
 
+AuthorizedTypeIndex typeIndexFromString(String typeIndex) {
+  switch (typeIndex) {
+    case "nose":
+      return AuthorizedTypeIndex.nose;
+    case "leftShoulder":
+      return AuthorizedTypeIndex.leftShoulder;
+    case "rightShoulder":
+      return AuthorizedTypeIndex.rightShoulder;
+    case "leftElbow":
+      return AuthorizedTypeIndex.leftElbow;
+    case "rightElbow":
+      return AuthorizedTypeIndex.rightElbow;
+    case "leftWrist":
+      return AuthorizedTypeIndex.leftWrist;
+    case "rightWrist":
+      return AuthorizedTypeIndex.rightWrist;
+    case "leftHip":
+      return AuthorizedTypeIndex.leftHip;
+    case "rightHip":
+      return AuthorizedTypeIndex.rightHip;
+    case "leftKnee":
+      return AuthorizedTypeIndex.leftKnee;
+    case "rightKnee":
+      return AuthorizedTypeIndex.rightKnee;
+    case "leftAnkle":
+      return AuthorizedTypeIndex.leftAnkle;
+    case "rightAnkle":
+      return AuthorizedTypeIndex.rightAnkle;
+    default:
+      throw const FormatException("Incorrect type name");
+  }
+}
+
+enum Comparator {
+  greater,
+  lesser,
+}
+
+Comparator comparatorFromString(String comparator) {
+  switch (comparator) {
+    case "greater":
+      return Comparator.greater;
+    case "lesser":
+      return Comparator.lesser;
+    default:
+      throw const FormatException("Incorrect comparator name");
+  }
+}
+
 class Exercise {
-  final int reps;
-  final int series;
-  final Criteria startMovement;
-  final Criteria endMovement;
-
-  const Exercise({
-    required this.reps,
-    required this.series,
+  final int repetitions;
+  final int sets;
+  final Criteria? startMovement;
+  final Criteria? endMovement;
+  final String description;
+  final String name;
+  final int difficulty;
+  final String id;
+  late final List<PoseLandmarkType> jointsOnScreen;
+
+  Exercise({
+    required this.repetitions,
+    required this.sets,
     required this.startMovement,
     required this.endMovement,
-  });
+    required this.description,
+    required this.name,
+    required this.difficulty,
+    required this.id,
+    required List<AuthorizedTypeIndex> jointsOnScreen,
+  }) {
+    this.jointsOnScreen = jointsOnScreen.map((joint) => authorizedType[joint.index]).toList();
+  }
 
   bool isAtStartMovement(MeanFilteredData meanFilteredData) {
-    return startMovement.isAtPosition(meanFilteredData);
+    final start = startMovement;
+    if (start != null) {
+      return start.isAtPosition(meanFilteredData);
+    }
+    return false;
   }
 
   bool isAtEndMovement(MeanFilteredData meanFilteredData) {
-    return endMovement.isAtPosition(meanFilteredData);
+    final end = endMovement;
+    if (end != null) {
+      return end.isAtPosition(meanFilteredData);
+    }
+    return false;
+  }
+
+  Map<String, dynamic> toMap() {
+    return {
+      "repetitions": repetitions,
+      "sets": sets,
+      "start_movement": startMovement?.toMap(),
+      "end_movement": endMovement?.toMap(),
+      "description": description,
+      "name": name,
+      "difficulty": difficulty,
+      "joints_on_screen": jointsOnScreen.map((j) => j.name).toList(),
+    };
+  }
+
+  factory Exercise.fromMap(String id, Map<String, dynamic> map) {
+    return Exercise(
+      repetitions: map["repetitions"],
+      sets: map["sets"],
+      startMovement: map.containsKey("start_movement") ? Criteria.fromMap(map["start_movement"]) : null,
+      endMovement: map.containsKey("end_movement") ? Criteria.fromMap(map["end_movement"]) : null,
+      description: map["description"],
+      name: map["name"],
+      difficulty: map["difficulty"],
+      id: id,
+      jointsOnScreen: map.containsKey("joints_on_screen") ? List<String>.from(map["joints_on_screen"]).map((e) => typeIndexFromString(e)).toList() : [],
+    );
   }
 
   static const authorizedType = [
@@ -59,19 +155,32 @@ class Exercise {
 
 abstract class Criteria {
   bool isAtPosition(MeanFilteredData meanFilteredData);
+  Map<String, dynamic> toMap();
+  static Criteria fromMap(Map<String, dynamic> map) {
+    switch (map["type"]) {
+      case "distance":
+        return CriteriaDistance.fromMap(map);
+      case "angle":
+        return CriteriaAngle.fromMap(map);
+      default:
+        throw FormatException("Type should be distance or angle but is ${map['type']}");
+    }
+  }
 }
 
 class CriteriaDistance implements Criteria {
   final AuthorizedTypeIndex jointStart;
   final AuthorizedTypeIndex jointEnd;
   final int axis;
-  final int threshold;
+  final num threshold;
+  final Comparator comparator;
 
   const CriteriaDistance({
     required this.jointStart,
     required this.jointEnd,
     required this.axis,
     required this.threshold,
+    required this.comparator,
   });
 
   @override
@@ -79,7 +188,36 @@ class CriteriaDistance implements Criteria {
     final start = meanFilteredData[jointStart.index][axis];
     final end = meanFilteredData[jointEnd.index][axis];
     final distance = (start - end).abs();
-    return distance < threshold;
+    return _compare(distance, threshold);
+  }
+
+  bool _compare(num distance, num threshold) {
+    if (comparator == Comparator.greater) {
+      return distance > threshold;
+    } else {
+      return distance < threshold;
+    }
+  }
+
+  @override
+  Map<String, dynamic> toMap() {
+    return {
+      "joint_start": jointStart.name,
+      "joint_end": jointEnd.name,
+      "threshold": threshold,
+      "comparator": comparator.name,
+      "type": "distance",
+    };
+  }
+
+  factory CriteriaDistance.fromMap(Map<String, dynamic> map) {
+    return CriteriaDistance(
+      jointStart: typeIndexFromString(map["joint_start"]),
+      jointEnd: typeIndexFromString(map["joint_end"]),
+      axis: map["axis"],
+      threshold: map["threshold"],
+      comparator: comparatorFromString(map["comparator"]),
+    );
   }
 }
 
@@ -87,13 +225,15 @@ class CriteriaAngle implements Criteria {
   final AuthorizedTypeIndex jointStart;
   final AuthorizedTypeIndex jointCenter;
   final AuthorizedTypeIndex jointEnd;
-  final int threshold;
+  final num threshold;
+  final Comparator comparator;
 
   const CriteriaAngle({
     required this.jointStart,
     required this.jointCenter,
     required this.jointEnd,
     required this.threshold,
+    required this.comparator,
   });
 
   @override
@@ -111,6 +251,36 @@ class CriteriaAngle implements Criteria {
         y: meanFilteredData[jointEnd.index][1],
         z: meanFilteredData[jointEnd.index][2]);
     final angle = DistanceUtils.angleBetweenThreePoints(start, center, end).round();
-    return angle > threshold;
+    return _compare(angle, threshold);
+  }
+
+  bool _compare(num angle, num threshold) {
+    if (comparator == Comparator.greater) {
+      return angle > threshold;
+    } else {
+      return angle < threshold;
+    }
+  }
+
+  @override
+  Map<String, dynamic> toMap() {
+    return {
+      "joint_start": jointStart.name,
+      "joint_center": jointCenter.name,
+      "joint_end": jointEnd.name,
+      "threshold": threshold,
+      "comparator": comparator.name,
+      "type": "angle",
+    };
+  }
+
+  factory CriteriaAngle.fromMap(Map<String, dynamic> map) {
+    return CriteriaAngle(
+      jointStart: typeIndexFromString(map["joint_start"]),
+      jointCenter: typeIndexFromString(map["joint_center"]),
+      jointEnd: typeIndexFromString(map["joint_end"]),
+      threshold: map["threshold"],
+      comparator: comparatorFromString(map["comparator"]),
+    );
   }
 }

+ 0 - 22
app/lib/exercises/exercises_validation/models/push_up.dart

@@ -1,22 +0,0 @@
-import 'package:physigo/exercises/exercises_validation/models/exercise.dart';
-
-const startMovement = CriteriaAngle(
-  jointStart: AuthorizedTypeIndex.rightShoulder,
-  jointCenter: AuthorizedTypeIndex.rightElbow,
-  jointEnd: AuthorizedTypeIndex.rightWrist,
-  threshold: 300,
-);
-
-const endMovement = CriteriaDistance(
-  jointStart: AuthorizedTypeIndex.leftShoulder,
-  jointEnd: AuthorizedTypeIndex.leftElbow,
-  axis: 1,
-  threshold: 40,
-);
-
-const pushUp = Exercise(
-  reps: 3,
-  series: 3,
-  startMovement: startMovement,
-  endMovement: endMovement,
-);

+ 0 - 22
app/lib/exercises/exercises_validation/models/squat.dart

@@ -1,22 +0,0 @@
-import 'package:physigo/exercises/exercises_validation/models/exercise.dart';
-
-const startMovement = CriteriaAngle(
-  jointStart: AuthorizedTypeIndex.rightShoulder,
-  jointCenter: AuthorizedTypeIndex.rightHip,
-  jointEnd: AuthorizedTypeIndex.rightKnee,
-  threshold: 320,
-);
-
-const endMovement = CriteriaDistance(
-  jointStart: AuthorizedTypeIndex.leftHip,
-  jointEnd: AuthorizedTypeIndex.leftKnee,
-  axis: 1,
-  threshold: 40,
-);
-
-const squat = Exercise(
-  reps: 3,
-  series: 3,
-  startMovement: startMovement,
-  endMovement: endMovement,
-);

+ 0 - 76
app/lib/exercises/exercises_validation/plot.py

@@ -1,76 +0,0 @@
-from matplotlib import pyplot as plt
-import numpy as np
-
-def plot_data_from(filename):
-  data = []
-  with open(filename) as f:
-    lines = f.read().splitlines()
-    for line in lines:
-      positions = []
-      for p in line.split(";")[:-1]:
-        positions.append(list(map(float, p.split(", "))))
-      data.append(positions)
-
-  number_of_joints = len(data[0])
-  x = [[] for i in range(number_of_joints)]
-  y = [[] for i in range(number_of_joints)]
-  z = [[] for i in range(number_of_joints)]
-
-  dx = [[] for i in range(number_of_joints)]
-  dy = [[] for i in range(number_of_joints)]
-  dz = [[] for i in range(number_of_joints)]
-
-  for time in range(len(data)):
-    for joint in range(len(data[time])):
-      x[joint].append(data[time][joint][0])
-      y[joint].append(data[time][joint][1])
-      z[joint].append(data[time][joint][2])
-      if (time > 0):
-        dx[joint].append(x[joint][-1] - x[joint][-2])
-        dy[joint].append(y[joint][-1] - y[joint][-2])
-        dz[joint].append(z[joint][-1] - z[joint][-2])
-
-  labels = ["nose","leftShoulder","rightShoulder","leftElbow","rightElbow","leftWrist","rightWrist","leftHip","rightHip","leftKnee","rightKnee","leftAnkle","rightAnkle"]
-  NUM_COLORS = len(labels)
-  cm = plt.get_cmap('tab20')
-  fig = plt.figure("x")
-  ax = fig.add_subplot(111)
-  ax.set_prop_cycle(color=[cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
-  for time, v in enumerate(x):
-    plt.plot(v, label=labels[time])
-  plt.legend(loc="center left")
-  fig = plt.figure("y")
-  ax = fig.add_subplot(111)
-  ax.set_prop_cycle(color=[cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
-  for time, v in enumerate(y):
-    plt.plot(v, label=labels[time])
-  plt.legend(loc="center left")
-  fig = plt.figure("z")
-  ax = fig.add_subplot(111)
-  ax.set_prop_cycle(color=[cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
-  for time, v in enumerate(z):
-    plt.plot(v, label=labels[time])
-  plt.legend(loc="center left")
-  fig = plt.figure("dx")
-  ax = fig.add_subplot(111)
-  ax.set_prop_cycle(color=[cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
-  for time, v in enumerate(dx):
-    plt.plot(v, label=labels[time])
-  plt.legend(loc="center left")
-  fig = plt.figure("dy")
-  ax = fig.add_subplot(111)
-  ax.set_prop_cycle(color=[cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
-  for time, v in enumerate(dy):
-    plt.plot(v, label=labels[time])
-  plt.legend(loc="center left")
-  fig = plt.figure("dz")
-  ax = fig.add_subplot(111)
-  ax.set_prop_cycle(color=[cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
-  for time, v in enumerate(dz):
-    plt.plot(v, label=labels[time])
-  plt.legend(loc="center left")
-
-
-  plt.show()
-
-plot_data_from("meanFilteredData.csv")

+ 89 - 0
app/lib/exercises/exercises_validation/widgets/exercise_indicator.dart

@@ -0,0 +1,89 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:physigo/exercises/exercises_validation/models/exercise.dart';
+import 'package:physigo/exercises/exercises_validation/widgets/pose_detector.dart';
+
+class ExerciseIndicator extends StatelessWidget {
+  const ExerciseIndicator({
+    Key? key,
+    required this.repCounter,
+    required this.stepExerciseController,
+    required this.exercise,
+  }) : super(key: key);
+
+  final Stream<int> repCounter;
+  final Exercise exercise;
+  final StreamController<StepExercise> stepExerciseController;
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      children: [
+        StreamBuilder<StepExercise>(
+          stream: stepExerciseController.stream,
+          builder: (context, snapshot) {
+            Color color;
+            if (!snapshot.hasData) {
+              color = Colors.black;
+            } else {
+              switch (snapshot.data!) {
+                case StepExercise.notInPlace:
+                  color = Colors.black;
+                  break;
+                case StepExercise.ready:
+                  color = Colors.green;
+                  break;
+                case StepExercise.start:
+                  color = Colors.blue;
+                  break;
+                case StepExercise.end:
+                  color = Colors.yellow;
+                  break;
+              }
+            }
+            return Container(
+              height: 100,
+              width: 100,
+              color: color,
+            );
+          },
+        ),
+        StreamBuilder<int>(
+          stream: repCounter,
+          builder: (context, snapshot) {
+            var repCounter = 0;
+            if (snapshot.hasData) {
+              repCounter = snapshot.data!;
+            }
+            return Column(
+              children: [
+                Text(
+                  "Reps: ${repCounter % exercise.repetitions}/${exercise.repetitions}",
+                  style: const TextStyle(fontSize: 40),
+                ),
+                Text(
+                  "Sets: ${repCounter ~/ exercise.repetitions}/${exercise.sets}",
+                  style: const TextStyle(fontSize: 40),
+                ),
+                const SizedBox(height: 10),
+                ElevatedButton(
+                  onPressed: () => Navigator.pop(context),
+                  style: ElevatedButton.styleFrom(
+                    padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 32),
+                  ),
+                  child: const Text("BACK",
+                      style: TextStyle(
+                        fontSize: 20,
+                        fontWeight: FontWeight.bold,
+                        letterSpacing: 1.3,
+                      )),
+                )
+              ],
+            );
+          },
+        ),
+      ],
+    );
+  }
+}

+ 74 - 70
app/lib/exercises/exercises_validation/widgets/pose_detector.dart

@@ -2,12 +2,14 @@ import 'dart:async';
 
 import 'package:body_detection/models/pose_landmark.dart';
 import 'package:physigo/exercises/exercises_validation/models/exercise.dart';
+import 'package:physigo/exercises/services/exercises_service.dart';
 import 'package:rxdart/rxdart.dart';
 import 'package:body_detection/body_detection.dart';
 import 'package:body_detection/models/image_result.dart';
 import 'package:body_detection/models/pose.dart';
 import 'package:flutter/material.dart';
 
+import 'exercise_indicator.dart';
 import 'pose_painter.dart';
 
 typedef MeanFilteredData = List<List<double>>;
@@ -34,6 +36,7 @@ class _PoseDetectorState extends State<PoseDetector> {
   final List<StreamController> _streamControllers = [];
   final StreamController<Pose> _poseController = StreamController.broadcast();
   final StreamController<StepExercise> _stepExerciseController = StreamController.broadcast();
+  late final Stream<List<PoseLandmark>> _exerciseJointsStream;
   Image? _cameraImage;
   Pose? _detectedPose;
   Size _imageSize = Size.zero;
@@ -46,34 +49,79 @@ class _PoseDetectorState extends State<PoseDetector> {
     super.initState();
     _streamControllers.add(_poseController);
     _streamControllers.add(_stepExerciseController);
+    _exerciseJointsStream = _getExerciseJointsStream(_poseController.stream);
+    CombineLatestStream.combine2(
+        _exerciseJointsStream
+            .map((event) => event.where((e) => widget.exercise.jointsOnScreen.contains(e.type)).toList()),
+        _stepExerciseController.stream,
+        (a, b) => [a, b]).listen(_handleNotInPlacePosition);
+    CombineLatestStream.combine2(
+        _exerciseJointsStream
+            .map((event) => event.where((e) => widget.exercise.jointsOnScreen.contains(e.type)).toList())
+            .bufferCount(5, 1),
+        _stepExerciseController.stream,
+        (a, b) => [a, b]).listen(_handleReadyPosition);
     _startCamera = _startCameraStream();
-    _meanFilterStream = _getMeanFilterStream(_poseController.stream);
+    _meanFilterStream = _getMeanFilterStream(_exerciseJointsStream);
     _streamSubscriptions.add(
-        CombineLatestStream.combine2(_stepExerciseController.stream, _meanFilterStream, (a, b) => [a, b]).listen((value) {
-      final stepExercise = value.first as StepExercise;
-      final meanFilteredData = value.last as MeanFilteredData;
-      final isStartOfExerciseMovement = widget.exercise.isAtStartMovement(meanFilteredData);
-      final isEndOfExerciseMovement = widget.exercise.isAtEndMovement(meanFilteredData);
-      if (stepExercise == StepExercise.notInPlace && isStartOfExerciseMovement) {
-        _stepExerciseController.add(StepExercise.ready);
-      }
-      if ((stepExercise == StepExercise.ready || stepExercise == StepExercise.start) && isEndOfExerciseMovement) {
-        _stepExerciseController.add(StepExercise.end);
-      }
-      if (stepExercise == StepExercise.end && isStartOfExerciseMovement) {
-        _stepExerciseController.add(StepExercise.start);
-      }
-    }));
+      CombineLatestStream.combine2(_stepExerciseController.stream, _meanFilterStream, (a, b) => [a, b]).listen(
+        (value) {
+          final stepExercise = value.first as StepExercise;
+          if (stepExercise == StepExercise.notInPlace) return;
+          final meanFilteredData = value.last as MeanFilteredData;
+          final isStartOfExerciseMovement = widget.exercise.isAtStartMovement(meanFilteredData);
+          final isEndOfExerciseMovement = widget.exercise.isAtEndMovement(meanFilteredData);
+          if ((stepExercise == StepExercise.start) && isEndOfExerciseMovement) {
+            _stepExerciseController.add(StepExercise.end);
+          }
+          if ((stepExercise == StepExercise.end || stepExercise == StepExercise.ready) && isStartOfExerciseMovement) {
+            _stepExerciseController.add(StepExercise.start);
+          }
+        },
+      ),
+    );
     _stepExerciseController.add(StepExercise.notInPlace);
     _repCounter = _stepExerciseController.stream
-        .where((event) => event == StepExercise.start)
+        .pairwise()
+        .where((event) => event.first == StepExercise.end && event.last == StepExercise.start)
         .scan((int accumulated, value, index) => accumulated + 1, 0);
+    _repCounter.listen((_) {
+      ExercisesService.addScoreToExercise(widget.exercise, 5 * (widget.exercise.difficulty + 1));
+    });
+  }
+
+  void _handleNotInPlacePosition(List<Object?> event) {
+    final poseLandmarks = event.first as List<PoseLandmark>;
+    final stepExercise = event.last as StepExercise;
+    if (stepExercise == StepExercise.notInPlace) return;
+    for (final poseLandmark in poseLandmarks) {
+      if (poseLandmark.inFrameLikelihood < 0.8) {
+        _stepExerciseController.add(StepExercise.notInPlace);
+        return;
+      }
+    }
   }
 
-  Stream<MeanFilteredData> _getMeanFilterStream(Stream<Pose> stream) {
+  void _handleReadyPosition(List<Object?> event) {
+    final poseLandmarksBuffered = event.first as List<List<PoseLandmark>>;
+    final stepExercise = event.last as StepExercise;
+    if (stepExercise != StepExercise.notInPlace) return;
+    for (final poseLandmarks in poseLandmarksBuffered) {
+      for (final poseLandmark in poseLandmarks) {
+        if (poseLandmark.inFrameLikelihood < 0.8) return;
+      }
+    }
+    _stepExerciseController.add(StepExercise.ready);
+  }
+
+  Stream<List<PoseLandmark>> _getExerciseJointsStream(Stream<Pose> stream) {
     return stream
         .where((pose) => pose.landmarks.isNotEmpty)
-        .map((pose) => pose.landmarks.where((landmark) => Exercise.authorizedType.contains(landmark.type)).toList())
+        .map((pose) => pose.landmarks.where((landmark) => Exercise.authorizedType.contains(landmark.type)).toList());
+  }
+
+  Stream<MeanFilteredData> _getMeanFilterStream(Stream<List<PoseLandmark>> stream) {
+    return stream
         // Get last [buffer] poses
         .bufferCount(meanFilterBuffer, 1)
         // Swap matrix [buffer] * [authorizedType.length]
@@ -143,7 +191,9 @@ class _PoseDetectorState extends State<PoseDetector> {
 
   void _handlePose(Pose? pose) {
     if (!mounted) return;
-    if (pose != null) _poseController.add(pose);
+    if (pose != null) {
+      _poseController.add(pose);
+    }
     setState(() {
       _detectedPose = pose;
     });
@@ -180,60 +230,14 @@ class _PoseDetectorState extends State<PoseDetector> {
                 ),
               ),
             ),
-            StreamBuilder<StepExercise>(
-              stream: _stepExerciseController.stream,
-              builder: (context, snapshot) {
-                Color color;
-                if (!snapshot.hasData) {
-                  color = Colors.black;
-                } else {
-                  switch (snapshot.data!) {
-                    case StepExercise.notInPlace:
-                      color = Colors.black;
-                      break;
-                    case StepExercise.ready:
-                      color = Colors.green;
-                      break;
-                    case StepExercise.start:
-                      color = Colors.blue;
-                      break;
-                    case StepExercise.end:
-                      color = Colors.yellow;
-                      break;
-                  }
-                }
-                return Container(
-                  height: 100,
-                  width: 100,
-                  color: color,
-                );
-              },
+            ExerciseIndicator(
+              stepExerciseController: _stepExerciseController,
+              repCounter: _repCounter,
+              exercise: widget.exercise,
             ),
-            StreamBuilder<int>(
-              stream: _repCounter,
-              builder: (context, snapshot) {
-                var repCounter = 0;
-                if (snapshot.hasData) {
-                  repCounter = snapshot.data!;
-                }
-                return Column(
-                  children: [
-                    Text(
-                      "${repCounter % widget.exercise.reps}/${widget.exercise.reps}",
-                      style: const TextStyle(fontSize: 40),
-                    ),
-                    Text(
-                      "${repCounter ~/ widget.exercise.reps}/${widget.exercise.series}",
-                      style: const TextStyle(fontSize: 40),
-                    ),
-                  ],
-                );
-              },
-            )
           ],
         );
       },
     );
   }
-
 }

+ 35 - 0
app/lib/exercises/services/exercises_service.dart

@@ -0,0 +1,35 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:physigo/exercises/exercises_validation/models/exercise.dart';
+
+class ExercisesService {
+  static final _db = FirebaseFirestore.instance;
+  static final _exercises = _db.collection("Exercises");
+  static final _exercisesScores = _db.collection("ExercisesScores");
+
+  static Future<List<Exercise>> getExercises() async {
+    final exercisesSnapshot = await _exercises.get();
+    if (exercisesSnapshot.docs.isEmpty) {
+      return [];
+    }
+    final exercises = exercisesSnapshot.docs.map((e) => Exercise.fromMap(e.id, e.data())).toList();
+    return exercises;
+  }
+
+  static Future<void> addScoreToExercise(Exercise exercise, int score) async {
+    final userId = FirebaseAuth.instance.currentUser!.uid;
+    final exerciseScoreSnapshot =
+        await _exercisesScores.where("user", isEqualTo: userId).where("exercise", isEqualTo: exercise.id).get();
+    if (exerciseScoreSnapshot.docs.isEmpty) {
+      await _exercisesScores.add({"score": score, "user": userId, "exercise": exercise.id});
+    } else {
+      final exerciseScore = exerciseScoreSnapshot.docs.first;
+      _exercisesScores.doc(exerciseScore.id).update({"score": exerciseScore.get("score") + score});
+    }
+    final user = await _db.collection("profileInfo").doc(FirebaseAuth.instance.currentUser?.uid).get();
+    _db
+        .collection("profileInfo")
+        .doc(FirebaseAuth.instance.currentUser?.uid)
+        .update({"total_points": user.get("total_points") + score});
+  }
+}

+ 19 - 21
app/lib/friends/friends_page.dart

@@ -10,34 +10,32 @@ class FriendsPage extends StatefulWidget {
 }
 
 class _FriendsPageState extends State<FriendsPage> {
-  int _selectedIndex = 0;
-
   static const List<Widget> _widgets = [
     FriendsList(),
     Requests(),
   ];
 
-  void _onItemTapped(int index) {
-    setState(() {
-      _selectedIndex = index;
-    });
-  }
-
   @override
   Widget build(BuildContext context) {
-    return Scaffold(
-        bottomNavigationBar: BottomNavigationBar(
-          items: const [
-            BottomNavigationBarItem(icon: Icon(Icons.people), label: 'Friends'),
-            BottomNavigationBarItem(icon: Icon(Icons.notifications), label: 'Requests'),
-          ],
-          currentIndex: _selectedIndex,
-          onTap: _onItemTapped,
+    return DefaultTabController(
+      length: _widgets.length,
+      child: Scaffold(
+        appBar: AppBar(
+          automaticallyImplyLeading: false,
+          bottom: const TabBar(
+            tabs: [
+              Tab(child: Text("Friends", style: TextStyle(fontSize: 18))),
+              Tab(child: Text("Requests", style: TextStyle(fontSize: 18))),
+            ],
+          ),
+        ),
+        body: const Center(
+          child: Padding(
+            padding: EdgeInsets.all(32.0),
+            child: TabBarView(children: _widgets),
+          ),
         ),
-        body: Center(
-            child: Padding(
-          padding: const EdgeInsets.all(32.0),
-          child: _widgets[_selectedIndex],
-        )));
+      ),
+    );
   }
 }

+ 6 - 3
app/lib/friends/widgets/friends_list.dart

@@ -13,7 +13,6 @@ class FriendsList extends StatefulWidget {
 }
 
 class _FriendsListState extends State<FriendsList> {
-
   void _shareLocation(String friendId) async {
     try {
       await ChallengeLocationService.shareChallengeLocation(friendId);
@@ -58,8 +57,12 @@ class _FriendsListState extends State<FriendsList> {
   Widget _friendTile(Friend friend) {
     return Card(
       child: ListTile(
-        title: Text("${friend.name} ${friend.surname}"),
-        trailing: TextButton(
+        title: Text(
+          "${friend.name} ${friend.surname}",
+          textAlign: TextAlign.center,
+          style: const TextStyle(fontSize: 18),
+        ),
+        subtitle: TextButton(
           child: const Text("Share challenge location"),
           onPressed: () => _shareLocation(friend.id),
         ),

+ 93 - 0
app/lib/home/home_page.dart

@@ -0,0 +1,93 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/Services/AuthService.dart';
+import 'package:physigo/challenge/daily_challenge_page.dart';
+import 'package:physigo/challenge/weekly_challenge_page.dart';
+
+import '../challenges/challenges_utils.dart';
+
+class HomePage extends StatelessWidget {
+  const HomePage({Key? key}) : super(key: key);
+
+  String get name => AuthenticationServices.user!.name;
+  num get totalPoint => AuthenticationServices.user!.totalPoints;
+
+  @override
+  Widget build(BuildContext context) {
+    ChallengesUtils.generateChallengeIfNeeded();
+    return Padding(
+      padding: const EdgeInsets.all(16.0),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          Text(
+            "Welcome back $name!",
+            textAlign: TextAlign.center,
+            style: const TextStyle(
+              fontSize: 28,
+              fontWeight: FontWeight.bold,
+            ),
+          ),
+          const SizedBox(height: 36),
+          Column(
+            children: const [
+              Align(
+                alignment: Alignment.bottomLeft,
+                child: Text(
+                  "Here are your challenges:",
+                  style: TextStyle(fontSize: 24),
+                ),
+              ),
+              SizedBox(height: 24),
+              ChallengeTile(
+                challengeType: "Daily",
+                challengePage: DailyChallengePage(),
+              ),
+              ChallengeTile(
+                challengeType: "Weekly",
+                challengePage: WeeklyChallengePage(),
+              ),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class ChallengeTile extends StatelessWidget {
+  final Widget challengePage;
+  const ChallengeTile({
+    required this.challengePage,
+    required this.challengeType,
+    Key? key,
+  }) : super(key: key);
+
+  final String challengeType;
+
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      width: MediaQuery.of(context).size.width * 0.6,
+      child: Card(
+        elevation: 3,
+        color: Colors.blueGrey,
+        child: ListTile(
+          title: Text(
+            challengeType.toUpperCase(),
+            textAlign: TextAlign.center,
+            style: const TextStyle(
+              fontSize: 18,
+              fontWeight: FontWeight.bold,
+              letterSpacing: 1.8,
+              color: Colors.white,
+            ),
+          ),
+          trailing: const Icon(Icons.arrow_forward, color: Colors.white),
+          onTap: () {
+            Navigator.push(context, MaterialPageRoute(builder: (_) => challengePage));
+          },
+        ),
+      ),
+    );
+  }
+}

+ 1 - 1
app/lib/logIn.dart

@@ -65,7 +65,7 @@ class _LogIn extends State<LogIn> {
   }
 
   void signInUser() async {
-    dynamic authResult = await _auth.loginUser(_email.text, _passwd.text);
+    dynamic authResult = await AuthenticationServices.loginUser(_email.text, _passwd.text);
     if (authResult == null) {
       print("log in error");
     } else {

+ 27 - 20
app/lib/main.dart

@@ -1,13 +1,14 @@
 import 'package:firebase_core/firebase_core.dart';
 import 'package:flutter/material.dart';
+import 'package:physigo/Services/AuthService.dart';
+import 'package:physigo/Services/logged_in_user.dart';
 
 import 'firebase_options.dart';
 import 'push_notifications_initializer.dart';
 import 'package:physigo/logIn.dart';
 import 'package:physigo/profilePage.dart';
-import 'package:physigo/widgets/rankingNavigation.dart';
 import 'package:physigo/SignupPage.dart';
-import 'package:physigo/mainPage.dart';
+import 'package:physigo/menu/main_page.dart';
 import 'package:physigo/welcomeScreen.dart';
 
 void main() async {
@@ -16,7 +17,6 @@ void main() async {
     options: DefaultFirebaseOptions.currentPlatform,
   );
   handleMessages();
-
   runApp(const PhysiGo());
 }
 
@@ -24,22 +24,29 @@ class PhysiGo extends StatelessWidget {
   const PhysiGo({Key? key}) : super(key: key);
   @override
   Widget build(BuildContext context) {
-    return MaterialApp(
-      title: 'PhisyGo',
-      theme: ThemeData(
-        primarySwatch: Colors.blueGrey,
-      ),
-      initialRoute: '/',
-      routes: {
-        '/': (context) => MyHomePage(title: ''),
-        '/register': (context) => SignupPage(),
-        //'/register2': (context) => RegisterScreen2(title: ''),
-        //'/register3': (context) => RegisterScreen3(title: ''),
-        '/login': (context) => LogIn(title: ''),
-        '/profilePage': (context) => ProfilePage(title: ''),
-        '/mainPage': (context) => MainPage()
-      },
-      //home: const MyHomePage(title: 'Flutter Demo Home Page'),
-    );
+    return FutureBuilder<LoggedInUser?>(
+        future: AuthenticationServices.getCurrentUser(),
+        builder: (context, snapshot) {
+          if (snapshot.connectionState == ConnectionState.waiting) {
+            return const Center(child: CircularProgressIndicator());
+          }
+          return MaterialApp(
+            title: 'PhysiGo',
+            theme: ThemeData(
+              primarySwatch: Colors.blueGrey,
+            ),
+            initialRoute: snapshot.hasData ? '/mainPage' : "/",
+            routes: {
+              '/': (context) => MyHomePage(title: ''),
+              '/register': (context) => SignupPage(),
+              //'/register2': (context) => RegisterScreen2(title: ''),
+              //'/register3': (context) => RegisterScreen3(title: ''),
+              '/login': (context) => LogIn(title: ''),
+              '/profilePage': (context) => ProfilePage(title: ''),
+              '/mainPage': (context) => MainPage()
+            },
+            //home: const MyHomePage(title: 'Flutter Demo Home Page'),
+          );
+        });
   }
 }

+ 0 - 37
app/lib/mainPage.dart

@@ -1,37 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:physigo/walking/walking_permission.dart';
-
-class MainPage extends StatefulWidget {
-  const MainPage({Key? key}) : super(key: key);
-  @override
-  State<MainPage> createState() => _MainPage();
-}
-class _MainPage extends State<MainPage> {
-
-
-  @override
-  Widget build(BuildContext context) {
-    final arguments = (ModalRoute.of(context)?.settings.arguments ?? <String, dynamic>{}) as Map;
-    final String id = arguments['uid'];
-    print(id);
-
-    return Scaffold(
-      appBar: AppBar(
-
-        title: const Text('Welcome Back!'),
-        leading: GestureDetector(
-          onTap: () {
-            print('going to profile page');
-            Navigator.pushNamed(context, '/profilePage',
-              arguments: {'id': id},
-            );
-            },
-          child: Icon(
-            Icons.account_circle_rounded,  // add custom icons also
-          ),
-        ),
-      ),
-      body: WalkingPermission()
-    );
-  }
-}

+ 52 - 0
app/lib/menu/main_page.dart

@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/exercises/exercises_page.dart';
+import 'package:physigo/friends/friends_page.dart';
+import 'package:physigo/home/home_page.dart';
+import 'package:physigo/ranking/ranking_page.dart';
+
+class MainPage extends StatefulWidget {
+  const MainPage({Key? key}) : super(key: key);
+  @override
+  State<MainPage> createState() => _MainPage();
+}
+
+class _MainPage extends State<MainPage> {
+  static const List<Widget> _widgets = [
+    HomePage(),
+    ExercisesPage(),
+    RankingPage(),
+    FriendsPage(),
+  ];
+  int _selectedIndex = 0;
+
+  void _onMenuTap(int newIndex) {
+    setState(() {
+      _selectedIndex = newIndex;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return SafeArea(
+      child: Scaffold(
+        bottomNavigationBar: BottomNavigationBar(
+          selectedItemColor: Colors.blueGrey.shade700,
+          selectedFontSize: 18,
+          selectedLabelStyle: const TextStyle(decoration: TextDecoration.underline),
+          selectedIconTheme: const IconThemeData(size: 32),
+          unselectedItemColor: Colors.black,
+          type: BottomNavigationBarType.fixed, // need to specify fixed with more than 3 items
+          currentIndex: _selectedIndex,
+          onTap: _onMenuTap,
+          items: const [
+            BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
+            BottomNavigationBarItem(icon: Icon(Icons.sports_gymnastics), label: "Exercises"),
+            BottomNavigationBarItem(icon: Icon(Icons.emoji_events), label: "Ranking"),
+            BottomNavigationBarItem(icon: Icon(Icons.people), label: "Social"),
+          ],
+        ),
+        body: _widgets[_selectedIndex],
+      ),
+    );
+  }
+}

+ 1 - 1
app/lib/navigation/models/directions.dart

@@ -26,7 +26,7 @@ class Directions {
     final coordinatesJson = List.from(json['features'][0]['geometry']['coordinates']);
     final List<LatLng> waypointsCoordinates = [];
     for (final coordinateJson in coordinatesJson) {
-      waypointsCoordinates.add(LatLng(coordinateJson[1], coordinateJson[0]));
+      waypointsCoordinates.add(LatLng(coordinateJson[1].toDouble(), coordinateJson[0].toDouble()));
     }
     return waypointsCoordinates;
   }

+ 3 - 2
app/lib/navigation/navigation_page.dart

@@ -1,10 +1,11 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
 import 'package:flutter/material.dart';
 import 'package:latlong2/latlong.dart';
 import 'package:physigo/navigation/utils/permissions_utils.dart';
 import 'package:physigo/navigation/widgets/navigation.dart';
 
 class NavigationPage extends StatelessWidget {
-  final LatLng destination;
+  final GeoPoint destination;
 
   const NavigationPage({required this.destination, Key? key}) : super(key: key);
 
@@ -22,7 +23,7 @@ class NavigationPage extends StatelessWidget {
             return Text(snapshot.error.toString());
           }
 
-          return Navigation(destination: destination);
+          return Navigation(destination: LatLng(destination.latitude, destination.longitude));
         },
       ),
     );

+ 3 - 1
app/lib/push_notifications_initializer.dart

@@ -1,8 +1,10 @@
 import 'package:awesome_notifications/awesome_notifications.dart';
 import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:firebase_auth/firebase_auth.dart';
 import 'package:firebase_messaging/firebase_messaging.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:physigo/Services/AuthService.dart';
 
 void handleMessages() async {
   FirebaseMessaging messaging = FirebaseMessaging.instance;
@@ -38,7 +40,7 @@ void handleMessages() async {
 
   messaging.onTokenRefresh.listen((fcmToken) async {
     final fcmToken = await messaging.getToken();
-    final user = _firestore.collection('Users').doc('tlmysIvwTBaoZKWqBofx');
+    final user = _firestore.collection('Users').doc(FirebaseAuth.instance.currentUser?.uid);
     user.update({
       'token': fcmToken,
     });

+ 49 - 0
app/lib/ranking/ranking_page.dart

@@ -0,0 +1,49 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/ranking/widgets/ranking.dart';
+import '../currentUser.dart';
+import '../widgets/datatable.dart';
+
+class RankingPage extends StatefulWidget {
+  const RankingPage({Key? key}) : super(key: key);
+
+  @override
+  State<RankingPage> createState() => _RankingPageState();
+}
+
+class _RankingPageState extends State<RankingPage> {
+  static const List<Widget> _widgets = [
+    Ranking(ranks: MyDataTable(name: "daily_points"), currentUser: CurrentUser(name: "daily_points")),
+    Ranking(ranks: MyDataTable(name: "weekly_points"), currentUser: CurrentUser(name: "weekly_points")),
+    Ranking(ranks: MyDataTable(name: "total_points"), currentUser: CurrentUser(name: "total_points")),
+    Ranking(ranks: MyDataTable(name: "total_points"), currentUser: CurrentUser(name: "total_points")),
+  ];
+
+  @override
+  Widget build(BuildContext context) {
+    return DefaultTabController(
+      length: _widgets.length,
+      child: SafeArea(
+        child: Scaffold(
+          appBar: AppBar(
+            automaticallyImplyLeading: false,
+            bottom: const TabBar(
+              isScrollable: true,
+              tabs: [
+                Tab(child: Text("Daily Challenge", style: TextStyle(fontSize: 18))),
+                Tab(child: Text("Weekly Challenge", style: TextStyle(fontSize: 18))),
+                Tab(child: Text("Total", style: TextStyle(fontSize: 18))),
+                Tab(child: Text("Exercises", style: TextStyle(fontSize: 18))),
+              ],
+            ),
+          ),
+          body: const Center(
+            child: Padding(
+              padding: EdgeInsets.all(32.0),
+              child: TabBarView(children: _widgets),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 22 - 0
app/lib/ranking/widgets/ranking.dart

@@ -0,0 +1,22 @@
+import 'package:flutter/material.dart';
+
+class Ranking extends StatelessWidget {
+  final Widget ranks;
+  final Widget currentUser;
+  const Ranking({required this.ranks, required this.currentUser, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      children: [
+        SizedBox(
+          height: 350,
+          child: ranks,
+        ),
+        Container(
+          child: currentUser,
+        )
+      ],
+    );
+  }
+}

+ 0 - 136
app/lib/walking/walking_counter.dart

@@ -1,136 +0,0 @@
-import 'dart:async';
-import 'package:flutter/material.dart';
-import 'package:flutter_activity_recognition/flutter_activity_recognition.dart';
-import 'package:geolocator/geolocator.dart' hide ActivityType;
-import 'package:rxdart/rxdart.dart';
-import 'models/score.dart';
-import 'services/walking_services.dart';
-
-class WalkingCounter extends StatefulWidget {
-  WalkingCounter({Key? key}) : super(key: key);
-
-  @override
-  State<WalkingCounter> createState() => _WalkingCounterState();
-}
-
-class _WalkingCounterState extends State<WalkingCounter> {
-  final FlutterActivityRecognition activityRecognition =
-      FlutterActivityRecognition.instance;
-  final StreamController<Position> _streamController =
-      StreamController.broadcast();
-  final StreamController<ActivityType> _simulatedActivityController =
-      StreamController.broadcast();
-  late Stream<dynamic> _activityPositionStream;
-
-  num distance = 0;
-  Position? lastPosition;
-  num points = 0;
-
-  @override
-  void initState() {
-    // _simulatedActivityController.stream.listen(print);
-    // activityRecognition.activityStream.listen((event) {
-    //   print(event.type);
-    // });
-    _activityPositionStream = CombineLatestStream.combine2(
-      _simulatedActivityController.stream,
-      Geolocator.getPositionStream(
-          locationSettings: LocationSettings(distanceFilter: 0)),
-      (ActivityType a, Position b) => [a, b],
-    );
-    super.initState();
-  }
-
-  @override
-  void dispose() {
-    _streamController.close();
-    super.dispose();
-  }
-
-  void addScore() {
-    WalkingServices.addScore(Score(points: 45, distance: 500));
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    Geolocator.getPositionStream(
-            locationSettings: LocationSettings(distanceFilter: 0))
-        .listen((position) {
-      _streamController.add(position);
-      print(position);
-    });
-    return Scaffold(
-      body: Center(
-        child: Column(
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: [
-            ElevatedButton(
-              onPressed: () =>
-                  _simulatedActivityController.add(ActivityType.WALKING),
-              child: Text("WALKING"),
-            ),
-            ElevatedButton(
-              onPressed: () =>
-                  _simulatedActivityController.add(ActivityType.RUNNING),
-              child: Text("RUNNING"),
-            ),
-            ElevatedButton(
-              onPressed: () =>
-                  _simulatedActivityController.add(ActivityType.STILL),
-              child: Text("STILL"),
-            ),
-            StreamBuilder<dynamic>(
-              stream: _activityPositionStream,
-              builder: (context, snapshot) {
-                if (!snapshot.hasData) {
-                  return CircularProgressIndicator();
-                }
-                var activity = snapshot.data![0];
-                var position = snapshot.data![1];
-                if (activity == ActivityType.WALKING || activity == ActivityType.RUNNING) {
-                  if (lastPosition != null) {
-                    distance = Geolocator.distanceBetween(
-                        lastPosition!.latitude,
-                        lastPosition!.longitude,
-                        position.latitude,
-                        position.longitude);
-                  }
-                }
-                if (distance > 0 && activity == ActivityType.WALKING) {
-                   points += distance / 10;
-                }
-                else if (distance > 0 && activity == ActivityType.RUNNING){
-                   points += distance / 8;
-                }
-                if (activity != ActivityType.WALKING && activity != ActivityType.RUNNING && points>0){
-                  WalkingServices.addScore(Score(
-                      points: points.floor(),distance: 0)
-
-                  );
-                  points=0;
-                }
-                lastPosition = position;
-                print(distance);
-                return Text("$distance");
-              },
-            ),
-          ],
-        ),
-      ),
-    );
-  }
-}
-
-/*
-Get distance only when walking or running
-
-if (walking || running) {
-  calculateScore()
-}
-*/
-
-// --W---R----W----S----W------
-
-// --P--P--P--P--P--P
-
-// --[W,P]--[W,P]--[R,P]--

+ 4 - 6
app/lib/walking/walking_permission.dart → app/lib/walking/walking_page.dart

@@ -1,12 +1,10 @@
-import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_activity_recognition/flutter_activity_recognition.dart';
-import 'package:flutter_activity_recognition/models/permission_request_result.dart';
 import 'package:geolocator/geolocator.dart';
-import 'package:physigo/walking/walking_counter.dart';
+import 'package:physigo/walking/widgets/walking.dart';
 
-class WalkingPermission extends StatelessWidget {
-  WalkingPermission({Key? key}) : super(key: key);
+class WalkingPage extends StatelessWidget {
+  WalkingPage({Key? key}) : super(key: key);
   final FlutterActivityRecognition activityRecognition =
       FlutterActivityRecognition.instance;
 
@@ -21,7 +19,7 @@ class WalkingPermission extends StatelessWidget {
           } else if (snapshot.hasError) {
             return Text("Error");
           } else {
-            return WalkingCounter();
+            return Walking();
           }
         },
       )

+ 104 - 0
app/lib/walking/widgets/walking.dart

@@ -0,0 +1,104 @@
+import 'dart:async';
+import 'package:flutter/material.dart';
+import 'package:flutter_activity_recognition/flutter_activity_recognition.dart';
+import 'package:geolocator/geolocator.dart' hide ActivityType;
+import 'package:rxdart/rxdart.dart';
+import '../models/score.dart';
+import '../services/walking_services.dart';
+
+class Walking extends StatefulWidget {
+  Walking({Key? key}) : super(key: key);
+
+  @override
+  State<Walking> createState() => _WalkingState();
+}
+
+class _WalkingState extends State<Walking> {
+  final FlutterActivityRecognition activityRecognition = FlutterActivityRecognition.instance;
+  final StreamController<Position> _streamController = StreamController.broadcast();
+  late Stream<dynamic> _activityPositionStream;
+
+  num distance = 0;
+  Position? lastPosition;
+  num points = 0;
+  num totalPoints = 0;
+  num totalDistance = 0;
+
+  @override
+  void initState() {
+    _activityPositionStream = CombineLatestStream.combine2(
+      activityRecognition.activityStream,
+      Geolocator.getPositionStream(locationSettings: LocationSettings(distanceFilter: 0)),
+      (Activity a, Position b) => [a, b],
+    );
+    _activityPositionStream.listen(_calculatePoints);
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    _streamController.close();
+    super.dispose();
+  }
+
+  void _updatePoints(num newPoints) {
+    setState(() {
+      totalPoints += newPoints;
+      points += newPoints;
+    });
+  }
+
+  void _updateDistance(num newDistance) {
+    setState(() {
+      totalDistance += newDistance;
+    });
+  }
+
+  void _calculatePoints(dynamic activityPosition) {
+    var activity = activityPosition[0] as Activity;
+    var position = activityPosition[1];
+    if (activity.type == ActivityType.WALKING || activity.type == ActivityType.RUNNING) {
+      if (lastPosition != null) {
+        distance = Geolocator.distanceBetween(
+            lastPosition!.latitude, lastPosition!.longitude, position.latitude, position.longitude);
+        _updateDistance(distance);
+      }
+    }
+    if (distance > 0 && activity.type == ActivityType.WALKING) {
+      _updatePoints(distance / 10);
+    } else if (distance > 0 && activity.type == ActivityType.RUNNING) {
+      _updatePoints(distance / 8);
+    }
+    if (activity.type != ActivityType.WALKING && activity.type != ActivityType.RUNNING && points.floor() > 0) {
+      WalkingServices.addScore(Score(points: points.floor(), distance: 0));
+      setState(() {
+        points = 0;
+      });
+    }
+    lastPosition = position;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      body: Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            Text(
+              "Walked distance since start:\n${totalDistance.round()} m",
+              textAlign: TextAlign.center,
+              style: const TextStyle(fontSize: 24),
+            ),
+            const SizedBox(height: 32),
+            Text(
+              "Earned points since start:\n${totalPoints.floor()} pts",
+              textAlign: TextAlign.center,
+              style: const TextStyle(fontSize: 24),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 72 - 92
app/lib/widgets/datatable.dart

@@ -4,125 +4,105 @@ import 'package:cloud_firestore/cloud_firestore.dart';
 import 'package:flutter/material.dart';
 
 class MyDataTable extends StatefulWidget {
-
-  const MyDataTable( {
-    required this.name, Key? key}):super(key: key);
+  const MyDataTable({required this.name, Key? key}) : super(key: key);
   final String name;
   @override
   State<MyDataTable> createState() => _MyDataTableState();
 }
 
 class _MyDataTableState extends State<MyDataTable> {
-  CollectionReference usersList =
-  FirebaseFirestore.instance.collection("Users");
+  CollectionReference usersList = FirebaseFirestore.instance.collection("Users");
   @override
   Widget build(BuildContext context) {
     Future<List> userList = getUsersList();
 
-    return  Container(
+    return Container(
       height: 450,
-      alignment: const Alignment(0,-1),
+      alignment: const Alignment(0, -1),
       child: Scrollbar(
-        child:SingleChildScrollView(
-            child: FutureBuilder<dynamic>(
-              future: userList,
-              builder: (context, snapshot) {
-                if(snapshot.connectionState == ConnectionState.waiting){
-                  return const Text('Wait a moment');
-                }
-                if(snapshot.connectionState == ConnectionState.done) {
-                  return buildDataTable(snapshot.data, widget.name);
-                }
-                if(snapshot.hasError) {
-                  return const Text('Something went wrong');
-                }
-                return const Text("  ");
-                },
+        child: SingleChildScrollView(
+          child: FutureBuilder<dynamic>(
+            future: userList,
+            builder: (context, snapshot) {
+              if (snapshot.connectionState == ConnectionState.waiting) {
+                return const Text('Wait a moment');
+              }
+              if (snapshot.connectionState == ConnectionState.done) {
+                return buildDataTable(snapshot.data, widget.name);
+              }
+              if (snapshot.hasError) {
+                return const Text('Something went wrong');
+              }
+              return const Text("  ");
+            },
           ),
         ),
       ),
-      );
+    );
   }
+
   @override
   void initState() {
     super.initState();
   }
 
-
   Future<List> getUsersList() async {
     var querySnapshot = await usersList.orderBy(widget.name, descending: true).get();
-      return querySnapshot.docs;
-  }
+    return querySnapshot.docs;
   }
+}
 
-  Widget buildDataTable(snapshotData, String name) {
-    final columns = ['Place', 'ID', 'Name', 'points'];
+Widget buildDataTable(snapshotData, String name) {
+  final columns = ['Place', 'ID', 'Name', 'points'];
 
-    return DataTable(
-      columnSpacing: 10.0,
-      columns: getColumns(columns),
-      rows: getRows(snapshotData, name),
-    );
-  }
-  List<DataColumn> getColumns(List<String> columns) {
-    return columns.map((String column) {
-      return DataColumn(
+  return DataTable(
+    columnSpacing: 10.0,
+    columns: getColumns(columns),
+    rows: getRows(snapshotData, name),
+  );
+}
 
-        label: Text(column),
-      );
-    }).toList();
-  }
+List<DataColumn> getColumns(List<String> columns) {
+  return columns.map((String column) {
+    return DataColumn(
+      label: Text(column),
+    );
+  }).toList();
+}
 
-  List<DataRow> getRows(dynamic snapshotData, String name) {
-    List<DataRow> rows = [];
-    int length = snapshotData.length;
-    for(int i = 0; i < length; i++){
-      dynamic user = snapshotData[i];
-      bool anonymous = user["anonymous"];
-      if (anonymous) {
-        rows.add(
-            DataRow(
-                cells: [
-                  DataCell(
-                      Text("${i+1}")
-                  ),
-                  const DataCell(
-                    Text("anonym"),
-                  ),
-                  const DataCell(
-                    Text("name"),
-                  ),
-                  DataCell(
-                    Text("${user[name]}"),
-                  ),
-                ]
-            )
-        );
-      }
-      else {
-        rows.add(
-            DataRow(
-                cells: [
-                  DataCell(
-                      Text("${i+1}")
-                  ),
-                  DataCell(
-                    Text("${user["shared_id"]}"),
-                  ),
-                  DataCell(
-                      Text(user["name"]),
-                  ),
-                  DataCell(
-                    Text("${user[name]}"),
-                  ),
-                ]
-            )
-        );
-      }
+List<DataRow> getRows(dynamic snapshotData, String name) {
+  List<DataRow> rows = [];
+  int length = snapshotData.length;
+  for (int i = 0; i < length; i++) {
+    dynamic user = snapshotData[i];
+    bool anonymous = user["anonymous"];
+    if (anonymous) {
+      rows.add(DataRow(cells: [
+        DataCell(Text("${i + 1}")),
+        const DataCell(
+          Text("anonym"),
+        ),
+        const DataCell(
+          Text("name"),
+        ),
+        DataCell(
+          Text("${user[name]}"),
+        ),
+      ]));
+    } else {
+      rows.add(DataRow(cells: [
+        DataCell(Text("${i + 1}")),
+        DataCell(
+          Text("${user["shared_id"]}"),
+        ),
+        DataCell(
+          Text(user["name"]),
+        ),
+        DataCell(
+          Text("${user[name]}"),
+        ),
+      ]));
     }
-    return rows;
   }
-
-
-
-
+  return rows;
+}

+ 0 - 77
app/lib/widgets/rankingNavigation.dart

@@ -1,77 +0,0 @@
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/material.dart';
-import '../currentUser.dart';
-import 'datatable.dart';
-
-
-class RankingNavigation extends StatefulWidget {
-  const RankingNavigation({Key? key}) : super(key: key);
-
-  @override
-  State<RankingNavigation> createState() => _RankingNavigationState();
-}
-
-class _RankingNavigationState extends State<RankingNavigation> {
-  int _selectedIndex = 0;
-  static const List<Widget> _widgetOptions = <Widget>[
-    MyDataTable(name: 'daily_points'),
-    MyDataTable(name: 'weekly_points'),
-    MyDataTable(name: 'total_points'),
-    ];
-  static const List<Widget> _widgetOptions2 = <Widget>[
-    CurrentUser(name: 'daily_points'),
-    CurrentUser(name: 'weekly_points'),
-    CurrentUser(name: 'total_points'),
-  ];
-
-  void _onItemTapped(int index) {
-    setState(() {
-      _selectedIndex = index;
-    });
-  }
-
-
-  @override
-  Widget build(BuildContext context) {
-
-    return Scaffold(
-      appBar: AppBar(
-        toolbarHeight: 50,
-        title: const Text('Ranking'),
-      ),
-      body: SafeArea(
-        child: Column(
-        children: [
-          SizedBox(
-            height: 350,
-          child:_widgetOptions.elementAt(_selectedIndex),
-        ),
-        Container(
-          child:_widgetOptions2.elementAt(_selectedIndex),
-        )
-      ],
-      )
-      ),
-
-      bottomNavigationBar: BottomNavigationBar(
-        items: const <BottomNavigationBarItem>[
-          BottomNavigationBarItem(
-            icon: Icon(Icons.event),
-            label: 'Daily',
-          ),
-          BottomNavigationBarItem(
-            icon: Icon(Icons.event),
-            label: 'Weekly',
-          ),
-          BottomNavigationBarItem(
-            icon: Icon(Icons.event),
-            label: 'Monthly',
-          ),
-        ],
-        currentIndex: _selectedIndex,
-        selectedItemColor: Colors.amber[800],
-        onTap: _onItemTapped,
-      ),
-    );
-  }
-}

+ 21 - 0
app/pubspec.lock

@@ -303,6 +303,27 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.1"
+  location:
+    dependency: "direct main"
+    description:
+      name: location
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "4.4.0"
+  location_platform_interface:
+    dependency: transitive
+    description:
+      name: location_platform_interface
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.3.0"
+  location_web:
+    dependency: transitive
+    description:
+      name: location_web
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.1.1"
   matcher:
     dependency: transitive
     description:

+ 1 - 0
app/pubspec.yaml

@@ -50,6 +50,7 @@ dependencies:
   geolocator: ^8.2.1
   provider: ^6.0.1
   geoflutterfire: ^3.0.3
+  location: ^4.0.0
 
 dev_dependencies:
   flutter_test:

+ 11 - 5
scripts/fetch_places.py

@@ -1,5 +1,6 @@
 import requests
 import json
+import pygeohash as pgh
 from google.cloud import firestore
 
 import os
@@ -15,24 +16,29 @@ with open('map features.txt', 'r') as f:
     for line in f:
         if line[0] == "#":
             continue
+        category, category_name = line.split(":")
+        category_name = category_name.replace("\"", "").strip()
         overpass_query = f"""
 [out:json];
 area["ISO3166-1"="PL"][admin_level=2];
-node[{line[:-1]}](area);
+node[{category}](area);
 out center;
 """
         response = requests.get(overpass_url, params={"data": overpass_query})
         try:
             elements = response.json()["elements"]
         except json.JSONDecodeError:
-            print(f"Could not decode data for {line[:-1]}")
+            print(f"Could not decode data for {category}")
             continue
-        object_type = line.split("=")[1].strip()
+        object_type = category.split("=")[1].strip()
         data = {}
         for el in elements:
             try:
-                data["name"] = el["tags"].get("name", object_type)
-                data["location"] = firestore.GeoPoint(el["lat"], el["lon"])
+                data["name"] = el["tags"].get("name", category_name)
+                data["location"] = {
+                    "geohash": pgh.encode(el["lat"], el["lon"], precision=9),
+                    "geopoint": firestore.GeoPoint(el["lat"], el["lon"])
+                }
                 data["category"] = object_type
                 batch.set(db.collection("Places").document(str(el["id"])), data)
                 count += 1

+ 13 - 13
scripts/map features.txt

@@ -3,17 +3,17 @@
 #amenity=college
 #amenity=library
 #amenity=university
-#amenity=arts_centre
-#amenity=community_centre
-#amenity=fountain
+amenity=arts_centre:"Arts centre"
+amenity=community_centre:"Community centre"
+amenity=fountain:"Fountain"
 #amenity=planetarium
 #amenity=public_bookcase
-#amenity=social_centre
+amenity=social_centre:"Social centre"
 #amenity=theatre
 #amenity=townhall
 #amenity=bbq
-#amenity=clock
-#boundary=forest
+amenity=clock:"Clock"
+boundary=forest:"Forest"
 #building=cathedral
 #building=chapel
 #building=church
@@ -30,13 +30,13 @@
 #historic=memorial
 #historic=monument
 #historic=ruins
-#leisure=dog_park
-#leisure=park
-#tourism=artwork
-#tourism=attraction
+leisure=dog_park:"Dog park"
+leisure=park:"Park"
+tourism=artwork:"Artwork"
+tourism=attraction:"Attraction"
 #tourism=gallery
 #tourism=museum
-#tourism=picnic_site
+tourism=picnic_site:"Picnic site"
 #tourism=theme_park
-#tourism=view_point
-#tourism=zoo
+tourism=view_point :View point"
+tourism=zoo:"Zoo"

Fișier diff suprimat deoarece este prea mare
+ 2435 - 1
serverless_functions/package-lock.json


Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff