Initial commit. Full UI and process of script conversion to Dart

This commit is contained in:
Francis Santelices 2025-03-24 16:05:57 +08:00
commit f7b249d1d9
159 changed files with 7333 additions and 0 deletions

45
.gitignore vendored Normal file
View File

@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

42
.metadata Normal file
View File

@ -0,0 +1,42 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "c23637390482d4cf9598c3ce3f2be31aa7332daf"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: android
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: ios
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: macos
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: web
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: windows
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

16
README.md Normal file
View File

@ -0,0 +1,16 @@
# surface_tension_calculator
App that calculates surface tension based on an image of a droplet
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

28
analysis_options.yaml Normal file
View File

@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

14
android/.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

View File

@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.upisc.surface_tension_calculator"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.upisc.surface_tension_calculator"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="surface_tension_calculator"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View File

@ -0,0 +1,5 @@
package com.upisc.surface_tension_calculator
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

21
android/build.gradle.kts Normal file
View File

@ -0,0 +1,21 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View File

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip

View File

@ -0,0 +1,25 @@
pluginManagement {
val flutterSdkPath = run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.0" apply false
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
}
include(":app")

BIN
assets/ISC_Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

BIN
assets/ISC_Logo_White.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
fonts/Montserrat-Black.ttf Normal file

Binary file not shown.

Binary file not shown.

BIN
fonts/Montserrat-Bold.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
fonts/Montserrat-Italic.ttf Normal file

Binary file not shown.

BIN
fonts/Montserrat-Light.ttf Normal file

Binary file not shown.

Binary file not shown.

BIN
fonts/Montserrat-Medium.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
fonts/Montserrat-Thin.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

93
fonts/OFL.txt Normal file
View File

@ -0,0 +1,93 @@
Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

34
ios/.gitignore vendored Normal file
View File

@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
</dict>
</plist>

View File

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

View File

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

43
ios/Podfile Normal file
View File

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

View File

@ -0,0 +1,616 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.upisc.surfaceTensionCalculator;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.upisc.surfaceTensionCalculator.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.upisc.surfaceTensionCalculator.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.upisc.surfaceTensionCalculator.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.upisc.surfaceTensionCalculator;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.upisc.surfaceTensionCalculator;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,13 @@
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
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.

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

49
ios/Runner/Info.plist Normal file
View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Surface Tension Calculator</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>surface_tension_calculator</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View File

@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

34
lib/main.dart Normal file
View File

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:surface_tension_calculator/routes/route_generator.dart';
import 'package:surface_tension_calculator/routes/routes.dart';
import 'package:surface_tension_calculator/utils/snackbar_utils.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
scaffoldMessengerKey: SnackBarUtils.scaffoldKey,
title: 'Surface Tension Calculator',
theme: ThemeData(
fontFamily: 'Montserrat',
useMaterial3: true,
appBarTheme: const AppBarTheme(
centerTitle: true,
iconTheme: IconThemeData(color: Colors.white),
elevation: 0,
),
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
initialRoute: MainUIRoutes.homeScreen,
onGenerateRoute: RouteGenerator.generateRoute,
);
}
}

View File

@ -0,0 +1,64 @@
class STCOutput {
String negCoordFile;
String posCoordFile;
double syringeWidth;
String radiusCurvRange;
String shapeFactorRange;
String initXCoordRange;
String initAngleRange;
int paramCombinations;
int numProcessors;
double juza;
List<double> bestOutput;
double lowestRMSD;
double surfaceTension;
double bestRadiusOfCurvature;
double bestShapeFactor;
double bestInitialXCoordinate;
double bestInitialAngle;
double elapsedTime;
STCOutput({
required this.negCoordFile,
required this.posCoordFile,
required this.syringeWidth,
required this.radiusCurvRange,
required this.shapeFactorRange,
required this.initXCoordRange,
required this.initAngleRange,
required this.paramCombinations,
required this.numProcessors,
required this.juza,
required this.bestOutput,
required this.lowestRMSD,
required this.surfaceTension,
required this.bestRadiusOfCurvature,
required this.bestShapeFactor,
required this.bestInitialXCoordinate,
required this.bestInitialAngle,
required this.elapsedTime,
});
static STCOutput clearOutput() {
return STCOutput(
negCoordFile: '',
posCoordFile: '',
syringeWidth: 0,
radiusCurvRange: '',
shapeFactorRange: '',
initXCoordRange: '',
initAngleRange: '',
paramCombinations: 0,
numProcessors: 0,
juza: 0,
bestOutput: [],
lowestRMSD: 0,
surfaceTension: 0,
bestRadiusOfCurvature: 0,
bestShapeFactor: 0,
bestInitialXCoordinate: 0,
bestInitialAngle: 0,
elapsedTime: 0,
);
}
}

4
lib/res/assets.dart Normal file
View File

@ -0,0 +1,4 @@
abstract class Assets {
static const String _assetPath = 'assets';
static const String logo = '$_assetPath/ISC_Logo_White.png';
}

22
lib/res/strings.dart Normal file
View File

@ -0,0 +1,22 @@
abstract class Strings {
static const sampleOut =
'Negative Cooridnate File : water-drop_Negative.dat\n'
'Positive Cooridnate File : water-drop_Positive.dat\n'
'Syringe Width (millimeter): 0.8049999999999999\n'
'Radius of Curvature Range : 0.0001-2.0001 (interval: 20)\n'
'Shape Factor Range : 0.0001-1.0001 (interval: 20)\n'
'Initial X Coordinate Range: 0.0001-0.5001 (interval: 5)\n'
'Initial Angle Range : 0.0001-0.5001 (interval: 5)\n'
'Parameter Combinations : 10000\n'
'Number of Process to Use : 8\n'
'Juza 5-Plane (1/H) Std.Dev: 0.005252355255611812\n'
'minimization success\n'
'best output: [0.003522111582892356, 0.8302552101788515, 0.4880084250271468, 0.059397867545048805, 0.08677238094443798]\n\n'
'Lowest RMSD : 0.003522111582892356\n'
'Computed Surface Tension : 16.67287006223331\n'
'Best Radius of Curvature : 0.8302552101788515\n'
'Best Shape Factor : 0.4880084250271468\n'
'Best Initial X Coordinate : 0.059397867545048805\n'
'Best Initial Angle : 0.08677238094443798\n'
'Lapsed Time in Seconds : 0.18291211128234863';
}

View File

@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:surface_tension_calculator/routes/routes.dart';
import '../screens/home_screen.dart';
class RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
// Getting arguments passed in while calling Navigator.pushNamed
final args = settings.arguments;
switch (settings.name) {
case MainUIRoutes.homeScreen:
return MaterialPageRoute<dynamic>(
settings: settings,
builder: (_) => const HomeScreen(),
);
case MainUIRoutes.resultsScreen:
// if (args is Map && args['student'] is Student) {
// return MaterialPageRoute(
// settings: settings,
// builder: (_) => DashboardScreen(student: args['student']));
// } else {
return _errorRoute();
// }
default:
// If there is no such named route in the switch statement
return _errorRoute();
}
}
static Route<dynamic> _errorRoute() {
return MaterialPageRoute(
builder: (_) {
return Scaffold(
appBar: AppBar(title: const Text('Error')),
body: const Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Icon(Icons.construction_rounded, size: 65),
Center(child: Text('This page is under construction.')),
],
),
);
},
);
}
}

4
lib/routes/routes.dart Normal file
View File

@ -0,0 +1,4 @@
class MainUIRoutes {
static const homeScreen = '/home';
static const resultsScreen = '/results';
}

View File

@ -0,0 +1,309 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:surface_tension_calculator/models/stc_output.dart';
import '../res/assets.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
PlatformFile? _imageFile;
STCOutput out = STCOutput.clearOutput();
static const String scriptPath = 'scripts/img_V01.exe';
Future<void> _pickImage() async {
try {
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.image,
);
if (result == null) return;
setState(() {
_imageFile = result.files.first;
});
} catch (e) {}
}
Future<void> _computeSTC() async {
final List<String> args = [
'-img',
_imageFile!.path ?? '',
'-out',
'water-drop',
'-proc',
'8',
];
await Process.run(scriptPath, args);
out = STCOutput(
negCoordFile: 'negCoordFile',
posCoordFile: 'posCoordFile',
syringeWidth: 0.123,
radiusCurvRange: 'asd',
shapeFactorRange: 'asd',
initXCoordRange: 'asd',
initAngleRange: 'asd',
paramCombinations: 123,
numProcessors: 123,
juza: 12.09,
bestOutput: [0.123, 12.213],
lowestRMSD: 0.123,
surfaceTension: 0.123,
bestRadiusOfCurvature: 0.123,
bestShapeFactor: 0.123,
bestInitialXCoordinate: 123.123,
bestInitialAngle: 234.123,
elapsedTime: 12.12345,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox.expand(
child: Container(
padding: const EdgeInsets.only(top: 35.0, left: 25, right: 25),
color: Color(0xFF7B1113),
child: Column(
children: [
buildHeader(),
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.all(25),
decoration: BoxDecoration(
color: Colors.grey.shade300,
borderRadius: BorderRadius.circular(30),
),
child: IntrinsicHeight(
child: Row(
children: [
buildLeftImage(),
const SizedBox(width: 20),
const VerticalDivider(width: 20),
const SizedBox(width: 20),
buildRightResults(),
],
),
),
),
const SizedBox(height: 20),
buildFooter(),
],
),
),
),
);
}
Widget buildFooter() {
return Center(
child: Text(
'\u00a9 2025 UP Intelligent Systems Center',
style: TextStyle(color: Colors.white),
),
);
}
Widget buildHeader() {
return IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(width: 20),
Image.asset(Assets.logo, height: 70),
const SizedBox(width: 10),
const VerticalDivider(),
const SizedBox(width: 10),
Text(
'SURFACE TENSION CALCULATOR',
style: TextStyle(
fontSize: 45,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
),
);
}
Column buildLeftImage() {
return Column(
children: [
if (_imageFile == null)
Container(
height: 400,
width: 500,
decoration: BoxDecoration(
color: Colors.grey.shade500,
borderRadius: BorderRadius.circular(30),
),
child: Center(child: Text('No image uploaded')),
),
if (_imageFile != null)
Container(
height: 400,
width: 500,
decoration: BoxDecoration(
color: Colors.grey.shade500,
borderRadius: BorderRadius.circular(30),
),
child: Image.memory(Uint8List.fromList(_imageFile!.bytes!)),
),
const SizedBox(height: 15),
Row(
children: [
ElevatedButton(
onPressed: _pickImage,
child: Text('Upload Droplet Image'),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: _imageFile == null ? null : _computeSTC,
child: Text('Compute Surface Tension'),
),
],
),
],
);
}
Widget buildRightResults() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Results:',
style: TextStyle(fontSize: 30, fontWeight: FontWeight.w600),
),
const SizedBox(height: 10),
SingleChildScrollView(child: buildResults()),
// Text(out),
],
);
}
Widget buildResults() {
return Row(
children: [
buildOutputFields(),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(out.posCoordFile),
Text(out.posCoordFile),
Text(out.syringeWidth.toString()),
Text(out.radiusCurvRange),
Text(out.shapeFactorRange),
Text(out.initXCoordRange),
Text(out.initAngleRange),
Text(out.paramCombinations.toString()),
Text(out.numProcessors.toString()),
Text(out.juza.toString()),
Text(''),
Text(''),
Text(out.bestOutput.toString()),
Text(''),
Text(out.lowestRMSD.toString()),
Text(out.surfaceTension.toString()),
Text(out.bestRadiusOfCurvature.toString()),
Text(out.bestShapeFactor.toString()),
Text(out.bestInitialXCoordinate.toString()),
Text(out.bestInitialAngle.toString()),
Text(out.elapsedTime.toString()),
],
),
],
); //Text(Strings.sampleOut);
}
Column buildOutputFields() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Negative Coordinate File: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Positive Coordinate File: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Syringe Width (millimeter): ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Radius of Curvature: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Shape Factor Range: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Initial X Coordinate: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Initial Angle Range: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Parameter Combinations: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Number of Process: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Juza 5-Plane (1/H) Std.Dev: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(''),
Text(
'minimization success',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text('best output: ', style: TextStyle(fontWeight: FontWeight.w600)),
Text('', style: TextStyle(fontWeight: FontWeight.w600)),
Text('Lowest RMSD: ', style: TextStyle(fontWeight: FontWeight.w600)),
Text(
'Computed Surface Tension: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Best Radius of Curvature: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Best Shape Factor: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Best Initial X Coordinate: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Best Initial Angle: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
Text(
'Lapsed Time (seconds): ',
style: TextStyle(fontWeight: FontWeight.w600),
),
],
);
}
}

