├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ ├── simulation-inaccuracy.md │ └── simulation-request.md └── workflows │ └── docs.yml ├── .gitignore ├── .gitmodules ├── .metadata ├── LICENSE ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── simulate │ │ │ │ └── MainActivity.java │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── simulate │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── settings_aar.gradle ├── assets ├── images │ └── builtree.svg └── simulations │ ├── BubbleSortDark.png │ ├── BubbleSortLight.png │ ├── Epicycloid.png │ ├── Epicycloid1Dark.png │ ├── Epicycloid1Light.png │ ├── EpicycloidDark.png │ ├── FourierSeriesDark.png │ ├── FourierSeriesLight.png │ ├── InsertionSortDark.png │ ├── InsertionSortLight.png │ ├── LissajousCurveDark.png │ ├── LissajousCurveLight.png │ ├── MaurerRoseDark.png │ ├── MaurerRoseLight.png │ ├── RosePatternDark.png │ ├── RosePatternLight.png │ ├── ToothpickPatternDark.png │ └── ToothpickPatternLight.png ├── docs ├── contributing.md ├── index.md └── simulations │ ├── algorithms │ └── ToothpickPattern.md │ ├── index.md │ └── mathematics │ ├── FourierSeries.md │ ├── LissajousPattern.md │ └── MaurerRosePattern.md ├── fonts ├── UFL.txt ├── Ubuntu-Bold.ttf ├── Ubuntu-BoldItalic.ttf ├── Ubuntu-Italic.ttf ├── Ubuntu-Light.ttf ├── Ubuntu-LightItalic.ttf ├── Ubuntu-Medium.ttf ├── Ubuntu-MediumItalic.ttf └── Ubuntu-Regular.ttf ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── Runner-Bridging-Header.h │ └── main.m ├── lib ├── main.dart └── src │ ├── custom_items │ ├── algorithms_page.dart │ ├── app_drawer.dart │ ├── chemistry_page.dart │ ├── home_page.dart │ ├── mathematics_page.dart │ ├── physics_page.dart │ └── simulation_card.dart │ ├── data │ ├── simulations.dart │ └── themedata.dart │ ├── home.dart │ └── simulations │ ├── bubble_sort.dart │ ├── epicycloid.dart │ ├── epicycloid_curve.dart │ ├── fourier_series.dart │ ├── insertion_sort.dart │ ├── langton_ant.dart │ ├── lissajous_curve.dart │ ├── maurer_rose.dart │ ├── pi_approximation.dart │ ├── rose_pattern.dart │ ├── selection_sort.dart │ └── toothpick.dart ├── mkdocs.yml ├── pubspec.yaml ├── requirements-docs.txt └── web ├── favicon.png ├── icons ├── Icon-192.png └── Icon-512.png ├── index.html └── manifest.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: 'dir1: dir2: [BUG]' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | - Device: [e.g. iPhone6] 27 | - OS: [e.g. iOS8.1] 28 | - Screen Width [#px] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE REQUESTED]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/simulation-inaccuracy.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Simulation Inaccuracy 3 | about: When the simulation isn't accurate 4 | title: 'lib: simulations: [SIMULATION NAME]: [Problem]' 5 | labels: simulations 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Simulation Name and Problem:** 11 | Which simulation and describe the problem 12 | 13 | **Proofs and References:** 14 | Any proof or reference if applicable 15 | 16 | **What is causing it?** 17 | If looked through the code, help us locate the problem 18 | 19 | **Proposed Solution:** 20 | Your solution 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/simulation-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Simulation Request 3 | about: Request/Propose a new simulation 4 | title: 'simulation: [SIMULATION TITLE]' 5 | labels: enhancement, new simulation, simulations 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Proposed Simulation Name** 11 | Brief description of the simulation 12 | 13 | **Category Proposed:** [CATEGORY] 14 | 15 | **Additional points/links:** 16 | Links and points 17 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Build docs with Simulate for Web 2 | on: 3 | workflow_dispatch: 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | with: 10 | fetch-depth: 0 11 | - name: Setup Python 12 | uses: actions/setup-python@v2 13 | with: 14 | python-version: '3.7' 15 | architecture: 'x64' 16 | - uses: subosito/flutter-action@v1 17 | with: 18 | channel: 'stable' 19 | - run: flutter pub get 20 | - run: flutter build web --release --base-href /simulate/web/ 21 | - name: Install dependencies 22 | run: | 23 | python3 -m pip install --upgrade pip 24 | python3 -m pip install mkdocs 25 | python3 -m pip install mkdocs-material 26 | python3 -m pip install pymdown-extensions 27 | python3 -m pip install mkdocs-git-revision-date-localized-plugin 28 | - name: Git setup and update 29 | run: | 30 | git config user.name "GitHub Action" && git config user.email "github-action@github.com" 31 | git fetch origin 32 | - name: Build Docs 33 | run: mkdocs build 34 | - name: Add latest web build and deploy 35 | run: | 36 | mv build/web/ site/ 37 | mkdocs gh-deploy --dirty 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | /site/ 12 | .venv/ 13 | 14 | # IntelliJ related 15 | *.iml 16 | *.ipr 17 | *.iws 18 | .idea/ 19 | 20 | # Visual Studio Code related 21 | .vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | .flutter-plugins-dependencies 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Generated.xcconfig 62 | **/ios/Flutter/app.flx 63 | **/ios/Flutter/app.zip 64 | **/ios/Flutter/flutter_assets/ 65 | **/ios/ServiceDefinitions.json 66 | **/ios/Runner/GeneratedPluginRegistrant.* 67 | 68 | # Exceptions to above rules. 69 | !**/ios/**/default.mode1v3 70 | !**/ios/**/default.mode2v3 71 | !**/ios/**/default.pbxuser 72 | !**/ios/**/default.perspectivev3 73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 74 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "site"] 2 | path = site 3 | url = https://github.com/builtree/simulate.git 4 | branch = gh-pages 5 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 codEd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | 6 |

7 | 8 | [![Gitter](https://badges.gitter.im/codEd-org/simulate.svg)](https://gitter.im/codEd-org/simulate?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 9 | 10 | Welcome to Simulate! Simulate aims to be a collection of simulations and visualizations from various domains like mathematics, physics, computer science etc. in a cross platform app made using [Flutter](https://flutter.dev/). 11 | 12 | You can find out more about Simulate and how it works or how you can share ideas and contribute to the project [here](https://builtree.github.io/simulate/). 13 | 14 |

15 | 16 | 17 | 18 | 19 |

20 | 21 | # Contributing 22 | 23 | Before contributing, we highly recommend introducing yourself and your ideas on [gitter](https://gitter.im/codEd-org/simulate). 24 | 25 | Some pre-requisites: 26 | 27 | 1. Make sure you have Flutter installed (https://flutter.dev/docs/get-started/install) 28 | 2. Try installing and experimenting with the development version on your phone 29 | 3. Don't hesitate in asking questions, we'll be happy to help 30 | 31 | 32 | All further guidelines can be found [here](https://builtree.github.io/simulate/contributing). 33 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.example.simulate" 37 | minSdkVersion 16 38 | targetSdkVersion 28 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 60 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 61 | } 62 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 12 | 15 | 22 | 26 | 27 | 28 | 31 | 32 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/simulate/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.simulate; 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | public class MainActivity extends FlutterActivity { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/simulate/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.simulate 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /assets/images/builtree.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/simulations/BubbleSortDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/BubbleSortDark.png -------------------------------------------------------------------------------- /assets/simulations/BubbleSortLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/BubbleSortLight.png -------------------------------------------------------------------------------- /assets/simulations/Epicycloid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/Epicycloid.png -------------------------------------------------------------------------------- /assets/simulations/Epicycloid1Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/Epicycloid1Dark.png -------------------------------------------------------------------------------- /assets/simulations/Epicycloid1Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/Epicycloid1Light.png -------------------------------------------------------------------------------- /assets/simulations/EpicycloidDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/EpicycloidDark.png -------------------------------------------------------------------------------- /assets/simulations/FourierSeriesDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/FourierSeriesDark.png -------------------------------------------------------------------------------- /assets/simulations/FourierSeriesLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/FourierSeriesLight.png -------------------------------------------------------------------------------- /assets/simulations/InsertionSortDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/InsertionSortDark.png -------------------------------------------------------------------------------- /assets/simulations/InsertionSortLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/InsertionSortLight.png -------------------------------------------------------------------------------- /assets/simulations/LissajousCurveDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/LissajousCurveDark.png -------------------------------------------------------------------------------- /assets/simulations/LissajousCurveLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/LissajousCurveLight.png -------------------------------------------------------------------------------- /assets/simulations/MaurerRoseDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/MaurerRoseDark.png -------------------------------------------------------------------------------- /assets/simulations/MaurerRoseLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/MaurerRoseLight.png -------------------------------------------------------------------------------- /assets/simulations/RosePatternDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/RosePatternDark.png -------------------------------------------------------------------------------- /assets/simulations/RosePatternLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/RosePatternLight.png -------------------------------------------------------------------------------- /assets/simulations/ToothpickPatternDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/ToothpickPatternDark.png -------------------------------------------------------------------------------- /assets/simulations/ToothpickPatternLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/assets/simulations/ToothpickPatternLight.png -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | !!! note "" 3 | Before contributing, we highly recommend introducing yourself and your ideas on [gitter](https://gitter.im/codEd-org/simulate#). 4 | 5 | ## Setting Up Simulate 6 | 7 | 1. Make sure you have Flutter installed (https://flutter.dev/docs/get-started/install) 8 | 2. Fork the repository 9 | 3. Clone the forked repository: 10 | ``` 11 | git clone https://github.com/[YOUR USERNAME]/simulate.git 12 | ``` 13 | 4. Connect a device or run an emulator (Make sure your connected device has USB Debugging enabled) 14 | 5. Navigate to the project directory and Run: 15 | ```bash 16 | cd simulate 17 | flutter run 18 | ``` 19 | 20 | --- 21 | 22 | ## Building your stuff 23 | 24 | #### Fixing a bug: 25 | 26 | 1. Open an issue or report on [gitter](https://gitter.im/codEd-org/simulate#) 27 | 2. Make a new branch 28 | ```console 29 | git checkout -b [BRANCH NAME] 30 | ``` 31 | 3. Fix the bug, push and open a pull request 32 | 33 | #### Adding a new Simulation: 34 | 35 | 1. Got an idea? Discuss the idea on [gitter](https://gitter.im/codEd-org/simulate#) or just open an issue with this [template](https://github.com/builtree/simulate/issues/new?assignees=&labels=enhancement%2C+new+simulation%2C+simulations&template=simulation-request.md&title=simulation%3A+%5BSIMULATION+TITLE%5D). If the maintainers find it interesting, the issue will be labeled accepted. If you want to work on it and don't know how to implement it yet, we'll help you get it done. 36 | 2. Even if you don't have an idea but are interested in working on one, checkout the [accepted simulation ideas](https://github.com/builtree/simulate/issues?q=is%3Aopen+is%3Aissue+label%3Aaccepted+label%3A%22new+simulation%22) and get assigned. 37 | 3. You can start working now! Make sure you ask for help when needed, it's okay to get stuck. 38 | 39 | --- 40 | 41 | ## Setting Up Docs 42 | 43 | 1. Install mkdocs and its requirements 44 | ```bash 45 | python3 -m pip install -r requirements-docs.txt 46 | ``` 47 | 2. Check the installations by: 48 | ```bash 49 | mkdocs --version 50 | ``` 51 | 52 | !!! warning "" 53 | If this doesn't work, try restarting the terminal 54 | 55 | 3. Use the below command to host the documentation on local server 56 | ```bash 57 | mkdocs serve --dev-addr 127.0.0.1:8000 58 | ``` 59 | {== MkDocs supports live reload so you don't have to run the server again and again. Just make your changes in the docs and you'll see the change immediately. ==} 60 | 4. All the documentation is present in the `docs` directory. 61 | 62 | --- 63 | 64 | !!! quote "" 65 | *Don't hesitate in asking questions, it's a part of learning and everyone is here to help.* 66 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Simulate 2 | [![Gitter](https://badges.gitter.im/codEd-org/simulate.svg)](https://gitter.im/codEd-org/simulate?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 3 | 4 | Welcome to Simulate! Simulate aims to be a collection of simulations and visualizations from various domains like mathematics, physics, computer science etc. in a cross platform app made using [Flutter](https://flutter.dev/). The outcomes of these simulations mostly are really smooth (thanks to Flutter) and extremely beautiful (thanks to mathematics/sciences behind it). 5 | 6 |
7 | [![Simulate for Web](https://raw.githubusercontent.com/builtree/assets/simulate/documentation/LaunchWebApp.svg){: style="width:300px"}](https://builtree.github.io/simulate/web){: target="_blank"} 8 |
9 | 10 | --- 11 | 12 | ## New to Simulate? 13 | 14 | You can start by introducing yourself and your ideas/doubts on [gitter](https://gitter.im/codEd-org/simulate). 15 | 16 | Interested in Contributing? Check the guidelines [here](contributing/). 17 | -------------------------------------------------------------------------------- /docs/simulations/algorithms/ToothpickPattern.md: -------------------------------------------------------------------------------- 1 | # Toothpick Pattern 2 | --- 3 | ![ToothpickPatternLight](https://raw.githubusercontent.com/builtree/simulate/master/assets/simulations/ToothpickPatternLight.png){: style="height:200px", align=right} 4 | 5 |

6 | The toothpick sequence in geometry is a 2D pattern sequence which is based on the previous 7 | state of the pattern for the next stage. This is done by repeating addition of new line segments to free ends of line segments in the previous stage of the pattern. These line segments are referred to as “Toothpicks”.

8 | 9 |

10 | The pattern begins at the first step wherein it has only one toothpick/line segment in the middle 11 | of the canvas. With each increasing step the free/exposed ends of the previous stage toothpicks 12 | are attached at right angles to the center of new toothpicks. Mathematically, this process results 13 | in a growth pattern in which the total line segments at a particular stage (n) oscillates between 14 | 0.45n2 and 0.67n2. 15 |

16 | 17 | The pattern closely resembles the T-square fractal or the cellular arrangement 18 | in Ulam–Warburton cellular automaton[^1]. An interesting observation is how the pattern almost 19 | forms a square at the powers of 2. The current stage in the simulation is also represented by a 20 | different colour than the rest of the pattern. 21 | [^1]: https://en.wikipedia.org/wiki/Ulam%E2%80%93Warburton_automaton 22 | 23 | === "Light Mode" 24 | ![ToothpickPatternLight](https://raw.githubusercontent.com/builtree/assets/simulate/simulations/ToothpickPatternLight.png){: style="width:300px"} 25 | 26 | === "Dark Mode" 27 | ![ToothpickPatternDark](https://raw.githubusercontent.com/builtree/assets/simulate/simulations/ToothpickPatternDark.png){: style="width:300px"} 28 | 29 | !!! tip "Fun Fact" 30 | The toothpick is considered such an essential that even Swiss Army knives - a popular brand of multi-function tools - have included one in their product 31 | -------------------------------------------------------------------------------- /docs/simulations/index.md: -------------------------------------------------------------------------------- 1 | # Simulations 2 | 3 | | Simulation | Category | Description | Image | 4 | | :---------------: | :---------------: | :---------------: | :---------------: | 5 | | Bubble Sort (Bars) | Algorithms | Bubble sort is a sorting algorithm that repeatedly steps through the list by comparing adjacent elements and swaps them if they are in the wrong order | ![Bubble Sort](https://raw.githubusercontent.com/builtree/simulate/master/assets/simulations/BubbleSortLight.png) | 6 | | Epicycloid Pattern(Pencil of Lines) | Mathematics | a.k.a hypercycloid, is a plane curve created by tracing a chosen point on the edge of a circle of radius r rolling on the outside of a circle of radius R | ![Epicycloid Pattern](https://raw.githubusercontent.com/builtree/assets/simulate/icons/Epicycloid1Light.png) | 7 | | Epicycloid Curve | Mathematics | a.k.a hypercycloid, is a plane curve created by tracing a chosen point on the edge of a circle of radius r rolling on the outside of a circle of radius R | ![Epicycloid Curve](https://raw.githubusercontent.com/builtree/assets/simulate/icons/EpicycloidLight.png) | 8 | | [Fourier Series](mathematics/FourierSeries.md) | Mathematics | A Fourier series is an expansion of a periodic function in terms of an infinite sum of sines and cosines | ![Fourier Series](https://raw.githubusercontent.com/builtree/assets/simulate/icons/FourierSeriesLight.png) | 9 | | Insertion Sort (Bars) | Algorithms | Insertion sort is a sorting algorithm that builds the final list one item at a time | ![Insertion Sort](https://raw.githubusercontent.com/builtree/simulate/master/assets/simulations/InsertionSortLight.png) | 10 | | [Lissajous Pattern](mathematics/LissajousPattern.md)| Mathematics | Lissajous pattern is generated by the junction of a pair of sinusoidal waves with axes that are perpendicular to one another |![Lissajous Pattern](https://raw.githubusercontent.com/builtree/assets/simulate/icons/LissajousCurveLight.png) | 11 | | [Maurer Rose Pattern](mathematics/MaurerRosePattern.md) | Mathematics | A Maurer Rose is a polygonal curve with vertices on a rose and can be described as a closed route in the polar plane | ![MaurerRose Pattern](https://raw.githubusercontent.com/builtree/assets/simulate/icons/MaurerRoseLight.png) | 12 | | Rose Pattern | Mathematics | A Rose pattern is generated by the junction of sinusoids plotted in polar coordinates | ![Rose Pattern](https://raw.githubusercontent.com/builtree/assets/simulate/icons/RosePatternLight.png) | 13 | | [Toothpick Pattern](algorithms/ToothpickPattern.md) | Algorithms | A Toothpick pattern is a sequence of 2D patterns formed by repeatedly adding line segments or _toothpicks_ to the previous pattern in the sequence. It starts with a single line segment | ![Toothpick Pattern](https://raw.githubusercontent.com/builtree/simulate/master/assets/simulations/ToothpickPatternLight.png) | 14 | -------------------------------------------------------------------------------- /docs/simulations/mathematics/FourierSeries.md: -------------------------------------------------------------------------------- 1 | # Fourier Series 2 | --- 3 | ![FourierSeries](https://raw.githubusercontent.com/builtree/simulate/master/assets/simulations/FourierSeriesLight.png){: style="height:200px", align=right} 4 | 5 |

6 | A Fourier transform is a way of breaking down a complex function into (infinite) sums of sine and cosine waves. In short, given a smoothie, it finds its recipe. Fourier series is the Fourier transform of a periodic function and it aims to represent the periodic function as a sum of sinusoidal waves. It is analogous to the Taylor series, which represents functions as an infinite sum of monomial terms.

7 | 8 |
9 | $S_{n}(x) = \frac{a_{o}}{2}+\sum_{n=1}^{N}(a_{n}\cos(\frac{2\pi nx}{P}) + b_{n}\sin(\frac{2\pi nx}{P}))$ 10 |
11 | 12 | This sum of sine and cosine waves can also be thought of as a list of phasors. Hence, the Fourier series generates a list of phasors which, when summed together, reproduces the original signal. The below animation shows how this can look like: [^1][^2] 13 | 14 | ![FourierSeriesPhasors](https://upload.wikimedia.org/wikipedia/commons/b/bd/Fourier_series_square_wave_circles_animation.svg){: style="width:300px; vertical-align: baseline"} 15 | ![FourierSeriesPhasors](https://upload.wikimedia.org/wikipedia/commons/1/1e/Fourier_series_sawtooth_wave_circles_animation.svg){: style="width:300px ; vertical-align: baseline" } 16 | 17 | Simulate approximates the following waves: 18 | 19 | 1. Sawtooth function 20 | 2. Square function 21 | 22 | We have the flexibility to fiddle with the following parameters: 23 | 24 | * **N**: The number of phasors (sine and cosine terms) generated 25 | 26 | * **Amplitude**: The coefficients ‘an’ and ‘bn’ 27 | 28 | * **Frequency**: The value of ‘n’ to adjust the frequency of sine and cosine terms 29 | 30 | [^1]: https://upload.wikimedia.org/wikipedia/commons/b/bd/Fourier_series_square_wave_circles_animation.svg 31 | [^2]: https://upload.wikimedia.org/wikipedia/commons/1/1e/Fourier_series_sawtooth_wave_circles_animation.svg 32 | 33 | === "Light Mode" 34 | ![FourierSeriesLight](https://raw.githubusercontent.com/builtree/assets/simulate/simulations/FourierSeriesLight.png){: style="width:600px"} 35 | 36 | === "Dark Mode" 37 | ![FourierSeriesDark](https://raw.githubusercontent.com/builtree/assets/simulate/simulations/FourierSeriesDark.png){: style="width:600px"} 38 | 39 | -------------------------------------------------------------------------------- /docs/simulations/mathematics/LissajousPattern.md: -------------------------------------------------------------------------------- 1 | # Lissajous Pattern 2 | --- 3 | ![LissajousPatternLight](https://raw.githubusercontent.com/builtree/simulate/master/assets/simulations/LissajousCurveLight.png){: style="height:200px", align=right} 4 | 5 |

6 | A Lissajous curve in mathematics, also known as Lissajous Figure or Bowditch curve is the 7 | graphical representation of the following system of parametric equations.

8 | 9 |
10 | $x = A sin(at + δ)$ 11 | $y = B sin(bt)$ 12 |
13 | 14 | These curves/patterns are highly sensitive to the values of $a$ and $b$, particularly to the ratio 15 | $a:b$. For a ratio of $1$, and $δ = \frac{π}{2} radians$, the figure is a *circle*. Similarly, for $a = b$ and $δ = 0$, the figure is a *line*. 16 | Another simple Lissajous figure is the *parabola* $(\frac{b}{a} = 2, δ = \frac{π}{4})$. 17 | 18 | === "Light Mode" 19 | ![LissajousPatternLight](https://raw.githubusercontent.com/builtree/assets/simulate/simulations/LissajousCurveLight.png){: style="width:300px"} 20 | 21 | === "Dark Mode" 22 | ![LissajousPatternDark](https://raw.githubusercontent.com/builtree/assets/simulate/simulations/LissajousCurveDark.png){: style="width:300px"} 23 | 24 | !!! tip "Fun Fact" 25 | The title sequence by John Whitney for Alfred Hitchcock's 1958 film Vertigo is based on Lissajous figures. 26 | -------------------------------------------------------------------------------- /docs/simulations/mathematics/MaurerRosePattern.md: -------------------------------------------------------------------------------- 1 | # Maurer Rose Pattern 2 | --- 3 | ![MaurerRoseLight](https://raw.githubusercontent.com/builtree/simulate/master/assets/simulations/MaurerRoseLight.png){: style="height:200px", align=right} 4 | 5 | A Maurer rose of the rose $r = sin(nθ)$ consists of the 360 lines successively connecting the mentioned below 6 | 361 points, where $n$ is a positive integer. The rose has $n$ petals if $n$ is odd, and $2n$ petals if $n$ is even. 7 | We take 361 points on the rose as: 8 |
9 | $(sin(nk), k) (k = 0, d, 2d, 3d, ..., 360d),$
10 | where $d$ is a positive integer and the angles are in degrees, not radians. Thus a Maurer rose is a polygonal curve with vertices on a rose. 11 | 12 | A Maurer rose can be described as a closed route in the polar plane. 13 | 14 | - A walker starts a journey from the origin $(0, 0)$, and walks along a line to the point $(sin(nd), d)$. 15 | - Then, in the second leg of the journey, the walker walks along a line to the next point, $(sin(n·2d), 2d)$, and so on. 16 | - Finally, in the final leg of the journey, the walker walks along a line, from $(sin(n·359d), 359d)$ to the ending 17 | point, $(sin(n·360d), 360d)$. 18 | A Maurer rose is a closed curve since the starting point, $(0, 0)$ and the ending point, $(sin(n·360d), 360d)$, coincide. 19 | 20 | === "Light Mode" 21 | ![MaurerRoseLight](https://raw.githubusercontent.com/builtree/assets/simulate/simulations/MaurerRoseLight.png){: style="width:300px"} 22 | 23 | === "Dark Mode" 24 | ![MaurerRoseDark](https://raw.githubusercontent.com/builtree/assets/simulate/simulations/MaurerRoseDark.png){: style="width:300px"} -------------------------------------------------------------------------------- /fonts/UFL.txt: -------------------------------------------------------------------------------- 1 | ------------------------------- 2 | UBUNTU FONT LICENCE Version 1.0 3 | ------------------------------- 4 | 5 | PREAMBLE 6 | This licence allows the licensed fonts to be used, studied, modified and 7 | redistributed freely. The fonts, including any derivative works, can be 8 | bundled, embedded, and redistributed provided the terms of this licence 9 | are met. The fonts and derivatives, however, cannot be released under 10 | any other licence. The requirement for fonts to remain under this 11 | licence does not require any document created using the fonts or their 12 | derivatives to be published under this licence, as long as the primary 13 | purpose of the document is not to be a vehicle for the distribution of 14 | the fonts. 15 | 16 | DEFINITIONS 17 | "Font Software" refers to the set of files released by the Copyright 18 | Holder(s) under this licence and clearly marked as such. This may 19 | include source files, build scripts and documentation. 20 | 21 | "Original Version" refers to the collection of Font Software components 22 | as received under this licence. 23 | 24 | "Modified Version" refers to any derivative made by adding to, deleting, 25 | or substituting -- in part or in whole -- any of the components of the 26 | Original Version, by changing formats or by porting the Font Software to 27 | a new environment. 28 | 29 | "Copyright Holder(s)" refers to all individuals and companies who have a 30 | copyright ownership of the Font Software. 31 | 32 | "Substantially Changed" refers to Modified Versions which can be easily 33 | identified as dissimilar to the Font Software by users of the Font 34 | Software comparing the Original Version with the Modified Version. 35 | 36 | To "Propagate" a work means to do anything with it that, without 37 | permission, would make you directly or secondarily liable for 38 | infringement under applicable copyright law, except executing it on a 39 | computer or modifying a private copy. Propagation includes copying, 40 | distribution (with or without modification and with or without charging 41 | a redistribution fee), making available to the public, and in some 42 | countries other activities as well. 43 | 44 | PERMISSION & CONDITIONS 45 | This licence does not grant any rights under trademark law and all such 46 | rights are reserved. 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a 49 | copy of the Font Software, to propagate the Font Software, subject to 50 | the below conditions: 51 | 52 | 1) Each copy of the Font Software must contain the above copyright 53 | notice and this licence. These can be included either as stand-alone 54 | text files, human-readable headers or in the appropriate machine- 55 | readable metadata fields within text or binary files as long as those 56 | fields can be easily viewed by the user. 57 | 58 | 2) The font name complies with the following: 59 | (a) The Original Version must retain its name, unmodified. 60 | (b) Modified Versions which are Substantially Changed must be renamed to 61 | avoid use of the name of the Original Version or similar names entirely. 62 | (c) Modified Versions which are not Substantially Changed must be 63 | renamed to both (i) retain the name of the Original Version and (ii) add 64 | additional naming elements to distinguish the Modified Version from the 65 | Original Version. The name of such Modified Versions must be the name of 66 | the Original Version, with "derivative X" where X represents the name of 67 | the new work, appended to that name. 68 | 69 | 3) The name(s) of the Copyright Holder(s) and any contributor to the 70 | Font Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except (i) as required by this licence, (ii) to 72 | acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with 73 | their explicit written permission. 74 | 75 | 4) The Font Software, modified or unmodified, in part or in whole, must 76 | be distributed entirely under this licence, and must not be distributed 77 | under any other licence. The requirement for fonts to remain under this 78 | licence does not affect any document created using the Font Software, 79 | except any version of the Font Software extracted from a document 80 | created using the Font Software may only be distributed under this 81 | licence. 82 | 83 | TERMINATION 84 | This licence becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF 91 | COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER 96 | DEALINGS IN THE FONT SOFTWARE. 97 | -------------------------------------------------------------------------------- /fonts/Ubuntu-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/fonts/Ubuntu-Bold.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/fonts/Ubuntu-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/fonts/Ubuntu-Italic.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/fonts/Ubuntu-Light.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/fonts/Ubuntu-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/fonts/Ubuntu-Medium.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/fonts/Ubuntu-MediumItalic.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/fonts/Ubuntu-Regular.ttf -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | simulate 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:simulate/src/home.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:simulate/src/data/simulations.dart'; 5 | import 'package:simulate/src/data/themedata.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | 8 | void main() async { 9 | WidgetsFlutterBinding.ensureInitialized(); 10 | final SharedPreferences sharedPreferences = 11 | await SharedPreferences.getInstance(); 12 | runApp( 13 | MultiProvider( 14 | providers: [ 15 | ChangeNotifierProvider( 16 | lazy: false, 17 | create: (context) => Simulations(), 18 | ), 19 | ChangeNotifierProvider( 20 | lazy: false, 21 | create: (context) => ThemeProvider(sharedPreferences), 22 | ), 23 | ], 24 | child: HomeCall(), 25 | ), 26 | ); 27 | } 28 | 29 | class HomeCall extends StatelessWidget { 30 | @override 31 | Widget build(BuildContext context) { 32 | final theme = Provider.of(context); 33 | return MaterialApp( 34 | debugShowCheckedModeBanner: false, 35 | title: 'Simulate', 36 | home: Home(), 37 | theme: theme.theme, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/custom_items/algorithms_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:simulate/src/data/simulations.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | class AlgorithmsPage extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | final appState = Provider.of(context); 9 | return Container( 10 | child: GridView.count( 11 | crossAxisCount: (MediaQuery.of(context).size.width < 600) 12 | ? 2 13 | : (MediaQuery.of(context).size.width / 200).floor(), 14 | children: appState.algorithms), 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/custom_items/app_drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:simulate/src/data/themedata.dart'; 5 | import 'package:url_launcher/url_launcher.dart'; 6 | 7 | class AppDrawer extends StatelessWidget { 8 | const AppDrawer({Key key}) : super(key: key); 9 | 10 | _launch(url) async { 11 | if (await canLaunch(url)) { 12 | await launch(url); 13 | } else { 14 | throw 'Could not launch'; 15 | } 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | final theme = Provider.of(context); 21 | 22 | return Drawer( 23 | child: ListView( 24 | padding: EdgeInsets.zero, 25 | children: [ 26 | DrawerHeader( 27 | child: Center( 28 | child: Text( 29 | 'Simulate', 30 | style: Theme.of(context).textTheme.headline6.copyWith( 31 | fontSize: 40, 32 | ), 33 | ), 34 | ), 35 | decoration: BoxDecoration( 36 | color: Theme.of(context).primaryColor, 37 | ), 38 | ), 39 | ListTile( 40 | leading: Text( 41 | "Documentation", 42 | style: Theme.of(context).textTheme.subtitle2.copyWith( 43 | fontSize: 20, 44 | ), 45 | ), 46 | trailing: Icon(Icons.open_in_new_rounded), 47 | onTap: () => _launch("https://www.builtree.org/simulate/"), 48 | ), 49 | ListTile( 50 | leading: Text( 51 | "License", 52 | style: Theme.of(context).textTheme.subtitle2.copyWith( 53 | fontSize: 20, 54 | ), 55 | ), 56 | trailing: Icon(Icons.open_in_new_rounded), 57 | onTap: () => _launch( 58 | "https://github.com/builtree/simulate/blob/master/LICENSE", 59 | ), 60 | ), 61 | ListTile( 62 | leading: Text( 63 | "Github", 64 | style: Theme.of(context).textTheme.subtitle2.copyWith( 65 | fontSize: 20, 66 | ), 67 | ), 68 | trailing: Icon(Icons.open_in_new_rounded), 69 | onTap: () => _launch("https://github.com/builtree/simulate/"), 70 | ), 71 | ListTile( 72 | leading: Text( 73 | "Submit an issue", 74 | style: Theme.of(context).textTheme.subtitle2.copyWith( 75 | fontSize: 20, 76 | ), 77 | ), 78 | trailing: Icon(Icons.open_in_new_rounded), 79 | onTap: () => _launch( 80 | "https://github.com/builtree/simulate/issues/new/choose", 81 | ), 82 | ), 83 | ListTile( 84 | leading: Text( 85 | "App Version", 86 | style: Theme.of(context).textTheme.subtitle2.copyWith( 87 | fontSize: 20, 88 | ), 89 | ), 90 | trailing: Text( 91 | "0.0.1", 92 | style: Theme.of(context).textTheme.subtitle2.copyWith( 93 | fontSize: 20, 94 | ), 95 | ), 96 | ), 97 | ListTile( 98 | leading: Text( 99 | "Dark Mode", 100 | style: Theme.of(context).textTheme.subtitle2.copyWith( 101 | fontSize: 20, 102 | ), 103 | ), 104 | trailing: Switch( 105 | value: theme.darkTheme, 106 | activeColor: Colors.black, 107 | onChanged: (bool value) { 108 | theme.toggleTheme(); 109 | }, 110 | ), 111 | ), 112 | ListTile( 113 | leading: Text( 114 | "Project By", 115 | style: Theme.of(context).textTheme.subtitle2.copyWith( 116 | fontSize: 20, 117 | ), 118 | ), 119 | trailing: 120 | SvgPicture.asset("assets/images/builtree.svg", width: 100), 121 | onTap: () => _launch("https://www.builtree.org/"), 122 | ), 123 | ], 124 | ), 125 | ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/src/custom_items/chemistry_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:simulate/src/data/simulations.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | class ChemistryPage extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | final appState = Provider.of(context); 9 | return Container( 10 | child: GridView.count( 11 | crossAxisCount: (MediaQuery.of(context).size.width < 600) 12 | ? 2 13 | : (MediaQuery.of(context).size.width / 200).floor(), 14 | children: appState.chemistry), 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/custom_items/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:simulate/src/data/simulations.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class HomePage extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | final appState = Provider.of(context); 10 | return LayoutBuilder( 11 | // ignore: missing_return 12 | builder: (_, BoxConstraints constraints) { 13 | if (constraints.maxWidth != 0) { 14 | ScreenUtil.init( 15 | constraints, 16 | context: context, 17 | designSize: Size(512.0, 850.0), 18 | minTextAdapt: false, 19 | ); 20 | return Container( 21 | child: ListView( 22 | children: [ 23 | HomeHorizontalList( 24 | listName: 'Favorites', 25 | elements: (appState.favorites.length != 0) 26 | ? appState.favorites 27 | : [ 28 | Container( 29 | width: MediaQuery.of(context).size.width - 20, 30 | child: Center( 31 | child: Text( 32 | "No favorites yet!", 33 | style: Theme.of(context).textTheme.caption, 34 | ), 35 | ), 36 | ), 37 | ], 38 | ), 39 | HomeHorizontalList( 40 | listName: 'All', 41 | elements: appState.all, 42 | ), 43 | ], 44 | ), 45 | ); 46 | } 47 | }, 48 | ); 49 | } 50 | } 51 | 52 | class HomeHorizontalList extends StatelessWidget { 53 | final String listName; 54 | final List elements; 55 | 56 | HomeHorizontalList( 57 | {Key key, @required this.listName, @required this.elements}) 58 | : super(key: key); 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | return Container( 63 | margin: EdgeInsets.fromLTRB(10, 10, 0, 10), 64 | child: Column( 65 | children: [ 66 | Container( 67 | margin: EdgeInsets.all(5), 68 | width: MediaQuery.of(context).size.width, 69 | child: Text( 70 | listName, 71 | style: Theme.of(context).textTheme.subtitle1, 72 | textAlign: TextAlign.left, 73 | ), 74 | ), 75 | Container( 76 | height: MediaQuery.of(context).size.height > 77 | MediaQuery.of(context).size.width 78 | ? ScreenUtil().setHeight(260) 79 | : 250, 80 | child: ListView( 81 | scrollDirection: Axis.horizontal, 82 | children: elements, 83 | ), 84 | ), 85 | ], 86 | ), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/src/custom_items/mathematics_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:simulate/src/data/simulations.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | class MathematicsPage extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | final appState = Provider.of(context); 9 | return Container( 10 | child: GridView.count( 11 | crossAxisCount: (MediaQuery.of(context).size.width < 600) 12 | ? 2 13 | : (MediaQuery.of(context).size.width / 200).floor(), 14 | children: appState.mathematics, 15 | ), 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/custom_items/physics_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:simulate/src/data/simulations.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | class PhysicsPage extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | final appState = Provider.of(context); 9 | return Container( 10 | child: GridView.count( 11 | crossAxisCount: (MediaQuery.of(context).size.width < 600) 12 | ? 2 13 | : (MediaQuery.of(context).size.width / 200).floor(), 14 | children: appState.physics, 15 | ), 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/custom_items/simulation_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:url_launcher/url_launcher.dart'; 5 | import 'package:provider/provider.dart'; 6 | import 'package:simulate/src/data/simulations.dart'; 7 | 8 | class SimulationCard extends StatefulWidget { 9 | final String simulationName; 10 | final Widget direct; 11 | final String image; 12 | final String infoLink; 13 | final int fav; 14 | final int id; 15 | SimulationCard( 16 | {Key key, 17 | @required this.id, 18 | @required this.simulationName, 19 | @required this.direct, 20 | @required this.image, 21 | @required this.infoLink, 22 | @required this.fav}) 23 | : super(key: key); 24 | 25 | _SimulationCardState createState() => _SimulationCardState(); 26 | } 27 | 28 | class _SimulationCardState extends State { 29 | int fav; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | fav = widget.fav; 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | final appState = Provider.of(context); 40 | return Container( 41 | width: MediaQuery.of(context).size.height > MediaQuery.of(context).size.width ? ScreenUtil().setWidth(200) : 200, 42 | child: GestureDetector( 43 | onTap: () { 44 | Navigator.push( 45 | context, 46 | CupertinoPageRoute(builder: (context) => widget.direct), 47 | ); 48 | }, 49 | child: Card( 50 | elevation: 5, 51 | color: Theme.of(context).primaryColor, 52 | shape: RoundedRectangleBorder( 53 | borderRadius: BorderRadius.circular(10.0), 54 | ), 55 | child: Column( 56 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 57 | children: [ 58 | Flexible( 59 | flex: 10, 60 | child: Container( 61 | padding: EdgeInsets.fromLTRB(5, 10, 5, 0), 62 | child: Image.asset( 63 | widget.image, 64 | fit: BoxFit.fill, 65 | ), 66 | ), 67 | ), 68 | Flexible( 69 | flex: 3, 70 | child: Container( 71 | padding: EdgeInsets.fromLTRB(3, 15, 3, 0), 72 | child: Center( 73 | child: FittedBox( 74 | alignment: Alignment.bottomCenter, 75 | fit: BoxFit.fitWidth, 76 | child: Text( 77 | widget.simulationName, 78 | style: Theme.of(context).textTheme.subtitle1, 79 | ), 80 | ), 81 | ), 82 | ), 83 | ), 84 | Flexible( 85 | flex: 4, 86 | child: Container( 87 | color: Colors.transparent, 88 | child: Row( 89 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 90 | children: [ 91 | IconButton( 92 | icon: Icon(Icons.info_outline), 93 | onPressed: () async { 94 | final url = widget.infoLink; 95 | if (await canLaunch(url)) { 96 | await launch(url); 97 | } else { 98 | throw 'Could not launch'; 99 | } 100 | }, 101 | ), 102 | IconButton( 103 | icon: (widget.fav == 1) 104 | ? Icon(Icons.favorite) 105 | : Icon(Icons.favorite_border), 106 | onPressed: () { 107 | setState(() { 108 | appState.toggleFavorite(widget.id); 109 | }); 110 | }, 111 | ), 112 | ], 113 | ), 114 | ), 115 | ), 116 | ], 117 | ), 118 | ), 119 | ), 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/src/data/simulations.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:simulate/src/custom_items/simulation_card.dart'; 3 | import 'package:simulate/src/data/themedata.dart'; 4 | import 'package:simulate/src/simulations/bubble_sort.dart'; 5 | import 'package:simulate/src/simulations/selection_sort.dart'; 6 | import 'package:simulate/src/simulations/epicycloid.dart'; 7 | import 'package:simulate/src/simulations/fourier_series.dart'; 8 | import 'package:simulate/src/simulations/rose_pattern.dart'; 9 | import 'package:simulate/src/simulations/toothpick.dart'; 10 | import 'package:simulate/src/simulations/insertion_sort.dart'; 11 | import 'package:shared_preferences/shared_preferences.dart'; 12 | import 'package:simulate/src/simulations/lissajous_curve.dart'; 13 | import 'package:simulate/src/simulations/epicycloid_curve.dart'; 14 | import 'package:simulate/src/simulations/maurer_rose.dart'; 15 | 16 | class Simulations with ChangeNotifier { 17 | static var _favorites = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1]; 18 | final _algorithm = [0, 1, 2, 9]; 19 | final _mathematics = [3, 4, 5, 6, 7, 8]; 20 | final _physics = []; 21 | final _chemistry = []; 22 | var prefs; 23 | final _searchTags = { 24 | 0: "toothpick pattern algorithm sequence ", 25 | 1: "bubble sort algorithm sorting bars ", 26 | 2: "insertion sort algorithm sorting bars ", 27 | 3: "rose pattern mathematics sequence ", 28 | 4: "fourier series mathematics ", 29 | 5: "lissajous curve pattern mathematics animation ", 30 | 6: "epicycloid curve pattern mathematics animation pencil lines ", 31 | 7: "epicycloid curve pattern mathematics animation ", 32 | 8: "maurer rose pattern mathematics animation", 33 | 9: "selection sort algorithm sorting bars " 34 | }; 35 | 36 | Simulations() { 37 | getFavorites(); 38 | } 39 | 40 | getFavorites() async { 41 | prefs = await SharedPreferences.getInstance(); 42 | List myList = (prefs.getStringList('favorites') ?? []); 43 | if (myList.length != 0) { 44 | _favorites = myList.map((i) => int.parse(i)).toList(); 45 | if (allSimulations().length > _favorites.length) { 46 | _favorites = List.from(_favorites) 47 | ..addAll( 48 | List.filled(allSimulations().length - _favorites.length, -1)); 49 | } 50 | } 51 | } 52 | 53 | List allSimulations() { 54 | final theme = ThemeProvider(prefs); 55 | return [ 56 | SimulationCard( 57 | id: 0, 58 | simulationName: 'Toothpick Pattern', 59 | image: theme.darkTheme 60 | ? 'assets/simulations/ToothpickPatternDark.png' 61 | : 'assets/simulations/ToothpickPatternLight.png', 62 | direct: ToothpickPattern(), 63 | infoLink: 'https://en.wikipedia.org/wiki/Toothpick_sequence', 64 | fav: _favorites[0], 65 | ), 66 | SimulationCard( 67 | id: 1, 68 | simulationName: 'Bubble Sort (Bars)', 69 | image: theme.darkTheme 70 | ? 'assets/simulations/BubbleSortDark.png' 71 | : 'assets/simulations/BubbleSortLight.png', 72 | direct: BubbleSortBars(), 73 | infoLink: 'https://en.wikipedia.org/wiki/Bubble_sort', 74 | fav: _favorites[1], 75 | ), 76 | SimulationCard( 77 | id: 2, 78 | simulationName: 'Insertion Sort', 79 | image: theme.darkTheme 80 | ? 'assets/simulations/InsertionSortDark.png' 81 | : 'assets/simulations/InsertionSortLight.png', 82 | direct: InsertionHome(), 83 | infoLink: 'https://en.wikipedia.org/wiki/Insertion_sort', 84 | fav: _favorites[2], 85 | ), 86 | SimulationCard( 87 | id: 3, 88 | simulationName: 'Rose Pattern', 89 | image: theme.darkTheme 90 | ? 'assets/simulations/RosePatternDark.png' 91 | : 'assets/simulations/RosePatternLight.png', 92 | direct: RosePattern(), 93 | infoLink: 'https://en.wikipedia.org/wiki/Rose_(mathematics)', 94 | fav: _favorites[3], 95 | ), 96 | SimulationCard( 97 | id: 4, 98 | simulationName: 'Fourier Series', 99 | image: theme.darkTheme 100 | ? 'assets/simulations/FourierSeriesDark.png' 101 | : 'assets/simulations/FourierSeriesLight.png', 102 | direct: FourierSeries(), 103 | infoLink: 'https://en.wikipedia.org/wiki/Fourier_series', 104 | fav: _favorites[4], 105 | ), 106 | SimulationCard( 107 | id: 5, 108 | simulationName: 'Lissajous Pattern', 109 | image: theme.darkTheme 110 | ? 'assets/simulations/LissajousCurveDark.png' 111 | : 'assets/simulations/LissajousCurveLight.png', 112 | direct: LissajousCurve(), 113 | infoLink: 'https://en.wikipedia.org/wiki/Lissajous_curve', 114 | fav: _favorites[5], 115 | ), 116 | SimulationCard( 117 | id: 6, 118 | simulationName: 'Epicycloid Pattern (Pencil of Lines)', 119 | image: theme.darkTheme 120 | ? 'assets/simulations/Epicycloid1Dark.png' 121 | : 'assets/simulations/Epicycloid1Light.png', 122 | direct: EpicycloidCurve(), 123 | infoLink: 'https://en.wikipedia.org/wiki/Epicycloid', 124 | fav: _favorites[6], 125 | ), 126 | SimulationCard( 127 | id: 7, 128 | simulationName: 'Epicycloid Curve', 129 | image: theme.darkTheme 130 | ? 'assets/simulations/EpicycloidDark.png' 131 | : 'assets/simulations/Epicycloid.png', 132 | direct: NormalEpicycloidCurve(), 133 | infoLink: 'https://en.wikipedia.org/wiki/Epicycloid', 134 | fav: _favorites[7], 135 | ), 136 | SimulationCard( 137 | id: 8, 138 | simulationName: 'Maurer Rose Pattern', 139 | image: theme.darkTheme 140 | ? 'assets/simulations/MaurerRoseDark.png' 141 | : 'assets/simulations/MaurerRoseLight.png', 142 | direct: MaurerRoseCurve(), 143 | infoLink: 'https://en.wikipedia.org/wiki/Maurer_rose', 144 | fav: _favorites[8], 145 | ), 146 | SimulationCard( 147 | id: 9, 148 | simulationName: 'Selection Sort', 149 | image: theme.darkTheme 150 | ? 'assets/simulations/InsertionSortDark.png' 151 | : 'assets/simulations/InsertionSortLight.png', 152 | direct: SelectionSortBars(), 153 | infoLink: 'https://en.wikipedia.org/wiki/Selection_sort', 154 | fav: _favorites[9], 155 | ), 156 | ]; 157 | } 158 | 159 | List get all { 160 | getFavorites(); 161 | return allSimulations(); 162 | } 163 | 164 | List get algorithms { 165 | getFavorites(); 166 | List widgets = []; 167 | List allWidgets = allSimulations(); 168 | _algorithm.forEach((index) => widgets.add(allWidgets[index])); 169 | return widgets; 170 | } 171 | 172 | List get physics { 173 | getFavorites(); 174 | List widgets = []; 175 | List allWidgets = allSimulations(); 176 | _physics.forEach((index) => widgets.add(allWidgets[index])); 177 | return widgets; 178 | } 179 | 180 | List get mathematics { 181 | getFavorites(); 182 | List widgets = []; 183 | List allWidgets = allSimulations(); 184 | _mathematics.forEach((index) => widgets.add(allWidgets[index])); 185 | return widgets; 186 | } 187 | 188 | List get chemistry { 189 | getFavorites(); 190 | List widgets = []; 191 | List allWidgets = allSimulations(); 192 | _chemistry.forEach((index) => widgets.add(allWidgets[index])); 193 | return widgets; 194 | } 195 | 196 | List get favorites { 197 | getFavorites(); 198 | List widgets = []; 199 | List allWidgets = allSimulations(); 200 | for (int i = 0; i < _favorites.length; ++i) { 201 | if (_favorites[i] == 1) { 202 | widgets.add(allWidgets[i]); 203 | } 204 | } 205 | return widgets; 206 | } 207 | 208 | List searchSims(String query) { 209 | query = query.toLowerCase(); 210 | List widgets = []; 211 | List allWidgets = allSimulations(); 212 | final regex = RegExp('$query[a-z]* '); 213 | _searchTags.forEach((key, tags) { 214 | if (regex.hasMatch(tags)) { 215 | widgets.add(allWidgets[key]); 216 | } 217 | }); 218 | return widgets; 219 | } 220 | 221 | void toggleFavorite(int index) async { 222 | _favorites[index] *= -1; 223 | List favorites = _favorites.map((i) => i.toString()).toList(); 224 | await prefs.setStringList('favorites', favorites); 225 | notifyListeners(); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /lib/src/data/themedata.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | class ThemeProvider with ChangeNotifier { 5 | bool _darkTheme; 6 | SharedPreferences prefs; 7 | Color _primaryColor = Colors.white; 8 | Color _counterColor = Colors.black; 9 | 10 | ThemeProvider(this.prefs) { 11 | _darkTheme = prefs.getBool('theme') ?? false; 12 | _primaryColor = _darkTheme ? Colors.black : Colors.white; 13 | _counterColor = _darkTheme ? Colors.white : Colors.black; 14 | } 15 | 16 | ThemeData get theme { 17 | return themeData(); 18 | } 19 | 20 | ThemeData themeData() { 21 | final ThemeData _theme = ThemeData( 22 | brightness: _darkTheme ? Brightness.dark : Brightness.light, 23 | fontFamily: 'Ubuntu', 24 | indicatorColor: _counterColor, 25 | primaryColor: _primaryColor, 26 | textTheme: TextTheme( 27 | headline6: TextStyle( 28 | color: _counterColor, 29 | fontFamily: 'Ubuntu', 30 | ), 31 | subtitle1: TextStyle( 32 | color: _counterColor, 33 | fontFamily: 'Ubuntu', 34 | ), 35 | caption: TextStyle( 36 | color: Colors.grey[400], 37 | fontSize: 24, 38 | fontFamily: 'Ubuntu', 39 | ), 40 | ), 41 | appBarTheme: AppBarTheme( 42 | color: _primaryColor, 43 | iconTheme: IconThemeData( 44 | color: _counterColor, 45 | ), 46 | ), 47 | tabBarTheme: TabBarTheme( 48 | labelColor: _counterColor, 49 | unselectedLabelColor: _counterColor.withOpacity(0.3), 50 | ), 51 | ); 52 | 53 | return _theme.copyWith( 54 | colorScheme: _theme.colorScheme.copyWith(secondary: _counterColor), 55 | ); 56 | } 57 | 58 | bool get darkTheme { 59 | return _darkTheme; 60 | } 61 | 62 | void toggleTheme() async { 63 | _darkTheme = !_darkTheme; 64 | await prefs.setBool('theme', _darkTheme); 65 | _primaryColor = _darkTheme ? Colors.black : Colors.white; 66 | _counterColor = _darkTheme ? Colors.white : Colors.black; 67 | notifyListeners(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/src/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | import 'package:simulate/src/custom_items/app_drawer.dart'; 4 | import 'data/simulations.dart'; 5 | import 'package:provider/provider.dart'; 6 | import 'package:simulate/src/custom_items/home_page.dart'; 7 | import 'package:simulate/src/custom_items/mathematics_page.dart'; 8 | import 'package:simulate/src/custom_items/algorithms_page.dart'; 9 | import 'package:simulate/src/custom_items/simulation_card.dart'; 10 | import 'package:simulate/src/data/themedata.dart'; 11 | 12 | class Home extends StatefulWidget { 13 | final List _categoryTabs = [ 14 | Tab( 15 | child: Text('Home'), 16 | ), 17 | Tab( 18 | child: Text('Algorithms'), 19 | ), 20 | Tab( 21 | child: Text('Mathematics'), 22 | ), 23 | ]; 24 | @override 25 | _HomeState createState() => _HomeState(); 26 | } 27 | 28 | class _HomeState extends State with SingleTickerProviderStateMixin { 29 | TabController _categoryController; 30 | @override 31 | void initState() { 32 | super.initState(); 33 | _categoryController = TabController( 34 | vsync: this, 35 | length: 3, 36 | ); 37 | } 38 | 39 | @override 40 | void dispose() { 41 | _categoryController.dispose(); 42 | super.dispose(); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | final theme = Provider.of(context); 48 | return Scaffold( 49 | appBar: AppBar( 50 | centerTitle: true, 51 | title: Text( 52 | 'Simulate', 53 | style: Theme.of(context).textTheme.headline6, 54 | ), 55 | actions: [ 56 | IconButton( 57 | icon: Icon(Icons.search), 58 | onPressed: () { 59 | showSearch( 60 | context: context, 61 | delegate: SimulationSearch(), 62 | ); 63 | }, 64 | ), 65 | ], 66 | bottom: TabBar( 67 | controller: _categoryController, 68 | isScrollable: true, 69 | tabs: widget._categoryTabs, 70 | ), 71 | ), 72 | drawer: AppDrawer(), 73 | body: TabBarView( 74 | controller: _categoryController, 75 | children: [ 76 | HomePage(), 77 | AlgorithmsPage(), 78 | MathematicsPage(), 79 | ], 80 | ), 81 | ); 82 | } 83 | } 84 | 85 | class SimulationSearch extends SearchDelegate { 86 | @override 87 | ThemeData appBarTheme(BuildContext context) { 88 | return Theme.of(context); 89 | } 90 | 91 | @override 92 | List buildActions(BuildContext context) { 93 | return [ 94 | IconButton( 95 | icon: Icon(Icons.clear), 96 | onPressed: () { 97 | query = ''; 98 | }, 99 | ), 100 | ]; 101 | } 102 | 103 | @override 104 | Widget buildLeading(BuildContext context) { 105 | return IconButton( 106 | icon: Icon(Icons.arrow_back), 107 | onPressed: () { 108 | close(context, null); 109 | }, 110 | ); 111 | } 112 | 113 | @override 114 | Widget buildResults(BuildContext context) { 115 | return Container(); 116 | } 117 | 118 | @override 119 | Widget buildSuggestions(BuildContext context) { 120 | final appState = Provider.of(context); 121 | return (query != '') 122 | ? (appState.searchSims(query).length != 0) 123 | ? GridView.count( 124 | crossAxisCount: (MediaQuery.of(context).size.width < 600) 125 | ? 2 126 | : (MediaQuery.of(context).size.width / 200).floor(), 127 | children: appState.searchSims(query), 128 | ) 129 | : Container( 130 | child: Center( 131 | child: Text( 132 | "Sorry, couldn't find a simulation", 133 | style: Theme.of(context).textTheme.caption, 134 | ), 135 | ), 136 | ) 137 | : Container( 138 | child: Center( 139 | child: Text( 140 | 'Search for Simulations', 141 | style: Theme.of(context).textTheme.caption, 142 | ), 143 | ), 144 | ); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /lib/src/simulations/bubble_sort.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | 6 | class BubbleSortBars extends StatefulWidget { 7 | @override 8 | _BubbleSortBarsState createState() => _BubbleSortBarsState(); 9 | } 10 | 11 | class _BubbleSortBarsState extends State { 12 | int _numberOfElements; 13 | List _elements = []; 14 | int i = 0, counter = 0; 15 | int n; 16 | int tmp, delay = 0, delay2 = 0; 17 | bool swap = false; 18 | double barwidth; 19 | List containerList = []; 20 | bool doNotRefresh = false; 21 | int finalIterator = 0; 22 | final ScrollController _scrollController = ScrollController(); 23 | 24 | @override 25 | void initState() { 26 | _numberOfElements = 2; 27 | i = 0; 28 | counter = 0; 29 | swap = false; 30 | doNotRefresh = false; 31 | SystemChrome.setPreferredOrientations([ 32 | DeviceOrientation.portraitUp, 33 | DeviceOrientation.portraitDown, 34 | ]); 35 | super.initState(); 36 | } 37 | 38 | @override 39 | dispose() { 40 | SystemChrome.setPreferredOrientations([ 41 | DeviceOrientation.portraitUp, 42 | DeviceOrientation.portraitDown, 43 | DeviceOrientation.landscapeLeft, 44 | DeviceOrientation.landscapeRight, 45 | ]); 46 | super.dispose(); 47 | } 48 | 49 | _containerList() { 50 | containerList.clear(); 51 | if (!doNotRefresh) { 52 | _elements.clear(); 53 | i = 0; 54 | var rng = new Random(); 55 | for (int i = 0; i < _numberOfElements; i++) { 56 | _elements.add(rng.nextInt(400)); 57 | } 58 | n = _elements.length; 59 | } 60 | this.barwidth = MediaQuery.of(context).size.width / (_elements.length + 1); 61 | if (n != 1) { 62 | for (int k = 0; k < _elements.length; ++k) { 63 | if (k == i) { 64 | containerList.add(Container( 65 | color: Colors.red, 66 | height: _elements[k] + 0.5, 67 | width: barwidth, 68 | )); 69 | } else if (k == i - 1) { 70 | containerList.add(Container( 71 | color: Colors.blue, 72 | height: _elements[k] + 0.5, 73 | width: barwidth, 74 | )); 75 | } else { 76 | containerList.add(Container( 77 | color: Theme.of(context).primaryColor, 78 | height: _elements[k] + 0.5, 79 | width: barwidth, 80 | )); 81 | } 82 | } 83 | } else { 84 | containerList.clear(); 85 | finalIterator++; 86 | 87 | for (int k = 0; k < _elements.length; ++k) { 88 | if (k <= finalIterator) { 89 | containerList.add(Container( 90 | color: Colors.greenAccent[400], 91 | height: _elements[k] + 0.5, 92 | width: barwidth, 93 | )); 94 | } else { 95 | containerList.add(Container( 96 | color: Theme.of(context).primaryColor, 97 | height: _elements[k] + 0.5, 98 | width: barwidth, 99 | )); 100 | } 101 | } 102 | if (finalIterator == _elements.length) { 103 | finalIterator = 0; 104 | } 105 | } 106 | } 107 | 108 | nextStep() async { 109 | await Future.delayed(Duration(milliseconds: delay)); 110 | if (!doNotRefresh) return; 111 | if (this.mounted) { 112 | setState(() { 113 | if (n == 1) { 114 | swap = false; 115 | return; 116 | } 117 | counter++; 118 | if (i == n - 1) { 119 | i = 0; 120 | n--; 121 | } 122 | if (_elements[i] > _elements[i + 1]) { 123 | tmp = _elements[i]; 124 | _elements[i] = _elements[i + 1]; 125 | _elements[i + 1] = tmp; 126 | i++; 127 | } else { 128 | i++; 129 | } 130 | }); 131 | } 132 | } 133 | 134 | @override 135 | Widget build(BuildContext context) { 136 | _containerList(); 137 | if (swap == true || finalIterator != 0) { 138 | WidgetsBinding.instance.addPostFrameCallback((_) => nextStep()); 139 | } 140 | 141 | return LayoutBuilder( 142 | // ignore: missing_return 143 | builder: (_, BoxConstraints constraints) { 144 | if (constraints.maxWidth != 0) { 145 | ScreenUtil.init( 146 | constraints, 147 | context: context, 148 | designSize: Size(512.0, 1024.0), 149 | minTextAdapt: true, 150 | ); 151 | return Scaffold( 152 | appBar: AppBar( 153 | automaticallyImplyLeading: false, 154 | leading: IconButton( 155 | icon: Icon(Icons.arrow_back_ios), 156 | onPressed: () { 157 | Navigator.pop(context); 158 | }, 159 | ), 160 | centerTitle: true, 161 | title: Text( 162 | 'Bubble Sort', 163 | style: Theme.of(context).textTheme.headline6, 164 | ), 165 | ), 166 | floatingActionButton: FloatingActionButton( 167 | backgroundColor: Colors.white, 168 | child: (!swap) 169 | ? Icon( 170 | Icons.play_arrow, 171 | color: Colors.black, 172 | ) 173 | : Icon( 174 | Icons.pause, 175 | color: Colors.black, 176 | ), 177 | onPressed: () { 178 | doNotRefresh = true; 179 | swap = !swap; 180 | setState(() {}); 181 | }), 182 | floatingActionButtonLocation: 183 | FloatingActionButtonLocation.centerDocked, 184 | bottomNavigationBar: Container( 185 | height: ScreenUtil().setHeight(1024 / 5.5), 186 | child: Material( 187 | elevation: 30, 188 | color: Theme.of(context).primaryColor, 189 | child: Scrollbar( 190 | controller: _scrollController, 191 | isAlwaysShown: true, 192 | child: ListView( 193 | controller: _scrollController, 194 | padding: EdgeInsets.all(8.0), 195 | children: [ 196 | SizedBox( 197 | height: 20, 198 | ), 199 | Slider( 200 | min: 2, 201 | max: 200, 202 | activeColor: Theme.of(context).colorScheme.secondary, 203 | inactiveColor: Colors.grey, 204 | onChanged: (value) { 205 | doNotRefresh = false; 206 | counter = 0; 207 | swap = false; 208 | finalIterator = 0; 209 | setState(() { 210 | _numberOfElements = value.toInt(); 211 | }); 212 | }, 213 | value: _numberOfElements.toDouble(), 214 | ), 215 | Center( 216 | child: Text( 217 | "Elements: ${_numberOfElements.toInt()}", 218 | style: Theme.of(context).textTheme.subtitle2, 219 | ), 220 | ), 221 | SizedBox( 222 | height: 20, 223 | ), 224 | Slider( 225 | min: 0, 226 | max: 100, 227 | divisions: 10, 228 | activeColor: Theme.of(context).colorScheme.secondary, 229 | inactiveColor: Colors.grey, 230 | onChanged: (value) { 231 | setState(() { 232 | delay2 = value.toInt(); 233 | }); 234 | }, 235 | onChangeEnd: (value) { 236 | setState(() { 237 | doNotRefresh = true; 238 | delay = value.toInt(); 239 | }); 240 | }, 241 | value: delay2.roundToDouble(), 242 | ), 243 | Center( 244 | child: Text( 245 | "Delay: ${delay2.toInt()} ms", 246 | style: Theme.of(context).textTheme.subtitle2, 247 | ), 248 | ), 249 | ], 250 | ), 251 | ), 252 | ), 253 | ), 254 | body: Stack( 255 | children: [ 256 | Container( 257 | color: Colors.grey[900], 258 | child: Column( 259 | children: [ 260 | Spacer(), 261 | Row( 262 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 263 | children: containerList, 264 | ), 265 | Spacer(), 266 | ], 267 | ), 268 | ), 269 | Positioned( 270 | top: 5, 271 | left: 5, 272 | child: Text( 273 | "Comparisons: $counter \nMax: ${_elements[i]} \nArray Iteration: ${_elements.length - n + 1}", 274 | style: Theme.of(context).textTheme.subtitle2, 275 | ), 276 | ), 277 | ], 278 | ), 279 | ); 280 | } 281 | }, 282 | ); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /lib/src/simulations/epicycloid_curve.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | 6 | GlobalKey<_EpicycloidState> globalKey = GlobalKey<_EpicycloidState>(); 7 | 8 | class EpicycloidCurve extends StatefulWidget { 9 | @override 10 | _EpicycloidCurveState createState() => _EpicycloidCurveState(); 11 | } 12 | 13 | class _EpicycloidCurveState extends State { 14 | double factor = 0; 15 | double total = 0; 16 | bool animatefactor = false; 17 | bool animatepoints = false; 18 | bool animating = false; 19 | final ScrollController _scrollController = ScrollController(); 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | } 25 | 26 | @override 27 | void dispose() { 28 | super.dispose(); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return LayoutBuilder( 34 | // ignore: missing_return 35 | builder: (_, BoxConstraints constraints) { 36 | if (constraints.maxWidth != 0) { 37 | ScreenUtil.init( 38 | constraints, 39 | context: context, 40 | designSize: Size(434.0, 924.0), 41 | minTextAdapt: true, 42 | ); 43 | return Scaffold( 44 | appBar: AppBar( 45 | automaticallyImplyLeading: false, 46 | leading: IconButton( 47 | icon: Icon(Icons.arrow_back_ios), 48 | onPressed: () { 49 | Navigator.pop(context); 50 | }, 51 | ), 52 | title: Text( 53 | 'Epicycloid Pattern (Pencil of Lines)', 54 | style: Theme.of(context).textTheme.headline6, 55 | ), 56 | centerTitle: true, 57 | ), 58 | floatingActionButtonLocation: 59 | FloatingActionButtonLocation.centerDocked, 60 | floatingActionButton: Padding( 61 | padding: const EdgeInsets.all(8.0), 62 | child: Visibility( 63 | visible: animatefactor || animatepoints, 64 | child: Row( 65 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 66 | children: [ 67 | FloatingActionButton( 68 | heroTag: null, 69 | backgroundColor: Colors.white, 70 | child: (!animating) 71 | ? Icon( 72 | Icons.play_arrow, 73 | color: Colors.black, 74 | ) 75 | : Icon( 76 | Icons.pause, 77 | color: Colors.black, 78 | ), 79 | onPressed: () { 80 | setState(() { 81 | animating = !animating; 82 | factor = globalKey.currentState.widget.factor; 83 | total = globalKey.currentState.widget.total; 84 | }); 85 | }), 86 | FloatingActionButton( 87 | heroTag: null, 88 | child: Icon( 89 | Icons.replay, 90 | color: Colors.black, 91 | ), 92 | backgroundColor: Colors.white, 93 | onPressed: () { 94 | setState(() { 95 | if (animatefactor) { 96 | factor = 0; 97 | } 98 | if (animatepoints) { 99 | total = 0; 100 | } 101 | }); 102 | }, 103 | ) 104 | ], 105 | ), 106 | ), 107 | ), 108 | bottomNavigationBar: Visibility( 109 | visible: !isLandscape(), 110 | child: parameters( 111 | context, 112 | ScreenUtil().setHeight(924 / 5), 113 | ), 114 | ), 115 | body: Row( 116 | children: [ 117 | Container( 118 | width: isLandscape() 119 | ? 2 * MediaQuery.of(context).size.width / 3 120 | : MediaQuery.of(context).size.width, 121 | child: Stack( 122 | children: [ 123 | Epicycloid( 124 | factor: factor, 125 | total: total, 126 | animatefactor: animatefactor, 127 | animatepoints: animatepoints, 128 | animating: animating, 129 | key: globalKey, 130 | isLandscape: isLandscape(), 131 | ), 132 | Positioned( 133 | left: 12, 134 | child: Row( 135 | mainAxisAlignment: MainAxisAlignment.end, 136 | children: [ 137 | Text( 138 | 'Animate with Factor:', 139 | ), 140 | Checkbox( 141 | onChanged: (animating) 142 | ? null 143 | : (_) { 144 | setState(() { 145 | animatefactor = !animatefactor; 146 | }); 147 | }, 148 | activeColor: Colors.red, 149 | value: animatefactor, 150 | ), 151 | ], 152 | ), 153 | ), 154 | Positioned( 155 | child: Row( 156 | mainAxisAlignment: MainAxisAlignment.end, 157 | children: [ 158 | Text( 159 | 'Animate with Points:', 160 | ), 161 | Checkbox( 162 | onChanged: (animating) 163 | ? null 164 | : (_) { 165 | setState(() { 166 | animatepoints = !animatepoints; 167 | }); 168 | }, 169 | activeColor: Colors.red, 170 | value: animatepoints, 171 | ), 172 | ], 173 | ), 174 | ), 175 | ], 176 | ), 177 | ), 178 | Visibility( 179 | visible: isLandscape(), 180 | child: Expanded( 181 | child: parameters( 182 | context, 183 | MediaQuery.of(context).size.height, 184 | ), 185 | ), 186 | ), 187 | ], 188 | ), 189 | ); 190 | } 191 | }, 192 | ); 193 | } 194 | 195 | bool isLandscape() { 196 | return MediaQuery.of(context).size.width > 197 | MediaQuery.of(context).size.height; 198 | } 199 | 200 | Container parameters(BuildContext context, num height) { 201 | return Container( 202 | height: height, 203 | child: Material( 204 | elevation: 30, 205 | color: Theme.of(context).primaryColor, 206 | child: Scrollbar( 207 | controller: _scrollController, 208 | isAlwaysShown: true, 209 | child: ListView( 210 | controller: _scrollController, 211 | padding: EdgeInsets.all(8.0), 212 | children: [ 213 | SizedBox( 214 | height: 20, 215 | ), 216 | Slider( 217 | min: 0, 218 | max: 500, 219 | divisions: 500, 220 | activeColor: Theme.of(context).colorScheme.secondary, 221 | inactiveColor: Colors.grey, 222 | onChanged: (animating) 223 | ? null 224 | : (value) { 225 | setState(() { 226 | total = double.parse(value.toStringAsFixed(1)); 227 | }); 228 | }, 229 | value: total, 230 | ), 231 | Center( 232 | child: Text( 233 | (animating && animatepoints) 234 | ? "Points: Animating" 235 | : "Points: ${total.toInt()}", 236 | style: Theme.of(context).textTheme.subtitle2, 237 | ), 238 | ), 239 | Slider( 240 | min: 0, 241 | max: 51, 242 | divisions: 510, 243 | activeColor: Theme.of(context).colorScheme.secondary, 244 | inactiveColor: Colors.grey, 245 | onChanged: (animating) 246 | ? null 247 | : (value) { 248 | setState(() { 249 | factor = double.parse(value.toStringAsFixed(1)); 250 | }); 251 | }, 252 | value: factor, 253 | ), 254 | Center( 255 | child: Text( 256 | (animating && animatefactor) 257 | ? "Factor: Animating" 258 | : "Factor: ${factor.toStringAsFixed(1)}", 259 | style: Theme.of(context).textTheme.subtitle2, 260 | ), 261 | ), 262 | ], 263 | ), 264 | ), 265 | ), 266 | ); 267 | } 268 | } 269 | 270 | class Epicycloid extends StatefulWidget { 271 | Epicycloid({ 272 | Key key, 273 | @required this.factor, 274 | @required this.total, 275 | @required this.animatefactor, 276 | @required this.animatepoints, 277 | @required this.animating, 278 | @required this.isLandscape, 279 | }) : super(key: key); 280 | 281 | double factor; 282 | double total; 283 | final bool animatefactor; 284 | final bool animatepoints; 285 | final bool animating; 286 | final bool isLandscape; 287 | 288 | @override 289 | _EpicycloidState createState() => _EpicycloidState(); 290 | } 291 | 292 | class _EpicycloidState extends State { 293 | nextStep() async { 294 | await Future.delayed(Duration(milliseconds: 10)); 295 | if (this.mounted) { 296 | setState(() { 297 | if (widget.animatefactor) { 298 | widget.factor += 0.01; 299 | } 300 | if (widget.animatepoints) { 301 | widget.total += 0.3; 302 | } 303 | if (widget.factor > 51) { 304 | widget.factor = 0; 305 | } 306 | if (widget.total > 500) { 307 | widget.total = 0; 308 | } 309 | }); 310 | } 311 | } 312 | 313 | @override 314 | Widget build(BuildContext context) { 315 | if (widget.animating) { 316 | WidgetsBinding.instance.addPostFrameCallback((_) { 317 | nextStep(); 318 | }); 319 | } 320 | return Stack( 321 | alignment: Alignment.center, 322 | children: [ 323 | Transform.scale( 324 | scale: widget.isLandscape ? 0.7 : 1, 325 | child: CustomPaint( 326 | painter: EpicycloidPainter( 327 | widget.factor, 328 | widget.total, 329 | (widget.isLandscape 330 | ? MediaQuery.of(context).size.width / 5 331 | : MediaQuery.of(context).size.width / 2.4) 332 | .roundToDouble(), 333 | (widget.isLandscape 334 | ? MediaQuery.of(context).size.width / 3 335 | : MediaQuery.of(context).size.width / 2) 336 | .roundToDouble(), 337 | (MediaQuery.of(context).size.height / 3).roundToDouble(), 338 | Theme.of(context).colorScheme.secondary), 339 | child: Container(), 340 | ), 341 | ), 342 | Visibility( 343 | visible: widget.animatepoints, 344 | child: Positioned( 345 | right: 15, 346 | top: 40, 347 | child: Text("Points: ${widget.total.toInt()}"), 348 | ), 349 | ), 350 | Visibility( 351 | visible: widget.animatefactor, 352 | child: Positioned( 353 | left: 12, 354 | top: 40, 355 | child: Text("Factor: ${widget.factor.toStringAsFixed(1)}"), 356 | ), 357 | ), 358 | ], 359 | ); 360 | } 361 | } 362 | 363 | class EpicycloidPainter extends CustomPainter { 364 | List points = []; 365 | double total, factor; 366 | double radius, tx, ty; 367 | Color color; 368 | 369 | EpicycloidPainter( 370 | this.factor, 371 | this.total, 372 | this.radius, 373 | this.tx, 374 | this.ty, 375 | this.color, 376 | ); 377 | 378 | @override 379 | void paint(Canvas canvas, Size size) { 380 | var paint = Paint(); 381 | paint.color = Colors.red; 382 | paint.strokeWidth = 3; 383 | paint.style = PaintingStyle.stroke; 384 | 385 | var paint2 = Paint(); 386 | paint2.color = color; 387 | paint2.strokeWidth = 1; 388 | double x = 2 * pi / total; 389 | double angle = pi; 390 | 391 | for (int i = 0; i < total; ++i) { 392 | points.add( 393 | Offset(radius * cos(angle), radius * sin(angle)).translate(tx, ty)); 394 | angle = angle + x; 395 | } 396 | for (double i = 0; i < total; i += 1) { 397 | canvas.drawLine(points[(i % total).toInt()], 398 | points[((i * factor) % total).toInt()], paint2); 399 | } 400 | canvas.drawCircle(Offset(tx, ty), radius, paint); 401 | } 402 | 403 | @override 404 | bool shouldRepaint(EpicycloidPainter oldDelegate) => true; 405 | 406 | @override 407 | bool shouldRebuildSemantics(EpicycloidPainter oldDelegate) => false; 408 | } 409 | -------------------------------------------------------------------------------- /lib/src/simulations/fourier_series.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 6 | 7 | List ys = []; 8 | 9 | class FourierSeries extends StatefulWidget { 10 | @override 11 | _FourierSeriesState createState() => _FourierSeriesState(); 12 | } 13 | 14 | class _FourierSeriesState extends State { 15 | bool control = true; 16 | double time = 0; 17 | double radius = 100; 18 | int _n = 1; 19 | double f = 0.01; 20 | String wave = 'Square Wave'; 21 | int delay = 0; 22 | 23 | @override 24 | void initState() { 25 | SystemChrome.setPreferredOrientations([ 26 | DeviceOrientation.landscapeLeft, 27 | DeviceOrientation.landscapeRight, 28 | ]); 29 | super.initState(); 30 | } 31 | 32 | @override 33 | void dispose() { 34 | ys.clear(); 35 | time = 0; 36 | SystemChrome.setPreferredOrientations([ 37 | DeviceOrientation.portraitUp, 38 | DeviceOrientation.portraitDown, 39 | DeviceOrientation.landscapeLeft, 40 | DeviceOrientation.landscapeRight, 41 | ]); 42 | super.dispose(); 43 | } 44 | 45 | update() { 46 | if (control == true) { 47 | setState(() { 48 | time -= f; 49 | if (ys.length == ScreenUtil().uiSize.width.toInt()) { 50 | ys.removeLast(); 51 | } 52 | }); 53 | } 54 | } 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | WidgetsBinding.instance.addPostFrameCallback((_) => update()); 59 | return LayoutBuilder( 60 | // ignore: missing_return 61 | builder: (_, BoxConstraints constraints) { 62 | if (constraints.maxWidth != 0) { 63 | ScreenUtil.init( 64 | constraints, 65 | context: context, 66 | designSize: Size(1024.0, 512.0), 67 | minTextAdapt: true, 68 | ); 69 | return Scaffold( 70 | appBar: AppBar( 71 | automaticallyImplyLeading: false, 72 | leading: IconButton( 73 | icon: Icon(Icons.arrow_back_ios), 74 | onPressed: () { 75 | Navigator.pop(context); 76 | }, 77 | ), 78 | centerTitle: true, 79 | title: Text( 80 | 'Fourier Series', 81 | style: Theme.of(context).textTheme.headline6, 82 | ), 83 | ), 84 | body: Row( 85 | children: [ 86 | Container( 87 | width: 2 * ScreenUtil().setWidth(1024 / 3), 88 | child: Transform.translate( 89 | offset: Offset( 90 | radius, (4 * MediaQuery.of(context).size.height) / 10), 91 | child: CustomPaint( 92 | painter: FourierPainter(radius, time, _n, wave, context), 93 | child: Container(), 94 | ), 95 | ), 96 | ), 97 | Expanded( 98 | child: Container( 99 | child: Material( 100 | elevation: 30, 101 | color: Theme.of(context).primaryColor, 102 | child: Column( 103 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 104 | children: [ 105 | DropdownButton( 106 | value: wave, 107 | iconSize: 30, 108 | style: Theme.of(context).textTheme.subtitle2, 109 | items: [ 110 | DropdownMenuItem( 111 | value: 'Square Wave', 112 | child: Text('Square Wave'), 113 | ), 114 | DropdownMenuItem( 115 | value: 'SawTooth Wave', 116 | child: Text('SawTooth Wave'), 117 | ), 118 | ], 119 | onChanged: (value) { 120 | wave = value; 121 | }, 122 | ), 123 | Slider( 124 | min: 1, 125 | max: 100, 126 | activeColor: 127 | Theme.of(context).colorScheme.secondary, 128 | inactiveColor: Colors.grey, 129 | onChanged: (value) { 130 | setState(() { 131 | _n = value.toInt(); 132 | }); 133 | }, 134 | value: _n.toDouble(), 135 | ), 136 | Center( 137 | child: Text( 138 | "N: ${_n.toInt()}", 139 | style: Theme.of(context).textTheme.subtitle2, 140 | ), 141 | ), 142 | Slider( 143 | min: 10, 144 | max: 200, 145 | activeColor: 146 | Theme.of(context).colorScheme.secondary, 147 | inactiveColor: Colors.grey, 148 | onChanged: (value) { 149 | setState(() { 150 | radius = value.roundToDouble(); 151 | }); 152 | }, 153 | value: radius, 154 | ), 155 | Center( 156 | child: Text( 157 | "Amplitude: ${radius.toInt()}", 158 | style: Theme.of(context).textTheme.subtitle2, 159 | ), 160 | ), 161 | Slider( 162 | min: 0, 163 | max: 0.3, 164 | activeColor: 165 | Theme.of(context).colorScheme.secondary, 166 | inactiveColor: Colors.grey, 167 | onChanged: (value) { 168 | setState(() { 169 | f = value; 170 | }); 171 | }, 172 | value: f, 173 | ), 174 | Center( 175 | child: Text( 176 | "- Frequency +", 177 | style: Theme.of(context).textTheme.subtitle2, 178 | ), 179 | ), 180 | ], 181 | ), 182 | ), 183 | ), 184 | ), 185 | ], 186 | ), 187 | ); 188 | } 189 | }, 190 | ); 191 | } 192 | } 193 | 194 | class FourierPainter extends CustomPainter { 195 | double radius; 196 | double time, r; 197 | Offset coor = new Offset(0, 0); 198 | Offset prevco; 199 | int n, _n; 200 | String wave; 201 | BuildContext context; 202 | List points = []; 203 | 204 | FourierPainter(this.r, this.time, this._n, this.wave, this.context); 205 | 206 | @override 207 | void paint(Canvas canvas, Size size) { 208 | Paint paint = new Paint(); 209 | paint.color = Theme.of(context).colorScheme.secondary; 210 | paint.style = PaintingStyle.stroke; 211 | paint.strokeWidth = 2; 212 | for (int i = 0; i < _n; i++) { 213 | prevco = coor; 214 | if (wave == 'Square Wave') { 215 | n = i * 2 + 1; 216 | radius = r * (2 / (n * pi)); 217 | coor += Offset((radius * cos(n * time)), (radius * sin(n * time))); 218 | } 219 | if (wave == 'SawTooth Wave') { 220 | n = i + 1; 221 | radius = r * (2 / (n * pi)); 222 | coor += Offset((radius * cos(n * time)), (radius * sin(n * time))); 223 | } 224 | canvas.drawCircle(prevco, radius.toDouble(), paint); 225 | canvas.drawLine(prevco, coor, paint); 226 | prevco = coor; 227 | } 228 | 229 | ys.insert(0, coor.dy); 230 | paint.color = Colors.red; 231 | canvas.drawLine(coor, Offset(r, ys[0]), paint); 232 | int iterator = 0; 233 | paint.color = Theme.of(context).colorScheme.secondary; 234 | ys.forEach((value) { 235 | points.add(Offset(iterator.toDouble() + r, value)); 236 | iterator++; 237 | }); 238 | 239 | canvas.drawPoints(PointMode.polygon, points, paint); 240 | } 241 | 242 | @override 243 | bool shouldRepaint(FourierPainter oldDelegate) => true; 244 | 245 | @override 246 | bool shouldRebuildSemantics(FourierPainter oldDelegate) => false; 247 | } 248 | -------------------------------------------------------------------------------- /lib/src/simulations/insertion_sort.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:io'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 6 | 7 | int sliderValue = 2, 8 | iterator = -1, 9 | i = -1, 10 | numSteps = 0, 11 | sleepDuration = 70, 12 | greenIterator = 0; 13 | bool isWorking = false, 14 | doNotRefresh = true, 15 | colorGreen = false, 16 | wasAlreadyWorking = false; 17 | 18 | class InsertionHome extends StatefulWidget { 19 | _InsertionHomeState createState() => _InsertionHomeState(); 20 | } 21 | 22 | class _InsertionHomeState extends State { 23 | var randomVar = Random(); 24 | List barValuesList = []; 25 | List barsList = []; 26 | final ScrollController _scrollController = ScrollController(); 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | SystemChrome.setPreferredOrientations([ 32 | DeviceOrientation.portraitUp, 33 | DeviceOrientation.portraitDown, 34 | ]); 35 | } 36 | 37 | makeContainers() { 38 | if (!doNotRefresh || barValuesList.length == 0) { 39 | barValuesList = List.generate( 40 | sliderValue.toInt(), 41 | (idx) => 42 | randomVar.nextInt(MediaQuery.of(context).size.height ~/ 1.7)); 43 | } else 44 | doNotRefresh = false; 45 | int temp = 0; 46 | barsList.clear(); 47 | greenIterator = 0; 48 | barValuesList.forEach((value) { 49 | barsList.add( 50 | Container( 51 | width: MediaQuery.of(context).size.width / sliderValue * 0.9, 52 | height: (value != 0) ? value.toDouble() : 0.5, 53 | color: (temp == iterator) 54 | ? Colors.red 55 | : (temp != i) 56 | ? Theme.of(context).primaryColor 57 | : (i != barValuesList.length - 1) 58 | ? Colors.blue 59 | : Theme.of(context).primaryColor, 60 | ), 61 | ); 62 | ++temp; 63 | }); 64 | sortBars(); 65 | } 66 | 67 | makeGreen() { 68 | setState(() { 69 | if (greenIterator < sliderValue) { 70 | int value = barValuesList[greenIterator]; 71 | barsList.removeAt(greenIterator); 72 | barsList.insert( 73 | greenIterator, 74 | Container( 75 | width: MediaQuery.of(context).size.width / sliderValue * 0.9, 76 | height: (value != 0) ? value.toDouble() : 0.5, 77 | color: Colors.greenAccent[400], 78 | ), 79 | ); 80 | } 81 | ++greenIterator; 82 | }); 83 | } 84 | 85 | sortBars() async { 86 | await Future.delayed(Duration(milliseconds: sleepDuration)); 87 | setState(() { 88 | if (!isWorking) return; 89 | colorGreen = false; 90 | if (iterator < barValuesList.length) { 91 | for (i = 0; i < iterator; i++) { 92 | ++numSteps; 93 | if (barValuesList[i] > barValuesList[iterator]) { 94 | int temp = barValuesList[iterator]; 95 | barValuesList.removeAt(iterator); 96 | barValuesList.insert(i, temp); 97 | break; 98 | } 99 | } 100 | ++iterator; 101 | } else if (iterator == barValuesList.length) { 102 | i = barValuesList.length - 1; 103 | ++iterator; 104 | } else { 105 | colorGreen = true; 106 | isWorking = false; 107 | } 108 | }); 109 | } 110 | 111 | @override 112 | Widget build(BuildContext context) { 113 | if (!colorGreen) 114 | makeContainers(); 115 | else 116 | makeGreen(); 117 | WidgetsBinding.instance.addPostFrameCallback((_) => setState(() { 118 | doNotRefresh = true; 119 | })); 120 | return LayoutBuilder( 121 | // ignore: missing_return 122 | builder: (_, BoxConstraints constraints) { 123 | if (constraints.maxWidth != 0) { 124 | ScreenUtil.init( 125 | constraints, 126 | context: context, 127 | designSize: Size(512.0, 1024.0), 128 | minTextAdapt: true, 129 | ); 130 | return Scaffold( 131 | appBar: AppBar( 132 | leading: IconButton( 133 | icon: Icon(Icons.arrow_back_ios), 134 | onPressed: () => Navigator.pop(context), 135 | ), 136 | elevation: 5, 137 | centerTitle: true, 138 | title: Text( 139 | "Insertion Sort", 140 | style: Theme.of(context).textTheme.headline6, 141 | ), 142 | ), 143 | body: Stack( 144 | children: [ 145 | Container( 146 | color: Colors.grey[900], 147 | child: Column( 148 | children: [ 149 | Spacer(), 150 | Row( 151 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 152 | children: barsList, 153 | ), 154 | Spacer(), 155 | ], 156 | ), 157 | ), 158 | Positioned( 159 | top: 5, 160 | left: 5, 161 | child: Text( 162 | "Counter: $numSteps", 163 | style: TextStyle( 164 | color: Colors.white, 165 | fontFamily: 'Ubuntu', 166 | ), 167 | ), 168 | ), 169 | ], 170 | ), 171 | bottomNavigationBar: Container( 172 | color: Colors.transparent, 173 | height: ScreenUtil().setHeight(1024 / 5.5), 174 | child: Material( 175 | elevation: 30, 176 | color: Theme.of(context).primaryColor, 177 | child: Scrollbar( 178 | controller: _scrollController, 179 | isAlwaysShown: true, 180 | child: ListView( 181 | controller: _scrollController, 182 | padding: EdgeInsets.all(8.0), 183 | children: [ 184 | SizedBox( 185 | height: 20, 186 | ), 187 | Slider( 188 | min: 2, 189 | max: 149, 190 | activeColor: Theme.of(context).colorScheme.secondary, 191 | inactiveColor: Colors.grey, 192 | onChanged: (value) { 193 | setState(() { 194 | colorGreen = false; 195 | isWorking = false; 196 | doNotRefresh = false; 197 | sliderValue = value.toInt(); 198 | iterator = -1; 199 | i = -1; 200 | numSteps = 0; 201 | }); 202 | }, 203 | value: sliderValue.toDouble(), 204 | ), 205 | Center( 206 | child: Text( 207 | "Elements: $sliderValue", 208 | style: Theme.of(context).textTheme.subtitle2, 209 | ), 210 | ), 211 | Slider( 212 | min: 0, 213 | max: 500, 214 | activeColor: Theme.of(context).colorScheme.secondary, 215 | inactiveColor: Colors.grey, 216 | onChangeStart: (value) { 217 | setState(() { 218 | if (isWorking) wasAlreadyWorking = true; 219 | doNotRefresh = true; 220 | isWorking = false; 221 | }); 222 | }, 223 | onChanged: (value) { 224 | setState(() { 225 | doNotRefresh = true; 226 | sleepDuration = value.toInt(); 227 | }); 228 | }, 229 | onChangeEnd: (value) { 230 | setState(() { 231 | doNotRefresh = true; 232 | if (wasAlreadyWorking) { 233 | isWorking = true; 234 | wasAlreadyWorking = false; 235 | } 236 | sleepDuration = value.toInt(); 237 | }); 238 | }, 239 | value: sleepDuration.toDouble(), 240 | ), 241 | Center( 242 | child: Text( 243 | "Delay (milliseconds): $sleepDuration", 244 | style: Theme.of(context).textTheme.subtitle2, 245 | ), 246 | ), 247 | ], 248 | ), 249 | ), 250 | ), 251 | ), 252 | floatingActionButton: FloatingActionButton( 253 | onPressed: () { 254 | setState(() { 255 | doNotRefresh = true; 256 | isWorking = !isWorking; 257 | (iterator == -1) ? iterator = 1 : iterator = iterator; 258 | }); 259 | }, 260 | child: (!isWorking || colorGreen) 261 | ? Icon( 262 | Icons.play_arrow, 263 | size: 30, 264 | color: Colors.black, 265 | ) 266 | : Icon( 267 | Icons.pause, 268 | size: 30, 269 | color: Colors.black, 270 | ), 271 | backgroundColor: Colors.white, 272 | elevation: 8, 273 | ), 274 | floatingActionButtonLocation: 275 | FloatingActionButtonLocation.centerDocked, 276 | ); 277 | } 278 | }, 279 | ); 280 | } 281 | 282 | @override 283 | void dispose() { 284 | sliderValue = 2; 285 | iterator = -1; 286 | i = -1; 287 | numSteps = 0; 288 | isWorking = false; 289 | doNotRefresh = true; 290 | sleepDuration = 70; 291 | greenIterator = 0; 292 | colorGreen = false; 293 | wasAlreadyWorking = false; 294 | SystemChrome.setPreferredOrientations([ 295 | DeviceOrientation.portraitUp, 296 | DeviceOrientation.portraitDown, 297 | DeviceOrientation.landscapeLeft, 298 | DeviceOrientation.landscapeRight, 299 | ]); 300 | super.dispose(); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /lib/src/simulations/langton_ant.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | final size = 29; 5 | var colorsList = 6 | List>.generate(size, (i) => List.generate(size, (j) => 0)); 7 | enum direction { up, right, down, left } 8 | direction headDir = direction.up; 9 | var x = size ~/ 2, y = size ~/ 2; 10 | var setupX = 0, setupY = 0; 11 | int steps = 0; 12 | bool shouldWork = false; 13 | 14 | class LangtonAnt extends StatefulWidget { 15 | _LangtonAntState createState() => _LangtonAntState(); 16 | } 17 | 18 | class _LangtonAntState extends State { 19 | _LangtonAntState() { 20 | colorsList[size ~/ 2][size ~/ 2] = 1; 21 | } 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | SystemChrome.setPreferredOrientations([ 27 | DeviceOrientation.portraitUp, 28 | DeviceOrientation.portraitDown, 29 | ]); 30 | } 31 | 32 | void nextPixel() { 33 | setState(() { 34 | ++steps; 35 | if (colorsList[x][y] == 0) { 36 | colorsList[x][y] = 1; 37 | if (headDir == direction.up) { 38 | if (x == 0) 39 | x = size - 1; 40 | else 41 | --x; 42 | } else if (headDir == direction.right) { 43 | if (y == 0) 44 | y = size - 1; 45 | else 46 | --y; 47 | } else if (headDir == direction.down) { 48 | if (x == size - 1) 49 | x = 0; 50 | else 51 | ++x; 52 | } else { 53 | if (y == size - 1) 54 | y = 0; 55 | else 56 | ++y; 57 | } 58 | headDir = direction.values[(direction.values.indexOf(headDir) - 1) % 4]; 59 | } else { 60 | colorsList[x][y] = 0; 61 | if (headDir == direction.up) { 62 | if (x == size - 1) 63 | x = 0; 64 | else 65 | ++x; 66 | } else if (headDir == direction.right) { 67 | if (y == size - 1) 68 | y = 0; 69 | else 70 | ++y; 71 | } else if (headDir == direction.down) { 72 | if (x == 0) 73 | x = size - 1; 74 | else 75 | --x; 76 | } else { 77 | if (y == 0) 78 | y = size - 1; 79 | else 80 | --y; 81 | } 82 | headDir = direction.values[(direction.values.indexOf(headDir) + 1) % 4]; 83 | } 84 | }); 85 | } 86 | 87 | @override 88 | Widget build(BuildContext context) { 89 | if (shouldWork) 90 | WidgetsBinding.instance.addPostFrameCallback((_) => nextPixel()); 91 | setupX = 0; 92 | setupY = 0; 93 | return Scaffold( 94 | backgroundColor: Colors.grey[300], 95 | appBar: AppBar( 96 | automaticallyImplyLeading: false, 97 | leading: IconButton( 98 | icon: Icon(Icons.arrow_back_ios), 99 | onPressed: () { 100 | Navigator.pop(context); 101 | }, 102 | ), 103 | title: Text( 104 | "Langton's Ant", 105 | style: TextStyle( 106 | color: Colors.black, 107 | fontFamily: 'Ubuntu', 108 | fontSize: 20, 109 | ), 110 | ), 111 | iconTheme: IconThemeData( 112 | color: Colors.black, 113 | ), 114 | backgroundColor: Colors.white, 115 | centerTitle: true, 116 | ), 117 | body: Stack( 118 | children: [ 119 | Align( 120 | alignment: Alignment(0, -0.2), 121 | child: Container( 122 | alignment: Alignment.center, 123 | height: MediaQuery.of(context).size.height / 2, 124 | child: GridView.builder( 125 | physics: NeverScrollableScrollPhysics(), 126 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 127 | crossAxisCount: size, 128 | ), 129 | itemCount: size * size, 130 | itemBuilder: buildItem, 131 | padding: EdgeInsets.all(5.0), 132 | ), 133 | ), 134 | ), 135 | Align( 136 | alignment: Alignment(0, -0.85), 137 | child: Text( 138 | "Steps: $steps", 139 | style: TextStyle( 140 | color: Colors.black, 141 | fontFamily: 'Ubuntu', 142 | fontSize: 18, 143 | ), 144 | ), 145 | ), 146 | ], 147 | ), 148 | floatingActionButton: FloatingActionButton( 149 | backgroundColor: Colors.white, 150 | child: Icon( 151 | (!shouldWork) ? Icons.play_arrow : Icons.pause, 152 | color: Colors.black, 153 | size: 24, 154 | ), 155 | onPressed: () => setState(() { 156 | shouldWork = !shouldWork; 157 | }), 158 | ), 159 | floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, 160 | ); 161 | } 162 | 163 | Widget buildItem(BuildContext context, int index) { 164 | if (colorsList[setupX][setupY] == 1) { 165 | if (setupY == size - 1) { 166 | setupY = 0; 167 | ++setupX; 168 | } else 169 | ++setupY; 170 | return GridTile( 171 | child: Container( 172 | decoration: BoxDecoration( 173 | border: Border.all( 174 | color: Colors.black, 175 | width: 0.8, 176 | ), 177 | color: Colors.white, 178 | ), 179 | ), 180 | ); 181 | } else { 182 | if (setupY == size - 1) { 183 | setupY = 0; 184 | ++setupX; 185 | } else 186 | ++setupY; 187 | return GridTile( 188 | child: Container( 189 | decoration: BoxDecoration( 190 | border: Border.all( 191 | color: Colors.white, 192 | width: 0.8, 193 | ), 194 | color: Colors.black, 195 | ), 196 | ), 197 | ); 198 | } 199 | } 200 | 201 | @override 202 | void dispose() { 203 | SystemChrome.setPreferredOrientations([ 204 | DeviceOrientation.portraitUp, 205 | DeviceOrientation.portraitDown, 206 | DeviceOrientation.landscapeLeft, 207 | DeviceOrientation.landscapeRight, 208 | ]); 209 | colorsList = List>.generate( 210 | size, (i) => List.generate(size, (j) => 0)); 211 | headDir = direction.up; 212 | x = size ~/ 2; 213 | y = size ~/ 2; 214 | steps = 0; 215 | shouldWork = false; 216 | super.dispose(); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /lib/src/simulations/lissajous_curve.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | 6 | GlobalKey<_LissajousState> globalKey = GlobalKey<_LissajousState>(); 7 | 8 | class LissajousCurve extends StatefulWidget { 9 | @override 10 | _LissajousCurveState createState() => _LissajousCurveState(); 11 | } 12 | 13 | class _LissajousCurveState extends State { 14 | double _a = 0; 15 | double _b = 0; 16 | double k = 0; 17 | double delta = 0; 18 | bool animate = false; 19 | bool animating = false; 20 | double thickness = 2; 21 | final ScrollController _scrollController = ScrollController(); 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | } 27 | 28 | @override 29 | void dispose() { 30 | super.dispose(); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return LayoutBuilder( 36 | // ignore: missing_return 37 | builder: (_, BoxConstraints constraints) { 38 | if (constraints.maxWidth != 0) { 39 | ScreenUtil.init( 40 | constraints, 41 | context: context, 42 | designSize: Size(512.0, 1024.0), 43 | minTextAdapt: true, 44 | ); 45 | return Scaffold( 46 | appBar: AppBar( 47 | automaticallyImplyLeading: false, 48 | leading: IconButton( 49 | icon: Icon(Icons.arrow_back_ios), 50 | onPressed: () { 51 | Navigator.pop(context); 52 | }, 53 | ), 54 | title: Text( 55 | 'Lissajous Pattern', 56 | style: Theme.of(context).textTheme.headline6, 57 | ), 58 | centerTitle: true, 59 | ), 60 | floatingActionButtonLocation: 61 | FloatingActionButtonLocation.centerDocked, 62 | floatingActionButton: Padding( 63 | padding: const EdgeInsets.all(8.0), 64 | child: Visibility( 65 | visible: animate, 66 | child: Row( 67 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 68 | children: [ 69 | FloatingActionButton( 70 | heroTag: null, 71 | backgroundColor: Colors.white, 72 | child: (!animating) 73 | ? Icon( 74 | Icons.play_arrow, 75 | color: Colors.black, 76 | ) 77 | : Icon( 78 | Icons.pause, 79 | color: Colors.black, 80 | ), 81 | onPressed: () { 82 | setState(() { 83 | animating = !animating; 84 | }); 85 | }), 86 | FloatingActionButton( 87 | heroTag: null, 88 | child: Icon( 89 | Icons.highlight_off, 90 | color: Colors.black, 91 | ), 92 | backgroundColor: Colors.white, 93 | onPressed: () { 94 | setState(() { 95 | globalKey.currentState.clearscreen(); 96 | }); 97 | }, 98 | ) 99 | ], 100 | ), 101 | ), 102 | ), 103 | bottomNavigationBar: Visibility( 104 | visible: !isLandscape(), 105 | child: parameters( 106 | context, 107 | ScreenUtil().setHeight(1024 / 5), 108 | ), 109 | ), 110 | body: Row( 111 | children: [ 112 | Container( 113 | width: isLandscape() 114 | ? 2 * MediaQuery.of(context).size.width / 3 115 | : MediaQuery.of(context).size.width, 116 | child: Stack( 117 | children: [ 118 | Lissajous( 119 | b: _b, 120 | a: _a, 121 | delta: delta, 122 | animate: animate, 123 | animating: animating, 124 | key: globalKey, 125 | thickness: thickness, 126 | isLandscape: isLandscape(), 127 | ), 128 | Positioned( 129 | top: 5, 130 | left: 5, 131 | child: Text( 132 | 'A:B ~ ${(_a / _b).toStringAsFixed(2)}', 133 | style: Theme.of(context).textTheme.subtitle2, 134 | ), 135 | ), 136 | Positioned( 137 | top: 0, 138 | right: 0, 139 | child: Row( 140 | mainAxisAlignment: MainAxisAlignment.end, 141 | children: [ 142 | Text('Animate: '), 143 | Checkbox( 144 | onChanged: (_) { 145 | setState(() { 146 | animate = !animate; 147 | if (animating) { 148 | animating = (animating && animate); 149 | } 150 | }); 151 | }, 152 | value: animate, 153 | activeColor: Colors.red, 154 | ), 155 | ], 156 | ), 157 | ) 158 | ], 159 | ), 160 | ), 161 | Visibility( 162 | visible: isLandscape(), 163 | child: Expanded( 164 | child: parameters( 165 | context, 166 | MediaQuery.of(context).size.height, 167 | ), 168 | ), 169 | ), 170 | ], 171 | ), 172 | ); 173 | } 174 | }, 175 | ); 176 | } 177 | 178 | bool isLandscape() { 179 | return MediaQuery.of(context).size.width > 180 | MediaQuery.of(context).size.height; 181 | } 182 | 183 | Container parameters(BuildContext context, num height) { 184 | return Container( 185 | height: height, 186 | child: Material( 187 | elevation: 30, 188 | color: Theme.of(context).primaryColor, 189 | child: Scrollbar( 190 | controller: _scrollController, 191 | isAlwaysShown: true, 192 | child: ListView( 193 | controller: _scrollController, 194 | padding: EdgeInsets.all(8.0), 195 | children: [ 196 | SizedBox( 197 | height: 20, 198 | ), 199 | Slider( 200 | min: 0, 201 | max: 10, 202 | divisions: 100, 203 | activeColor: Theme.of(context).colorScheme.secondary, 204 | inactiveColor: Colors.grey, 205 | onChanged: (value) { 206 | setState(() { 207 | _a = double.parse(value.toStringAsFixed(1)); 208 | }); 209 | }, 210 | value: _a, 211 | ), 212 | Center( 213 | child: Text( 214 | "A: $_a", 215 | style: Theme.of(context).textTheme.subtitle2, 216 | ), 217 | ), 218 | Slider( 219 | min: 0, 220 | max: 10, 221 | divisions: 100, 222 | activeColor: Theme.of(context).colorScheme.secondary, 223 | inactiveColor: Colors.grey, 224 | onChanged: (value) { 225 | setState(() { 226 | _b = double.parse(value.toStringAsFixed(1)); 227 | }); 228 | }, 229 | value: _b, 230 | ), 231 | Center( 232 | child: Text( 233 | "B: $_b", 234 | style: Theme.of(context).textTheme.subtitle2, 235 | ), 236 | ), 237 | Slider( 238 | min: 0, 239 | max: 6.28, 240 | divisions: 100, 241 | activeColor: Theme.of(context).colorScheme.secondary, 242 | inactiveColor: Colors.grey, 243 | onChanged: (value) { 244 | setState(() { 245 | delta = double.parse(value.toStringAsFixed(2)); 246 | }); 247 | }, 248 | value: delta, 249 | ), 250 | Center( 251 | child: Text( 252 | "Delta: $delta", 253 | style: Theme.of(context).textTheme.subtitle2, 254 | ), 255 | ), 256 | Slider( 257 | min: 2, 258 | max: 6, 259 | divisions: 100, 260 | activeColor: Theme.of(context).colorScheme.secondary, 261 | inactiveColor: Colors.grey, 262 | onChanged: (value) { 263 | setState(() { 264 | thickness = double.parse(value.toStringAsFixed(2)); 265 | }); 266 | }, 267 | value: thickness, 268 | ), 269 | Center( 270 | child: Text("Thickness: $thickness", 271 | style: Theme.of(context).textTheme.subtitle2), 272 | ), 273 | ], 274 | ), 275 | ), 276 | ), 277 | ); 278 | } 279 | } 280 | 281 | class Lissajous extends StatefulWidget { 282 | Lissajous({ 283 | Key key, 284 | @required double b, 285 | @required double a, 286 | @required this.delta, 287 | @required this.animate, 288 | @required this.animating, 289 | @required this.thickness, 290 | @required this.isLandscape, 291 | }) : _b = b, 292 | _a = a, 293 | super(key: key); 294 | 295 | final double _b; 296 | final double _a; 297 | final double delta; 298 | final bool animate; 299 | final bool animating; 300 | final double thickness; 301 | final bool isLandscape; 302 | 303 | @override 304 | _LissajousState createState() => _LissajousState(); 305 | } 306 | 307 | class _LissajousState extends State { 308 | List points = []; 309 | double loopi = 0; 310 | double r, n, d, c, transformx, transformy; 311 | double looplength = 2 * pi; 312 | double tx, ty; 313 | bool orientationChanged = true; 314 | 315 | void dispose() { 316 | super.dispose(); 317 | } 318 | 319 | void clearscreen() { 320 | points.clear(); 321 | looplength = 2 * pi; 322 | looplength += loopi; 323 | } 324 | 325 | void clear() { 326 | clearscreen(); 327 | loopi = 0; 328 | looplength = 2 * pi; 329 | } 330 | 331 | nextStep() async { 332 | if (loopi >= looplength) { 333 | clear(); 334 | } 335 | await Future.delayed(Duration(milliseconds: 10)); 336 | if (this.mounted) { 337 | setState(() { 338 | loopi += 0.01; 339 | points.add(Offset(r * sin(widget._a * loopi + widget.delta), 340 | r * sin(widget._b * loopi)) 341 | .translate(tx.roundToDouble(), ty.roundToDouble())); 342 | }); 343 | } 344 | } 345 | 346 | @override 347 | Widget build(BuildContext context) { 348 | tx = widget.isLandscape 349 | ? MediaQuery.of(context).size.width / 3 350 | : MediaQuery.of(context).size.width / 2; 351 | ty = MediaQuery.of(context).size.height / 3; 352 | r = (widget.isLandscape 353 | ? MediaQuery.of(context).size.width / 4 354 | : MediaQuery.of(context).size.width / 2.5) 355 | .roundToDouble(); 356 | 357 | if (widget.animating) { 358 | WidgetsBinding.instance.addPostFrameCallback((_) { 359 | nextStep(); 360 | }); 361 | } 362 | if (!(widget.isLandscape ^ orientationChanged)) { 363 | clear(); 364 | orientationChanged = !orientationChanged; 365 | } 366 | 367 | return Transform.scale( 368 | scale: widget.isLandscape ? 0.5 : 1, 369 | child: CustomPaint( 370 | painter: LissajousPainter( 371 | widget._b, 372 | widget._a, 373 | tx.roundToDouble(), 374 | ty.roundToDouble(), 375 | r, 376 | widget.delta, 377 | widget.animate, 378 | points, 379 | widget.thickness, 380 | ), 381 | child: Container(), 382 | ), 383 | ); 384 | } 385 | } 386 | 387 | class LissajousPainter extends CustomPainter { 388 | double d, r, n, c; 389 | double k, transformx, transformy; 390 | List points = []; 391 | bool animate; 392 | double thickness; 393 | LissajousPainter( 394 | this.d, 395 | this.n, 396 | this.transformx, 397 | this.transformy, 398 | this.r, 399 | this.c, 400 | this.animate, 401 | points, 402 | this.thickness, 403 | ) { 404 | this.points = new List.from(points); 405 | k = n / d; 406 | } 407 | 408 | @override 409 | void paint(Canvas canvas, Size size) { 410 | var paint = Paint(); 411 | paint.color = Colors.red; 412 | paint.strokeWidth = thickness; 413 | if (!animate) { 414 | this.points.clear(); 415 | for (double i = 0; i <= 2 * pi; i += 0.01) { 416 | this.points.add(Offset(r * sin(n * i + c), r * sin(d * i)) 417 | .translate(transformx, transformy)); 418 | } 419 | } 420 | if (points.length > 0) { 421 | canvas.drawPoints(PointMode.polygon, points, paint); 422 | } 423 | } 424 | 425 | @override 426 | bool shouldRepaint(LissajousPainter oldDelegate) => true; 427 | 428 | @override 429 | bool shouldRebuildSemantics(LissajousPainter oldDelegate) => false; 430 | } 431 | -------------------------------------------------------------------------------- /lib/src/simulations/pi_approximation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'dart:math' as math; 4 | 5 | double pi = 0, total = 0, insideCircle = 0; 6 | List> coordinates = List(); 7 | 8 | class PiApproximation extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | final size = MediaQuery.of(context).size; 12 | return Scaffold( 13 | appBar: AppBar( 14 | leading: IconButton( 15 | icon: Icon( 16 | Icons.arrow_back_ios, 17 | color: Colors.black, 18 | ), 19 | onPressed: () => Navigator.pop(context), 20 | ), 21 | centerTitle: true, 22 | title: Text( 23 | "Pi Approximation (Monte Carlo Method)", 24 | style: TextStyle( 25 | color: Colors.black, 26 | fontFamily: 'Ubuntu', 27 | ), 28 | ), 29 | ), 30 | backgroundColor: Colors.grey[850], 31 | body: Stack( 32 | children: [ 33 | RepaintBoundary( 34 | child: CustomPaint( 35 | painter: BackgroundPainter(), 36 | willChange: false, 37 | isComplex: true, 38 | child: Container(), 39 | ), 40 | ), 41 | MakeDots(), 42 | Positioned( 43 | bottom: 0, 44 | child: Container( 45 | color: Colors.white, 46 | width: size.width, 47 | height: size.height / 8.3, 48 | ), 49 | ), 50 | Align( 51 | alignment: Alignment(0, 0.5), 52 | child: Row( 53 | mainAxisAlignment: MainAxisAlignment.center, 54 | children: [ 55 | Values(), 56 | ], 57 | ), 58 | ), 59 | Align( 60 | alignment: Alignment(0, 0.9), 61 | child: Row( 62 | mainAxisAlignment: MainAxisAlignment.center, 63 | children: [ 64 | Text( 65 | "Pi (approx): ", 66 | style: TextStyle( 67 | fontFamily: 'Ubuntu', 68 | fontSize: 20, 69 | color: Colors.black, 70 | ), 71 | ), 72 | PiValue(), 73 | ], 74 | ), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | 82 | class BackgroundPainter extends CustomPainter { 83 | double R; 84 | @override 85 | void paint(Canvas canvas, Size size) { 86 | R = size.width / 2.1; 87 | 88 | final brush = Paint() 89 | ..color = Colors.white 90 | ..strokeWidth = 2 91 | ..style = PaintingStyle.stroke; 92 | 93 | canvas.drawCircle( 94 | Offset(size.width / 2, size.height / 3), 95 | R, 96 | brush, 97 | ); 98 | 99 | canvas.drawRect( 100 | Rect.fromCenter( 101 | center: Offset(size.width / 2, size.height / 3), 102 | height: 2 * R, 103 | width: 2 * R, 104 | ), 105 | brush, 106 | ); 107 | } 108 | 109 | @override 110 | bool shouldRepaint(BackgroundPainter oldDelegate) => false; 111 | 112 | @override 113 | bool shouldRebuildSemantics(BackgroundPainter oldDelegate) => false; 114 | } 115 | 116 | class Values extends StatefulWidget { 117 | _ValuesState createState() => _ValuesState(); 118 | } 119 | 120 | class _ValuesState extends State { 121 | @override 122 | Widget build(BuildContext context) { 123 | WidgetsBinding.instance.addPostFrameCallback((_) => setState(() {})); 124 | return Text( 125 | "\tDots inside circle (Red): ${insideCircle.toInt()}\n" 126 | "Total dots (Red + Green): ${total.toInt()}", 127 | style: TextStyle( 128 | fontFamily: 'Ubuntu', 129 | fontSize: 20, 130 | color: Colors.white, 131 | ), 132 | ); 133 | } 134 | } 135 | 136 | class PiValue extends StatefulWidget { 137 | _PiValueState createState() => _PiValueState(); 138 | } 139 | 140 | class _PiValueState extends State { 141 | @override 142 | Widget build(BuildContext context) { 143 | WidgetsBinding.instance.addPostFrameCallback((_) => setState(() {})); 144 | return Text( 145 | "${pi.toStringAsFixed(20)}", 146 | style: TextStyle( 147 | fontFamily: 'Ubuntu', 148 | fontSize: 20, 149 | color: Colors.black, 150 | ), 151 | ); 152 | } 153 | } 154 | 155 | class MakeDots extends StatefulWidget { 156 | _MakeDotsState createState() => _MakeDotsState(); 157 | } 158 | 159 | class _MakeDotsState extends State { 160 | @override 161 | void initState() { 162 | SystemChrome.setPreferredOrientations([ 163 | DeviceOrientation.portraitUp, 164 | DeviceOrientation.portraitDown, 165 | ]); 166 | super.initState(); 167 | } 168 | 169 | @override 170 | void dispose() { 171 | coordinates.clear(); 172 | total = 0; 173 | insideCircle = 0; 174 | SystemChrome.setPreferredOrientations([ 175 | DeviceOrientation.portraitUp, 176 | DeviceOrientation.portraitDown, 177 | DeviceOrientation.landscapeLeft, 178 | DeviceOrientation.landscapeRight, 179 | ]); 180 | super.dispose(); 181 | } 182 | 183 | @override 184 | Widget build(BuildContext context) { 185 | WidgetsBinding.instance.addPostFrameCallback((_) => setState(() {})); 186 | return CustomPaint( 187 | painter: DotPainter(), 188 | child: Container(), 189 | ); 190 | } 191 | } 192 | 193 | class DotPainter extends CustomPainter { 194 | var brush = Paint(); 195 | final random = math.Random(); 196 | double R; 197 | 198 | @override 199 | void paint(Canvas canvas, Size size) { 200 | double x, y; 201 | R = size.width / 2.1; 202 | 203 | for (int i = 0; i < 50; i++) { 204 | x = -R + 2 * random.nextDouble() * R; 205 | y = -R + 2 * random.nextDouble() * R; 206 | 207 | coordinates.add([x, y]); 208 | if (x * x + y * y <= R * R) ++insideCircle; 209 | ++total; 210 | } 211 | 212 | coordinates.forEach((coordinate) { 213 | x = coordinate[0]; 214 | y = coordinate[1]; 215 | 216 | (x * x + y * y > R * R) 217 | ? brush.color = Colors.greenAccent[400] 218 | : brush.color = Colors.red; 219 | 220 | canvas.drawCircle( 221 | Offset(size.width / 2 + x, size.height / 3 + y), 222 | 1, 223 | brush, 224 | ); 225 | }); 226 | 227 | pi = 4 * insideCircle / total; 228 | } 229 | 230 | @override 231 | bool shouldRepaint(DotPainter oldDelegate) => true; 232 | 233 | @override 234 | bool shouldRebuildSemantics(DotPainter oldDelegate) => false; 235 | } 236 | -------------------------------------------------------------------------------- /lib/src/simulations/rose_pattern.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | 6 | GlobalKey<_RoseState> globalKey = GlobalKey<_RoseState>(); 7 | 8 | class RosePattern extends StatefulWidget { 9 | @override 10 | _RosePatternState createState() => _RosePatternState(); 11 | } 12 | 13 | class _RosePatternState extends State { 14 | double _n = 0; 15 | double _d = 0; 16 | double k = 0; 17 | double offset = 0; 18 | bool animate = false; 19 | bool animating = false; 20 | final ScrollController _scrollController = ScrollController(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | super.dispose(); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return LayoutBuilder( 35 | // ignore: missing_return 36 | builder: (_, BoxConstraints constraints) { 37 | if (constraints.maxWidth != 0) { 38 | ScreenUtil.init( 39 | constraints, 40 | context: context, 41 | designSize: Size(512.0, 1024.0), 42 | minTextAdapt: true, 43 | ); 44 | return Scaffold( 45 | appBar: AppBar( 46 | automaticallyImplyLeading: false, 47 | leading: IconButton( 48 | icon: Icon(Icons.arrow_back_ios), 49 | onPressed: () { 50 | Navigator.pop(context); 51 | }, 52 | ), 53 | title: Text( 54 | 'Rose Pattern', 55 | style: Theme.of(context).textTheme.headline6, 56 | ), 57 | centerTitle: true, 58 | ), 59 | floatingActionButtonLocation: 60 | FloatingActionButtonLocation.centerDocked, 61 | floatingActionButton: Padding( 62 | padding: EdgeInsets.all(8.0), 63 | child: Visibility( 64 | visible: animate, 65 | child: Row( 66 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 67 | children: [ 68 | FloatingActionButton( 69 | heroTag: null, 70 | backgroundColor: Colors.white, 71 | child: (!animating) 72 | ? Icon( 73 | Icons.play_arrow, 74 | color: Colors.black, 75 | ) 76 | : Icon( 77 | Icons.pause, 78 | color: Colors.black, 79 | ), 80 | onPressed: () { 81 | setState(() { 82 | animating = !animating; 83 | }); 84 | }, 85 | ), 86 | FloatingActionButton( 87 | heroTag: null, 88 | backgroundColor: Colors.white, 89 | child: Icon( 90 | Icons.highlight_off, 91 | color: Colors.black, 92 | ), 93 | onPressed: () { 94 | setState(() { 95 | globalKey.currentState.clearScreen(); 96 | }); 97 | }, 98 | ), 99 | ], 100 | ), 101 | ), 102 | ), 103 | bottomNavigationBar: Visibility( 104 | visible: !isLandscape(), 105 | child: parameters(context, ScreenUtil().setHeight(1024 / 5))), 106 | body: Row( 107 | children: [ 108 | Container( 109 | width: isLandscape() 110 | ? 2 * MediaQuery.of(context).size.width / 3 111 | : MediaQuery.of(context).size.width, 112 | child: Stack( 113 | children: [ 114 | Rose( 115 | d: _d, 116 | n: _n, 117 | c: offset, 118 | animate: animate, 119 | animating: animating, 120 | key: globalKey, 121 | isLandscape: isLandscape(), 122 | ), 123 | Positioned( 124 | top: 5, 125 | left: 5, 126 | child: Text( 127 | 'k ~ ${(_n / _d).toStringAsFixed(2)}', 128 | style: Theme.of(context).textTheme.subtitle2, 129 | ), 130 | ), 131 | Positioned( 132 | top: 0, 133 | right: 0, 134 | child: Row( 135 | mainAxisAlignment: MainAxisAlignment.end, 136 | children: [ 137 | Text("Animate"), 138 | Checkbox( 139 | onChanged: (_) { 140 | setState(() { 141 | animate = !animate; 142 | if (animating) 143 | animating = (animating && animate); 144 | }); 145 | }, 146 | value: animate, 147 | activeColor: Colors.red, 148 | ) 149 | ], 150 | ), 151 | ), 152 | ], 153 | ), 154 | ), 155 | Visibility( 156 | visible: isLandscape(), 157 | child: Expanded( 158 | child: parameters( 159 | context, 160 | MediaQuery.of(context).size.height, 161 | ), 162 | ), 163 | ), 164 | ], 165 | ), 166 | ); 167 | } 168 | }, 169 | ); 170 | } 171 | 172 | bool isLandscape() { 173 | return MediaQuery.of(context).size.width > 174 | MediaQuery.of(context).size.height; 175 | } 176 | 177 | Container parameters(BuildContext context, num height) { 178 | return Container( 179 | height: height, 180 | child: Material( 181 | elevation: 30, 182 | color: Theme.of(context).primaryColor, 183 | child: Scrollbar( 184 | controller: _scrollController, 185 | isAlwaysShown: true, 186 | child: ListView( 187 | controller: _scrollController, 188 | padding: EdgeInsets.all(8.0), 189 | children: [ 190 | SizedBox( 191 | height: 20, 192 | ), 193 | Slider( 194 | min: 0, 195 | max: 10, 196 | divisions: 1000, 197 | activeColor: Theme.of(context).colorScheme.secondary, 198 | inactiveColor: Colors.grey, 199 | onChanged: (value) { 200 | setState(() { 201 | _n = double.parse(value.toStringAsFixed(2)); 202 | }); 203 | }, 204 | value: _n, 205 | ), 206 | Center( 207 | child: Text( 208 | "Numerator: $_n", 209 | style: Theme.of(context).textTheme.subtitle2, 210 | ), 211 | ), 212 | Slider( 213 | min: 0, 214 | max: 10, 215 | divisions: 1000, 216 | activeColor: Theme.of(context).colorScheme.secondary, 217 | inactiveColor: Colors.grey, 218 | onChanged: (value) { 219 | setState(() { 220 | _d = double.parse(value.toStringAsFixed(2)); 221 | }); 222 | }, 223 | value: _d, 224 | ), 225 | Center( 226 | child: Text( 227 | "Denominator: $_d", 228 | style: Theme.of(context).textTheme.subtitle2, 229 | ), 230 | ), 231 | Slider( 232 | min: 0, 233 | max: 1, 234 | divisions: 100, 235 | activeColor: Theme.of(context).colorScheme.secondary, 236 | inactiveColor: Colors.grey, 237 | onChanged: (value) { 238 | setState(() { 239 | offset = double.parse(value.toStringAsFixed(2)); 240 | }); 241 | }, 242 | value: offset, 243 | ), 244 | Center( 245 | child: Text( 246 | "Offset: $offset", 247 | style: Theme.of(context).textTheme.subtitle2, 248 | ), 249 | ), 250 | ], 251 | ), 252 | ), 253 | ), 254 | ); 255 | } 256 | } 257 | 258 | class Rose extends StatefulWidget { 259 | Rose({ 260 | Key key, 261 | @required this.d, 262 | @required this.n, 263 | @required this.c, 264 | @required this.animate, 265 | @required this.animating, 266 | @required this.isLandscape, 267 | }) : super(key: key); 268 | 269 | final double d; 270 | final double n; 271 | final double c; 272 | final bool animate; 273 | final bool animating; 274 | final bool isLandscape; 275 | 276 | @override 277 | _RoseState createState() => _RoseState(); 278 | } 279 | 280 | class _RoseState extends State { 281 | List points = []; 282 | double loopi = 0; 283 | double r, k; 284 | double looplength = 2 * pi; 285 | double tx, ty; 286 | bool orientationChanged = true; 287 | 288 | void dispose() { 289 | super.dispose(); 290 | } 291 | 292 | void clearScreen() { 293 | points.clear(); 294 | looplength = 2 * pi * widget.d; 295 | looplength += loopi; 296 | } 297 | 298 | nextStep() { 299 | if (loopi >= looplength) { 300 | clearScreen(); 301 | loopi = 0; 302 | } 303 | 304 | setState(() { 305 | if (!(widget.d == 0) && !(widget.d == 0 && widget.n == 0)) { 306 | if (loopi == 0) { 307 | looplength = 2 * pi * widget.d; 308 | } 309 | loopi += 0.04; 310 | k = widget.n / widget.d; 311 | points.add(Offset(r * (cos(k * loopi) + widget.c) * cos(loopi), 312 | r * (cos(k * loopi) + widget.c) * sin(loopi)) 313 | .translate(tx.roundToDouble(), ty.roundToDouble())); 314 | } 315 | }); 316 | } 317 | 318 | @override 319 | Widget build(BuildContext context) { 320 | tx = widget.isLandscape 321 | ? MediaQuery.of(context).size.width / 3 322 | : MediaQuery.of(context).size.width / 2; 323 | ty = MediaQuery.of(context).size.height / 3; 324 | r = (widget.isLandscape 325 | ? MediaQuery.of(context).size.width / 6.2 326 | : MediaQuery.of(context).size.width / 4) 327 | .roundToDouble(); 328 | 329 | if (widget.animating) { 330 | WidgetsBinding.instance.addPostFrameCallback((_) { 331 | nextStep(); 332 | }); 333 | } 334 | if (!(widget.isLandscape ^ orientationChanged)) { 335 | clearScreen(); 336 | orientationChanged = !orientationChanged; 337 | } 338 | 339 | return Transform.scale( 340 | scale: widget.isLandscape ? 0.5 : 1, 341 | child: CustomPaint( 342 | painter: RosePainter( 343 | widget.d, 344 | widget.n, 345 | tx.roundToDouble(), 346 | ty.roundToDouble(), 347 | r, 348 | widget.c, 349 | widget.animate, 350 | points, 351 | ), 352 | child: Container(), 353 | ), 354 | ); 355 | } 356 | } 357 | 358 | class RosePainter extends CustomPainter { 359 | double d, r, n, c; 360 | double k, transformx, transformy; 361 | List points = []; 362 | bool animate; 363 | RosePainter(this.d, this.n, this.transformx, this.transformy, this.r, this.c, 364 | this.animate, points) { 365 | k = n / d; 366 | this.points = new List.from(points); 367 | } 368 | 369 | @override 370 | void paint(Canvas canvas, Size size) { 371 | var paint = Paint(); 372 | paint.color = Colors.red; 373 | paint.strokeWidth = 2; 374 | if (!animate) { 375 | this.points.clear(); 376 | for (double i = 0; i < 2 * d * pi; i += 0.01) { 377 | points.add( 378 | Offset(r * (cos(k * i) + c) * cos(i), r * (cos(k * i) + c) * sin(i)) 379 | .translate(transformx, transformy)); 380 | } 381 | } 382 | if (points.length > 0) { 383 | canvas.drawPoints(PointMode.polygon, points, paint); 384 | } 385 | } 386 | 387 | @override 388 | bool shouldRepaint(RosePainter oldDelegate) => true; 389 | 390 | @override 391 | bool shouldRebuildSemantics(RosePainter oldDelegate) => false; 392 | } 393 | -------------------------------------------------------------------------------- /lib/src/simulations/selection_sort.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | 6 | class SelectionSortBars extends StatefulWidget { 7 | @override 8 | _SelectionSortBarsState createState() => _SelectionSortBarsState(); 9 | } 10 | 11 | class _SelectionSortBarsState extends State { 12 | int _numberOfElements; 13 | List _elements = []; 14 | int i = 0, j = 1, counter = 0; 15 | int minIdx = 0; 16 | int n; 17 | int tmp, delay = 0, delay2 = 0; 18 | bool swap = false; 19 | bool findingMin = false; 20 | double barwidth; 21 | List containerList = []; 22 | bool doNotRefresh = false; 23 | int finalIterator = 0; 24 | final ScrollController _scrollController = ScrollController(); 25 | 26 | @override 27 | void initState() { 28 | _numberOfElements = 2; 29 | i = 0; 30 | j = 1; 31 | counter = 0; 32 | swap = false; 33 | findingMin = false; 34 | doNotRefresh = false; 35 | SystemChrome.setPreferredOrientations([ 36 | DeviceOrientation.portraitUp, 37 | DeviceOrientation.portraitDown, 38 | ]); 39 | super.initState(); 40 | } 41 | 42 | @override 43 | dispose() { 44 | SystemChrome.setPreferredOrientations([ 45 | DeviceOrientation.portraitUp, 46 | DeviceOrientation.portraitDown, 47 | DeviceOrientation.landscapeLeft, 48 | DeviceOrientation.landscapeRight, 49 | ]); 50 | super.dispose(); 51 | } 52 | 53 | _containerList() { 54 | containerList.clear(); 55 | if (!doNotRefresh) { 56 | _elements.clear(); 57 | i = 0; 58 | j = 1; 59 | var rng = new Random(); 60 | for (int i = 0; i < _numberOfElements; i++) { 61 | _elements.add(rng.nextInt(400)); 62 | } 63 | n = _elements.length; 64 | } 65 | this.barwidth = MediaQuery.of(context).size.width / (_elements.length + 1); 66 | if (n != 1) { 67 | for (int k = 0; k < _elements.length; ++k) { 68 | if (k == minIdx) { 69 | containerList.add(Container( 70 | color: Colors.red, 71 | height: _elements[k] + 0.5, 72 | width: barwidth, 73 | )); 74 | } else if (k == i) { 75 | containerList.add(Container( 76 | color: Colors.blue, 77 | height: _elements[k] + 0.5, 78 | width: barwidth, 79 | )); 80 | } else { 81 | containerList.add(Container( 82 | color: Theme.of(context).primaryColor, 83 | height: _elements[k] + 0.5, 84 | width: barwidth, 85 | )); 86 | } 87 | } 88 | } else { 89 | containerList.clear(); 90 | finalIterator++; 91 | 92 | for (int k = 0; k < _elements.length; ++k) { 93 | if (k <= finalIterator) { 94 | containerList.add(Container( 95 | color: Colors.greenAccent[400], 96 | height: _elements[k] + 0.5, 97 | width: barwidth, 98 | )); 99 | } else { 100 | containerList.add(Container( 101 | color: Theme.of(context).primaryColor, 102 | height: _elements[k] + 0.5, 103 | width: barwidth, 104 | )); 105 | } 106 | } 107 | if (finalIterator == _elements.length) { 108 | finalIterator = 0; 109 | } 110 | } 111 | } 112 | 113 | findMin() { 114 | findingMin = true; 115 | j = i + 1; 116 | while (j < n) { 117 | setState(() { 118 | if (_elements[minIdx] > _elements[j]) { 119 | minIdx = j; 120 | } 121 | j++; 122 | }); 123 | } 124 | findingMin = false; 125 | } 126 | 127 | nextStep() async { 128 | await Future.delayed(Duration(milliseconds: delay)); 129 | if (!doNotRefresh) return; 130 | if (this.mounted) { 131 | setState(() { 132 | if (n == 1) { 133 | swap = false; 134 | return; 135 | } 136 | counter++; 137 | if (i == n - 1) { 138 | i = 0; 139 | n = 1; 140 | swap = false; 141 | } 142 | minIdx = i; 143 | findMin(); 144 | tmp = _elements[i]; 145 | _elements[i] = _elements[minIdx]; 146 | _elements[minIdx] = tmp; 147 | i++; 148 | }); 149 | } 150 | } 151 | 152 | @override 153 | Widget build(BuildContext context) { 154 | _containerList(); 155 | if ((swap == true || finalIterator != 0) && (findingMin == false)) { 156 | WidgetsBinding.instance.addPostFrameCallback((_) => nextStep()); 157 | } 158 | 159 | return Scaffold( 160 | appBar: AppBar( 161 | automaticallyImplyLeading: false, 162 | leading: IconButton( 163 | icon: Icon(Icons.arrow_back_ios), 164 | onPressed: () { 165 | Navigator.pop(context); 166 | }, 167 | ), 168 | centerTitle: true, 169 | title: Text( 170 | 'Selection Sort', 171 | style: Theme.of(context).textTheme.headline6, 172 | ), 173 | ), 174 | floatingActionButton: FloatingActionButton( 175 | backgroundColor: Colors.white, 176 | child: (!swap) 177 | ? Icon( 178 | Icons.play_arrow, 179 | color: Colors.black, 180 | ) 181 | : Icon( 182 | Icons.pause, 183 | color: Colors.black, 184 | ), 185 | onPressed: () { 186 | doNotRefresh = true; 187 | swap = !swap; 188 | setState(() {}); 189 | }), 190 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 191 | bottomNavigationBar: Container( 192 | height: ScreenUtil().setHeight(1024 / 5.5), 193 | child: Material( 194 | elevation: 30, 195 | color: Theme.of(context).primaryColor, 196 | child: Scrollbar( 197 | controller: _scrollController, 198 | isAlwaysShown: true, 199 | child: ListView( 200 | controller: _scrollController, 201 | padding: EdgeInsets.all(8.0), 202 | children: [ 203 | SizedBox( 204 | height: 20, 205 | ), 206 | Slider( 207 | min: 2, 208 | max: 200, 209 | activeColor: Theme.of(context).colorScheme.secondary, 210 | inactiveColor: Colors.grey, 211 | onChanged: (value) { 212 | doNotRefresh = false; 213 | counter = 0; 214 | swap = false; 215 | finalIterator = 0; 216 | setState(() { 217 | _numberOfElements = value.toInt(); 218 | }); 219 | }, 220 | value: _numberOfElements.toDouble(), 221 | ), 222 | Center( 223 | child: Text( 224 | "Elements: ${_numberOfElements.toInt()}", 225 | style: Theme.of(context).textTheme.subtitle2, 226 | ), 227 | ), 228 | SizedBox( 229 | height: 20, 230 | ), 231 | Slider( 232 | min: 0, 233 | max: 100, 234 | divisions: 10, 235 | activeColor: Theme.of(context).colorScheme.secondary, 236 | inactiveColor: Colors.grey, 237 | onChanged: (value) { 238 | setState(() { 239 | delay2 = value.toInt(); 240 | }); 241 | }, 242 | onChangeEnd: (value) { 243 | setState(() { 244 | doNotRefresh = true; 245 | delay = value.toInt(); 246 | }); 247 | }, 248 | value: delay2.roundToDouble(), 249 | ), 250 | Center( 251 | child: Text( 252 | "Delay: ${delay2.toInt()} ms", 253 | style: Theme.of(context).textTheme.subtitle2, 254 | ), 255 | ), 256 | ], 257 | ), 258 | ), 259 | ), 260 | ), 261 | body: Stack( 262 | children: [ 263 | Container( 264 | color: Colors.grey[900], 265 | child: Column( 266 | children: [ 267 | Spacer(), 268 | Row( 269 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 270 | children: containerList, 271 | ), 272 | Spacer(), 273 | ], 274 | ), 275 | ), 276 | Positioned( 277 | top: 5, 278 | left: 5, 279 | child: Text( 280 | "Swaps: $counter", 281 | style: Theme.of(context).textTheme.subtitle2, 282 | ), 283 | ), 284 | ], 285 | ), 286 | ); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /lib/src/simulations/toothpick.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:collection/collection.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | 6 | Function listEq = const DeepCollectionEquality().equals; 7 | 8 | class Toothpick { 9 | bool alignment; 10 | var end1 = new List(2); 11 | var end2 = new List(2); 12 | var center = new List(2); 13 | Toothpick(this.center, this.alignment) { 14 | if (alignment == true) { 15 | this.end1[0] = center[0]; 16 | this.end1[1] = center[1] - 20; 17 | this.end2[0] = center[0]; 18 | this.end2[1] = center[1] + 20; 19 | } else { 20 | this.end1[0] = center[0] - 20; 21 | this.end1[1] = center[1]; 22 | this.end2[0] = center[0] + 20; 23 | this.end2[1] = center[1]; 24 | } 25 | } 26 | 27 | bool compareEnd1(otherPicks) { 28 | int flag; 29 | otherPicks.forEach((pick) { 30 | if (!listEq(pick.center, this.center)) { 31 | if (listEq(pick.end1, this.end1) | 32 | listEq(pick.end2, this.end1) | 33 | listEq(pick.center, this.end1)) { 34 | flag = 1; 35 | } 36 | } 37 | }); 38 | if (flag == 1) { 39 | return false; 40 | } else { 41 | return true; 42 | } 43 | } 44 | 45 | bool compareEnd2(otherPicks) { 46 | int flag = 0; 47 | otherPicks.forEach((pick) { 48 | if (!listEq(pick.center, this.center)) { 49 | if (listEq(pick.end1, this.end2) | 50 | listEq(pick.end2, this.end2) | 51 | listEq(pick.center, this.end2)) { 52 | flag = 1; 53 | } 54 | } 55 | }); 56 | if (flag == 1) { 57 | return false; 58 | } else { 59 | return true; 60 | } 61 | } 62 | } 63 | 64 | class ToothpickPattern extends StatefulWidget { 65 | @override 66 | _ToothpickPatternState createState() => _ToothpickPatternState(); 67 | } 68 | 69 | class _ToothpickPatternState extends State { 70 | int step = 0; 71 | var activeToothPicks = new List(); 72 | var prevToothPicks = new List>(); 73 | var toothPicks = new List(); 74 | var extra; 75 | double _scaleAmount = 1.0; 76 | 77 | addStep() { 78 | setState(() { 79 | step++; 80 | prevToothPicks.add([]..addAll(activeToothPicks)); 81 | toothPicks += prevToothPicks[prevToothPicks.length - 1]; 82 | activeToothPicks.clear(); 83 | prevToothPicks[prevToothPicks.length - 1].forEach((pick) { 84 | extra = pick.compareEnd1(toothPicks); 85 | if (extra) { 86 | activeToothPicks += [new Toothpick(pick.end1, !pick.alignment)]; 87 | } 88 | extra = pick.compareEnd2(toothPicks); 89 | if (extra) { 90 | activeToothPicks += [new Toothpick(pick.end2, !pick.alignment)]; 91 | } 92 | }); 93 | }); 94 | } 95 | 96 | subtract() { 97 | if (step == 0) { 98 | return; 99 | } 100 | setState(() { 101 | step--; 102 | activeToothPicks.clear(); 103 | activeToothPicks += prevToothPicks[prevToothPicks.length - 1]; 104 | for (int i = 0; 105 | i < prevToothPicks[prevToothPicks.length - 1].length; 106 | i++) { 107 | toothPicks.removeLast(); 108 | } 109 | prevToothPicks.removeLast(); 110 | }); 111 | } 112 | 113 | @override 114 | void initState() { 115 | SystemChrome.setPreferredOrientations([ 116 | DeviceOrientation.portraitUp, 117 | DeviceOrientation.portraitDown, 118 | ]); 119 | super.initState(); 120 | } 121 | 122 | @override 123 | void dispose() { 124 | SystemChrome.setPreferredOrientations([ 125 | DeviceOrientation.portraitUp, 126 | DeviceOrientation.portraitDown, 127 | DeviceOrientation.landscapeLeft, 128 | DeviceOrientation.landscapeRight, 129 | ]); 130 | super.dispose(); 131 | } 132 | 133 | @override 134 | Widget build(BuildContext context) { 135 | if (step == 1) { 136 | activeToothPicks.add(new Toothpick([ 137 | (MediaQuery.of(context).size.width / 2).roundToDouble(), 138 | (MediaQuery.of(context).size.height / 2 - 100).roundToDouble() 139 | ], true)); 140 | } 141 | return LayoutBuilder( 142 | // ignore: missing_return 143 | builder: (_, BoxConstraints constraints) { 144 | if (constraints.maxWidth != 0) { 145 | ScreenUtil.init( 146 | constraints, 147 | context: context, 148 | designSize: Size(512.0, 1024.0), 149 | minTextAdapt: true, 150 | ); 151 | return Scaffold( 152 | appBar: AppBar( 153 | automaticallyImplyLeading: false, 154 | leading: IconButton( 155 | icon: Icon(Icons.arrow_back_ios), 156 | onPressed: () { 157 | Navigator.pop(context); 158 | }, 159 | ), 160 | title: Text( 161 | "Toothpick Pattern", 162 | style: Theme.of(context).textTheme.headline6, 163 | ), 164 | centerTitle: true, 165 | ), 166 | floatingActionButtonLocation: 167 | FloatingActionButtonLocation.centerDocked, 168 | floatingActionButton: FloatingActionButton.extended( 169 | onPressed: null, 170 | elevation: 10, 171 | label: Text( 172 | 'Step: $step', 173 | style: TextStyle( 174 | color: Colors.white, 175 | ), 176 | ), 177 | backgroundColor: Colors.red, 178 | ), 179 | bottomNavigationBar: Material( 180 | elevation: 30, 181 | child: Container( 182 | height: ScreenUtil().setHeight(1024 / 9), 183 | color: Theme.of(context).primaryColor, 184 | child: ListView( 185 | children: [ 186 | Row( 187 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 188 | children: [ 189 | IconButton( 190 | icon: Icon(Icons.remove), 191 | onPressed: () { 192 | subtract(); 193 | }, 194 | ), 195 | IconButton( 196 | icon: Icon(Icons.add), 197 | onPressed: () { 198 | addStep(); 199 | }, 200 | ), 201 | ], 202 | ), 203 | Row( 204 | children: [ 205 | IconButton( 206 | icon: Icon(Icons.zoom_out), 207 | onPressed: () { 208 | setState(() { 209 | _scaleAmount -= 210 | _scaleAmount - 0.1 <= 0.01 ? 0 : 0.1; 211 | }); 212 | }, 213 | ), 214 | Expanded( 215 | child: Slider( 216 | value: _scaleAmount, 217 | activeColor: Colors.red, 218 | min: 0.01, 219 | max: 2, 220 | inactiveColor: Colors.grey, 221 | onChanged: (value) { 222 | setState(() { 223 | _scaleAmount = value; 224 | }); 225 | }, 226 | ), 227 | ), 228 | IconButton( 229 | icon: Icon(Icons.zoom_in), 230 | onPressed: () { 231 | setState(() { 232 | _scaleAmount += _scaleAmount + 0.1 > 2 ? 0 : 0.1; 233 | }); 234 | }, 235 | ), 236 | ], 237 | ), 238 | ], 239 | ), 240 | ), 241 | ), 242 | body: Container( 243 | child: Transform.scale( 244 | scale: _scaleAmount, 245 | child: CustomPaint( 246 | painter: ToothpickPainter( 247 | activeToothPicks, 248 | toothPicks, 249 | Theme.of(context).colorScheme.secondary, 250 | ), 251 | child: Container(), 252 | ), 253 | ), 254 | ), 255 | ); 256 | } 257 | }, 258 | ); 259 | } 260 | } 261 | 262 | class ToothpickPainter extends CustomPainter { 263 | var activeToothpicks = new List(); 264 | var toothpicks = new List(); 265 | var colorTheme = new Color(0); 266 | ToothpickPainter(this.activeToothpicks, this.toothpicks, this.colorTheme); 267 | @override 268 | void paint(Canvas canvas, Size size) { 269 | var paint = Paint(); 270 | paint.strokeWidth = 2; 271 | paint.color = this.colorTheme; 272 | for (int i = 0; i < toothpicks.length; i++) { 273 | canvas.drawLine(Offset(toothpicks[i].end1[0], toothpicks[i].end1[1]), 274 | Offset(toothpicks[i].end2[0], toothpicks[i].end2[1]), paint); 275 | } 276 | paint.color = Colors.red; 277 | for (int i = 0; i < activeToothpicks.length; i++) { 278 | canvas.drawLine( 279 | Offset(activeToothpicks[i].end1[0], activeToothpicks[i].end1[1]), 280 | Offset(activeToothpicks[i].end2[0], activeToothpicks[i].end2[1]), 281 | paint); 282 | } 283 | } 284 | 285 | @override 286 | bool shouldRepaint(CustomPainter oldDelegate) => false; 287 | } 288 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: "Simulate" 2 | site_description: "Official site for Simulate" 3 | site_author: "Yash Lamba" 4 | site_url: "https://builtree.github.io/simulate/" 5 | 6 | # Repository 7 | repo_name: "builtree/simulate" 8 | repo_url: "https://github.com/builtree/simulate" 9 | 10 | # Copyright 11 | nav: 12 | - Home: "index.md" 13 | - Contributing: "contributing.md" 14 | # - Documentation: 15 | # - Home: 'doc/index.md' 16 | - Simulations: 17 | - All Simulations: "simulations/index.md" 18 | - Algorithms: 19 | - Toothpick Pattern: "simulations/algorithms/ToothpickPattern.md" 20 | - Mathematics: 21 | - Lissajous Pattern: "simulations/mathematics/LissajousPattern.md" 22 | - Maurer Rose Pattern: "simulations/mathematics/MaurerRosePattern.md" 23 | - Fourier Series: "simulations/mathematics/FourierSeries.md" 24 | - Simulate Web: 25 | - Home: "https://builtree.github.io/simulate/web" 26 | 27 | # Configuration 28 | theme: 29 | name: "material" 30 | features: 31 | - navigation.tabs 32 | palette: 33 | primary: "black" 34 | accent: "white" 35 | font: 36 | text: "Ubuntu" 37 | code: "Ubuntu Mono" 38 | icon: 39 | logo: material/atom 40 | repo: octicons/mark-github-16 41 | 42 | # Customization 43 | 44 | # Extensions 45 | markdown_extensions: 46 | - admonition 47 | - codehilite: 48 | guess_lang: false 49 | - toc: 50 | permalink: true 51 | - pymdownx.arithmatex 52 | - pymdownx.betterem: 53 | smart_enable: all 54 | - pymdownx.caret 55 | - pymdownx.critic 56 | - pymdownx.details 57 | - pymdownx.emoji: 58 | emoji_generator: !!python/name:pymdownx.emoji.to_svg 59 | - pymdownx.inlinehilite 60 | - pymdownx.magiclink 61 | - pymdownx.mark 62 | - pymdownx.smartsymbols 63 | - pymdownx.superfences 64 | - footnotes 65 | - pymdownx.tasklist: 66 | custom_checkbox: true 67 | - pymdownx.tabbed 68 | - pymdownx.tilde 69 | - attr_list 70 | 71 | # Plugins 72 | plugins: 73 | - search 74 | - git-revision-date-localized: 75 | type: date 76 | 77 | extra_javascript: 78 | - https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML 79 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: simulate 2 | description: A collection of simulations and visualizations 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.1.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | cupertino_icons: ^1.0.4 14 | url_launcher: ^6.0.18 15 | provider: ^6.0.2 16 | shared_preferences: ^2.0.12 17 | flutter_screenutil: ^5.1.0 18 | flutter_svg: ^1.0.3 19 | 20 | dev_dependencies: 21 | flutter_test: 22 | sdk: flutter 23 | 24 | flutter: 25 | uses-material-design: true 26 | 27 | assets: 28 | - assets/images/ 29 | - assets/simulations/ 30 | - android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 31 | 32 | fonts: 33 | - family: Ubuntu 34 | fonts: 35 | - asset: fonts/Ubuntu-Regular.ttf 36 | -------------------------------------------------------------------------------- /requirements-docs.txt: -------------------------------------------------------------------------------- 1 | mkdocs>=1.1.2 2 | pymdown-extensions>=8.0.1 3 | mkdocs-git-revision-date-localized-plugin>=0.7.3 4 | mkdocs-material>=8.1.6 5 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yashlamba/simulate/66fde1ae8f825104b093daf738fd34778771102a/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | simulate 30 | 31 | 32 | 33 | 36 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simulate", 3 | "short_name": "simulate", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | --------------------------------------------------------------------------------