├── .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 | [](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 | [](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 | [{: 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 | {: 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 | {: style="width:300px"}
25 |
26 | === "Dark Mode"
27 | {: 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 |  |
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 |  |
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 |  |
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 |  |
9 | | Insertion Sort (Bars) | Algorithms | Insertion sort is a sorting algorithm that builds the final list one item at a time |  |
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 | |
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 |  |
12 | | Rose Pattern | Mathematics | A Rose pattern is generated by the junction of sinusoids plotted in polar coordinates |  |
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 |  |
14 |
--------------------------------------------------------------------------------
/docs/simulations/mathematics/FourierSeries.md:
--------------------------------------------------------------------------------
1 | # Fourier Series
2 | ---
3 | {: 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 | {: style="width:300px; vertical-align: baseline"}
15 | {: 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 | {: style="width:600px"}
35 |
36 | === "Dark Mode"
37 | {: style="width:600px"}
38 |
39 |
--------------------------------------------------------------------------------
/docs/simulations/mathematics/LissajousPattern.md:
--------------------------------------------------------------------------------
1 | # Lissajous Pattern
2 | ---
3 | {: 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 | {: style="width:300px"}
20 |
21 | === "Dark Mode"
22 | {: 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 | {: 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 | {: style="width:300px"}
22 |
23 | === "Dark Mode"
24 | {: 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 |
--------------------------------------------------------------------------------