361
lib/scripts/compute_st.dart Normal file
View File

@ -0,0 +1,361 @@
import 'dart:io';
import 'dart:math';
import 'package:scidart/scidart.dart';
import 'extract_edge_coordinate.dart';
import 'find_optimal_params_bashforthadams.dart';
import 'juszas_equation.dart';
import 'minimize_params_optimization.dart';
void computeST(List<String> sysArgs) {
// Initial variable definitions
String imageFilePath = "";
String outputFileName = "";
String posPointsFileName = "";
String negPointsFileName = "";
int processCount = 2;
double syringeWidth = 0.70 * 1.15;
double minaRadcurv = 0.0001;
double maxaRadcurv = 2.0001;
double minbShapefact = 0.0001;
double maxbShapefact = 1.0001;
double minX = 0.0001;
double maxX = 0.5001;
double mintAngle = 0.0001;
double maxtAngle = 0.5001;
int aStep = 20;
int bStep = 20;
int xStep = 5;
int tStep = 5;
int imageFlag = 0;
int posFileFlag = 0;
int negFileFlag = 0;
int outFlag = 0;
int counter = 0;
int solveMethod = 0;
// Argument processing loop.
while (counter <= sysArgs.length - 1) {
String currentOption = sysArgs[counter];
String currentValue = "";
try {
currentValue = sysArgs[counter + 1];
} catch (e) {
exit(0);
}
if (currentOption == "-help") {
exit(0);
} else if (currentOption == "-img") {
imageFilePath = currentValue;
imageFlag = 1;
} else if (currentOption == "-filep") {
posPointsFileName = currentValue;
posFileFlag = 1;
} else if (currentOption == "-filen") {
negPointsFileName = currentValue;
negFileFlag = 1;
} else if (currentOption == "-out") {
outputFileName = currentValue;
outFlag = 1;
} else if (currentOption == "-proc") {
processCount = int.parse(currentValue);
} else if (currentOption == "-syrg") {
syringeWidth = double.parse(currentValue);
} else if (currentOption == "-a") {
minaRadcurv = double.parse(currentValue);
} else if (currentOption == "-A") {
maxaRadcurv = double.parse(currentValue);
} else if (currentOption == "-b") {
minbShapefact = double.parse(currentValue);
} else if (currentOption == "-B") {
maxbShapefact = double.parse(currentValue);
} else if (currentOption == "-x") {
minX = double.parse(currentValue);
} else if (currentOption == "-X") {
maxX = double.parse(currentValue);
} else if (currentOption == "-t") {
mintAngle = double.parse(currentValue);
} else if (currentOption == "-T") {
maxtAngle = double.parse(currentValue);
} else if (currentOption == "-ai") {
aStep = int.parse(currentValue);
} else if (currentOption == "-bi") {
bStep = int.parse(currentValue);
} else if (currentOption == "-xi") {
xStep = int.parse(currentValue);
} else if (currentOption == "-ti") {
tStep = int.parse(currentValue);
} else if (currentOption == "-solver") {
solveMethod = int.parse(currentValue);
} else {
exit(0);
}
counter = counter + 2;
}
if (outFlag == 0 && imageFlag == 1) {
print("Output file name not set. (-out)");
exit(0);
}
if (imageFlag == 1 && ((posFileFlag == 1) || (negFileFlag == 1))) {
print("Image(-img) can not be set with files option (-filep, -filen).");
print("Either set image only or files only.");
exit(0);
}
if (imageFlag == 0 &&
((posFileFlag == 1 && negFileFlag == 0) ||
(posFileFlag == 0 && negFileFlag == 1))) {
print(
"File inputs (-filep, -filen) should both be set if image input is not set.",
);
exit(0);
}
// Calculate ARange_radCurv
List<double> ARange_radCurv = [];
double a = minaRadcurv;
double ahop = (maxaRadcurv - minaRadcurv) / (aStep - 1);
while (a <= maxaRadcurv) {
ARange_radCurv.add(a);
a += ahop;
if (ahop == 0.0) {
break;
}
}
if ((maxaRadcurv - ARange_radCurv[ARange_radCurv.length - 1] - ahop).abs() <
0.000001 &&
ahop != 0.0) {
ARange_radCurv.add(maxaRadcurv);
}
// Calculate bRange_shapeFact
List<double> brangeShapefact = [];
double b = minbShapefact;
double bhop = (maxbShapefact - minbShapefact) / (bStep - 1);
while (b <= maxbShapefact) {
brangeShapefact.add(b);
b += bhop;
if (bhop == 0) {
break;
}
}
if ((maxbShapefact - brangeShapefact[brangeShapefact.length - 1] - bhop)
.abs() <
0.000001 &&
bhop != 0.0) {
brangeShapefact.add(maxbShapefact);
}
// Calculate XRange
List<double> XRange = [];
double x = minX;
double xhop = (maxX - minX) / (xStep - 1);
while (x <= maxX) {
XRange.add(x);
x += xhop;
if (xhop == 0) {
break;
}
}
if ((maxX - XRange[XRange.length - 1] - xhop).abs() < 0.000001 &&
xhop != 0.0) {
XRange.add(maxX);
}
// Calculate TRange_angle
List<double> TRange_angle = [];
double t = mintAngle;
double thop = (maxtAngle - mintAngle) / (tStep - 1);
while (t <= maxtAngle) {
TRange_angle.add(t);
t += thop;
if (thop == 0.0) {
break;
}
}
if ((maxtAngle - TRange_angle[TRange_angle.length - 1] - thop).abs() <
0.000001 &&
thop != 0.0) {
TRange_angle.add(maxtAngle);
}
print("\n");
if (imageFlag == 1) {
print("Image File : $imageFilePath");
print("Output Name : $outputFileName");
print("Negative Coordinate File : ${outputFileName}_Negative.dat");
print("Positive Coordinate File : ${outputFileName}_Positive.dat");
print("Syringe Width (millimeter): $syringeWidth");
} else {
print("Negative Coordinate File : $negPointsFileName");
print("Positive Coordinate File : $posPointsFileName");
}
print(
"Radius of Curvature Range : $minaRadcurv-$maxaRadcurv (interval: ${ARange_radCurv.length})",
);
print(
"Shape Factor Range : $minbShapefact-$maxbShapefact (interval: ${brangeShapefact.length})",
);
print("Initial X Coordinate Range: $minX-$maxX (interval: ${XRange.length})");
print(
"Initial Angle Range : $mintAngle-$maxtAngle (interval: ${TRange_angle.length})",
);
print(
"Parameter Combinations : ${ARange_radCurv.length * brangeShapefact.length * XRange.length * TRange_angle.length}",
);
print("Number of Process to Use : $processCount");
List<List<double>> posEdgePts;
List<List<double>> negEdgePts;
if (imageFlag == 1) {
var result = extractEdgeCoordinate(
imageFilePath,
outputFileName,
syringeWidth,
);
posEdgePts = result[0];
negEdgePts = result[1];
double deScaledMaxWidth = result[2];
List<double> dmsDiameters = List<double>.from(result[3]);
double HInv1 = HJuza(dmsDiameters[7] / deScaledMaxWidth, 0.8);
double HInv2 = HJuza(dmsDiameters[8] / deScaledMaxWidth, 0.9);
double HInv3 = HJuza(dmsDiameters[9] / deScaledMaxWidth, 1.0);
double HInv4 = HJuza(dmsDiameters[10] / deScaledMaxWidth, 1.1);
double HInv5 = HJuza(dmsDiameters[11] / deScaledMaxWidth, 1.2);
double HInvM = (0.20) * (HInv1 + HInv2 + HInv3 + HInv4 + HInv5);
double HInvSD = sqrt(
(0.20) *
((HInv1 - HInvM) * (HInv1 - HInvM) +
(HInv2 - HInvM) * (HInv2 - HInvM) +
(HInv3 - HInvM) * (HInv3 - HInvM) +
(HInv4 - HInvM) * (HInv4 - HInvM) +
(HInv5 - HInvM) * (HInv5 - HInvM)),
);
print("Juza 5-Plane (1/H) Std.Dev: $HInvSD");
} else {
posEdgePts = [];
try {
List<String> positiveLines = File(posPointsFileName).readAsLinesSync();
for (var Line in positiveLines) {
List<String> coordinate = Line.split(RegExp(r'\s+'));
//print(coordinate);
posEdgePts.add([
double.parse(coordinate[0]),
double.parse(coordinate[1]),
]);
}
} catch (e) {
print("Error reading positive points file.");
exit(0);
}
negEdgePts = [];
try {
List<String> negativeLines = File(negPointsFileName).readAsLinesSync();
for (var Line in negativeLines) {
List<String> coordinate = Line.split(RegExp(r'\s+'));
//print(Coordinate);
negEdgePts.add([
double.parse(coordinate[0]),
double.parse(coordinate[1]),
]);
}
} catch (e) {
print("Error reading negative points file.");
exit(0);
}
}
// Nested function bruteForce
List<dynamic> bruteForce() {
List<List<double>> posList = [];
List<List<double>> negList = [];
for (int processNo = 0; processNo < processCount; processNo++) {
posList.add(posEdgePts);
negList.add(negEdgePts);
//print(processNo);
}
List<List<double>> aRanges = [];
for (int processNo = 0; processNo < processCount; processNo++) {
aRanges.add([]);
}
int i = 0;
for (var A in ARange_radCurv) {
aRanges[i % processCount].add(A);
i += 1;
}
List<List<dynamic>> bestParams = []; // will store candidate tuples
List<int> ticksQueue = []; // simulate ticks
// Simulate process creation and execution.
for (int processNo = 0; processNo < processCount; processNo++) {
// In a real multiprocessing environment, each process would run concurrently.
// Here, we directly call the function.
findOptimalParameters(
posList[processNo],
negList[processNo],
aRanges[processNo],
brangeShapefact,
XRange,
TRange_angle,
bestParams,
ticksQueue,
);
}
stdout.write("\rParameter Search Progress : 0%");
stdout.flush();
int count = 0;
int totalTicks = ARange_radCurv.length;
// Simulate receiving ticks.
while (count < totalTicks) {
// Since ticks have been already added, just increment Count.
if (ticksQueue.isNotEmpty) {
ticksQueue.removeAt(0);
count += 1;
stdout.write(
"\rParameter Search Progress : ${(100 * (count / totalTicks)).toInt()}%",
);
stdout.flush();
}
}
print("");
List<dynamic> bestOutput = [999999999, 1, 1, 1, 1];
for (var output in bestParams) {
if (output[0] < bestOutput[0]) {
bestOutput = output;
}
}
return bestOutput;
}
// Nested function Optimize
List<dynamic> optimize() {
return optimizeParameters(posEdgePts, negEdgePts, 0.95, 0.3, 0.1, 0.2);
}
// Record start time
var startTime = DateTime.now();
// Choose which solver to use.
List<dynamic> bestOutput = (solveMethod != 1) ? optimize() : bruteForce();
print("best output: $bestOutput");
print("\nLowest RMSD : ${bestOutput[0]}");
// Computed Surface Tension: 9.8*bestOutput[1]/bestOutput[2]
print("Computed Surface Tension : ${9.8 * bestOutput[1] / bestOutput[2]}");
print("Best Radius of Curvature : ${bestOutput[1]}");
print("Best Shape Factor : ${bestOutput[2]}");
print("Best Initial X Coordinate : ${bestOutput[3]}");
print("Best Initial Angle : ${bestOutput[4]}");
var elapsed = DateTime.now().difference(startTime).inSeconds;
print("Lapsed Time in Seconds : $elapsed");
}

