38 Комити b3278f78f0 ... 1bf18cb614

Аутор SHA1 Порука Датум
  Marcin Jaborski 1bf18cb614 Merge remote-tracking branch 'origin/push-notifications' into push-notifications пре 3 година
  Marcin Jaborski 27170cc7b5 Generate daily and weekly challenge, add checking to avoid generating multiple challenges пре 3 година
  Marcin Jaborski f480945cc3 Generate daily challenge пре 3 година
  Marcin Jaborski ab1adc1c07 Receive push notifications пре 3 година
  Marcin Jaborski 898e664bad Find place for challenge with geoflutterfire пре 3 година
  Marcin Jaborski 28532a6813 Challenge widget пре 3 година
  Marcin Jaborski a87a71c69d Generating challenges пре 3 година
  Léo Salé 5b6642209f fix rebase conflicts пре 3 година
  iwa-42 644d88207d files of David. I changed only AuthService file пре 3 година
  iwa-42 1fb110bf8e Ranking with fixed bugs пре 3 година
  iwa-42 2ccc2ee79c Ranking with live updates пре 3 година
  iwa-42 b3a5c2a53c Order by points пре 3 година
  iwa-42 99c258638e Connection between application and Firebase and basic ranking пре 3 година
  Rébite b00ff63bca updatepointsfromuserProfil пре 3 година
  Rébite d71e029050 permission + running's points пре 3 година
  Rébite 05b164ab57 permission + running's points пре 3 година
  Léo Salé 30476beb07 feat: calculate points based on distance пре 3 година
  Léo Salé efab437603 feat: good luck remi пре 3 година
  Léo Salé ced30a7ce4 fix: typo пре 3 година
  David Sokol Zelazko cd43f42da5 problems with register page fixed пре 3 година
  David Sokol Zelazko 80ac91bdc5 profile page with userdata пре 3 година
  David Sokol Zelazko 85554795d1 writting in firebase done пре 3 година
  David Sokol Zelazko a4bc2accb6 test of writting data on firebase пре 3 година
  David Sokol Zelazko 8d689885cc sign up and log in authentification done пре 3 година
  David Sokol Zelazko ce9c773cf6 Navigation in SignUp form пре 3 година
  David Sokol Zelazko 55e041f6c8 LogIn & Register graphic only пре 3 година
  Léo Salé 9e5fe602cf feat: send push notification on events пре 3 година
  Léo Salé 74a9910592 fix: reset shared id text when adding friend пре 3 година
  Léo Salé 8283c3bc2b feat: display push notifications пре 3 година
  Léo Salé 4d3cef9ade feat: friends requests and location sharing пре 3 година
  Léo Salé 3688e869fd feat: add push up exercise пре 3 година
  Léo Salé bd798315fe feat: model exercise пре 3 година
  Léo Salé b6c426536e feat: can count squat rep пре 3 година
  Léo Salé 2bc57137db to reset пре 3 година
  Léo Salé f074fd6228 feat: mean filter pose пре 3 година
  Léo Salé d26f6e2bc3 feat: display detected pose пре 3 година
  Léo Salé 8bfa2796aa feat: display camera images пре 3 година
  Léo Salé 8b1b426194 feat: handle camera permissions пре 3 година
59 измењених фајлова са 3324 додато и 124 уклоњено
  1. 3 2
      app/android/app/build.gradle
  2. 46 0
      app/android/app/google-services.json
  3. 10 12
      app/android/app/src/main/AndroidManifest.xml
  4. BIN
      app/assets/communicate.png
  5. BIN
      app/assets/hello.png
  6. BIN
      app/assets/id-card.png
  7. BIN
      app/assets/teamlogo.png
  8. BIN
      app/assets/user.png
  9. 1 0
      app/ios/Flutter/Debug.xcconfig
  10. 1 0
      app/ios/Flutter/Release.xcconfig
  11. 46 0
      app/ios/Podfile
  12. 138 0
      app/ios/Podfile.lock
  13. 86 0
      app/ios/Runner.xcodeproj/project.pbxproj
  14. 3 0
      app/ios/Runner.xcworkspace/contents.xcworkspacedata
  15. 2 0
      app/ios/Runner/Info.plist
  16. 47 0
      app/lib/Services/AuthService.dart
  17. 44 0
      app/lib/Services/DatabaseManager.dart
  18. 341 0
      app/lib/SignupPage.dart
  19. 69 0
      app/lib/currentUser.dart
  20. 50 0
      app/lib/exercises/exercises_page.dart
  21. 30 0
      app/lib/exercises/exercises_validation/exercise_validation_page.dart
  22. 115 0
      app/lib/exercises/exercises_validation/meanFilteredData.csv
  23. 116 0
      app/lib/exercises/exercises_validation/models/exercise.dart
  24. 22 0
      app/lib/exercises/exercises_validation/models/push_up.dart
  25. 22 0
      app/lib/exercises/exercises_validation/models/squat.dart
  26. 76 0
      app/lib/exercises/exercises_validation/plot.py
  27. 17 0
      app/lib/exercises/exercises_validation/utils/permissions_utils.dart
  28. 239 0
      app/lib/exercises/exercises_validation/widgets/pose_detector.dart
  29. 60 0
      app/lib/exercises/exercises_validation/widgets/pose_painter.dart
  30. 43 0
      app/lib/friends/friends_page.dart
  31. 17 0
      app/lib/friends/models/challenge_location_request.dart
  32. 22 0
      app/lib/friends/models/friend.dart
  33. 40 0
      app/lib/friends/models/friend_relation.dart
  34. 7 0
      app/lib/friends/models/friend_request.dart
  35. 88 0
      app/lib/friends/services/challenge_location_service.dart
  36. 119 0
      app/lib/friends/services/friends_service.dart
  37. 67 0
      app/lib/friends/widgets/add_friend.dart
  38. 87 0
      app/lib/friends/widgets/challenge_location_requests.dart
  39. 69 0
      app/lib/friends/widgets/friends_list.dart
  40. 87 0
      app/lib/friends/widgets/friends_requests.dart
  41. 19 0
      app/lib/friends/widgets/requests.dart
  42. 80 0
      app/lib/logIn.dart
  43. 21 84
      app/lib/main.dart
  44. 37 0
      app/lib/mainPage.dart
  45. 13 0
      app/lib/navigation/utils/geometry_utils.dart
  46. 101 0
      app/lib/profilePage.dart
  47. 70 0
      app/lib/push_notifications_initializer.dart
  48. 35 0
      app/lib/services/push_notifications_service.dart
  49. 10 0
      app/lib/walking/models/score.dart
  50. 35 0
      app/lib/walking/services/walking_services.dart
  51. 136 0
      app/lib/walking/walking_counter.dart
  52. 79 0
      app/lib/walking/walking_permission.dart
  53. 48 0
      app/lib/welcomeScreen.dart
  54. 128 0
      app/lib/widgets/datatable.dart
  55. 77 0
      app/lib/widgets/rankingNavigation.dart
  56. 105 7
      app/pubspec.lock
  57. 17 2
      app/pubspec.yaml
  58. 42 0
      serverless_functions/api/push-notification.ts
  59. 41 17
      serverless_functions/package-lock.json

+ 3 - 2
app/android/app/build.gradle