View File

@ -0,0 +1,344 @@
import 'dart:io';
import 'dart:math';
List<dynamic> extractEdgeCoordinate(
String imageFilePath,
String outputFileName,
double syringeWidth,
) {
// Detect the edges of the image.
// All edge pixels will white(255) and rest of the pixels will be black(0).
List<List<dynamic>> Image = CV2.imread(imageFilePath, CV2.IMREAD_COLOR);
List<List<int>> EdgeImage = CV2.Canny(Image, 100, 200);
// Find the first edge point (white pixel).
// It is assumed that first edge point part of the syringe.
int NumOfRow = EdgeImage.length;
int NumOfCol = EdgeImage[0].length;
List<int> EdgePoint = [0, 0];
int EdgePointFlag = 0;
for (int RowIndex = 0; RowIndex < NumOfRow; RowIndex++) {
for (int ColIndex = 0; ColIndex < NumOfCol; ColIndex++) {
if (EdgeImage[RowIndex][ColIndex] == 255) {
EdgePoint = [RowIndex, ColIndex];
EdgePointFlag = 1;
break;
}
}
if (EdgePointFlag == 1) {
break;
}
}
// Color the all the syringe and droplet pixels differently (70).
// Determine the left-most, right-most, and bottom droplet pixels.
EdgeImage[EdgePoint[0]][EdgePoint[1]] = 70;
List<List<int>> DropletEdgePoints = [];
DropletEdgePoints.add(EdgePoint);
int LeftMostCol = 9999999999;
int RightMostCol = 0;
int BottomMostRow = 0;
int BottomMostRow2 = 0;
int CenterCol = 0;
while (DropletEdgePoints.length != 0) {
List<int> CurrentPoint = DropletEdgePoints[0];
DropletEdgePoints.remove(CurrentPoint);
List<List<int>> NeighborPoints = [];
for (int RowOffset = -3; RowOffset < 4; RowOffset++) {
for (int ColOffset = -3; ColOffset < 4; ColOffset++) {
if (RowOffset != 0 || ColOffset != 0) {
NeighborPoints.add([
CurrentPoint[0] + RowOffset,
CurrentPoint[1] + ColOffset,
]);
}
}
}
for (List<int> point in NeighborPoints) {
int PointRow = point[0];
int PointCol = point[1];
if ((0 <= PointRow && PointRow < NumOfRow) &&
(0 <= PointCol && PointCol < NumOfCol)) {
if (EdgeImage[PointRow][PointCol] == 255) {
DropletEdgePoints.add([PointRow, PointCol]);
EdgeImage[PointRow][PointCol] = 70;
if (PointRow > BottomMostRow) {
BottomMostRow = PointRow;
}
if (PointCol > RightMostCol) {
RightMostCol = PointCol;
}
if (PointCol < LeftMostCol) {
LeftMostCol = PointCol;
}
}
}
}
}
CenterCol = (LeftMostCol + RightMostCol) ~/ 2;
for (int Row = 0; Row < BottomMostRow + 1; Row++) {
if (EdgeImage[Row][CenterCol] == 70) {
BottomMostRow2 = Row;
}
}
BottomMostRow = BottomMostRow2;
// Determine the transition row between the syringe pixels and the droplet pixels.
int PreviousWidth = 0;
int WidthIncCount = 5;
List<int> WidthHistory = List.filled(WidthIncCount + 1, 0);
int TransitionRow = 0;
for (int Row = 0; Row < BottomMostRow; Row++) {
int LeftEdgeCol = 0;
int RightEdgeCol = 9999999;
for (int Col = LeftMostCol - 1; Col < RightMostCol + 1; Col++) {
if (EdgeImage[Row][Col] == 70) {
LeftEdgeCol = Col;
break;
}
}
for (int Col = RightMostCol + 1; Col >= LeftMostCol - 1; Col--) {
if (EdgeImage[Row][Col] == 70) {
RightEdgeCol = Col;
break;
}
}
int CurrentWidth = RightEdgeCol - LeftEdgeCol;
if ((CurrentWidth != PreviousWidth) &&
(0 < CurrentWidth) &&
(CurrentWidth < (9999999 - NumOfCol))) {
for (int i = 0; i < WidthIncCount; i++) {
WidthHistory[i] = WidthHistory[i + 1];
}
WidthHistory[WidthIncCount] = CurrentWidth;
}
int WidthIncreaseCount = 0;
for (int i = 0; i < WidthIncCount; i++) {
if (WidthHistory[i] < WidthHistory[i + 1]) {
WidthIncreaseCount += 1;
}
}
if (WidthIncreaseCount == WidthIncCount) {
TransitionRow = Row;
break;
}
if ((0 < CurrentWidth) && (CurrentWidth < (9999999 - NumOfCol))) {
PreviousWidth = CurrentWidth;
}
}
// Determine the width of the syringe in terms of pixel count
int WidthSum = 0;
int WidthCount = 0;
int SyringeLevel = (0.75 * TransitionRow).toInt();
for (int Row = 0; Row < TransitionRow; Row++) {
int RightEdgeCol = 9999999;
int LeftEdgeCol = 0;
for (int Col = LeftMostCol; Col < CenterCol + 1; Col++) {
if (EdgeImage[Row][Col] == 70) {
EdgeImage[Row][Col] = 255;
LeftEdgeCol = Col;
break;
}
}
for (int Col = RightMostCol; Col > CenterCol - 1; Col--) {
if (EdgeImage[Row][Col] == 70) {
EdgeImage[Row][Col] = 255;
RightEdgeCol = Col;
break;
}
}
int CurrentWidth = RightEdgeCol - LeftEdgeCol;
if (WidthCount <= SyringeLevel) {
if ((0 < CurrentWidth) && (CurrentWidth < (9999999 - NumOfCol))) {
WidthSum += CurrentWidth;
WidthCount += 1;
}
}
}
double AverageWidth = (WidthSum.toDouble()) / (WidthCount.toDouble());
// Extract the pixel coordinates of all droplet pixels (color: 70)
List<List<int>> PositiveEdgePoints = [];
List<List<int>> NegativeEdgePoints = [];
for (int Row = BottomMostRow; Row > TransitionRow - 1; Row--) {
for (int Col = LeftMostCol; Col < CenterCol + 1; Col++) {
if (EdgeImage[Row][Col] == 70) {
NegativeEdgePoints.add([Row, Col]);
break;
}
}
for (int Col = RightMostCol; Col > CenterCol; Col--) {
if (EdgeImage[Row][Col] == 70) {
PositiveEdgePoints.add([Row, Col]);
break;
}
}
}
// Scale the point coordinates by using the syringe length
double SyringeWidth = syringeWidth;
double ScalingFactor = SyringeWidth / AverageWidth;
List<List<double>> ScaledNegativeEdgePoints = [];
String negativeFileName = outputFileName + "_Negative.dat";
IOSink NegativeCoordinatesFile = File(negativeFileName).openWrite();
for (List<int> point in NegativeEdgePoints) {
int Row = point[0];
int Col = point[1];
double Row2 = (ScalingFactor * (BottomMostRow - Row).toDouble()) + 0.0001;
double Col2 = ScalingFactor * (Col - CenterCol).toDouble();
NegativeCoordinatesFile.write("$Col2 $Row2\n");
ScaledNegativeEdgePoints.add([Col2, Row2]);
}
NegativeCoordinatesFile.close();
List<List<double>> ScaledPositiveEdgePoints = [];
String positiveFileName = "${outputFileName}_Positive.dat";
IOSink PositiveCoordinatesFile = File(positiveFileName).openWrite();
for (List<int> point in PositiveEdgePoints) {
int Row = point[0];
int Col = point[1];
double Row2 = (ScalingFactor * (BottomMostRow - Row).toDouble()) + 0.0001;
double Col2 = ScalingFactor * (Col - CenterCol).toDouble();
PositiveCoordinatesFile.write("$Col2 $Row2\n");
ScaledPositiveEdgePoints.add([Col2, Row2]);
}
PositiveCoordinatesFile.close();
// Draw the detected edge points of the droplet.
// List<List<dynamic>> Image2 = deepCopy(Image);
for (int Row = 0; Row < NumOfRow; Row++) {
for (int Col = 0; Col < NumOfCol; Col++) {
Image[Row][Col] = 0;
// For EdgeImage, since it is a separate 2D list of ints, set to 0.
EdgeImage[Row][Col] = 0;
}
}
for (List<int> Point in PositiveEdgePoints) {
Image[Point[0]][Point[1]] = [0, 0, 255];
EdgeImage[Point[0]][Point[1]] = 255;
}
for (List<int> Point in NegativeEdgePoints) {
Image[Point[0]][Point[1]] = [0, 0, 255];
EdgeImage[Point[0]][Point[1]] = 255;
}
// Draw the center column of the drop
CV2.line(
Image,
Point(CenterCol.toDouble(), TransitionRow.toDouble()),
Point(CenterCol.toDouble(), BottomMostRow.toDouble()),
[0, 255, 0],
1,
); // image, start, end, color(BGR), thickness
double MaxWidthRatio = 0.0;
for (int Row = BottomMostRow; Row > TransitionRow - 1; Row--) {
int RightCol = 99999;
int LeftCol = -1;
for (int Col = LeftMostCol; Col < CenterCol + 1; Col++) {
if (EdgeImage[Row][Col] == 255) {
LeftCol = Col;
break;
}
}
for (int Col = RightMostCol; Col > CenterCol; Col--) {
if (EdgeImage[Row][Col] == 255) {
RightCol = Col;
break;
}
}
double WidthRatio =
(RightCol - LeftCol).toDouble() /
(RightMostCol - LeftMostCol).toDouble();
if (WidthRatio < 1.0 && WidthRatio > MaxWidthRatio) {
MaxWidthRatio = WidthRatio;
}
}
List<List<int>> RowWithMaxWidth = [];
for (int Row = BottomMostRow; Row > TransitionRow - 1; Row--) {
int RightCol = 99999;
int LeftCol = -1;
for (int Col = LeftMostCol; Col < CenterCol + 1; Col++) {
if (EdgeImage[Row][Col] == 255) {
LeftCol = Col;
break;
}
}
for (int Col = RightMostCol; Col > CenterCol; Col--) {
if (EdgeImage[Row][Col] == 255) {
RightCol = Col;
break;
}
}
double WidthRatio =
(RightCol - LeftCol).toDouble() /
(RightMostCol - LeftMostCol).toDouble();
if (WidthRatio == MaxWidthRatio) {
RowWithMaxWidth.add([Row, LeftCol, RightCol]);
}
}
// Draw the main diameter
int n = RowWithMaxWidth.length;
n = (n / 2).toInt();
int Row = RowWithMaxWidth[n][0];
int LeftCol = RowWithMaxWidth[n][1];
int RightCol = RowWithMaxWidth[n][2];
CV2.line(
Image,
Point(LeftCol.toDouble(), Row.toDouble()),
Point(RightCol.toDouble(), Row.toDouble()),
[0, 255, 0],
1,
);
// Measure and draw the ten diameters
List<int> Diameters = [];
int MaxWidth = RightMostCol - LeftMostCol;
for (int i = 1; i < 13; i++) {
for (int Col = LeftMostCol; Col < CenterCol + 1; Col++) {
if (EdgeImage[BottomMostRow - (i * MaxWidth ~/ 10)][Col] == 255) {
LeftCol = Col;
break;
}
}
for (int Col = RightMostCol; Col > CenterCol; Col--) {
if (EdgeImage[BottomMostRow - (i * MaxWidth ~/ 10)][Col] == 255) {
RightCol = Col;
break;
}
}
Diameters.add(RightCol - LeftCol);
CV2.line(
Image,
Point(
LeftCol.toDouble(),
(BottomMostRow - (i * MaxWidth ~/ 10)).toDouble(),
),
Point(
RightCol.toDouble(),
(BottomMostRow - (i * MaxWidth ~/ 10)).toDouble(),
),
[255, 0, 0],
1,
);
}
// Scale the 12 drop diameters
for (int i = 0; i < 12; i++) {
Diameters[i] = (Diameters[i] * ScalingFactor).toInt();
}
double ScaledMaxWidth = ScalingFactor * MaxWidth.toDouble();
// double ScaledUpWidth = ScalingFactor * (RightCol - LeftCol).toDouble();
return [
ScaledPositiveEdgePoints,
ScaledNegativeEdgePoints,
ScaledMaxWidth,
Diameters,
];
}

View File

@ -0,0 +1,63 @@
import 'dart:math';
import 'package:surface_tension_calculator/scripts/rk4.dart';
/// ========== FINDING OPTIMAL PARAMETERS for BASHFORTH-ADAMS SOLUTION ==========
void findOptimalParameters(
List<double> posEdgePoints,
List<double> negEdgePoints,
List<double> aRangeRadCurv,
List<double> bRangeShapeFact,
List<double> xRange,
List<double> tRangeAngle,
List<dynamic> params,
List<dynamic> ticks,
) {
var rk4PosTable = makeRK4Table(posEdgePoints);
var rk4NegTable = makeRK4Table(negEdgePoints);
double minimumRMSD = 999999.0;
List<double> bestParameters = [0, 0, 0, 0];
for (var aRadCurv in aRangeRadCurv) {
for (var bShapeFact in bRangeShapeFact) {
for (var x in xRange) {
for (var tAngle in tRangeAngle) {
double fTotalDiffSquared = 0.0;
rk4PosTable[0][3] = x;
rk4PosTable[0][4] = tAngle;
rk4NegTable[0][3] = x;
rk4NegTable[0][4] = tAngle;
fTotalDiffSquared += bashforthAdamsRK4(
rk4PosTable,
aRadCurv,
bShapeFact,
1.0,
);
fTotalDiffSquared += bashforthAdamsRK4(
rk4NegTable,
aRadCurv,
bShapeFact,
-1.0,
);
double currentRMSD = sqrt(
fTotalDiffSquared / (posEdgePoints.length + negEdgePoints.length),
);
if (currentRMSD < minimumRMSD) {
minimumRMSD = currentRMSD;
bestParameters = [aRadCurv, bShapeFact, x, tAngle];
}
}
ticks.add("#");
}
}
}
double aRadCurv = bestParameters[0];
double bShapeFact = bestParameters[1];
double x = bestParameters[2];
double tAngle = bestParameters[3];
// double st = (9.8 * aRadCurv) / bShapeFact;
params.add((minimumRMSD, aRadCurv, bShapeFact, x, tAngle));
}

BIN
lib/scripts/img_V01 Executable file

Binary file not shown.

799
lib/scripts/img_V01.py Normal file
View File