@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 
 android {
-    compileSdkVersion flutter.compileSdkVersion
+    compileSdkVersion 31
 
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
@@ -45,7 +45,8 @@ android {
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         applicationId "com.example.physigo"
         minSdkVersion 23
-        targetSdkVersion flutter.targetSdkVersion
+        multiDexEnabled true
+        targetSdkVersion 31
         versionCode flutterVersionCode.toInteger()
         versionName flutterVersionName
     }

+ 46 - 0
app/android/app/google-services.json

@@ -0,0 +1,46 @@
+{
+  "project_info": {
+    "project_number": "33414107403",
+    "project_id": "physigo",
+    "storage_bucket": "physigo.appspot.com"
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:33414107403:android:aaf9d5af5ed2a103a1920e",
+        "android_client_info": {
+          "package_name": "com.example.physigo"
+        }
+      },
+      "oauth_client": [
+        {
+          "client_id": "33414107403-f05g2s2bhagi9kdqqbfc4aac98t7s9f2.apps.googleusercontent.com",
+          "client_type": 3
+        }
+      ],
+      "api_key": [
+        {
+          "current_key": "AIzaSyBUb8raBFHQA_AwR5cUrdCT6TkoWjj_jO8"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": [
+            {
+              "client_id": "33414107403-f05g2s2bhagi9kdqqbfc4aac98t7s9f2.apps.googleusercontent.com",
+              "client_type": 3
+            },
+            {
+              "client_id": "33414107403-7e3vb8825iemlec2geietj4b00ldhbbj.apps.googleusercontent.com",
+              "client_type": 2,
+              "ios_info": {
+                "bundle_id": "com.example.physigo"
+              }
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 10 - 12
app/android/app/src/main/AndroidManifest.xml

@@ -3,6 +3,10 @@
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE"/>
+   <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
+    <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
    <application
         android:label="PhysiGo"
         android:name="${applicationName}"
@@ -19,24 +23,18 @@
                  the Android process has started. This theme is visible to the user
                  while the Flutter UI initializes. After that, this theme continues
                  to determine the Window background behind the Flutter UI. -->
-            <meta-data
-              android:name="io.flutter.embedding.android.NormalTheme"
-              android:resource="@style/NormalTheme"
-              />
+            <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" />
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
-
-            <intent-filter>
+            <!-- <intent-filter>
                 <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                 <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
+            </intent-filter> -->
         </activity>
         <!-- Don't delete the meta-data below.
              This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
-        <meta-data
-            android:name="flutterEmbedding"
-            android:value="2" />
+        <meta-data android:name="flutterEmbedding" android:value="2" />
     </application>
 </manifest>

BIN
app/assets/communicate.png


BIN
app/assets/hello.png


BIN
app/assets/id-card.png


BIN
app/assets/teamlogo.png


BIN
app/assets/user.png


+ 1 - 0
app/ios/Flutter/Debug.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
 #include "Generated.xcconfig"

+ 1 - 0
app/ios/Flutter/Release.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
 #include "Generated.xcconfig"

+ 46 - 0
app/ios/Podfile

@@ -0,0 +1,46 @@
+# Uncomment this line to define a global platform for your project
+platform :ios, '10.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+  'Debug' => :debug,
+  'Profile' => :release,
+  'Release' => :release,
+}
+
+
+def flutter_root
+  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+  unless File.exist?(generated_xcode_build_settings_path)
+    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+  end
+
+  File.foreach(generated_xcode_build_settings_path) do |line|
+    matches = line.match(/FLUTTER_ROOT\=(.*)/)
+    return matches[1].strip if matches
+  end
+  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+  use_frameworks!
+  use_modular_headers!
+
+  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+  pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '8.15.0'
+
+end
+
+
+post_install do |installer|
+  installer.pods_project.targets.each do |target|
+    flutter_additional_ios_build_settings(target)
+  end
+end
+

+ 138 - 0
app/ios/Podfile.lock

@@ -0,0 +1,138 @@
+PODS:
+  - cloud_firestore (3.1.15):
+    - Firebase/Firestore (= 8.15.0)
+    - firebase_core
+    - Flutter
+  - Firebase/Auth (8.15.0):
+    - Firebase/CoreOnly
+    - FirebaseAuth (~> 8.15.0)
+  - Firebase/CoreOnly (8.15.0):
+    - FirebaseCore (= 8.15.0)
+  - Firebase/Firestore (8.15.0):
+    - Firebase/CoreOnly
+    - FirebaseFirestore (~> 8.15.0)
+  - firebase_auth (3.3.18):
+    - Firebase/Auth (= 8.15.0)
+    - firebase_core
+    - Flutter
+  - firebase_core (1.17.0):
+    - Firebase/CoreOnly (= 8.15.0)
+    - Flutter
+  - FirebaseAuth (8.15.0):
+    - FirebaseCore (~> 8.0)
+    - GoogleUtilities/AppDelegateSwizzler (~> 7.7)
+    - GoogleUtilities/Environment (~> 7.7)
+    - GTMSessionFetcher/Core (~> 1.5)
+  - FirebaseCore (8.15.0):
+    - FirebaseCoreDiagnostics (~> 8.0)
+    - GoogleUtilities/Environment (~> 7.7)
+    - GoogleUtilities/Logger (~> 7.7)
+  - FirebaseCoreDiagnostics (8.15.0):
+    - GoogleDataTransport (~> 9.1)
+    - GoogleUtilities/Environment (~> 7.7)
+    - GoogleUtilities/Logger (~> 7.7)
+    - nanopb (~> 2.30908.0)
+  - FirebaseFirestore (8.15.0):
+    - FirebaseFirestore/AutodetectLeveldb (= 8.15.0)
+  - FirebaseFirestore/AutodetectLeveldb (8.15.0):
+    - FirebaseFirestore/Base
+    - FirebaseFirestore/WithLeveldb
+  - FirebaseFirestore/Base (8.15.0)
+  - FirebaseFirestore/WithLeveldb (8.15.0):
+    - FirebaseFirestore/Base
+  - Flutter (1.0.0)
+  - flutter_compass (0.0.1):
+    - Flutter
+  - geolocator_apple (1.2.0):
+    - Flutter
+  - GoogleDataTransport (9.1.4):
+    - GoogleUtilities/Environment (~> 7.7)
+    - nanopb (< 2.30910.0, >= 2.30908.0)
+    - PromisesObjC (< 3.0, >= 1.2)
+  - GoogleUtilities/AppDelegateSwizzler (7.7.0):
+    - GoogleUtilities/Environment
+    - GoogleUtilities/Logger
+    - GoogleUtilities/Network
+  - GoogleUtilities/Environment (7.7.0):
+    - PromisesObjC (< 3.0, >= 1.2)
+  - GoogleUtilities/Logger (7.7.0):
+    - GoogleUtilities/Environment
+  - GoogleUtilities/Network (7.7.0):
+    - GoogleUtilities/Logger
+    - "GoogleUtilities/NSData+zlib"
+    - GoogleUtilities/Reachability
+  - "GoogleUtilities/NSData+zlib (7.7.0)"
+  - GoogleUtilities/Reachability (7.7.0):
+    - GoogleUtilities/Logger
+  - GTMSessionFetcher/Core (1.7.2)
+  - nanopb (2.30908.0):
+    - nanopb/decode (= 2.30908.0)
+    - nanopb/encode (= 2.30908.0)
+  - nanopb/decode (2.30908.0)
+  - nanopb/encode (2.30908.0)
+  - PromisesObjC (2.1.0)
+
+DEPENDENCIES:
+  - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
+  - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
+  - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
+  - FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `8.15.0`)
+  - Flutter (from `Flutter`)
+  - flutter_compass (from `.symlinks/plugins/flutter_compass/ios`)
+  - geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`)
+
+SPEC REPOS:
+  trunk:
+    - Firebase
+    - FirebaseAuth
+    - FirebaseCore
+    - FirebaseCoreDiagnostics
+    - GoogleDataTransport
+    - GoogleUtilities
+    - GTMSessionFetcher
+    - nanopb
+    - PromisesObjC
+
+EXTERNAL SOURCES:
+  cloud_firestore:
+    :path: ".symlinks/plugins/cloud_firestore/ios"
+  firebase_auth:
+    :path: ".symlinks/plugins/firebase_auth/ios"
+  firebase_core:
+    :path: ".symlinks/plugins/firebase_core/ios"
+  FirebaseFirestore:
+    :git: https://github.com/invertase/firestore-ios-sdk-frameworks.git
+    :tag: 8.15.0
+  Flutter:
+    :path: Flutter
+  flutter_compass:
+    :path: ".symlinks/plugins/flutter_compass/ios"
+  geolocator_apple:
+    :path: ".symlinks/plugins/geolocator_apple/ios"
+
+CHECKOUT OPTIONS:
+  FirebaseFirestore:
+    :git: https://github.com/invertase/firestore-ios-sdk-frameworks.git
+    :tag: 8.15.0
+
+SPEC CHECKSUMS:
+  cloud_firestore: 76e73835a6ac79ac825190eefd876a1e100c7835
+  Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d
+  firebase_auth: ace975b530600827bd901a9a18584f7cdaf6d160
+  firebase_core: aa1b92020533f5c23955e388c347c58fd64f8627
+  FirebaseAuth: 3e73bf8abf4fbb40f8b421f361f4cc48ee57388c
+  FirebaseCore: 5743c5785c074a794d35f2fff7ecc254a91e08b1
+  FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb
+  FirebaseFirestore: cb361b7f8f225a225c9f11b8d42066baebb1630c
+  Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
+  flutter_compass: cbbd285cea1584c7ac9c4e0c3e1f17cbea55e855
+  geolocator_apple: cc556e6844d508c95df1e87e3ea6fa4e58c50401
+  GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b
+  GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
+  GTMSessionFetcher: 5595ec75acf5be50814f81e9189490412bad82ba
+  nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
+  PromisesObjC: 99b6f43f9e1044bd87a95a60beff28c2c44ddb72
+
+PODFILE CHECKSUM: f99a294491bb4a8b0ed5ee7ec97f8c8001a75c19
+
+COCOAPODS: 1.11.3

+ 86 - 0
app/ios/Runner.xcodeproj/project.pbxproj

@@ -13,6 +13,7 @@
 		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
 		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
 		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+		AEC8ADA59409895D03733F7A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14A3BA377ED9242B31B10B26 /* Pods_Runner.framework */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -31,6 +32,8 @@
 /* Begin PBXFileReference section */
 		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
 		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+		14A3BA377ED9242B31B10B26 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		1B1E3B130A92F3A6CC5D4508 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
 		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
 		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
 		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -42,6 +45,8 @@
 		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		A936D2F453AE9AAA9478C7D6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
+		E88252A05C17DBF07881EBAB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -49,12 +54,32 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				AEC8ADA59409895D03733F7A /* Pods_Runner.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		01C38152DAD16C1BC69EF05C /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				14A3BA377ED9242B31B10B26 /* Pods_Runner.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		14F2D831B115D42B82C7A7E1 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				A936D2F453AE9AAA9478C7D6 /* Pods-Runner.debug.xcconfig */,
+				E88252A05C17DBF07881EBAB /* Pods-Runner.release.xcconfig */,
+				1B1E3B130A92F3A6CC5D4508 /* Pods-Runner.profile.xcconfig */,
+			);
+			name = Pods;
+			path = Pods;
+			sourceTree = "<group>";
+		};
 		9740EEB11CF90186004384FC /* Flutter */ = {
 			isa = PBXGroup;
 			children = (
@@ -72,6 +97,8 @@
 				9740EEB11CF90186004384FC /* Flutter */,
 				97C146F01CF9000F007C117D /* Runner */,
 				97C146EF1CF9000F007C117D /* Products */,
+				14F2D831B115D42B82C7A7E1 /* Pods */,
+				01C38152DAD16C1BC69EF05C /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -105,12 +132,15 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
 			buildPhases = (
+				921990EA0522D53EB760F73A /* [CP] Check Pods Manifest.lock */,
 				9740EEB61CF901F6004384FC /* Run Script */,
 				97C146EA1CF9000F007C117D /* Sources */,
 				97C146EB1CF9000F007C117D /* Frameworks */,
 				97C146EC1CF9000F007C117D /* Resources */,
 				9705A1C41CF9048500538489 /* Embed Frameworks */,
 				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+				A39D21FDDD6C92E2BC5C1DF1 /* [CP] Embed Pods Frameworks */,
+				71A126D59B4B2A205423702B /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -183,6 +213,45 @@
 			shellPath = /bin/sh;
 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
 		};
+		71A126D59B4B2A205423702B /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Copy Pods Resources";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		921990EA0522D53EB760F73A /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
 		9740EEB61CF901F6004384FC /* Run Script */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
@@ -197,6 +266,23 @@
 			shellPath = /bin/sh;
 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
 		};
+		A39D21FDDD6C92E2BC5C1DF1 /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */

+ 3 - 0
app/ios/Runner.xcworkspace/contents.xcworkspacedata

@@ -4,4 +4,7 @@
    <FileRef
       location = "group:Runner.xcodeproj">
    </FileRef>
+   <FileRef
+      location = "group:Pods/Pods.xcodeproj">
+   </FileRef>
 </Workspace>

+ 2 - 0
app/ios/Runner/Info.plist

@@ -26,6 +26,8 @@
 	<true/>
 	<key>NSLocationWhenInUseUsageDescription</key>
 	<string>Physigo needs access to location when open.</string>
+	<key>NSCameraUsageDescription</key>
+	<string>To verify exercise's execution, please grant camera access</string>
 	<key>UILaunchStoryboardName</key>
 	<string>LaunchScreen</string>
 	<key>UIMainStoryboardFile</key>

+ 47 - 0
app/lib/Services/AuthService.dart

@@ -0,0 +1,47 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:physigo/Services/DatabaseManager.dart';
+
+class AuthenticationServices {
+  final FirebaseAuth _auth = FirebaseAuth.instance;
+
+  //Register a user
+  Future createNewUser(String address, 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(),
+          email, name, phone, sharedID, surname, 0, DateTime.now(), 'null', user!.uid);
+
+      return user;
+    } catch (e) {
+      print(e.toString());
+    }
+  }
+
+  //LogIn with user
+  Future loginUser(String email, String password) async {
+    try {
+      UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
+      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;
+  }
+
+  Stream<int> getPlace(dynamic user) async* {
+    var users = await FirebaseFirestore.instance
+        .collection('Users')
+        .where("total_points", isGreaterThan: user["total_points"])
+        .get();
+    yield users.docs.length + 1;
+  }
+}

+ 44 - 0
app/lib/Services/DatabaseManager.dart

@@ -0,0 +1,44 @@
+//import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:cloud_firestore/cloud_firestore.dart';
+
+class DatabaseManager {
+  late final String points;
+  final CollectionReference profileList =
+  FirebaseFirestore.instance.collection('profileInfo');
+  CollectionReference usersList =
+  FirebaseFirestore.instance.collection("Users");
+
+  DatabaseManager(this.points);
+
+  Future<void> createUserData(String address,
+      bool anonymous,
+      String birth_date,
+      DateTime create_date,
+      DateTime lastChallengeDate,
+      String mail,
+      String name,
+      String phoneNumber,
+      String shared_id,
+      String surname,
+      int totalpoints,
+      DateTime updated_date,
+      String weeklyplace,
+      String uid) async {
+    print('----------- ' + uid);
+    return await profileList.doc(uid).set({
+      'address': address,
+      'anonymous': anonymous,
+      'birth_date': birth_date,
+      'created_date': create_date,
+      'last_challenge_date': lastChallengeDate,
+      'mail': mail,
+      'name': name,
+      'phone_number': phoneNumber,
+      'shared_id': shared_id,
+      'surname': surname,
+      'total_points': totalpoints,
+      'updated_date': updated_date,
+      'weekly_place': weeklyplace,
+    });
+  }
+}

+ 341 - 0
app/lib/SignupPage.dart

@@ -0,0 +1,341 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/Services/AuthService.dart';
+
+
+//Fields..
+TextEditingController _name = TextEditingController();
+TextEditingController _surname = TextEditingController();
+TextEditingController _mail = TextEditingController();
+TextEditingController _phoneNumber = TextEditingController();
+TextEditingController _address = TextEditingController();
+TextEditingController _username = TextEditingController();
+TextEditingController _passwd = TextEditingController();
+TextEditingController _passwd2 = TextEditingController();
+
+bool anonymous = false;
+String? _dateString;
+
+final AuthenticationServices _auth = AuthenticationServices();
+
+
+class SignupPage extends StatefulWidget {
+  const SignupPage({Key? key}) : super(key: key);
+
+  @override
+  State<SignupPage> createState() => _SignupPageState();
+}
+
+class _SignupPageState extends State<SignupPage> {
+  int _pageIndex = 0;
+
+  List<Widget> forms = [
+    SignupForm1(
+      title: '',
+    ),
+    SignupForm2(
+      title: '',
+    ),
+    SignupForm3(
+      title: '',
+    )
+  ];
+
+  void _updatePageIndex(int newPageIndex) {
+    if (newPageIndex == -1) {
+      Navigator.pushNamed(context, '/');
+    } else if (newPageIndex == 3) {
+      //autentification
+      //Navigator.pop(context);
+      newPageIndex == 2;
+    } else {
+      setState(() {
+        _pageIndex = newPageIndex;
+      });
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      floatingActionButton: Row(
+        mainAxisAlignment: MainAxisAlignment.end,
+        children: [
+          FloatingActionButton(
+              heroTag: "btn1",
+              child: Icon(Icons.navigate_before_outlined),
+              onPressed: () => _updatePageIndex(_pageIndex - 1)),
+          SizedBox(
+            width: 25,
+          ),
+          FloatingActionButton(
+              heroTag: "btn2",
+              child: Icon(Icons.navigate_next_outlined),
+              onPressed: () => _updatePageIndex(_pageIndex + 1))
+        ],
+      ),
+      body: Center(child: forms[_pageIndex]),
+    );
+  }
+}
+
+class SignupForm1 extends StatefulWidget {
+  const SignupForm1({Key? key, required this.title}) : super(key: key);
+  final String title;
+
+  @override
+  //_RegisterScreen createState() => _RegisterScreen();
+  State<SignupForm1> createState() => _SignupForm1();
+}
+
+class _SignupForm1 extends State<SignupForm1> {
+  DateTime? _dateTime;
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Register Screen'),
+      ),
+      body: Center(
+          child: SizedBox(
+        width: 300,
+        child: Column(children: <Widget>[
+          const SizedBox(height: 30),
+          Text(
+            'Personal Information',
+            style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
+          ),
+          const SizedBox(height: 30),
+          Image.asset(
+            'assets/user.png',
+            width: 150,
+          ),
+          const SizedBox(height: 30),
+          TextFormField(
+            controller: _name,
+            decoration: InputDecoration(
+              hintText: 'Name',
+            ),
+          ),
+          const SizedBox(height: 10),
+          TextFormField(
+            controller: _surname,
+            decoration: InputDecoration(
+              hintText: 'Surname',
+            ),
+          ),
+          const SizedBox(height: 10),
+          Row(children: <Widget>[
+            Text(_dateString == null
+                ? 'Select birthday date :'
+                : _dateString.toString()),
+            const SizedBox(width: 49),
+            ElevatedButton(
+              onPressed: () {
+                showDatePicker(
+                        context: context,
+                        initialDate: DateTime.now(),
+                        firstDate: DateTime(1900),
+                        lastDate: DateTime.now())
+                    .then((date) {
+                  setState(() {
+                    _dateTime = date!;
+                    _dateString = "${date.day}-${date.month}-${date.year}";
+                  });
+                });
+              },
+              child: const Text('Select date'),
+            ),
+          ]),
+          const SizedBox(height: 10),
+        ]),
+      )),
+    );
+  }
+}
+
+class SignupForm2 extends StatefulWidget {
+  const SignupForm2({Key? key, required this.title}) : super(key: key);
+  final String title;
+
+  @override
+  //_RegisterScreen createState() => _RegisterScreen();
+  State<SignupForm2> createState() => _SignupForm2();
+}
+
+class _SignupForm2 extends State<SignupForm2> {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Register Screen'),
+      ),
+      body: Center(
+          child: SizedBox(
+        width: 300,
+        child: Column(children: <Widget>[
+          const SizedBox(height: 30),
+          Text(
+            'Contact Details',
+            style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
+          ),
+          const SizedBox(height: 30),
+          Image.asset(
+            'assets/communicate.png',
+            width: 150,
+          ),
+          const SizedBox(height: 30),
+          TextFormField(
+            controller: _mail,
+            decoration: InputDecoration(
+              hintText: 'Mail',
+            ),
+          ),
+          const SizedBox(height: 10),
+          TextFormField(
+            controller: _phoneNumber,
+            decoration: InputDecoration(
+              hintText: 'Phone Number',
+            ),
+          ),
+          const SizedBox(height: 10),
+          TextFormField(
+            controller: _address,
+            decoration: InputDecoration(
+              hintText: 'Home Adress..',
+            ),
+          ),
+          const SizedBox(height: 10),
+          Row(children: <Widget>[
+            Text('Anonymous'),
+            Checkbox(
+              checkColor: Colors.white,
+              value: anonymous,
+              onChanged: (bool? value) {
+                setState(() {
+                  anonymous = value!;
+                });
+              },
+            ),
+          ]),
+          const SizedBox(height: 10),
+        ]),
+      )),
+    );
+  }
+}
+
+class SignupForm3 extends StatefulWidget {
+  const SignupForm3({Key? key, required this.title}) : super(key: key);
+  final String title;
+
+  @override
+  //_RegisterScreen createState() => _RegisterScreen();
+  State<SignupForm3> createState() => _SignupForm3();
+}
+
+class _SignupForm3 extends State<SignupForm3> {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Register Screen'),
+      ),
+      body: Center(
+          child: SizedBox(
+        width: 300,
+        child: Column(children: <Widget>[
+          const SizedBox(height: 30),
+          Text(
+            'User details',
+            style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
+          ),
+          const SizedBox(height: 30),
+          Image.asset(
+            'assets/id-card.png',
+            width: 150,
+          ),
+          const SizedBox(height: 30),
+          TextFormField(
+            controller: _username,
+            decoration: InputDecoration(
+              hintText: 'Username',
+            ),
+          ),
+          const SizedBox(height: 10),
+          TextFormField(
+            controller: _passwd,
+            obscureText: true,
+            enableSuggestions: false,
+            autocorrect: false,
+            decoration: InputDecoration(
+              hintText: 'Password',
+            ),
+          ),
+          const SizedBox(height: 10),
+          TextFormField(
+            controller: _passwd2,
+            obscureText: true,
+            enableSuggestions: false,
+            autocorrect: false,
+            decoration: InputDecoration(
+              hintText: 'Repeat password',
+            ),
+          ),
+          const SizedBox(height: 15),
+          ElevatedButton(
+              onPressed: () {
+                try {
+                  if (_passwd.text == _passwd2.text) {
+                    createUser();
+                    Navigator.pop(context);
+                  } else {
+                    print("passwd is not the same");
+                  }
+                } catch (e) {
+                  print(e.toString());
+                }
+              },
+              child: const Text('Finish Register')),
+
+        ]),
+      )),
+    );
+  }
+
+  void createUser() async {
+    dynamic result =
+        await _auth.createNewUser(_address.text, anonymous, _dateString.toString(), _name.text, _mail.text, _passwd.text,
+            _phoneNumber.text, _username.text, _surname.text);
+    if (result == null) {
+      print('mail not valid');
+    } else {
+      //print(result.toString());
+      //[51.787378° N, 19.449455° E]
+
+      print('*****  address: ' + _address.text +
+          ' anonymous: ' + anonymous.toString() +
+          ' birthdate: ' + _dateString.toString() +
+          ' createDate: ' + DateTime.now().toString()+
+          ' last_challenge_data: ' + 'null'+
+          ' mail: ' + _mail.text +
+          ' name: ' + _name.text +
+          ' passwd: ' + '??? ' +
+          ' phone_number: ' + _phoneNumber.text+
+          ' shared_id: ' + _username.text +
+          ' surname: ' + _surname.text +
+          ' totalpoints: 0 ' +
+          ' update_date: ' + DateTime.now().toString()+
+          ' weeklyplace: ' + 'null');
+
+      _name.clear();
+      _surname.clear();
+      _mail.clear();
+      _phoneNumber.clear();
+      _address.clear();
+      _username.clear();
+      _passwd.clear();
+      _passwd2.clear();
+    }
+  }
+}

+ 69 - 0
app/lib/currentUser.dart

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

+ 50 - 0
app/lib/exercises/exercises_page.dart

@@ -0,0 +1,50 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/exercises/exercises_validation/models/squat.dart';
+
+import 'exercises_validation/exercise_validation_page.dart';
+import 'exercises_validation/models/push_up.dart';
+
+class ExercisesPage extends StatelessWidget {
+  const ExercisesPage({Key? key}) : super(key: key);
+
+  @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,
+                    ),
+                  ),
+                );
+              },
+              child: const Text('Push up'),
+            ),
+          ),
+          Center(
+            child: ElevatedButton(
+              onPressed: () {
+                Navigator.push(
+                  context,
+                  MaterialPageRoute(
+                    builder: (context) => const ExerciseValidationPage(
+                      exercise: squat,
+                    ),
+                  ),
+                );
+              },
+              child: const Text('Squat'),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 30 - 0
app/lib/exercises/exercises_validation/exercise_validation_page.dart

@@ -0,0 +1,30 @@
+import 'package:flutter/material.dart';
+
+import 'models/exercise.dart';
+import 'utils/permissions_utils.dart';
+import 'widgets/pose_detector.dart';
+
+class ExerciseValidationPage extends StatelessWidget {
+  final Exercise exercise;
+  const ExerciseValidationPage({required this.exercise, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      body: FutureBuilder<void>(
+        future: PermissionUtils.determineCameraPermission(),
+        builder: ((context, snapshot) {
+          if (snapshot.connectionState == ConnectionState.waiting) {
+            return const Center(child: CircularProgressIndicator());
+          }
+          if (snapshot.hasError) {
+            return Center(child: Text(snapshot.error.toString()));
+          }
+          return PoseDetector(
+            exercise: exercise,
+          );
+        }),
+      ),
+    );
+  }
+}

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

@@ -0,0 +1,115 @@
+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;

+ 116 - 0
app/lib/exercises/exercises_validation/models/exercise.dart

@@ -0,0 +1,116 @@
+import 'package:body_detection/models/pose_landmark_type.dart';
+
+import '../../../navigation/utils/geometry_utils.dart';
+import '../widgets/pose_detector.dart';
+
+enum AuthorizedTypeIndex {
+  nose,
+  leftShoulder,
+  rightShoulder,
+  leftElbow,
+  rightElbow,
+  leftWrist,
+  rightWrist,
+  leftHip,
+  rightHip,
+  leftKnee,
+  rightKnee,
+  leftAnkle,
+  rightAnkle,
+}
+
+class Exercise {
+  final int reps;
+  final int series;
+  final Criteria startMovement;
+  final Criteria endMovement;
+
+  const Exercise({
+    required this.reps,
+    required this.series,
+    required this.startMovement,
+    required this.endMovement,
+  });
+
+  bool isAtStartMovement(MeanFilteredData meanFilteredData) {
+    return startMovement.isAtPosition(meanFilteredData);
+  }
+
+  bool isAtEndMovement(MeanFilteredData meanFilteredData) {
+    return endMovement.isAtPosition(meanFilteredData);
+  }
+
+  static const authorizedType = [
+    PoseLandmarkType.nose,
+    PoseLandmarkType.leftShoulder,
+    PoseLandmarkType.rightShoulder,
+    PoseLandmarkType.leftElbow,
+    PoseLandmarkType.rightElbow,
+    PoseLandmarkType.leftWrist,
+    PoseLandmarkType.rightWrist,
+    PoseLandmarkType.leftHip,
+    PoseLandmarkType.rightHip,
+    PoseLandmarkType.leftKnee,
+    PoseLandmarkType.rightKnee,
+    PoseLandmarkType.leftAnkle,
+    PoseLandmarkType.rightAnkle,
+  ];
+}
+
+abstract class Criteria {
+  bool isAtPosition(MeanFilteredData meanFilteredData);
+}
+
+class CriteriaDistance implements Criteria {
+  final AuthorizedTypeIndex jointStart;
+  final AuthorizedTypeIndex jointEnd;
+  final int axis;
+  final int threshold;
+
+  const CriteriaDistance({
+    required this.jointStart,
+    required this.jointEnd,
+    required this.axis,
+    required this.threshold,
+  });
+
+  @override
+  bool isAtPosition(MeanFilteredData meanFilteredData) {
+    final start = meanFilteredData[jointStart.index][axis];
+    final end = meanFilteredData[jointEnd.index][axis];
+    final distance = (start - end).abs();
+    return distance < threshold;
+  }
+}
+
+class CriteriaAngle implements Criteria {
+  final AuthorizedTypeIndex jointStart;
+  final AuthorizedTypeIndex jointCenter;
+  final AuthorizedTypeIndex jointEnd;
+  final int threshold;
+
+  const CriteriaAngle({
+    required this.jointStart,
+    required this.jointCenter,
+    required this.jointEnd,
+    required this.threshold,
+  });
+
+  @override
+  bool isAtPosition(MeanFilteredData meanFilteredData) {
+    final start = Point3D(
+        x: meanFilteredData[jointStart.index][0],
+        y: meanFilteredData[jointStart.index][1],
+        z: meanFilteredData[jointStart.index][2]);
+    final center = Point3D(
+        x: meanFilteredData[jointCenter.index][0],
+        y: meanFilteredData[jointCenter.index][1],
+        z: meanFilteredData[jointCenter.index][2]);
+    final end = Point3D(
+        x: meanFilteredData[jointEnd.index][0],
+        y: meanFilteredData[jointEnd.index][1],
+        z: meanFilteredData[jointEnd.index][2]);
+    final angle = DistanceUtils.angleBetweenThreePoints(start, center, end).round();
+    return angle > threshold;
+  }
+}

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

@@ -0,0 +1,22 @@
+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,
+);

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

@@ -0,0 +1,22 @@
+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,
+);

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

@@ -0,0 +1,76 @@
+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")

+ 17 - 0
app/lib/exercises/exercises_validation/utils/permissions_utils.dart

@@ -0,0 +1,17 @@
+import 'package:permission_handler/permission_handler.dart';
+
+class PermissionUtils {
+  static Future<void> determineCameraPermission() async {
+    await Permission.storage.request();
+    var permission = await Permission.camera.status;
+    if (permission.isDenied) {
+      permission = await Permission.camera.request();
+      if (permission.isDenied) {
+        return Future.error('Camera permission is denied');
+      }
+    }
+    if (permission.isPermanentlyDenied) {
+      return Future.error('Camera permission is permanently denied, we cannot request permissions');
+    }
+  }
+}

+ 239 - 0
app/lib/exercises/exercises_validation/widgets/pose_detector.dart

@@ -0,0 +1,239 @@
+import 'dart:async';
+
+import 'package:body_detection/models/pose_landmark.dart';
+import 'package:physigo/exercises/exercises_validation/models/exercise.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 'pose_painter.dart';
+
+typedef MeanFilteredData = List<List<double>>;
+typedef LandmarkVariations = List<List<double>>;
+
+enum StepExercise {
+  notInPlace,
+  ready,
+  start,
+  end,
+}
+
+class PoseDetector extends StatefulWidget {
+  final Exercise exercise;
+  const PoseDetector({required this.exercise, Key? key}) : super(key: key);
+
+  @override
+  State<PoseDetector> createState() => _PoseDetectorState();
+}
+
+class _PoseDetectorState extends State<PoseDetector> {
+  static const meanFilterBuffer = 5;
+  final List<StreamSubscription> _streamSubscriptions = [];
+  final List<StreamController> _streamControllers = [];
+  final StreamController<Pose> _poseController = StreamController.broadcast();
+  final StreamController<StepExercise> _stepExerciseController = StreamController.broadcast();
+  Image? _cameraImage;
+  Pose? _detectedPose;
+  Size _imageSize = Size.zero;
+  late Future<void> _startCamera;
+  late Stream<MeanFilteredData> _meanFilterStream;
+  late Stream<int> _repCounter;
+
+  @override
+  initState() {
+    super.initState();
+    _streamControllers.add(_poseController);
+    _streamControllers.add(_stepExerciseController);
+    _startCamera = _startCameraStream();
+    _meanFilterStream = _getMeanFilterStream(_poseController.stream);
+    _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);
+      }
+    }));
+    _stepExerciseController.add(StepExercise.notInPlace);
+    _repCounter = _stepExerciseController.stream
+        .where((event) => event == StepExercise.start)
+        .scan((int accumulated, value, index) => accumulated + 1, 0);
+  }
+
+  Stream<MeanFilteredData> _getMeanFilterStream(Stream<Pose> stream) {
+    return stream
+        .where((pose) => pose.landmarks.isNotEmpty)
+        .map((pose) => pose.landmarks.where((landmark) => Exercise.authorizedType.contains(landmark.type)).toList())
+        // Get last [buffer] poses
+        .bufferCount(meanFilterBuffer, 1)
+        // Swap matrix [buffer] * [authorizedType.length]
+        .map(_swapMatrixDimensions)
+        // For every landmarks, get meanFilter of size [buffer]
+        .map((filteredLandmarks) => filteredLandmarks.map(_meanFilter).toList());
+  }
+
+  List<double> _meanFilter(List<PoseLandmark> landmarks) {
+    return landmarks
+        .map((landmark) => landmark.position)
+        .map((position) => [
+              position.x / meanFilterBuffer,
+              position.y / meanFilterBuffer,
+              position.z / meanFilterBuffer,
+            ])
+        .reduce((value, element) => [
+              value[0] + element[0],
+              value[1] + element[1],
+              value[2] + element[2],
+            ]);
+  }
+
+  List<List<T>> _swapMatrixDimensions<T>(List<List<T>> matrix) {
+    final height = matrix.length;
+    final width = matrix[0].length;
+    List<List<T>> newMatrix = [];
+    for (int col = 0; col < width; col++) {
+      List<T> newRow = [];
+      for (int row = 0; row < height; row++) {
+        newRow.add(matrix[row][col]);
+      }
+      newMatrix.add(newRow);
+    }
+    return newMatrix;
+  }
+
+  Future<void> _startCameraStream() async {
+    await BodyDetection.startCameraStream(onFrameAvailable: _handleCameraImage, onPoseAvailable: _handlePose);
+    await BodyDetection.enablePoseDetection();
+  }
+
+  Future<void> _stopCameraStream() async {
+    await BodyDetection.disablePoseDetection();
+    await BodyDetection.stopCameraStream();
+  }
+
+  void _handleCameraImage(ImageResult result) {
+    if (!mounted) return;
+
+    // To avoid a memory leak issue.
+    // https://github.com/flutter/flutter/issues/60160
+    PaintingBinding.instance?.imageCache?.clear();
+    PaintingBinding.instance?.imageCache?.clearLiveImages();
+
+    final image = Image.memory(
+      result.bytes,
+      gaplessPlayback: true,
+      fit: BoxFit.contain,
+    );
+
+    setState(() {
+      _cameraImage = image;
+      _imageSize = result.size;
+    });
+  }
+
+  void _handlePose(Pose? pose) {
+    if (!mounted) return;
+    if (pose != null) _poseController.add(pose);
+    setState(() {
+      _detectedPose = pose;
+    });
+  }
+
+  @override
+  void dispose() {
+    _stopCameraStream();
+    for (final ss in _streamSubscriptions) {
+      ss.cancel();
+    }
+    for (final sc in _streamControllers) {
+      sc.close();
+    }
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder<void>(
+      future: _startCamera,
+      builder: (context, snapshot) {
+        if (snapshot.connectionState == ConnectionState.waiting) {
+          return const Center(child: CircularProgressIndicator());
+        }
+        return Column(
+          children: [
+            Center(
+              child: CustomPaint(
+                child: _cameraImage,
+                foregroundPainter: PosePainter(
+                  pose: _detectedPose,
+                  imageSize: _imageSize,
+                ),
+              ),
+            ),
+            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(
+                      "${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),
+                    ),
+                  ],
+                );
+              },
+            )
+          ],
+        );
+      },
+    );
+  }
+
+}

+ 60 - 0
app/lib/exercises/exercises_validation/widgets/pose_painter.dart

@@ -0,0 +1,60 @@
+import 'package:body_detection/models/pose.dart';
+import 'package:body_detection/models/pose_landmark.dart';
+import 'package:body_detection/models/pose_landmark_type.dart';
+import 'package:flutter/widgets.dart';
+
+class PosePainter extends CustomPainter {
+  final Pose? pose;
+  final Size imageSize;
+  PosePainter({required this.pose, required this.imageSize, Key? key});
+
+  final pointPaint = Paint()..color = const Color.fromRGBO(255, 255, 255, 0.8);
+  final leftPointPaint = Paint()..color = const Color.fromRGBO(223, 157, 80, 1);
+  final rightPointPaint = Paint()..color = const Color.fromRGBO(100, 208, 218, 1);
+  final linePaint = Paint()
+    ..color = const Color.fromARGB(228, 0, 0, 0)
+    ..strokeWidth = 3;
+
+  @override
+  void paint(Canvas canvas, Size size) {
+    if (pose == null) return;
+
+    final double hRatio = imageSize.width == 0 ? 1 : size.width / imageSize.width;
+    final double vRatio = imageSize.height == 0 ? 1 : size.height / imageSize.height;
+
+    offsetForPart(PoseLandmark part) => Offset(part.position.x * hRatio, part.position.y * vRatio);
+
+    // Landmark connections
+    final landmarksByType = {for (final it in pose!.landmarks) it.type: it};
+    for (final connection in connections) {
+      if (landmarksByType[connection[0]] != null &&
+          landmarksByType[connection[1]] != null &&
+          landmarksByType[connection[0]]!.inFrameLikelihood > 0.5 &&
+          landmarksByType[connection[1]]!.inFrameLikelihood > 0.5) {
+        final point1 = offsetForPart(landmarksByType[connection[0]]!);
+        final point2 = offsetForPart(landmarksByType[connection[1]]!);
+        canvas.drawLine(point1, point2, linePaint);
+      }
+    }
+  }
+
+  @override
+  bool shouldRepaint(PosePainter oldDelegate) {
+    return oldDelegate.pose != pose || oldDelegate.imageSize != imageSize;
+  }
+
+  static const List<List<PoseLandmarkType>> connections = [
+    [PoseLandmarkType.leftShoulder, PoseLandmarkType.rightShoulder],
+    [PoseLandmarkType.leftShoulder, PoseLandmarkType.leftHip],
+    [PoseLandmarkType.rightShoulder, PoseLandmarkType.rightHip],
+    [PoseLandmarkType.rightShoulder, PoseLandmarkType.rightElbow],
+    [PoseLandmarkType.rightWrist, PoseLandmarkType.rightElbow],
+    [PoseLandmarkType.leftHip, PoseLandmarkType.rightHip],
+    [PoseLandmarkType.leftHip, PoseLandmarkType.leftKnee],
+    [PoseLandmarkType.rightHip, PoseLandmarkType.rightKnee],
+    [PoseLandmarkType.rightKnee, PoseLandmarkType.rightAnkle],
+    [PoseLandmarkType.leftKnee, PoseLandmarkType.leftAnkle],
+    [PoseLandmarkType.leftElbow, PoseLandmarkType.leftShoulder],
+    [PoseLandmarkType.leftWrist, PoseLandmarkType.leftElbow],
+  ];
+}

+ 43 - 0
app/lib/friends/friends_page.dart

@@ -0,0 +1,43 @@
+import 'package:flutter/material.dart';
+import 'widgets/friends_list.dart';
+import 'widgets/requests.dart';
+
+class FriendsPage extends StatefulWidget {
+  const FriendsPage({Key? key}) : super(key: key);
+
+  @override
+  State<FriendsPage> createState() => _FriendsPageState();
+}
+
+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,
+        ),
+        body: Center(
+            child: Padding(
+          padding: const EdgeInsets.all(32.0),
+          child: _widgets[_selectedIndex],
+        )));
+  }
+}

+ 17 - 0
app/lib/friends/models/challenge_location_request.dart

@@ -0,0 +1,17 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+
+class ChallengeLocationRequest {
+  final String id;
+  final String name;
+  final String surname;
+  final String locationName;
+  final DocumentReference locationRef;
+
+  const ChallengeLocationRequest({
+    required this.id,
+    required this.name,
+    required this.surname,
+    required this.locationName,
+    required this.locationRef,
+  });
+}

+ 22 - 0
app/lib/friends/models/friend.dart

@@ -0,0 +1,22 @@
+import 'package:physigo/friends/models/friend_relation.dart';
+
+class Friend {
+  final String id;
+  final String name;
+  final String surname;
+  final RequestStatus status;
+  final String relationId;
+
+  const Friend({
+    required this.id,
+    required this.name,
+    required this.surname,
+    required this.status,
+    required this.relationId,
+  });
+
+  @override
+  String toString() {
+    return "$name $surname: ${status.name}";
+  }
+}

+ 40 - 0
app/lib/friends/models/friend_relation.dart

@@ -0,0 +1,40 @@
+class FriendRelation {
+  final String id;
+  final String friendId;
+  final RequestStatus requestStatus;
+
+  const FriendRelation({
+    required this.id,
+    required this.friendId,
+    required this.requestStatus,
+  });
+
+  factory FriendRelation.fromMap(String relationId, Map<String, dynamic> map, String userId) {
+    return FriendRelation(
+      id: relationId,
+      friendId: _getId(map, userId),
+      requestStatus: getRequestStatus(map["request_status"]),
+    );
+  }
+
+  static String _getId(Map<String, dynamic> map, String userId) {
+    if (map['user_id'] == userId) {
+      return map['friend_id'];
+    } else {
+      return map['user_id'];
+    }
+  }
+
+  static RequestStatus getRequestStatus(String status) {
+    if (status == "pending") return RequestStatus.pending;
+    if (status == "accepted") return RequestStatus.accepted;
+    if (status == "rejected") return RequestStatus.rejected;
+    throw Exception("Request status invalid");
+  }
+}
+
+enum RequestStatus {
+  pending,
+  accepted,
+  rejected,
+}

+ 7 - 0
app/lib/friends/models/friend_request.dart

@@ -0,0 +1,7 @@
+class FriendRequest {
+  final String requestId;
+  final String friendName;
+  final String friendSurname;
+
+  const FriendRequest({required this.requestId, required this.friendName, required this.friendSurname});
+}

+ 88 - 0
app/lib/friends/services/challenge_location_service.dart

@@ -0,0 +1,88 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:physigo/friends/models/challenge_location_request.dart';
+import 'package:physigo/services/push_notifications_service.dart';
+
+const userId = "tlmysIvwTBaoZKWqBofx";
+const name = "John";
+const surname = "Smith";
+
+class ChallengeLocationService {
+  static final _db = FirebaseFirestore.instance;
+  static final _users = _db.collection("Users");
+  static final _shareLocationRequests = _db.collection("ShareLocationRequests");
+  static final _friends = _db.collection("Friends");
+
+  static Future<List<ChallengeLocationRequest>> getShareChallengeLocationRequests() async {
+    final requests = await _shareLocationRequests.where("user_id", isEqualTo: userId).get();
+    if (requests.docs.isEmpty) {
+      return [];
+    }
+    final requesterIdLocationId = {
+      for (final request in requests.docs)
+        request.data()['requester_id']: {"requestId": request.id, "locationRef": request.data()['location']}
+    };
+    final requesters = await _users.where(FieldPath.documentId, whereIn: requesterIdLocationId.keys.toList()).get();
+    final shareChallengeLocationRequests = await Future.wait(requesters.docs.map((requester) async {
+      final data = requester.data();
+      final place = (await requesterIdLocationId[requester.id]!["locationRef"].get()).data();
+      return ChallengeLocationRequest(
+          id: requesterIdLocationId[requester.id]!["requestId"]!,
+          name: data['name'],
+          surname: data['surname'],
+          locationName: place['name'],
+          locationRef: requesterIdLocationId[requester.id]!["locationRef"]!);
+    }));
+    return shareChallengeLocationRequests;
+  }
+
+  static Future<void> shareChallengeLocation(String friendId) async {
+    await Future.wait([_checkExistingFriendRelation(friendId), _checkExistingChallengeLocationRequest(friendId)]);
+    final user = await _users.doc(userId).get();
+    final shareLocationRequest = {"user_id": friendId, "requester_id": userId, "location": user['weekly_place']};
+    await _shareLocationRequests.add(shareLocationRequest);
+    PushNotificationsService.sendChallengeLocationRequestNotification(userId, name, surname);
+  }
+
+  static Future<void> acceptChallengeLocationRequest(ChallengeLocationRequest request) async {
+    final newLocation = {"weekly_place": request.locationRef};
+    await _users.doc(userId).update(newLocation);
+    await _shareLocationRequests.doc(request.id).delete();
+  }
+
+  static Future<void> refuseChallengeLocationRequest(ChallengeLocationRequest request) async {
+    await _shareLocationRequests.doc(request.id).delete();
+  }
+
+  static Future<void> _checkExistingFriendRelation(String friendId) async {
+    if (friendId == userId) {
+      return Future.error("You cannot share a location with yourself");
+    }
+
+    var friendRelationByUserId = _friends
+        .where("user_id", isEqualTo: userId)
+        .where("friend_id", isEqualTo: friendId)
+        .where("request_status", isEqualTo: "accepted")
+        .get();
+
+    var friendRelationByFriendId = _friends
+        .where("friend_id", isEqualTo: userId)
+        .where("user_id", isEqualTo: friendId)
+        .where("request_status", isEqualTo: "accepted")
+        .get();
+
+    final relations = await Future.wait([friendRelationByUserId, friendRelationByFriendId]);
+    if (relations.expand((relation) => relation.docs).isEmpty) {
+      return Future.error("You must be friend to share a location");
+    }
+  }
+
+  static Future<void> _checkExistingChallengeLocationRequest(String friendId) async {
+    final requests = await _shareLocationRequests
+        .where("requester_id", isEqualTo: userId)
+        .where("user_id", isEqualTo: friendId)
+        .get();
+    if (requests.docs.isNotEmpty) {
+      return Future.error("You already shared your location with this person");
+    }
+  }
+}

+ 119 - 0
app/lib/friends/services/friends_service.dart

@@ -0,0 +1,119 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:physigo/friends/models/friend_request.dart';
+import 'package:physigo/services/push_notifications_service.dart';
+import '../models/friend.dart';
+import '../models/friend_relation.dart';
+
+const userId = "tlmysIvwTBaoZKWqBofx";
+const name = "John";
+const surname = "Smith";
+
+class FriendsService {
+  static final _db = FirebaseFirestore.instance;
+  static final _friends = _db.collection("Friends");
+  static final _users = _db.collection("Users");
+
+  static Future<List<Friend>> getFriends() async {
+    return _getFriendsByStatus(RequestStatus.accepted);
+  }
+
+  static Future<List<FriendRequest>> getPendingRequests() async {
+    final friendRequests =
+        await _friends.where("friend_id", isEqualTo: userId).where("request_status", isEqualTo: "pending").get();
+    if (friendRequests.docs.isEmpty) {
+      return [];
+    }
+    final friendsIdRequestsId = {for (final r in friendRequests.docs) r.data()['user_id']: r.id};
+    final friendsInfo = await _users.where(FieldPath.documentId, whereIn: friendsIdRequestsId.keys.toList()).get();
+    return friendsInfo.docs
+        .map((f) => FriendRequest(
+              requestId: friendsIdRequestsId[f.id]!,
+              friendName: f.data()['name'],
+              friendSurname: f.data()['surname'],
+            ))
+        .toList();
+  }
+
+  static Future<void> addFriend(String friendSharedId) async {
+    try {
+      final query = (await _users.where("shared_id", isEqualTo: friendSharedId).get());
+      if (query.docs.isEmpty) {
+        return Future.error("This account doesn't exist");
+      }
+      final friend = query.docs.first;
+      await _checkExistingFriendRelation(friend.id);
+      final friendRequest = {"user_id": userId, "friend_id": friend.id, "request_status": "pending"};
+      await _friends.add(friendRequest);
+      PushNotificationsService.sendFriendRequestNotification(userId, name, surname);
+    } catch (error) {
+      return Future.error(error);
+    }
+  }
+
+  static Future<void> acceptFriendRequest(FriendRequest friendRequest) async {
+    final newStatus = {"request_status": "accepted"};
+    await _friends.doc(friendRequest.requestId).update(newStatus);
+    PushNotificationsService.sendFriendRequestAcceptedNotification(userId, name, surname);
+  }
+
+  static Future<void> refuseFriendRequest(FriendRequest friendRequest) async {
+    final newStatus = {"request_status": "rejected"};
+    await _friends.doc(friendRequest.requestId).update(newStatus);
+  }
+
+  static Future<List<Friend>> _getFriendsByStatus(RequestStatus status) async {
+    final friendsByUserId = _friends.where("user_id", isEqualTo: userId).get();
+    final friendsByFriendId = _friends.where("friend_id", isEqualTo: userId).get();
+    final friendRelations = (await Future.wait([friendsByUserId, friendsByFriendId]))
+        .expand((query) => query.docs)
+        .map((friendRelation) => FriendRelation.fromMap(friendRelation.id, friendRelation.data(), userId))
+        .where((friendRelation) => friendRelation.requestStatus == status)
+        .toList();
+
+    final friends = await _getFriendsFromFriendRelations(friendRelations);
+    return friends;
+  }
+
+  static Future<List<Friend>> _getFriendsFromFriendRelations(List<FriendRelation> friendRelations) async {
+    final users = await _db
+        .collection('Users')
+        .where(FieldPath.documentId, whereIn: friendRelations.map((e) => e.friendId).toList())
+        .get();
+    final friends = users.docs.map((friend) {
+      final data = friend.data();
+      final friendRelation = friendRelations.firstWhere((element) => element.friendId == friend.id);
+      return Friend(
+        id: friend.id,
+        name: data['name'],
+        surname: data['surname'],
+        status: friendRelation.requestStatus,
+        relationId: friendRelation.id,
+      );
+    }).toList();
+    return friends;
+  }
+
+  static Future<void> _checkExistingFriendRelation(String friendId) async {
+    if (friendId == userId) {
+      return Future.error("You already are your own friend");
+    }
+
+    var friendRelation = await _db
+        .collection("Friends")
+        .where("user_id", isEqualTo: userId)
+        .where("friend_id", isEqualTo: friendId)
+        .get();
+    if (friendRelation.docs.isNotEmpty) {
+      return Future.error("You already sent a request to this person");
+    }
+
+    friendRelation = await _db
+        .collection("Friends")
+        .where("friend_id", isEqualTo: userId)
+        .where("user_id", isEqualTo: friendId)
+        .get();
+    if (friendRelation.docs.isNotEmpty) {
+      return Future.error("They already sent you a request");
+    }
+  }
+}

+ 67 - 0
app/lib/friends/widgets/add_friend.dart

@@ -0,0 +1,67 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/friends/services/friends_service.dart';
+
+class AddFriend extends StatefulWidget {
+  const AddFriend({Key? key}) : super(key: key);
+
+  @override
+  State<AddFriend> createState() => _AddFriendState();
+}
+
+class _AddFriendState extends State<AddFriend> {
+  late final TextEditingController _controller;
+
+  @override
+  void initState() {
+    _controller = TextEditingController();
+    super.initState();
+  }
+
+  void _addFriend() async {
+    try {
+      await FriendsService.addFriend(_controller.text);
+      _controller.clear();
+      _showSnackBar("Friend request sent!", Colors.green);
+    } catch (error) {
+      _showSnackBar(error.toString(), Colors.red);
+    }
+  }
+
+  void _showSnackBar(String message, Color color) {
+    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+      content: Text(message),
+      backgroundColor: color,
+      duration: const Duration(milliseconds: 1500),
+    ));
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        TextField(
+          onSubmitted: (_) => _addFriend(),
+          controller: _controller,
+          decoration: const InputDecoration(
+            border: OutlineInputBorder(),
+            hintText: "Enter a friend's ID to add them",
+          ),
+        ),
+        const SizedBox(height: 32),
+        ElevatedButton(
+          onPressed: _addFriend,
+          style: ElevatedButton.styleFrom(
+            padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 32),
+          ),
+          child: const Text("ADD",
+              style: TextStyle(
+                fontSize: 20,
+                fontWeight: FontWeight.bold,
+                letterSpacing: 1.3,
+              )),
+        )
+      ],
+    );
+  }
+}

+ 87 - 0
app/lib/friends/widgets/challenge_location_requests.dart

@@ -0,0 +1,87 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/friends/models/challenge_location_request.dart';
+import 'package:physigo/friends/services/challenge_location_service.dart';
+
+class ChallengeLocationRequests extends StatefulWidget {
+  const ChallengeLocationRequests({Key? key}) : super(key: key);
+
+  @override
+  State<ChallengeLocationRequests> createState() => _ChallengeLocationRequestsState();
+}
+
+class _ChallengeLocationRequestsState extends State<ChallengeLocationRequests> {
+  List<ChallengeLocationRequest> _requests = [];
+  late Future<List<ChallengeLocationRequest>> _requestsQuery;
+
+  @override
+  void initState() {
+    _requestsQuery = ChallengeLocationService.getShareChallengeLocationRequests();
+    _requestsQuery.then((requests) => setState(() => _requests = requests));
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      children: [
+        const Text(
+          "Challenge Location's Requests",
+          style: TextStyle(fontSize: 24),
+          textAlign: TextAlign.center,
+        ),
+        FutureBuilder<List<ChallengeLocationRequest>>(
+          future: _requestsQuery,
+          builder: (context, snapshot) {
+            if (!snapshot.hasData) {
+              return const CircularProgressIndicator();
+            }
+            if (_requests.isEmpty) {
+              return const Text("You don't have any share location's requests");
+            }
+            return ListView.builder(
+              shrinkWrap: true,
+              itemBuilder: ((context, index) => _requestTile(_requests[index])),
+              itemCount: _requests.length,
+            );
+          },
+        ),
+      ],
+    );
+  }
+
+  void _acceptRequest(ChallengeLocationRequest request) async {
+    await ChallengeLocationService.acceptChallengeLocationRequest(request);
+    setState(() {
+      _requests = _requests.where((r) => r.id != request.id).toList();
+    });
+  }
+
+  void _refuseRequest(ChallengeLocationRequest request) async {
+    await ChallengeLocationService.refuseChallengeLocationRequest(request);
+    setState(() {
+      _requests = _requests.where((r) => r.id != request.id).toList();
+    });
+  }
+
+  Widget _requestTile(ChallengeLocationRequest request) {
+    return Card(
+      child: ListTile(
+        title: Text(request.locationName, maxLines: 2, overflow: TextOverflow.ellipsis,),
+        subtitle: Text("From ${request.name} ${request.surname}"),
+        trailing: Row(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            TextButton(
+              child: const Text("Refuse"),
+              onPressed: () => _refuseRequest(request),
+            ),
+            TextButton(
+              child: const Text("Accept"),
+              onPressed: () => _acceptRequest(request),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 69 - 0
app/lib/friends/widgets/friends_list.dart

@@ -0,0 +1,69 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/friends/services/challenge_location_service.dart';
+import 'package:physigo/friends/widgets/add_friend.dart';
+
+import '../models/friend.dart';
+import '../services/friends_service.dart';
+
+class FriendsList extends StatefulWidget {
+  const FriendsList({Key? key}) : super(key: key);
+
+  @override
+  State<FriendsList> createState() => _FriendsListState();
+}
+
+class _FriendsListState extends State<FriendsList> {
+
+  void _shareLocation(String friendId) async {
+    try {
+      await ChallengeLocationService.shareChallengeLocation(friendId);
+      _showSnackBar("Challenge's location shared!", Colors.green);
+    } catch (error) {
+      _showSnackBar(error.toString(), Colors.red);
+    }
+  }
+
+  void _showSnackBar(String message, Color color) {
+    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+      content: Text(message),
+      backgroundColor: color,
+      duration: const Duration(milliseconds: 1500),
+    ));
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        const AddFriend(),
+        FutureBuilder<List<Friend>>(
+          future: FriendsService.getFriends(),
+          builder: (context, snapshot) {
+            if (!snapshot.hasData) {
+              return const CircularProgressIndicator();
+            }
+            final friends = snapshot.data!;
+            return ListView.builder(
+              shrinkWrap: true,
+              itemBuilder: ((context, index) => _friendTile(friends[index])),
+              itemCount: friends.length,
+            );
+          },
+        ),
+      ],
+    );
+  }
+
+  Widget _friendTile(Friend friend) {
+    return Card(
+      child: ListTile(
+        title: Text("${friend.name} ${friend.surname}"),
+        trailing: TextButton(
+          child: const Text("Share challenge location"),
+          onPressed: () => _shareLocation(friend.id),
+        ),
+      ),
+    );
+  }
+}

+ 87 - 0
app/lib/friends/widgets/friends_requests.dart

@@ -0,0 +1,87 @@
+import 'package:flutter/material.dart';
+
+import '../models/friend_request.dart';
+import '../services/friends_service.dart';
+
+class FriendsRequests extends StatefulWidget {
+  const FriendsRequests({Key? key}) : super(key: key);
+
+  @override
+  State<FriendsRequests> createState() => _FriendsRequestsState();
+}
+
+class _FriendsRequestsState extends State<FriendsRequests> {
+    List<FriendRequest> _requests = [];
+  late Future<List<FriendRequest>> _requestsQuery;
+
+  @override
+  void initState() {
+    _requestsQuery = FriendsService.getPendingRequests();
+    _requestsQuery.then((requests) => setState(() => _requests = requests));
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      children: [
+        const Text(
+          "Friend's Requests",
+          style: TextStyle(fontSize: 24),
+          textAlign: TextAlign.center,
+        ),
+        FutureBuilder<List<FriendRequest>>(
+          future: _requestsQuery,
+          builder: (context, snapshot) {
+            if (!snapshot.hasData) {
+              return const CircularProgressIndicator();
+            }
+            if (_requests.isEmpty) {
+              return const Text("You don't have any friend's requests");
+            }
+            return ListView.builder(
+              shrinkWrap: true,
+              itemBuilder: ((context, index) => _requestTile(_requests[index])),
+              itemCount: _requests.length,
+            );
+          },
+        ),
+      ],
+    );
+  }
+
+  void _acceptRequest(FriendRequest request) async {
+    await FriendsService.acceptFriendRequest(request);
+    setState(() {
+      _requests = _requests.where((r) => r.requestId != request.requestId).toList();
+    });
+  }
+
+  void _refuseRequest(FriendRequest request) async {
+    await FriendsService.refuseFriendRequest(request);
+    setState(() {
+      _requests = _requests.where((r) => r.requestId != request.requestId).toList();
+    });
+  }
+
+  Widget _requestTile(FriendRequest friendRequest) {
+    return Card(
+      child: ListTile(
+        title: Text("${friendRequest.friendName} ${friendRequest.friendSurname}"),
+        trailing: Row(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            TextButton(
+              child: const Text("Refuse"),
+              onPressed: () => _refuseRequest(friendRequest),
+            ),
+            TextButton(
+              child: const Text("Accept"),
+              onPressed: () => _acceptRequest(friendRequest),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 19 - 0
app/lib/friends/widgets/requests.dart

@@ -0,0 +1,19 @@
+import 'package:flutter/material.dart';
+import 'package:physigo/friends/widgets/friends_requests.dart';
+import 'package:physigo/friends/widgets/challenge_location_requests.dart';
+
+class Requests extends StatelessWidget {
+  const Requests({ Key? key }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: const [
+        ChallengeLocationRequests(),
+        SizedBox(height: 32),
+        FriendsRequests(),
+      ],
+    );
+  }
+}

+ 80 - 0
app/lib/logIn.dart

@@ -0,0 +1,80 @@
+import 'package:flutter/material.dart';
+import 'Services/AuthService.dart';
+
+class LogIn extends StatefulWidget {
+  const LogIn({Key? key, required this.title}) : super(key: key);
+  final String title;
+
+  @override
+  //_RegisterScreen createState() => _RegisterScreen();
+  State<LogIn> createState() => _LogIn();
+}
+
+class _LogIn extends State<LogIn> {
+  TextEditingController _email = TextEditingController();
+  TextEditingController _passwd = TextEditingController();
+  final AuthenticationServices _auth = AuthenticationServices();
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Login Screen'),
+      ),
+      body: Center(
+          child: SizedBox(
+        width: 300,
+        child: Column(children: <Widget>[
+          const SizedBox(height: 30),
+          Text(
+            'Welcome back!',
+            style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
+          ),
+          const SizedBox(height: 30),
+          Image.asset(
+            'assets/hello.png',
+            width: 150,
+          ),
+          const SizedBox(height: 30),
+          TextFormField(
+            controller: _email,
+            decoration: InputDecoration(
+              hintText: 'Mail',
+            ),
+          ),
+          const SizedBox(height: 10),
+          TextFormField(
+            controller: _passwd,
+            obscureText: true,
+            enableSuggestions: false,
+            autocorrect: false,
+            decoration: InputDecoration(
+              hintText: 'Password',
+            ),
+          ),
+          const SizedBox(height: 10),
+          ElevatedButton(
+            onPressed: () {
+              signInUser();
+            },
+            child: const Text('Log In'),
+          ),
+        ]),
+      )),
+    );
+  }
+
+  void signInUser() async {
+    dynamic authResult = await _auth.loginUser(_email.text, _passwd.text);
+    if (authResult == null) {
+      print("log in error");
+    } else {
+      _email.clear();
+      _passwd.clear();
+      print("Succesful login ");
+      Navigator.pushNamed(context, '/mainPage',
+        arguments: {'uid': authResult.uid},
+      );
+    }
+  }
+}

+ 21 - 84
app/lib/main.dart

@@ -1,12 +1,14 @@
 import 'package:firebase_core/firebase_core.dart';
 import 'package:flutter/material.dart';
-import 'package:latlong2/latlong.dart';
-import 'navigation/navigation_page.dart';
-import 'challenges/challenge_page.dart';
-import 'package:firebase_messaging/firebase_messaging.dart';
-import 'package:cloud_firestore/cloud_firestore.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/welcomeScreen.dart';
 
 void main() async {
   WidgetsFlutterBinding.ensureInitialized();
@@ -14,95 +16,30 @@ void main() async {
     options: DefaultFirebaseOptions.currentPlatform,
   );
   handleMessages();
+
   runApp(const PhysiGo());
 }
 
 class PhysiGo extends StatelessWidget {
   const PhysiGo({Key? key}) : super(key: key);
-
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
-      title: 'PhysiGo',
+      title: 'PhisyGo',
       theme: ThemeData(
-        primarySwatch: Colors.blue,
+        primarySwatch: Colors.blueGrey,
       ),
-      home: const HomePage(),
+      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'),
     );
   }
 }
-
-class HomePage extends StatelessWidget {
-  const HomePage({Key? key}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return Scaffold(
-      body: Column(
-        mainAxisAlignment: MainAxisAlignment.center,
-        crossAxisAlignment: CrossAxisAlignment.center,
-        children: [Center(
-          child: TextButton(
-              onPressed: () {
-                Navigator.push(
-                  context,
-                  MaterialPageRoute(
-                    // Example on how to use NavigationPage
-                    builder: (context) => NavigationPage(
-                      destination: LatLng(51.78036111980833, 19.451262207821234),
-                    ),
-                  ),
-                );
-              },
-              child: const Text('Navigation')),
-        ),
-        Center(
-          child: TextButton(
-              onPressed: () {
-                Navigator.push(
-                  context,
-                  MaterialPageRoute(
-                    // Example on how to use NavigationPage
-                    builder: (context) => const ChallengePage(),
-                  ),
-                );
-              },
-              child: const Text('Challenges')),
-        ),
-      ],
-    ));
-  }
-}
-
-void handleMessages() async {
-  FirebaseMessaging messaging = FirebaseMessaging.instance;
-  final _firestore = FirebaseFirestore.instance;
-
-  NotificationSettings settings = await messaging.requestPermission(
-    alert: true,
-    announcement: false,
-    badge: true,
-    carPlay: false,
-    criticalAlert: false,
-    provisional: false,
-    sound: true,
-  );
-
-
-  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
-    print('Got a message whilst in the foreground!');
-    print('Message data: ${message.data}');
-
-    if (message.notification != null) {
-      print('Message also contained a notification: ${message.notification}');
-    }
-  });
-
-  messaging.onTokenRefresh.listen((fcmToken) async {
-    final fcmToken = await messaging.getToken();
-    final user = _firestore.collection('Users').doc('tlmysIvwTBaoZKWqBofx');
-    user.update({
-      'token': fcmToken,
-    });
-  });
-}

+ 37 - 0
app/lib/mainPage.dart

@@ -0,0 +1,37 @@
+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()
+    );
+  }
+}

+ 13 - 0
app/lib/navigation/utils/geometry_utils.dart

@@ -102,4 +102,17 @@ class DistanceUtils {
   static num _dotProduct(Point3D v, Point3D w) {
     return (v.x * w.x) + (v.y * w.y) + (v.z * w.z);
   }
+
+  static num angleBetweenThreePoints(Point3D start, Point3D center, Point3D end) {
+    final a = center - start;
+    final b = center - end;
+    final aLength = _lengthVector(a);
+    final bLength = _lengthVector(b);
+    final dotProduct = _dotProduct(a, b);
+    return acos(dotProduct / (aLength * bLength)) * 360 / pi;
+  }
+
+  static num _lengthVector(Point3D vector) {
+    return sqrt(_sqr(vector.x) + _sqr(vector.y) + _sqr(vector.z));
+  }
 }

+ 101 - 0
app/lib/profilePage.dart

@@ -0,0 +1,101 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:flutter/material.dart';
+
+class ProfilePage extends StatefulWidget {
+  const ProfilePage({Key? key, required this.title}) : super(key: key);
+  final String title;
+
+  @override
+  //_RegisterScreen createState() => _RegisterScreen();
+  State<ProfilePage> createState() => _ProfilePage();
+}
+
+class _ProfilePage extends State<ProfilePage> {
+  String name = '', surname = '', birth_date = '', mail = '', shared_id = '',
+      phone_number = '', anonymous = '', address = '';
+
+  @override
+  Widget build(BuildContext context) {
+    final arguments = (ModalRoute.of(context)?.settings.arguments ??
+        <String, dynamic>{}) as Map;
+    print('******' + arguments['id']);
+
+    //getting info of the user one by one
+    FirebaseFirestore.instance.collection('profileInfo').doc(arguments['id'])
+        .get().then((DocumentSnapshot) {
+      name = DocumentSnapshot.get('name').toString();
+    });
+    FirebaseFirestore.instance.collection('profileInfo').doc(arguments['id'])
+        .get().then((DocumentSnapshot) {
+      surname = DocumentSnapshot.get('surname').toString();
+    });
+    FirebaseFirestore.instance.collection('profileInfo').doc(arguments['id'])
+        .get().then((DocumentSnapshot) {
+      birth_date = DocumentSnapshot.get('birth_date').toString();
+    });
+    FirebaseFirestore.instance.collection('profileInfo').doc(arguments['id'])
+        .get().then((DocumentSnapshot) {
+      mail = DocumentSnapshot.get('mail').toString();
+    });
+    FirebaseFirestore.instance.collection('profileInfo').doc(arguments['id'])
+        .get().then((DocumentSnapshot) {
+      shared_id = DocumentSnapshot.get('shared_id').toString();
+    });
+    FirebaseFirestore.instance.collection('profileInfo').doc(arguments['id'])
+        .get().then((DocumentSnapshot) {
+      phone_number = DocumentSnapshot.get('phone_number').toString();
+    });
+    FirebaseFirestore.instance.collection('profileInfo').doc(arguments['id'])
+        .get().then((DocumentSnapshot) {
+      anonymous = DocumentSnapshot.get('anonymous').toString();
+    });
+    FirebaseFirestore.instance.collection('profileInfo').doc(arguments['id'])
+        .get().then((DocumentSnapshot) {
+      address = DocumentSnapshot.get('address').toString();
+    });
+
+    return Scaffold(
+      appBar: AppBar(title: const Text('Profile Page')),
+      body: Center(
+        child: SizedBox(
+          width: 300,
+          child: Column(
+            children: [
+              const SizedBox(height: 30),
+              Text(
+                'Personal Information',
+                style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
+              ),
+              const SizedBox(height: 30),
+              Image.asset(
+                'assets/user.png',
+                width: 150,
+              ),
+              const SizedBox(height: 30),
+              Text('Name: ' + name + ' ' + surname),
+              const SizedBox(height: 10),
+              Text('Birth Date: ' + birth_date),
+              const SizedBox(height: 10),
+              Text('Username: ' + shared_id),
+              const SizedBox(height: 10),
+              Text('Mail: ' + mail),
+              const SizedBox(height: 10),
+              Text('Phone: ' + phone_number),
+              const SizedBox(height: 10),
+              Text('Address: ' + address),
+              const SizedBox(height: 10),
+              Text('Anonymous: ' + anonymous),
+              const SizedBox(height: 30),
+              ElevatedButton(
+                onPressed: () {
+                  setState(() {});
+                },
+                child: const Text('Refresh'),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 70 - 0
app/lib/push_notifications_initializer.dart

@@ -0,0 +1,70 @@
+import 'package:awesome_notifications/awesome_notifications.dart';
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+void handleMessages() async {
+  FirebaseMessaging messaging = FirebaseMessaging.instance;
+  final _firestore = FirebaseFirestore.instance;
+
+  NotificationSettings settings = await messaging.requestPermission(
+    alert: true,
+    announcement: false,
+    badge: true,
+    carPlay: false,
+    criticalAlert: false,
+    provisional: false,
+    sound: true,
+  );
+
+  localNotificationsSetup();
+
+  if (settings.authorizationStatus == AuthorizationStatus.denied) {
+    return;
+  }
+
+  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
+    AwesomeNotifications().createNotification(
+      content: NotificationContent(
+        id: 10,
+        channelKey: 'basic_channel',
+        title: message.notification?.title,
+        body: message.notification?.body,
+        color: Colors.blue,
+      )
+    );
+  });
+
+  messaging.onTokenRefresh.listen((fcmToken) async {
+    final fcmToken = await messaging.getToken();
+    final user = _firestore.collection('Users').doc('tlmysIvwTBaoZKWqBofx');
+    user.update({
+      'token': fcmToken,
+    });
+  });
+}
+
+void localNotificationsSetup() {
+  AwesomeNotifications().initialize(
+      // set the icon to null if you want to use the default app icon
+      null,
+      [
+        NotificationChannel(
+          channelGroupKey: 'basic_channel_group',
+          channelKey: 'basic_channel',
+          channelName: 'Basic notifications',
+          channelDescription: 'Notification channel for basic tests',
+        )
+      ],
+      debug: kDebugMode);
+
+  AwesomeNotifications().isNotificationAllowed().then((isAllowed) {
+    if (!isAllowed) {
+      // This is just a basic example. For real apps, you must show some
+      // friendly dialog box before call the request method.
+      // This is very important to not harm the user experience
+      AwesomeNotifications().requestPermissionToSendNotifications();
+    }
+  });
+}

+ 35 - 0
app/lib/services/push_notifications_service.dart

@@ -0,0 +1,35 @@
+import 'package:flutter/foundation.dart';
+import 'package:http/http.dart' as http;
+
+class PushNotificationsService {
+  static const _url = 'https://physigo.vercel.app/api/push-notification';
+
+  static void sendFriendRequestNotification(String userId, String name, String surname) {
+    const title = "Friend Request";
+    final body = "$name $surname wants to be your friend";
+    _sendPushNotification(userId, title, body);
+  }
+
+  static void sendChallengeLocationRequestNotification(String userId, String name, String surname) {
+    const title = "Shared Challenge location request";
+    final body = "$name $surname shared their challenge's location with you";
+    _sendPushNotification(userId, title, body);
+  }
+
+  static void sendFriendRequestAcceptedNotification(String userId, String name, String surname) {
+    const title = "New Friend";
+    final body = "$name $surname is now your friend!";
+    _sendPushNotification(userId, title, body);
+  }
+
+  static Future<void> _sendPushNotification(String userId, String title, String body) async {
+    try {
+      await http.post(Uri.parse('$_url?user_id=$userId&title=$title&body=$body'));
+    } catch (error) {
+      // We don't care if sending the notification fails
+      if (kDebugMode) {
+        debugPrint(error.toString());
+      }
+    }
+  }
+}

+ 10 - 0
app/lib/walking/models/score.dart

@@ -0,0 +1,10 @@
+class Score {
+  int points;
+  int distance;
+
+  Score({required this.points, required this.distance});
+
+  Map<String, dynamic> toMap(){
+      return{"points" : points, "distance": distance};
+  }
+}

+ 35 - 0
app/lib/walking/services/walking_services.dart

@@ -0,0 +1,35 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:firebase_auth/firebase_auth.dart';
+
+import '../models/score.dart';
+
+class WalkingServices {
+  static final db = FirebaseFirestore.instance;
+
+  static dynamic addScore(Score score) async {
+    // final user = {"score": score.points, "distance": score.distance};
+    final walkingScore = await db
+        .collection("WalkingScores")
+        .where("user", isEqualTo: FirebaseAuth.instance.currentUser?.uid)
+        .get();
+    if(walkingScore.docs.isEmpty){
+      db
+      .collection("WalkingScores")
+      .add({"score":score.points,"user":FirebaseAuth.instance.currentUser?.uid });
+    }
+    else{
+      db
+          .collection("WalkingScores")
+          .doc(walkingScore.docs.first.id)
+          .update({"score": walkingScore.docs.first.get("score") + score.points});
+    }
+    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.points});
+
+  }
+}

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

@@ -0,0 +1,136 @@
+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]--

+ 79 - 0
app/lib/walking/walking_permission.dart

@@ -0,0 +1,79 @@
+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';
+
+class WalkingPermission extends StatelessWidget {
+  WalkingPermission({Key? key}) : super(key: key);
+  final FlutterActivityRecognition activityRecognition =
+      FlutterActivityRecognition.instance;
+
+  @override
+  Widget build(BuildContext context) {
+    return(
+     FutureBuilder(
+        future: Future.wait([isPermissionGrants(), _getPermissions()]),
+        builder: (context, snapshot) {
+          if (snapshot.connectionState == ConnectionState.waiting) {
+            return CircularProgressIndicator();
+          } else if (snapshot.hasError) {
+            return Text("Error");
+          } else {
+            return WalkingCounter();
+          }
+        },
+      )
+    );
+  }
+
+  Future<void> isPermissionGrants() async {
+    // Check if the user has granted permission. If not, request permission.
+    PermissionRequestResult reqResult;
+    reqResult = await activityRecognition.checkPermission();
+    if (reqResult == PermissionRequestResult.PERMANENTLY_DENIED) {
+      print('Permission is permanently denied.');
+      return Future.error('Activity permissions are denied');
+    } else if (reqResult == PermissionRequestResult.DENIED) {
+      reqResult = await activityRecognition.requestPermission();
+      if (reqResult != PermissionRequestResult.GRANTED) {
+        print('Permission is denied.');
+        return Future.error('Activity permissions are denied');
+      }
+    }
+  }
+
+  Future<void> _getPermissions() async {
+    bool serviceEnabled;
+    LocationPermission permission;
+
+    // Test if location services are enabled.
+    serviceEnabled = await Geolocator.isLocationServiceEnabled();
+    if (!serviceEnabled) {
+      // Location services are not enabled don't continue
+      // accessing the position and request users of the
+      // App to enable the location services.
+      return Future.error('Location services are disabled.');
+    }
+
+    permission = await Geolocator.checkPermission();
+    if (permission == LocationPermission.denied) {
+      permission = await Geolocator.requestPermission();
+      if (permission == LocationPermission.denied) {
+        // Permissions are denied, next time you could try
+        // requesting permissions again (this is also where
+        // Android's shouldShowRequestPermissionRationale
+        // returned true. According to Android guidelines
+        // your App should show an explanatory UI now.
+        return Future.error('Location permissions are denied');
+      }
+    }
+
+    if (permission == LocationPermission.deniedForever) {
+      // Permissions are denied forever, handle appropriately.
+      return Future.error(
+          'Location permissions are permanently denied, we cannot request permissions.');
+    }
+  }
+}

+ 48 - 0
app/lib/welcomeScreen.dart

@@ -0,0 +1,48 @@
+
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+
+class MyHomePage extends StatefulWidget {
+  const MyHomePage({Key? key, required this.title}) : super(key: key);
+  final String title;
+
+  @override
+  State<MyHomePage> createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State<MyHomePage> {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Welcome to PhysiGo'),
+      ),
+      body: Center(
+        child: Column(
+          children: <Widget>[
+            const SizedBox(height: 30),
+            Image.asset(
+              'assets/teamlogo.png',
+              width: 260,
+            ),
+            const SizedBox(height: 30),
+            ElevatedButton(
+              // Within the `FirstScreen` widget
+              onPressed: () {
+                // Navigate to the second screen using a named route.
+                Navigator.pushNamed(context, '/register');
+              },
+              child: Text('Register'),
+            ),
+            ElevatedButton(
+              onPressed: () {
+                Navigator.pushNamed(context, '/login');
+              },
+              child: const Text('Log In'),
+            )
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 128 - 0
app/lib/widgets/datatable.dart

@@ -0,0 +1,128 @@
+import 'dart:async';
+
+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);
+  final String name;
+  @override
+  State<MyDataTable> createState() => _MyDataTableState();
+}
+
+class _MyDataTableState extends State<MyDataTable> {
+  CollectionReference usersList =
+  FirebaseFirestore.instance.collection("Users");
+  @override
+  Widget build(BuildContext context) {
+    Future<List> userList = getUsersList();
+
+    return  Container(
+      height: 450,
+      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("  ");
+                },
+          ),
+        ),
+      ),
+      );
+  }
+  @override
+  void initState() {
+    super.initState();
+  }
+
+
+  Future<List> getUsersList() async {
+    var querySnapshot = await usersList.orderBy(widget.name, descending: true).get();
+      return querySnapshot.docs;
+  }
+  }
+
+  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(
+
+        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]}"),
+                  ),
+                ]
+            )
+        );
+      }
+    }
+    return rows;
+  }
+
+
+
+

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

@@ -0,0 +1,77 @@
+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,
+      ),
+    );
+  }
+}

+ 105 - 7
app/pubspec.lock

@@ -8,6 +8,20 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.8.2"
+  awesome_notifications:
+    dependency: "direct main"
+    description:
+      name: awesome_notifications
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.6.21"
+  body_detection:
+    dependency: "direct main"
+    description:
+      name: body_detection
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.0.3"
   boolean_selector:
     dependency: transitive
     description:
@@ -42,21 +56,21 @@ packages:
       name: cloud_firestore
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.1.14"
+    version: "3.1.15"
   cloud_firestore_platform_interface:
     dependency: transitive
     description:
       name: cloud_firestore_platform_interface
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "5.5.5"
+    version: "5.5.6"
   cloud_firestore_web:
     dependency: transitive
     description:
       name: cloud_firestore_web
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.6.14"
+    version: "2.6.15"
   collection:
     dependency: transitive
     description:
@@ -78,13 +92,34 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.2.0"
+  firebase_auth:
+    dependency: "direct main"
+    description:
+      name: firebase_auth
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.3.18"
+  firebase_auth_platform_interface:
+    dependency: transitive
+    description:
+      name: firebase_auth_platform_interface
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "6.2.6"
+  firebase_auth_web:
+    dependency: transitive
+    description:
+      name: firebase_auth_web
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.3.15"
   firebase_core:
     dependency: "direct main"
     description:
       name: firebase_core
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.15.0"
+    version: "1.17.0"
   firebase_core_platform_interface:
     dependency: transitive
     description:
@@ -125,6 +160,13 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_activity_recognition:
+    dependency: "direct main"
+    description:
+      name: flutter_activity_recognition
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.3.0"
   flutter_compass:
     dependency: transitive
     description:
@@ -171,12 +213,12 @@ packages:
     source: hosted
     version: "3.0.3"
   geolocator:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: geolocator
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "8.2.0"
+    version: "8.2.1"
   geolocator_android:
     dependency: transitive
     description:
@@ -289,6 +331,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.0"
+  nested:
+    dependency: transitive
+    description:
+      name: nested
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.0"
   path:
     dependency: transitive
     description:
@@ -296,6 +345,48 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.8.0"
+  permission_handler:
+    dependency: "direct main"
+    description:
+      name: permission_handler
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "9.2.0"
+  permission_handler_android:
+    dependency: transitive
+    description:
+      name: permission_handler_android
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "9.0.2+1"
+  permission_handler_apple:
+    dependency: transitive
+    description:
+      name: permission_handler_apple
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "9.0.4"
+  permission_handler_platform_interface:
+    dependency: transitive
+    description:
+      name: permission_handler_platform_interface
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.7.0"
+  permission_handler_windows:
+    dependency: transitive
+    description:
+      name: permission_handler_windows
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.1.0"
+  platform:
+    dependency: transitive
+    description:
+      name: platform
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.1.0"
   plugin_platform_interface:
     dependency: transitive
     description:
@@ -317,6 +408,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.0"
+  provider:
+    dependency: "direct main"
+    description:
+      name: provider
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "6.0.3"
   quiver:
     dependency: transitive
     description:
@@ -325,7 +423,7 @@ packages:
     source: hosted
     version: "3.0.1+1"
   rxdart:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: rxdart
       url: "https://pub.dartlang.org"

+ 17 - 2
app/pubspec.yaml

@@ -31,6 +31,7 @@ dependencies:
     sdk: flutter
 
 
+
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
   cupertino_icons: ^1.0.2
@@ -38,9 +39,17 @@ dependencies:
   http: ^0.13.4
   flutter_map: ^0.14.0
   flutter_map_location_marker: ^3.1.0
-  cloud_firestore: ^3.1.14
-  geoflutterfire: ^3.0.3
+  permission_handler: ^9.2.0
+  body_detection: ^0.0.3
+  rxdart: ^0.27.3
+  cloud_firestore: ^3.1.15
   firebase_messaging: ^11.4.0
+  awesome_notifications: ^0.6.21
+  firebase_auth: ^3.3.17
+  flutter_activity_recognition: ^1.3.0
+  geolocator: ^8.2.1
+  provider: ^6.0.1
+  geoflutterfire: ^3.0.3
 
 dev_dependencies:
   flutter_test:
@@ -68,6 +77,12 @@ flutter:
   # assets:
   #   - images/a_dot_burr.jpeg
   #   - images/a_dot_ham.jpeg
+  assets:
+    - assets/teamlogo.png
+    - assets/user.png
+    - assets/communicate.png
+    - assets/id-card.png
+    - assets/hello.png
 
   # An image asset can refer to one or more resolution-specific "variants", see
   # https://flutter.dev/assets-and-images/#resolution-aware.

+ 42 - 0
serverless_functions/api/push-notification.ts

@@ -0,0 +1,42 @@
+import { VercelRequest, VercelResponse } from '@vercel/node';
+import { initializeApp, cert } from 'firebase-admin/app';
+import { getFirestore } from 'firebase-admin/firestore';
+import { getMessaging } from 'firebase-admin/messaging';
+import { Message } from 'firebase-admin/lib/messaging/messaging-api';
+
+const serviceAccountStringified = Buffer.from(process.env.FIREBASE_SERVICE_ACCOUNT!, 'base64').toString('ascii');
+const serviceAccount = JSON.parse(serviceAccountStringified);
+initializeApp({
+  credential: cert(serviceAccount),
+});
+const firestore = getFirestore();
+const messaging = getMessaging();
+export default async function sendPushNotification(req: VercelRequest, res: VercelResponse) {
+  const { user_id, title, body } = req.query as { user_id: string; title: string; body: string };
+
+  const friend = await firestore
+    .collection('Users')
+    .doc(user_id)
+    .get();
+  if (!friend.exists) {
+    return res.status(400).json({ error: "User doesn't exist" });
+  }
+  const token = friend.data()!['token'];
+  const message: Message = {
+    notification: {
+      title,
+      body,
+    },
+    token,
+  };
+  messaging
+    .send(message)
+    .then((response) => {
+      console.log('Successfully sent message:', response);
+      res.status(200);
+    })
+    .catch((error) => {
+      console.log('Error sending message:', error);
+      res.status(500).send(error);
+    });
+}

+ 41 - 17
serverless_functions/package-lock.json

@@ -434,9 +434,9 @@
       "optional": true
     },
     "@google-cloud/storage": {
-      "version": "5.20.3",
-      "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.20.3.tgz",
-      "integrity": "sha512-nVrEB4nhJV8ZgTAaJ21pibcMa+kFS/s0JolVZnV/EDveZeGPv7SC09A8C232U1+bsuZI0v/wGwHY+E73vJa8BQ==",
+      "version": "5.20.5",
+      "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.20.5.tgz",
+      "integrity": "sha512-lOs/dCyveVF8TkVFnFSF7IGd0CJrTm91qiK6JLu+Z8qiT+7Ag0RyVhxZIWkhiACqwABo7kSHDm8FdH8p2wxSSw==",
       "optional": true,
       "requires": {
         "@google-cloud/paginator": "^3.0.7",
@@ -460,6 +460,7 @@
         "retry-request": "^4.2.2",
         "stream-events": "^1.0.4",
         "teeny-request": "^7.1.3",
+        "uuid": "^8.0.0",
         "xdg-basedir": "^4.0.0"
       }
     },
@@ -1154,6 +1155,29 @@
         "proto3-json-serializer": "^0.1.8",
         "protobufjs": "6.11.2",
         "retry-request": "^4.0.0"
+      },
+      "dependencies": {
+        "protobufjs": {
+          "version": "6.11.2",
+          "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz",
+          "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==",
+          "optional": true,
+          "requires": {
+            "@protobufjs/aspromise": "^1.1.2",
+            "@protobufjs/base64": "^1.1.2",
+            "@protobufjs/codegen": "^2.0.4",
+            "@protobufjs/eventemitter": "^1.1.0",
+            "@protobufjs/fetch": "^1.1.0",
+            "@protobufjs/float": "^1.0.2",
+            "@protobufjs/inquire": "^1.1.0",
+            "@protobufjs/path": "^1.1.2",
+            "@protobufjs/pool": "^1.1.0",
+            "@protobufjs/utf8": "^1.1.0",
+            "@types/long": "^4.0.1",
+            "@types/node": ">=13.7.0",
+            "long": "^4.0.0"
+          }
+        }
       }
     },
     "google-p12-pem": {
@@ -1339,14 +1363,14 @@
       }
     },
     "jszip": {
-      "version": "3.9.1",
-      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.9.1.tgz",
-      "integrity": "sha512-H9A60xPqJ1CuC4Ka6qxzXZeU8aNmgOeP5IFqwJbQQwtu2EUYxota3LdsiZWplF7Wgd9tkAd0mdu36nceSaPuYw==",
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz",
+      "integrity": "sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q==",
       "requires": {
         "lie": "~3.3.0",
         "pako": "~1.0.2",
         "readable-stream": "~2.3.6",
-        "set-immediate-shim": "~1.0.1"
+        "setimmediate": "^1.0.5"
       }
     },
     "jwa": {
@@ -1361,9 +1385,9 @@
       }
     },
     "jwks-rsa": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.1.2.tgz",
-      "integrity": "sha512-QW2SNEUVvMqULiBJVsIJXSuX73Qj9il/DMCsJZsmCI8AQFZ6BjXgQW6h9wOlPr1LswQBNDdyFT2LPogwMRr1ew==",
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.1.3.tgz",
+      "integrity": "sha512-+zSFnZYHckwxEIgjEu81AAWDyqx8z5/OVtmhuXPbchUA9Y4bipVDqvL/5Z5Qhe088e/uxPvp8QDpIDOmI2cEBg==",
       "requires": {
         "@types/express": "^4.17.13",
         "debug": "^4.3.4",
@@ -1591,9 +1615,9 @@
       }
     },
     "protobufjs": {
-      "version": "6.11.2",
-      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz",
-      "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==",
+      "version": "6.11.3",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
+      "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==",
       "requires": {
         "@protobufjs/aspromise": "^1.1.2",
         "@protobufjs/base64": "^1.1.2",
@@ -1700,10 +1724,10 @@
       "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
       "optional": true
     },
-    "set-immediate-shim": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
-      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
     },
     "signal-exit": {
       "version": "3.0.7",