@ -0,0 +1,799 @@
#!/usr/bin/env python
# Surface Tension Calculation via Pendant Drop Method (Bashforth-Adams)
# Created by : Ren Tristan A. de la Cruz
# Created on : 2016 July 26
# Last Modified : 2025 January 27
# Contact : rentristandelacruz@gmail.com
# Contact : radelacruz@up.edu.ph
# ========== IMPORTS ==========
import math
import copy
import time
import sys
import cv2
import multiprocessing
import numpy as np
import scipy.optimize #as spo
# ========== READ THE IMAGE & EXTRACT THE EDGE COORDINATES ==========
def ExtractEdgeCoordinate(_ImageFilePath, _OutputFileName, _SyringeWidth):
# Detect the edges of the image.
# All edge pixels will white(255) and rest of the pixels will be black(0).
Image = cv2.imread(_ImageFilePath, cv2.IMREAD_COLOR)
EdgeImage = cv2.Canny(Image, 100, 200)
# Find the first edge point (white pixel).
# It is assumed that first edge point part of the syringe.
NumOfRow, NumOfCol = EdgeImage.shape
EdgePoint = (0, 0)
EdgePointFlag = 0
for RowIndex in range(NumOfRow):
for ColIndex in range(NumOfCol):
if EdgeImage[RowIndex, ColIndex] == 255:
EdgePoint = (RowIndex, ColIndex)
EdgePointFlag = 1
break
if EdgePointFlag == 1:
break
# Color the all the syringe and droplet pixels differently (70).
# Determine the left-most, right-most, and bottom droplet pixels.
EdgeImage[EdgePoint] = 70
DropletEdgePoints = []
DropletEdgePoints.append(EdgePoint)
LeftMostCol = 9999999999
RightMostCol = 0
BottomMostRow = 0
BottomMostRow2 = 0
CenterCol = 0
while (len(DropletEdgePoints) != 0):
CurrentPoint = DropletEdgePoints[0]
DropletEdgePoints.remove(CurrentPoint)
NeighborPoints = []
for RowOffset in range(-3,4, 1):
for ColOffset in range(-3,4,1):
if(RowOffset != 0 or ColOffset != 0):
NeighborPoints.append((CurrentPoint[0]+RowOffset, CurrentPoint[1]+ColOffset))
for PointRow, PointCol in NeighborPoints:
if (0 <= PointRow and PointRow < NumOfRow) and (0 <= PointCol and PointCol < NumOfCol):
if EdgeImage[PointRow][PointCol] == 255:
DropletEdgePoints.append((PointRow, PointCol))
EdgeImage[PointRow, PointCol] = 70
if PointRow > BottomMostRow:
BottomMostRow = PointRow
if PointCol > RightMostCol:
RightMostCol = PointCol
if PointCol < LeftMostCol:
LeftMostCol = PointCol
CenterCol = (LeftMostCol + RightMostCol)//2
for Row in range(BottomMostRow + 1):
if EdgeImage[Row][CenterCol] == 70:
BottomMostRow2 = Row
BottomMostRow = BottomMostRow2
# Determine the transition row between the syringe pixels and the droplet pixels.
PreviousWidth = 0
WidthIncCount = 5
WidthHistory = [0]*(WidthIncCount+1)
TransitionRow = 0
for Row in range(BottomMostRow):
LeftEdgeCol = 0
RightEdgeCol = 9999999
for Col in range(LeftMostCol-1, RightMostCol+1, 1):
if EdgeImage[Row,Col] == 70:
LeftEdgeCol = Col
break
for Col in range(RightMostCol+1, LeftMostCol-1, -1):
if EdgeImage[Row,Col] == 70:
RightEdgeCol = Col
break
CurrentWidth = RightEdgeCol - LeftEdgeCol
if (CurrentWidth != PreviousWidth) and (0 < CurrentWidth) and (CurrentWidth < (9999999-NumOfCol)):
for i in range(WidthIncCount):
WidthHistory[i] = WidthHistory[i+1]
WidthHistory[WidthIncCount] = CurrentWidth
WidthIncreaseCount = 0
for i in range(WidthIncCount):
if WidthHistory[i] < WidthHistory[i+1]:
WidthIncreaseCount += 1
if WidthIncreaseCount == WidthIncCount:
TransitionRow = Row
break
if (0 < CurrentWidth) and (CurrentWidth < (9999999-NumOfCol)):
PreviousWidth = CurrentWidth
# Determine the width of the syringe in terms of pixel count
WidthSum = 0
WidthCount = 0
SyringeLevel = int(0.75 * TransitionRow)
for Row in range(TransitionRow):
RightEdgeCol = 9999999
LeftEdgeCol = 0
for Col in range(LeftMostCol, CenterCol + 1, 1):
if EdgeImage[Row, Col] == 70:
EdgeImage[Row, Col] = 255
LeftEdgeCol = Col
break
for Col in range(RightMostCol, CenterCol - 1, -1):
if EdgeImage[Row, Col] == 70:
EdgeImage[Row, Col] = 255
RightEdgeCol = Col
break
CurrentWidth = RightEdgeCol - LeftEdgeCol
if WidthCount <= SyringeLevel:
if (0 < CurrentWidth) and (CurrentWidth < (9999999-NumOfCol)):
WidthSum += CurrentWidth
WidthCount += 1
AverageWidth = float(WidthSum)/float(WidthCount)
# Extract the pixel coordinates of all droplet pixels (color: 70)
PositiveEdgePoints = []
NegativeEdgePoints = []
for Row in range(BottomMostRow, TransitionRow-1 ,-1):
for Col in range(LeftMostCol, CenterCol + 1, 1):
if EdgeImage[Row, Col] == 70:
NegativeEdgePoints.append((Row, Col))
break
for Col in range(RightMostCol, CenterCol, -1):
if EdgeImage[Row, Col] == 70:
PositiveEdgePoints.append((Row, Col))
break
# Scale the point coordinates by using the syringe length
SyringeWidth = _SyringeWidth
ScalingFactor = SyringeWidth/AverageWidth
ScaledNegativeEdgePoints = []
NegativeCoordinatesFile = open(_OutputFileName+"_Negative.dat", 'w')
for Row, Col in NegativeEdgePoints:
Row2 = (ScalingFactor*float(BottomMostRow-Row)) + 0.0001
Col2 = ScalingFactor*float(Col - CenterCol)
NegativeCoordinatesFile.write(str(Col2) + " " + str(Row2) + "\n")
ScaledNegativeEdgePoints.append((Col2,Row2))
NegativeCoordinatesFile.close()
ScaledPositiveEdgePoints = []
PositiveCoordinatesFile = open(_OutputFileName+"_Positive.dat", 'w')
for Row, Col in PositiveEdgePoints:
Row2 = (ScalingFactor*float(BottomMostRow-Row)) + 0.0001
Col2 = ScalingFactor*float(Col - CenterCol)
PositiveCoordinatesFile.write(str(Col2) + " " + str(Row2) + "\n")
ScaledPositiveEdgePoints.append((Col2, Row2))
PositiveCoordinatesFile.close()
# Draw the detected edge points of the droplet.
Image2 = copy.deepcopy(Image)
for Row in range(0, NumOfRow):
for Col in range(0, NumOfCol):
Image[(Row,Col)] = 0
EdgeImage[(Row,Col)] = 0
for Point in PositiveEdgePoints:
Image[Point] = (0, 0, 255)
EdgeImage[Point] = 255
for Point in NegativeEdgePoints:
Image[Point] = (0, 0, 255)
EdgeImage[Point] = 255
# Draw the center column of the drop
cv2.line(Image, (CenterCol, TransitionRow), (CenterCol, BottomMostRow), (0, 255, 0), 1) # image, start, end, color(BGR), thickness
MaxWidthRatio = 0.0
for Row in range(BottomMostRow, TransitionRow-1 ,-1):
RightCol = 99999
LeftCol = -1
for Col in range(LeftMostCol, CenterCol + 1, 1):
if EdgeImage[Row, Col] == 255:
LeftCol = Col
break
for Col in range(RightMostCol, CenterCol, -1):
if EdgeImage[Row, Col] == 255:
RightCol = Col
break
WidthRatio = float(RightCol - LeftCol)/float(RightMostCol-LeftMostCol)
if WidthRatio < 1.0 and WidthRatio > MaxWidthRatio:
MaxWidthRatio = WidthRatio
RowWithMaxWidth = []
for Row in range(BottomMostRow, TransitionRow-1 ,-1):
RightCol = 99999
LeftCol = -1
for Col in range(LeftMostCol, CenterCol + 1, 1):
if EdgeImage[Row, Col] == 255:
LeftCol = Col
break
for Col in range(RightMostCol, CenterCol, -1):
if EdgeImage[Row, Col] == 255:
RightCol = Col
break
WidthRatio = float(RightCol - LeftCol)/float(RightMostCol-LeftMostCol)
if WidthRatio == MaxWidthRatio:
RowWithMaxWidth.append((Row, LeftCol, RightCol))
# Draw the main diameter
n = len(RowWithMaxWidth)
n = int(n/2)
Row = RowWithMaxWidth[n][0]
LeftCol = RowWithMaxWidth[n][1]
RightCol = RowWithMaxWidth[n][2]
cv2.line(Image, (LeftCol, Row), (RightCol, Row), (0, 255, 0), 1)
# Measure and draw the ten diameters
Diameters = []
MaxWidth = RightMostCol - LeftMostCol
for i in range(1, 13, 1):
for Col in range(LeftMostCol, CenterCol + 1, 1):
if EdgeImage[BottomMostRow-int(i*MaxWidth/10.0), Col] == 255:
LeftCol = Col
break
for Col in range(RightMostCol, CenterCol, -1):
if EdgeImage[BottomMostRow-int(i*MaxWidth/10.0), Col] == 255:
RightCol = Col
break
Diameters.append(RightCol-LeftCol)
cv2.line(Image, (LeftCol, BottomMostRow-int(i*MaxWidth/10.0)), (RightCol, BottomMostRow-int(i*MaxWidth/10.0)), (255, 0, 0), 1)
#DELETE THIS
#cv2.imwrite("1.jpg", Image)
#sys.exit()
# Scale the 12 drop diameters
for i in range(12):
Diameters[i] *= ScalingFactor
ScaledMaxWidth = ScalingFactor*float(MaxWidth)
ScaledUpWidth = ScalingFactor*float(RightCol - LeftCol)
return ScaledPositiveEdgePoints, ScaledNegativeEdgePoints, ScaledMaxWidth, Diameters
# ========== RK4 for BASHFORTH-ADAMS FORMULATION ==========
def BashforthAdamsRK4(_afRK4Table, _fApexCurvature, _fShapeFactor, _fSign):
A = _fApexCurvature
A2 = 2.0/A
B = _fShapeFactor
KX0 = 1.0
KT0 = 1.0
Inv6 = 1.0/6.0
fTotalDiffSquared = 0.0
n = 1
while n < len(_afRK4Table):
x = _afRK4Table[n-1][3] # predicted x
y = _afRK4Table[n-1][1] # actual y
t = _afRK4Table[n-1][4] # predicted angle
dy = _afRK4Table[n][2] # actual y diff
AG = 1.0 #AG: Added
ydy = y + 0.5 * dy
KX1 = dy * (1.0 / math.tan(t))
KT1 = dy * (1.0 / math.sin(t)) * (A2 - (B * y) - (math.sin(t) / (AG * x))) # A -> AG
KX2 = dy * (1.0 / math.tan(t + 0.5 * KT1))
STKT1 = math.sin(t + 0.5 * KT1)
KT2 = dy * (1.0 / STKT1) * (A2 - (B * ydy) - (STKT1 / (AG * (x + 0.5 * KX1)))) # A -> AG
KX3 = dy * (1.0 / math.tan(t + 0.5 * KT2))
STKT2 = math.sin(t + 0.5 * KT2)
KT3 = dy * (1.0 / STKT2) * (A2 - (B * ydy) - (STKT2 / (AG * (x + 0.5 * KX2)))) # A -> AG
KX4 = dy * (1.0 / math.tan(t + KT3))
STKT3 = math.sin(t + KT3)
KT4 = dy * (1.0 / STKT3) * (A2 - (B * (y + dy)) - (STKT3 / (AG * (x + KX3)))) # A -> AG
KX = (KX1 + 2.0 * (KX2 + KX3) + KX4) * Inv6
KT = (KT1 + 2.0 * (KT2 + KT3) + KT4) * Inv6
_afRK4Table[n][3] = x + KX
_afRK4Table[n][4] = t + KT
fTotalDiffSquared += (_afRK4Table[n][0] - (_fSign * _afRK4Table[n][3])) ** 2
n += 1
return fTotalDiffSquared
def MakeRK4table(_edgePoints):
InitialValues = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0]
rk4table = []
rk4table.append(InitialValues)
yPrev = 0.0
for x, y in _edgePoints:
EntryValues = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
EntryValues[0] = x
EntryValues[1] = y
EntryValues[2] = y - yPrev
yPrev = y
rk4table.append(EntryValues)
return rk4table
"""
RK4NegTable = []
RK4NegTable.append(InitialValues)
yPrev = 0.0
for x, y in _NegEdgePoints:
EntryValues = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
EntryValues[0] = x
EntryValues[1] = y
EntryValues[2] = y - yPrev
yPrev = y
RK4NegTable.append(EntryValues)
return RK4NegTable
"""
# ========== FINDING OPTIMAL PARAMETERS for BASHFORTH-ADAMS SOLUTION ==========
def FindOptimalParameters(_PosEdgePoints, _NegEdgePoints, _ARange_radCurv, _BRange_shapeFact, _XRange, _TRange_angle, _Params, _Ticks):
RK4PosTable = MakeRK4table(_PosEdgePoints)
RK4NegTable = MakeRK4table(_NegEdgePoints)
MinimumRMSD = 999999.0
BestParameters = [0, 0, 0, 0]
for A_radCurv in _ARange_radCurv:
for B_shapeFact in _BRange_shapeFact:
for X in _XRange:
for T_angle in _TRange_angle:
# x, y, dy, x*, t*, KX1, KT1, KX2, KT2, KX3, KT3, KX4, KT4
fTotalDiffSquared = 0.0
RK4PosTable[0][3] = X
RK4PosTable[0][4] = T_angle
RK4NegTable[0][3] = X
RK4NegTable[0][4] = T_angle
fTotalDiffSquared += BashforthAdamsRK4(RK4PosTable, A_radCurv, B_shapeFact, 1.0)
fTotalDiffSquared += BashforthAdamsRK4(RK4NegTable, A_radCurv, B_shapeFact, -1.0)
CurrentRMSD = (fTotalDiffSquared/(len(_PosEdgePoints) + len(_NegEdgePoints)))**0.5
if CurrentRMSD < MinimumRMSD:
MinimumRMSD = CurrentRMSD
BestParameters = [A_radCurv, B_shapeFact, X, T_angle]
_Ticks.put("#")
A_radCurv = BestParameters[0]
B_shapeFact = BestParameters[1]
X = BestParameters[2]
T_angle = BestParameters[3]
ST = (9.8 * A_radCurv) / B_shapeFact
_Params.put((MinimumRMSD, A_radCurv, B_shapeFact, X, T_angle))
# ========== FINDING OPTIMAL PARAMETERS USING MINIMZATION ==========
def OptimizeParameters(_PosEdgePoints, _NegEdgePoints, _AGuess_radCurv, _BGuess_shapeFact, _XGuess, _TGuess_angle):
RK4PosTable = MakeRK4table(_PosEdgePoints)
RK4NegTable = MakeRK4table(_NegEdgePoints)
"""
ARG_RAD = 0
ARG_SHAPE = 1
ARG_X = 2
ARG_ANGLE = 3
"""
def msd(args):
A_radCurv = args[0]
B_shapeFact = args[1]
X = args[2]
T_angle = args[3]
RK4PosTable[0][3] = X
RK4PosTable[0][4] = T_angle
RK4NegTable[0][3] = X
RK4NegTable[0][4] = T_angle
fTotalDiffSquared = BashforthAdamsRK4(RK4PosTable, A_radCurv, B_shapeFact, 1.0) + BashforthAdamsRK4(RK4NegTable, A_radCurv, B_shapeFact, -1.0)
return (fTotalDiffSquared/(len(_PosEdgePoints) + len(_NegEdgePoints)))**0.5
optResult = scipy.optimize.minimize(msd, [_AGuess_radCurv, _BGuess_shapeFact, _XGuess, _TGuess_angle])
if optResult.success:
print("minimization success")
#print(optResult.x)
#print(optResult.fun)
return [float(optResult.fun)] + optResult.x.tolist()
else:
return [9999999,0,0,0,0]
# ========== Juza's equation for 1/H ==========
def HJuza(_S_diamRatio, _K):
# LowerS, UpperS, Ke, Kex, Kep, K3, K2, K1, K0
# 0.82 - 0.999
Coefficient8 = [[ 0.82, 0.83, 1791452.8, 533.85850, 170.55837, -81676.15812, 202187.89690, -166836.27680, 45888.22973],
[ 0.83, 0.84, 189.07578, 272.52455, 72.730022, -19963.60152, 50019.79936, -41775.38186, 11629.85610],
[ 0.84, 0.85, 7.1549709, 165.45945, 35.285687, -6944.66848, 17609.43832, -14883.84379, 4193.34232],
[ 0.85, 0.87, 1.1496269, 95.066407, 12.600013, -2158.91585, 5571.60176, -4792.82331, 1374.26272],
[ 0.87, 0.895, 0.47873040, 50.357240, 0.089787883, -567.76534, 1503.51828, -1327.11835, 390.45562],
[ 0.895, 0.93, 0.35255000, 25.498893, -5.4176608, -165.99710, 454.58851, -414.93772, 126.23908],
[ 0.93, 0.97, 0.32002037, 6.8474560, -8.0901766, -102.84970, 293.25377, -278.69176, 88.27639],
[ 0.97, 0.99, 0.30845061, -32.343947, -10.455428, -475.69091, 1398.86173, -1371.17931, 448.00538],
[ 0.99, 0.999, 0.30110729, -333.50440, -15.711260, -11334.69334, 33822.93507, -33642.61426, 11154.37157]]
# 0.635 - 0.997
Coefficient9 = [[ 0.635, 0.645, 58249804, 119.64583, 89.483167, -28072.11478, 53918.28040, -34519.94034, 7366.77050],
[ 0.645, 0.66, 5100.2910, 70.920100, 46.811007, -9051.10879, 17726.62108, -15572.21470, 2518.10214],
[ 0.66, 0.685, 19.518159, 38.509198, 19.951285, -2338.00251, 4719.64936, -3175.58038, 712.17229],
[ 0.685, 0.72, 1.3823760, 20.055606, 5.9746092, -522.67397, 1102.26575, -774.75596, 181.49582],
[ 0.72, 0.77, 0.49074291, 10.484929, -0.31885445, -111.99730, 250.54286, -186.78287, 46.40581],
[ 0.77, 0.84, 0.34607161, 5.4063548, -2.9788620, -24.21100, 58.53312, -47.15244, 12.65668],
[ 0.84, 0.93, 0.31342828, 2.1140055, -4.1151543, -9.16969, 24.37544, -21.58758, 6.36954],
[ 0.93, 0.98, 0.30397966, -3.6334200, -4.9395699, -29.80626, 85.43510, -81.61766, 25.98669],
[ 0.98, 0.997, 0.30007321, -34.095653, -6.1531194, -434.11439, 1287.65238, -1273.10762, 419.56923]]
# 0.17 - 0.983
Coefficient10 = [[ 0.17, 0.30, 0.31081678, -0.086278355, -2.7023254, -13.95071, 10.30398, -2.49619, 0.19805],
[ 0.30, 0.45, 0.30636442, -0.094871613, -2.7246428, 0.38766, -0.44128, 0.16613, -0.02068],
[ 0.45, 0.68, 0.31156188, -0.063734909, -2.6789763, 0.41537, -0.71168, 0.40321, -0.07551],
[ 0.68, 0.90, 0.31195754, -0.092720991, -2.6863859, -0.44813, 1.06637, -0.84260, 0.22106],
[ 0.90, 0.983, 0.30712046, -1.5619311, -2.9809169, -5.74538, 16.23781, -15.29128, 4.79808]]
# 0.06 - 0.953
Coefficient11 = [[ 0.06, 0.08, 1.6030433, 0.043380701, -0.15208454, -1341.32950, 282.27311, -19.71010, 0.45664],
[ 0.08, 0.13, 0.85491410, -0.056761738, -0.65372414, -160.97221, 50.59168, -5.23625, 0.17842],
[ 0.13, 0.20, 0.57401485, -0.15265815, -1.0443069, -42.79943, 21.22139, -3.47357, 0.18765],
[ 0.20, 0.30, 0.41622542, -0.27871823, -1.4464670, -14.08741, 10.58863, -2.63159, 0.21621],
[ 0.30, 0.44, 0.33587066, -0.42674255, -1.8022482, -3.22540, 3.58964, -1.32194, 0.16106],
[ 0.44, 0.75, 0.31504207, -0.51197565, -1.9499037, 0.07609, -0.13681, 0.08090, -0.01572],
[ 0.75, 0.953, 0.31658938, -0.49808609, -1.9290368, -0.24018, 0.61450, -0.52258, 0.14771]]
# 0.10 - 0.902
Coefficient12 = [[ 0.10, 0.12, 103.56308, 1.2019976, 4.4869062, -6625.95443, 2292.46751, -264.28560, 10.15218],
[ 0.12, 0.14, 2.1585254, 0.34129627, 0.83653215, -742.15388, 289.52312, -37.59979, 1.62555],
[ 0.14, 0.19, 0.65654048, 0.031877859, -0.37693028, -66.92134, 33.18430, -5.45788, 0.29773],
[ 0.19, 0.27, 0.45076908, -0.10378254, -0.82836350, -10.66154, 7.36263, -1.68453, 0.12768],
[ 0.27, 0.40, 0.37085833, -0.21965481, -1.1287016, -2.87493, 2.88810, -0.95966, 0.10546],
[ 0.40, 0.55, 0.33605594, -0.33317467, -1.3397591, -0.70394, 1.00402, -0.47492, 0.07450],
[ 0.55, 0.902, 0.32790337, -0.39597877, -1.4180887, 0.00490, -0.01070, 0.00769, -0.00182]]
if ( _K == 0.8):
Coeff = Coefficient8
if ( _K == 0.9):
Coeff = Coefficient9
if ( _K == 1.0):
Coeff = Coefficient10
if ( _K == 1.1):
Coeff = Coefficient11
if ( _K == 1.2):
Coeff = Coefficient12
# Juza 1996/1997 Equation: 8
for Entry_k in Coeff:
if (Entry_k[0] <= _S_diamRatio) and (_S_diamRatio <= Entry_k[1]):
HInv = Entry_k[2] * _S_diamRatio**(Entry_k[3] * math.log(_S_diamRatio) + Entry_k[4]) + Entry_k[5] * (_S_diamRatio**3.0) + Entry_k[6] * (_S_diamRatio **2.0) + Entry_k[7] * _S_diamRatio + Entry_k[8]
return HInv
break
return 0
# ========== USAGE PRINTER ==========
def PrintCorrectUsage():
print("\nDESCRIPTION:\n")
print("This program will take an image or a set of files (coordinate points) representing")
print("a droplet and will process the data to calculate the surface tension of the droplet.")
print("When an image file is set, the program will extract the coordinate of the points")
print("representing the edge of the droplet. When the files are set, it is assume that they")
print("will already contain the points representing the edge of the droplet. RK4 will be")
print("use to solve the Bashforth-Adams set of equations to get the parameters for surface")
print("tension calculation. parameters: A (apex radius curvature) and B (shape factor)")
print("\nUSAGE:\n")
print("Syntax 1: ./img.py -option1 value1 -option2 value2")
print("Syntax 2: python img.py -option1 value1 -option2 value2\n")
print("Options:")
print("-help : Prints this help message. ")
print("-out : Reference name for all the output files (coordinate files, RK4 dump, etc.).")
print(" : This should always be set. ")
print("-img : Image file to be processed. ")
print(" : When this is set, the file name inputs (-filep, -filen) should not be set.")
print("-filep : File containing the positive(x) point coordinates to be processed.")
print(" : If this is set, -filen should also be set and -img should not be set.")
print("-filen : File containing the negative(x) point coordinates to be processed.")
print(" : If this is set, -filep should also be set and -img should not be set.")
print("-proc : The number of processor to use.")
print(" : If not specified the default value is 2.")
print("-syrg : This is the syringe width in millimeters.")
print(" : If not specified the default value is 0.805 mm.")
print("-a : RK4 Parameter: minimum value for alpha (radius of curvature at apex).")
print(" : If not specified the default value is 0.0001")
print("-A : RK4 Parameter: maximum value for alpha (radius of curvature at apex).")
print(" : If not specified the default value is 2.0001")
print("-b : RK4 Parameter: minimum value for beta (shape factor).")
print(" : If not specified the default value is 0.0001")
print("-B : RK4 Parameter: minimum value for beta (shape factor).")
print(" : If not specified the default value is 0.5001.")
print("-x : RK4 Parameter: minimum value for x (initial guessed x coordinate).")
print(" : If not specified the default value is 0.0001.")
print("-X : RK4 Parameter: maximum value for x (initial guessed x coordinate).")
print(" : If not specified the default value is 0.5001.")
print("-t : RK4 Parameter: minimum value for t (initial guessed angle).")
print(" : If not specified the default value is 0.0001.")
print("-T : RK4 Parameter: maximum value for t (initial guessed angle).")
print(" : If not specified the default value is 0.5001.")
print("-ai : RK4 Parameter: alpha interval - the number of values to consider between min alpha and max alpha.")
print(" : If not specified the default value is 20.")
print("-bi : RK4 Parameter: beta interval - the number of values to consider between min beta and max beta.")
print(" : If not specified the default value is 20.")
print("-xi : RK4 Parameter: x interval - the number of values to consider between min x and max x.")
print(" : If not specified the default value is 5.")
print("-ti : RK4 Parameter: angle interval - the number of values to consider between min angle and max angle.")
print(" : If not specified the default value is 5.")
print("-solver : 1 for divide and conquer scanning.")
print(" : If not specified, optimization will be used to solve the parameters.")
print("\nSAMPLE USAGE:\n")
print("1. Calculate surface tension of a drop using default values.")
print("./img.py -img water-drop.jpg -out water-drop")
print("python img.py -img water-drop.jpg -out water-drop\n")
print("2. Uses 4 processors for calculation.")
print("./img.py -img water-drop.jpg -out water-drop -proc 4\n")
print("3. Setting interval sizes for A and B parameters (search for 30 possible values for A and B).")
print("./img.py -img water-drop.jpg -out water-drop -ai 30 -bi 30\n")
print("4. Setting min (0.00001) and max (1.5) values for parameter.")
print("./img.py -img water-drop.jpg -out water-drop -a 0.00001 -A 1.5\n")
# ========== MAIN ==========
if __name__ == '__main__':
ImageFilePath = ""
OutputFileName = ""
PosPointsFileName = ""
NegPointsFileName = ""
ProcessCount = 2
SyringeWidth = 0.70 * 1.15
MinA_radCurv = 0.0001
MaxA_radCurv = 2.0001
MinB_shapeFact = 0.0001
MaxB_shapeFact = 1.0001
MinX = 0.0001
MaxX = 0.5001
MinT_angle = 0.0001
MaxT_angle = 0.5001
AStep = 20
BStep = 20
XStep = 5
TStep = 5
if (len(sys.argv) <= 1):
PrintCorrectUsage()
sys.exit()
ImageFlag = 0
PosFileFlag = 0
NegFileFlag = 0
OutFlag = 0
Counter = 1
SolveMethod = 0
while (Counter <= len(sys.argv)-1):
CurrentOption = sys.argv[Counter]
try:
CurrentValue = sys.argv[Counter+1]
except:
PrintCorrectUsage()
sys.exit()
if CurrentOption == "-help":
PrintCorrectUsage()
sys.exit()
elif CurrentOption == "-img":
ImageFilePath = CurrentValue
ImageFlag = 1
elif CurrentOption == "-filep":
PosPointsFileName = CurrentValue
PosFileFlag = 1
elif CurrentOption == "-filen":
NegPointsFileName = CurrentValue
NegFileFlag = 1
elif CurrentOption == "-out":
OutputFileName = CurrentValue
OutFlag = 1
elif CurrentOption == "-proc":
ProcessCount = int(CurrentValue)
elif CurrentOption == "-syrg":
SyringeWidth = float(CurrentValue)
elif CurrentOption == "-a":
MinA_radCurv = float(CurrentValue)
elif CurrentOption == "-A":
MaxA_radCurv = float(CurrentValue)
elif CurrentOption == "-b":
MinB_shapeFact = float(CurrentValue)
elif CurrentOption == "-B":
MaxB_shapeFact = float(CurrentValue)
elif CurrentOption == "-x":
MinX = float(CurrentValue)
elif CurrentOption == "-X":
MaxX = float(CurrentValue)
elif CurrentOption == "-t":
MinT_angle = float(CurrentValue)
elif CurrentOption == "-T":
MaxT_angle = float(CurrentValue)
elif CurrentOption == "-ai":
AStep = int(CurrentValue)
elif CurrentOption == "-bi":
BStep = int(CurrentValue)
elif CurrentOption == "-xi":
XStep = int(CurrentValue)
elif CurrentOption == "-ti":
TStep = int(CurrentValue)
elif CurrentOption == "-solver":
SolveMethod = int(CurrentValue)
else:
PrintCorrectUsage()
sys.exit()
Counter = Counter + 2
if (OutFlag == 0 and ImageFlag == 1):
print("Output file name not set. (-out)")
PrintCorrectUsage()
sys.exit()
if (ImageFlag == 1 and ((PosFileFlag == 1) or (NegFileFlag == 1))):
print("Image(-img) can not be set with files option (-filep, -filen).")
print("Either set image only or files only.")
PrintCorrectUsage()
sys.exit()
if (ImageFlag == 0 and ((PosFileFlag == 1 and NegFileFlag == 0) or (PosFileFlag == 0 and NegFileFlag == 1))):
print("File inputs (-filep, -filen) should both be set if image input is not set.")
PrintCorrectUsage()
sys.exit()
ARange_radCurv = []
a = MinA_radCurv
ahop = float(MaxA_radCurv - MinA_radCurv)/float(AStep-1)
while (a <= MaxA_radCurv):
ARange_radCurv.append(a)
a += ahop
if ahop == 0.0:
break
if(np.abs(MaxA_radCurv - ARange_radCurv[len(ARange_radCurv)-1] - ahop) < 0.000001 and ahop != 0.0):
ARange_radCurv.append(MaxA_radCurv)
BRange_shapeFact = []
b = MinB_shapeFact
bhop = float(MaxB_shapeFact - MinB_shapeFact)/float(BStep-1)
while (b <= MaxB_shapeFact):
BRange_shapeFact.append(b)
b += bhop
if bhop == 0:
break
if(np.abs(MaxB_shapeFact - BRange_shapeFact[len(BRange_shapeFact)-1] - bhop) < 0.000001 and bhop != 0.0):
BRange_shapeFact.append(MaxB_shapeFact)
XRange = []
x = MinX
xhop = float(MaxX - MinX)/float(XStep-1)
while (x <= MaxX):
XRange.append(x)
x += xhop
if xhop == 0:
break
if(np.abs(MaxX - XRange[len(XRange)-1] - xhop) < 0.000001 and xhop != 0.0):
XRange.append(MaxX)
TRange_angle = []
t = MinT_angle
thop = float(MaxT_angle - MinT_angle)/float(TStep-1)
while (t <= MaxT_angle):
TRange_angle.append(t)
t += thop
if thop == 0.0:
break
if(np.abs(MaxT_angle - TRange_angle[len(TRange_angle)-1] - thop) < 0.000001 and thop != 0.0):
TRange_angle.append(MaxT_angle)
print("\n")
if (ImageFlag == 1):
print("Image File : " + ImageFilePath)
print("Output Name : " + OutputFileName)
print("Negative Cooridnate File : " + OutputFileName + "_Negative.dat")
print("Positive Cooridnate File : " + OutputFileName + "_Positive.dat")
print("Syringe Width (millimeter): " + str(SyringeWidth))
else:
print("Negative Cooridnate File : " + NegPointsFileName)
print("Positive Cooridnate File : " + PosPointsFileName)
print("Radius of Curvature Range : " + str(MinA_radCurv) + "-" + str(MaxA_radCurv) + " (interval: "+str(len(ARange_radCurv))+")")
print("Shape Factor Range : " + str(MinB_shapeFact) + "-" + str(MaxB_shapeFact) + " (interval: "+str(len(BRange_shapeFact))+")")
print("Initial X Coordinate Range: " + str(MinX) + "-" + str(MaxX) + " (interval: "+str(len(XRange))+")")
print("Initial Angle Range : " + str(MinT_angle) + "-" + str(MaxT_angle) + " (interval: "+str(len(TRange_angle))+")")
print("Parameter Combinations : " + str(len(ARange_radCurv) * len(BRange_shapeFact) * len(XRange) * len(TRange_angle)))
print("Number of Process to Use : " + str(ProcessCount))
if (ImageFlag == 1):
Pos_edgePts, Neg_edgePts, De_scaledMaxWidth, Dms_diamters = ExtractEdgeCoordinate(ImageFilePath, OutputFileName, SyringeWidth)
HInv1 = HJuza(Dms_diamters[7]/De_scaledMaxWidth, 0.8)
HInv2 = HJuza(Dms_diamters[8]/De_scaledMaxWidth, 0.9)
HInv3 = HJuza(Dms_diamters[9]/De_scaledMaxWidth, 1.0)
HInv4 = HJuza(Dms_diamters[10]/De_scaledMaxWidth, 1.1)
HInv5 = HJuza(Dms_diamters[11]/De_scaledMaxWidth, 1.2)
HInvM = (0.20)*(HInv1 + HInv2 + HInv3 + HInv4 + HInv5)
HInvSD = math.sqrt((0.20)*( (HInv1-HInvM)**2.0 + (HInv2-HInvM)**2.0 + (HInv3-HInvM)**2.0 + (HInv4-HInvM)**2.0 + (HInv5-HInvM)**2.0))
print("Juza 5-Plane (1/H) Std.Dev: " + str(HInvSD))
else:
Pos_edgePts = []
PositivePointFile = open(PosPointsFileName,'r')
for Line in PositivePointFile:
Coordinate = Line.split()
#print(Coordinate)
Pos_edgePts.append((float(Coordinate[0]), float(Coordinate[1])))
PositivePointFile.close()
Neg_edgePts = []
NegativePointFile = open(NegPointsFileName,'r')
for Line in NegativePointFile:
Coordinate = Line.split()
#print(Coordinate)
Neg_edgePts.append((float(Coordinate[0]), float(Coordinate[1])))
NegativePointFile.close()
def BruteForce():
PosList = []
NegList = []
for ProcessNo in range(ProcessCount):
PosList.append(copy.deepcopy(Pos_edgePts))
NegList.append(copy.deepcopy(Neg_edgePts))
#print(ProcessNo)
ARanges = []
for ProcessNo in range(ProcessCount):
ARanges.append([])
i = 0
for A in ARange_radCurv:
ARanges[i%ProcessCount].append(A)
i += 1
BestParams = multiprocessing.Queue()
TicksQueue = multiprocessing.Queue()
Processes = []
for ProcessNo in range(ProcessCount):
Processes.append(multiprocessing.Process(name='Search ' + str(ProcessNo), target=FindOptimalParameters, args=(PosList[ProcessNo], NegList[ProcessNo], ARanges[ProcessNo], BRange_shapeFact, XRange, TRange_angle, BestParams, TicksQueue)))
for aProcess in Processes:
aProcess.start()
sys.stdout.write("\rParameter Search Progress : 0%")
sys.stdout.flush()
Count = 0
while(Count < len(ARange_radCurv)):
t = TicksQueue.get()
Count += 1
sys.stdout.write("\rParameter Search Progress : " + str(int(100*float(Count)/float(len(ARange_radCurv)))) + "%")
sys.stdout.flush()
for aProcess in Processes:
aProcess.join()
BestOutput = (999999999, 1, 1, 1 ,1)
while not BestParams.empty():
Output = BestParams.get()
if Output[0] < BestOutput[0]:
BestOutput = Output
return BestOutput
def Optimize():
return OptimizeParameters(Pos_edgePts, Neg_edgePts, 0.95, 0.3, 0.1, 0.2)
starttime = time.time()
BestOutput = Optimize() if 1 != SolveMethod else BruteForce()
print("best output: " + str(BestOutput))
print("\nLowest RMSD : " + str(BestOutput[0]))
print("Computed Surface Tension : " + str(9.8*BestOutput[1]/BestOutput[2]))
print("Best Radius of Curvature : " + str(BestOutput[1]))
print("Best Shape Factor : " + str(BestOutput[2]))
print("Best Initial X Coordinate : " + str(BestOutput[3]))
print("Best Initial Angle : " + str(BestOutput[4]))
print("Lapsed Time in Seconds : " + str(time.time() -starttime))

View File

@ -0,0 +1,466 @@
// ========== Juza's equation for 1/H ==========
import 'dart:math';
double HJuza(double _S_diamRatio, double _K) {
// LowerS, UpperS, Ke, Kex, Kep, K3, K2, K1, K0
// 0.82 - 0.999
List<List<double>> Coefficient8 = [
[
0.82,
0.83,
1791452.8,
533.85850,
170.55837,
-81676.15812,
202187.89690,
-166836.27680,
45888.22973,
],
[
0.83,
0.84,
189.07578,
272.52455,
72.730022,
-19963.60152,
50019.79936,
-41775.38186,
11629.85610,
],
[
0.84,
0.85,
7.1549709,
165.45945,
35.285687,
-6944.66848,
17609.43832,
-14883.84379,
4193.34232,
],
[
0.85,
0.87,
1.1496269,
95.066407,
12.600013,
-2158.91585,
5571.60176,
-4792.82331,
1374.26272,
],
[
0.87,
0.895,
0.47873040,
50.357240,
0.089787883,
-567.76534,
1503.51828,
-1327.11835,
390.45562,
],
[
0.895,
0.93,
0.35255000,
25.498893,
-5.4176608,
-165.99710,
454.58851,
-414.93772,
126.23908,
],
[
0.93,
0.97,
0.32002037,
6.8474560,
-8.0901766,
-102.84970,
293.25377,
-278.69176,
88.27639,
],
[
0.97,
0.99,
0.30845061,
-32.343947,
-10.455428,
-475.69091,
1398.86173,
-1371.17931,
448.00538,
],
[
0.99,
0.999,
0.30110729,
-333.50440,
-15.711260,
-11334.69334,
33822.93507,
-33642.61426,
11154.37157,
],
];
// 0.635 - 0.997
List<List<double>> Coefficient9 = [
[
0.635,
0.645,
58249804,
119.64583,
89.483167,
-28072.11478,
53918.28040,
-34519.94034,
7366.77050,
],
[
0.645,
0.66,
5100.2910,
70.920100,
46.811007,
-9051.10879,
17726.62108,
-15572.21470,
2518.10214,
],
[
0.66,
0.685,
19.518159,
38.509198,
19.951285,
-2338.00251,
4719.64936,
-3175.58038,
712.17229,
],
[
0.685,
0.72,
1.3823760,
20.055606,
5.9746092,
-522.67397,
1102.26575,
-774.75596,
181.49582,
],
[
0.72,
0.77,
0.49074291,
10.484929,
-0.31885445,
-111.99730,
250.54286,
-186.78287,
46.40581,
],
[
0.77,
0.84,
0.34607161,
5.4063548,
-2.9788620,
-24.21100,
58.53312,
-47.15244,
12.65668,
],
[
0.84,
0.93,
0.31342828,
2.1140055,
-4.1151543,
-9.16969,
24.37544,
-21.58758,
6.36954,
],
[
0.93,
0.98,
0.30397966,
-3.6334200,
-4.9395699,
-29.80626,
85.43510,
-81.61766,
25.98669,
],
[
0.98,
0.997,
0.30007321,
-34.095653,
-6.1531194,
-434.11439,
1287.65238,
-1273.10762,
419.56923,
],
];
// 0.17 - 0.983
List<List<double>> Coefficient10 = [
[
0.17,
0.30,
0.31081678,
-0.086278355,
-2.7023254,
-13.95071,
10.30398,
-2.49619,
0.19805,
],
[
0.30,
0.45,
0.30636442,
-0.094871613,
-2.7246428,
0.38766,
-0.44128,
0.16613,
-0.02068,
],
[
0.45,
0.68,
0.31156188,
-0.063734909,
-2.6789763,
0.41537,
-0.71168,
0.40321,
-0.07551,
],
[
0.68,
0.90,
0.31195754,
-0.092720991,
-2.6863859,
-0.44813,
1.06637,
-0.84260,
0.22106,
],
[
0.90,
0.983,
0.30712046,
-1.5619311,
-2.9809169,
-5.74538,
16.23781,
-15.29128,
4.79808,
],
];
// 0.06 - 0.953
List<List<double>> Coefficient11 = [
[
0.06,
0.08,
1.6030433,
0.043380701,
-0.15208454,
-1341.32950,
282.27311,
-19.71010,
0.45664,
],
[
0.08,
0.13,
0.85491410,
-0.056761738,
-0.65372414,
-160.97221,
50.59168,
-5.23625,
0.17842,
],
[
0.13,
0.20,
0.57401485,
-0.15265815,
-1.0443069,
-42.79943,
21.22139,
-3.47357,
0.18765,
],
[
0.20,
0.30,
0.41622542,
-0.27871823,
-1.4464670,
-14.08741,
10.58863,
-2.63159,
0.21621,
],
[
0.30,
0.44,
0.33587066,
-0.42674255,
-1.8022482,
-3.22540,
3.58964,
-1.32194,
0.16106,
],
[
0.44,
0.75,
0.31504207,
-0.51197565,
-1.9499037,
0.07609,
-0.13681,
0.08090,
-0.01572,
],
[
0.75,
0.953,
0.31658938,
-0.49808609,
-1.9290368,
-0.24018,
0.61450,
-0.52258,
0.14771,
],
];
// 0.10 - 0.902
List<List<double>> Coefficient12 = [
[
0.10,
0.12,
103.56308,
1.2019976,
4.4869062,
-6625.95443,
2292.46751,
-264.28560,
10.15218,
],
[
0.12,
0.14,
2.1585254,
0.34129627,
0.83653215,
-742.15388,
289.52312,
-37.59979,
1.62555,
],
[
0.14,
0.19,
0.65654048,
0.031877859,
-0.37693028,
-66.92134,
33.18430,
-5.45788,
0.29773,
],
[
0.19,
0.27,
0.45076908,
-0.10378254,
-0.82836350,
-10.66154,
7.36263,
-1.68453,
0.12768,
],
[
0.27,
0.40,
0.37085833,
-0.21965481,
-1.1287016,
-2.87493,
2.88810,
-0.95966,
0.10546,
],
[
0.40,
0.55,
0.33605594,
-0.33317467,
-1.3397591,
-0.70394,
1.00402,
-0.47492,
0.07450,
],
[
0.55,
0.902,
0.32790337,
-0.39597877,
-1.4180887,
0.00490,
-0.01070,
0.00769,
-0.00182,
],
];
List<List<double>> Coeff = [];
if (_K == 0.8) {
Coeff = Coefficient8;
}
if (_K == 0.9) {
Coeff = Coefficient9;
}
if (_K == 1.0) {
Coeff = Coefficient10;
}
if (_K == 1.1) {
Coeff = Coefficient11;
}
if (_K == 1.2) {
Coeff = Coefficient12;
}
// Juza 1996/1997 Equation: 8
for (var Entry_k in Coeff) {
if (Entry_k[0] <= _S_diamRatio && _S_diamRatio <= Entry_k[1]) {
double HInv =
Entry_k[2] *
pow(_S_diamRatio, (Entry_k[3] * log(_S_diamRatio) + Entry_k[4])) +
Entry_k[5] * pow(_S_diamRatio, 3.0) +
Entry_k[6] * pow(_S_diamRatio, 2.0) +
Entry_k[7] * _S_diamRatio +
Entry_k[8];
return HInv;
}
}
return 0;
}

View File

@ -0,0 +1,48 @@
import 'dart:math';
import 'package:surface_tension_calculator/scripts/rk4.dart';
/// ========== FINDING OPTIMAL PARAMETERS USING MINIMZATION ==========
List<double> optimizeParameters(
List<double> posEdgePoints,
List<double> negEdgePoints,
double aGuessRadCurv,
double bGuessShapeFact,
double xGuess,
double tGuessAngle,
) {
var rk4PosTable = makeRK4Table(posEdgePoints);
var rk4NegTable = makeRK4Table(negEdgePoints);
double msd(List<double> args) {
double aRadCurv = args[0];
double bShapeFact = args[1];
double x = args[2];
double tAngle = args[3];
rk4PosTable[0][3] = x;
rk4PosTable[0][4] = tAngle;
rk4NegTable[0][3] = x;
rk4NegTable[0][4] = tAngle;
double fTotalDiffSquared =
bashforthAdamsRK4(rk4PosTable, aRadCurv, bShapeFact, 1.0) +
bashforthAdamsRK4(rk4NegTable, aRadCurv, bShapeFact, -1.0);
return sqrt(
fTotalDiffSquared / (posEdgePoints.length + negEdgePoints.length),
);
}
var optResult = minimize(msd, [
aGuessRadCurv,
bGuessShapeFact,
xGuess,
tGuessAngle,
]);
if (optResult.success) {
print("minimization success");
return [optResult.fun.toDouble()] + optResult.x;
} else {
return [9999999, 0, 0, 0, 0];
}
}

Some files were not shown because too many files have changed in this diff Show More