diff --git a/README.md b/README.md index 407336e..cfba9f5 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,35 @@ A comprehensive kotlin library designed for FTC. Based on [Road Runner](https:// ## Installation +For the `command` and `navigation` modules, installation is as follows: + ### Gradle -```gradle +```groovy +repositories { + maven { url 'https://jitpack.io' } +} + +dependencies { + implementation "com.github.amarcolini.joos:$module:0.4.3-alpha" +} +``` + +Note that since the `command` module implicitly imports the `navigation` module, +only one implementation statement is needed. + +To use the GUI, you can either download the image specific to your platform from the releases page, +or import it like so: + +###Gradle + +````groovy repositories { maven { url 'https://jitpack.io' } } + dependencies { - implementation "com.github.amarcolini.joos:$module:0.4.2-alpha" + // Change 'win' to 'linux' or 'mac' depending on your operating system + implementation "com.github.amarcolini.joos:gui:0.4.3-alpha:win" } -``` \ No newline at end of file +```` \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3b23f6b..56e0526 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,11 @@ buildscript { ext { kotlin_version = "1.6.10" - lib_version = "0.4.2-alpha" + lib_version = "0.4.3-alpha" } repositories { - jcenter() + gradlePluginPortal() google() } @@ -17,7 +17,6 @@ buildscript { allprojects { repositories { - jcenter() mavenCentral() } diff --git a/command/src/main/kotlin/com/amarcolini/joos/hardware/MotorGroup.kt b/command/src/main/kotlin/com/amarcolini/joos/hardware/MotorGroup.kt index 5ea3759..c6ffc73 100644 --- a/command/src/main/kotlin/com/amarcolini/joos/hardware/MotorGroup.kt +++ b/command/src/main/kotlin/com/amarcolini/joos/hardware/MotorGroup.kt @@ -4,6 +4,7 @@ import com.amarcolini.joos.command.Command import com.amarcolini.joos.command.Component import com.amarcolini.joos.control.FeedforwardCoefficients import com.amarcolini.joos.control.PIDCoefficients +import com.amarcolini.joos.geometry.Angle import com.amarcolini.joos.hardware.Motor.RunMode import com.qualcomm.robotcore.hardware.HardwareMap @@ -55,13 +56,13 @@ class MotorGroup(vararg val motors: Motor) : Component { * The maximum revolutions per minute that all motors in the group can achieve. */ @JvmField - val maxRPM = motors.minOf { it.maxRPM } + val maxRPM: Double = motors.minOf { it.maxRPM } /** * The maximum distance velocity that all motors in the group can achieve. */ @JvmField - val maxDistanceVelocity = motors.minOf { it.maxDistanceVelocity } + val maxDistanceVelocity: Double = motors.minOf { it.maxDistanceVelocity } /** * The maximum ticks per second velocity that all motors in the group can achieve. @@ -69,16 +70,21 @@ class MotorGroup(vararg val motors: Motor) : Component { @JvmField val maxTPS: Double = motors.minOf { it.maxTPS } + /** + * A list of the individual rotations of each motor. + */ + val rotation: List get() = motors.map { it.rotation } + /** * The maximum distance that all motors in the group have travelled. * @see distanceVelocity */ - val distance get() = motors.minOf { it.distance } + val distance: Double get() = motors.minOf { it.distance } /** * The minimum distance velocity out of all motors in the group. */ - val distanceVelocity get() = motors.minOf { it.distanceVelocity } + val distanceVelocity: Double get() = motors.minOf { it.distanceVelocity } /** * Whether the motor group is reversed. @@ -119,14 +125,14 @@ class MotorGroup(vararg val motors: Motor) : Component { velocity: Double, acceleration: Double = 0.0, unit: Motor.RotationUnit = Motor.RotationUnit.RPM - ) = motors.forEach { it.setSpeed(velocity, acceleration, unit) } + ): Unit = motors.forEach { it.setSpeed(velocity, acceleration, unit) } /** * Sets the percentage of power/velocity of the motor group in the range `[-1.0, 1.0]`. * * *Note*: Since power is expressed as a percentage, motors may move at different speeds. */ - fun setPower(power: Double) = motors.forEach { it.power = power } + fun setPower(power: Double): Unit = motors.forEach { it.power = power } var zeroPowerBehavior: Motor.ZeroPowerBehavior = Motor.ZeroPowerBehavior.FLOAT set(value) { @@ -143,7 +149,7 @@ class MotorGroup(vararg val motors: Motor) : Component { /** * PID coefficients used in [RunMode.RUN_USING_ENCODER]. */ - var veloCoefficients = PIDCoefficients(1.0) + var veloCoefficients: PIDCoefficients = PIDCoefficients(1.0) set(value) { motors.forEach { it.veloCoefficients = value } field = value @@ -152,7 +158,7 @@ class MotorGroup(vararg val motors: Motor) : Component { /** * PID coefficients used in [RunMode.RUN_TO_POSITION]. */ - var positionCoefficients = PIDCoefficients(1.0) + var positionCoefficients: PIDCoefficients = PIDCoefficients(1.0) set(value) { motors.forEach { it.positionCoefficients = value } field = value @@ -161,7 +167,7 @@ class MotorGroup(vararg val motors: Motor) : Component { /** * Feedforward coefficients used in both [RunMode.RUN_USING_ENCODER] and [RunMode.RUN_WITHOUT_ENCODER]. */ - var feedforwardCoefficients = FeedforwardCoefficients(1 / maxDistanceVelocity) + var feedforwardCoefficients: FeedforwardCoefficients = FeedforwardCoefficients(1 / maxDistanceVelocity) set(value) { motors.forEach { it.feedforwardCoefficients = value } field = value @@ -188,7 +194,7 @@ class MotorGroup(vararg val motors: Motor) : Component { /** * Returns a command that runs all the motors in the group until all of them have reached the desired position. */ - fun goToPosition(position: Int) = Command.of { + fun goToPosition(position: Int): Command = Command.of { runMode = RunMode.RUN_TO_POSITION targetPosition = position } @@ -203,7 +209,7 @@ class MotorGroup(vararg val motors: Motor) : Component { /** * Returns a command that runs all the motors in the group until all of them have reached the desired distance. */ - fun goToDistance(distance: Double) = Command.of { + fun goToDistance(distance: Double): Command = Command.of { runMode = RunMode.RUN_TO_POSITION motors.forEach { it.targetPosition = (distance / it.distancePerRev * it.TPR).toInt() @@ -222,12 +228,12 @@ class MotorGroup(vararg val motors: Motor) : Component { /** * Resets the encoders of all the motors in the group. */ - fun resetEncoder() = motors.forEach { it.resetEncoder() } + fun resetEncoder(): Unit = motors.forEach { it.resetEncoder() } /** * Returns whether any of the motors in the group are currently moving towards the desired setpoint using [RunMode.RUN_TO_POSITION]. */ - fun isBusy() = motors.any { it.isBusy() } + fun isBusy(): Boolean = motors.any { it.isBusy() } /** * Updates both [RunMode.RUN_USING_ENCODER] and [RunMode.RUN_TO_POSITION]. Running this method is diff --git a/gui/build.gradle b/gui/build.gradle index e1a8422..d4362ed 100644 --- a/gui/build.gradle +++ b/gui/build.gradle @@ -1,9 +1,10 @@ plugins { id 'application' - id 'org.openjfx.javafxplugin' version '0.0.8' + id 'org.openjfx.javafxplugin' version '0.0.10' id 'kotlin' id 'maven-publish' - id "com.github.johnrengelman.shadow" version "7.1.0" +// id "com.github.johnrengelman.shadow" version "7.1.0" + id 'org.beryx.jlink' version '2.25.0' } repositories { @@ -12,18 +13,34 @@ repositories { application { mainClass = "${rootProject.group}.gui.ApplicationKt" + mainModule = 'joos.gui' + applicationDefaultJvmArgs = ['--add-opens=javafx.graphics/javafx.scene=tornadofx'] } javafx { - version = "11.0.2" - modules = ['javafx.controls', 'javafx.graphics', 'javafx.swing'] + version = "15.0.1" + modules = ['javafx.controls'] } -shadowJar { - destinationDirectory.set(buildDir) - archiveBaseName.set("$rootProject.name-gui") - archiveClassifier.set("all") - archiveVersion.set(lib_version) +//shadowJar { +// destinationDirectory.set(buildDir) +// archiveBaseName.set("$rootProject.name-gui") +// archiveClassifier.set("all") +// archiveVersion.set(lib_version) +//} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } + withSourcesJar() +} + +configurations { + imageDependency { + extendsFrom implementation + canBeResolved(true) + } } dependencies { @@ -31,28 +48,53 @@ dependencies { implementation 'no.tornado:tornadofx:1.7.20' implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:win" - runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:linux" - runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:mac" - runtimeOnly "org.openjfx:javafx-controls:$javafx.version" + implementation 'io.github.classgraph:classgraph:4.8.146' + + imageDependency "org.openjfx:javafx-controls:$javafx.version:win" + imageDependency "org.openjfx:javafx-controls:$javafx.version:mac" + imageDependency "org.openjfx:javafx-controls:$javafx.version:linux" } -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 +compileKotlin { + kotlinOptions { + jvmTarget = "11" + apiVersion = "1.5" + } + destinationDirectory.set(compileJava.destinationDirectory.get()) } -task sourcesJar(type: Jar) { +task windowsJar(type: Jar) { from sourceSets.main.allSource - classifier('sources') + dependencies { + implementation "org.openjfx:javafx-controls:$javafx.version:win" + } + classifier('win') +} + +task linuxJar(type: Jar) { + from sourceSets.main.allSource + dependencies { + implementation "org.openjfx:javafx-controls:$javafx.version:linux" + } + classifier('linux') +} + +task macJar(type: Jar) { + from sourceSets.main.allSource + dependencies { + implementation "org.openjfx:javafx-controls:$javafx.version:mac" + } + classifier('mac') } publishing { publications { release(MavenPublication) { from components.java - artifact sourcesJar artifactId = "gui" + artifact windowsJar + artifact linuxJar + artifact macJar pom { name = "Joos" description = "A comprehensive kotlin library designed for FTC." @@ -73,4 +115,41 @@ publishing { url = '../testRepo' } } +} + +jlink { + options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] + configuration = 'imageDependency' + addExtraDependencies("javafx") + forceMerge('javafx') + + mergedModule { + excludeProvides servicePattern: 'com.fasterxml.jackson.*' + uses 'kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader' + uses 'kotlin.reflect.jvm.internal.impl.resolve.ExternalOverridabilityCondition' + uses 'kotlin.reflect.jvm.internal.impl.util.ModuleVisibilityHelper' + } + + launcher { + jvmArgs = ['--add-opens=javafx.graphics/javafx.scene=tornadofx'] + noConsole = true + } + + targetPlatform("linux") { + jdkHome = jdkDownload("https://api.adoptium.net/v3/binary/latest/11/ga/linux/x64/jdk/hotspot/normal/eclipse") { + archiveExtension = 'tar.gz' + } + } + + targetPlatform("win") { + jdkHome = jdkDownload("https://api.adoptium.net/v3/binary/latest/11/ga/windows/x64/jdk/hotspot/normal/eclipse") { + archiveExtension = 'zip' + } + } + + targetPlatform("mac") { + jdkHome = jdkDownload("https://api.adoptium.net/v3/binary/latest/11/ga/mac/x64/jdk/hotspot/normal/eclipse") { + archiveExtension = 'tar.gz' + } + } } \ No newline at end of file diff --git a/gui/src/main/java/module-info.java b/gui/src/main/java/module-info.java new file mode 100644 index 0000000..edf4561 --- /dev/null +++ b/gui/src/main/java/module-info.java @@ -0,0 +1,15 @@ +module joos.gui { + requires javafx.controls; + requires javafx.graphics; + requires tornadofx; + requires transitive kotlin.stdlib; + requires kotlin.reflect; + uses kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader; + uses kotlin.reflect.jvm.internal.impl.resolve.ExternalOverridabilityCondition; + uses kotlin.reflect.jvm.internal.impl.util.ModuleVisibilityHelper; + requires io.github.classgraph; + exports com.amarcolini.joos.gui; + exports com.amarcolini.joos.gui.rendering; + exports com.amarcolini.joos.gui.style; + exports com.amarcolini.joos.gui.trajectory; +} \ No newline at end of file diff --git a/gui/src/main/kotlin/com/amarcolini/joos/gui/Application.kt b/gui/src/main/kotlin/com/amarcolini/joos/gui/Application.kt index 74a2080..e15cf95 100644 --- a/gui/src/main/kotlin/com/amarcolini/joos/gui/Application.kt +++ b/gui/src/main/kotlin/com/amarcolini/joos/gui/Application.kt @@ -10,8 +10,8 @@ import javafx.scene.control.TextInputDialog import javafx.scene.image.Image import javafx.scene.image.ImageView import javafx.scene.layout.* +import javafx.scene.paint.Color import javafx.scene.shape.Path -import javafx.scene.text.Text import javafx.stage.FileChooser import javafx.stage.Screen import javafx.stage.Stage @@ -27,12 +27,11 @@ internal class MainApp : App(MainView::class, Dark::class) { override fun start(stage: Stage) { stage.isMaximized = true -// stage.minHeight = 500.0 -// stage.minWidth = 500.0 stage.maxWidth = Screen.getPrimary().visualBounds.width stage.maxHeight = Screen.getPrimary().visualBounds.height stage.width = Screen.getPrimary().visualBounds.width stage.height = Screen.getPrimary().visualBounds.height + stage.title = "Joos GUI" when (parameters.named["theme"]?.lowercase()) { "light" -> Global.theme.value = Light() else -> { @@ -44,7 +43,7 @@ internal class MainApp : App(MainView::class, Dark::class) { } internal class MainView : View() { - val background = when (app.parameters.named["background"]?.lowercase()) { + val background = when (app.parameters.named["joos/gui/background"]?.lowercase()) { "freightfrenzy" -> Backgrounds.FreightFrenzy.image "ultimategoal" -> Backgrounds.UltimateGoal.image else -> Global.background @@ -59,6 +58,18 @@ internal class MainView : View() { override val root = vbox { menubar { + menu(null, ImageView(run { + val url = "joos/gui/logo.png" + Global.resources?.get(url)?.get(0)?.open()?.let { Image(it, 60.0, 100.0, true, true) } ?: Image( + url, + 60.0, + 100.0, + true, + true + ) + })).style { + backgroundColor += Color.TRANSPARENT + } menu("_Theme") { item("_Dark").action { Global.theme.value = Dark() diff --git a/gui/src/main/kotlin/com/amarcolini/joos/gui/Global.kt b/gui/src/main/kotlin/com/amarcolini/joos/gui/Global.kt index e699da7..42104ab 100644 --- a/gui/src/main/kotlin/com/amarcolini/joos/gui/Global.kt +++ b/gui/src/main/kotlin/com/amarcolini/joos/gui/Global.kt @@ -6,6 +6,7 @@ import com.amarcolini.joos.gui.style.Theme import com.amarcolini.joos.gui.trajectory.WaypointTrajectory import com.amarcolini.joos.trajectory.config.GenericConstraints import com.amarcolini.joos.trajectory.config.TrajectoryConstraints +import io.github.classgraph.ClassGraph import javafx.beans.property.SimpleObjectProperty import javafx.scene.image.Image @@ -16,4 +17,14 @@ internal object Global { var background: Lazy = Backgrounds.Generic.image val extraBackgrounds: MutableMap = HashMap() val extraThemes: MutableMap = HashMap() + internal val resources = run { + val classGraph = ClassGraph() + classGraph.acceptPaths("joos/gui") + if (this::class.java.module.name != null) + classGraph + .acceptModules(this::class.java.module.name) + .rejectClasses() + .scan().allResources + else null + } } \ No newline at end of file diff --git a/gui/src/main/kotlin/com/amarcolini/joos/gui/rendering/Backgrounds.kt b/gui/src/main/kotlin/com/amarcolini/joos/gui/rendering/Backgrounds.kt index 11b6e30..e025e25 100644 --- a/gui/src/main/kotlin/com/amarcolini/joos/gui/rendering/Backgrounds.kt +++ b/gui/src/main/kotlin/com/amarcolini/joos/gui/rendering/Backgrounds.kt @@ -1,9 +1,14 @@ package com.amarcolini.joos.gui.rendering import javafx.scene.image.Image +import com.amarcolini.joos.gui.Global -enum class Backgrounds(val image: Lazy) { - Generic(lazy { Image("background/Generic.png") }), - FreightFrenzy(lazy { Image("background/FreightFrenzy.png") }), - UltimateGoal(lazy { Image("background/UltimateGoal.png") }) +enum class Backgrounds(url: String) { + Generic("joos/gui/background/Generic.png"), + FreightFrenzy("joos/gui/background/FreightFrenzy.png"), + UltimateGoal("joos/gui/background/UltimateGoal.png"); + + val image = lazy { + Global.resources?.get(url)?.get(0)?.open()?.let { Image(it) } ?: Image(url) + } } \ No newline at end of file diff --git a/gui/src/main/kotlin/com/amarcolini/joos/gui/style/Theme.kt b/gui/src/main/kotlin/com/amarcolini/joos/gui/style/Theme.kt index f43fe27..6dd4234 100644 --- a/gui/src/main/kotlin/com/amarcolini/joos/gui/style/Theme.kt +++ b/gui/src/main/kotlin/com/amarcolini/joos/gui/style/Theme.kt @@ -21,7 +21,7 @@ sealed class Theme : Stylesheet() { val error by csspseudoclass() val errorText by cssclass() val padding by cssclass() - val mono = loadFont("/fonts/JetBrainsMono[wght].ttf", 40)!! + val mono = Font.loadFont(this::class.java.getResourceAsStream("/joos/gui/fonts/JetBrainsMono[wght].ttf"), 40.0)!! } abstract val base: Color diff --git a/gui/src/main/resources/background/FreightFrenzy.png b/gui/src/main/resources/joos/gui/background/FreightFrenzy.png similarity index 100% rename from gui/src/main/resources/background/FreightFrenzy.png rename to gui/src/main/resources/joos/gui/background/FreightFrenzy.png diff --git a/gui/src/main/resources/background/Generic.png b/gui/src/main/resources/joos/gui/background/Generic.png similarity index 100% rename from gui/src/main/resources/background/Generic.png rename to gui/src/main/resources/joos/gui/background/Generic.png diff --git a/gui/src/main/resources/background/UltimateGoal.png b/gui/src/main/resources/joos/gui/background/UltimateGoal.png similarity index 100% rename from gui/src/main/resources/background/UltimateGoal.png rename to gui/src/main/resources/joos/gui/background/UltimateGoal.png diff --git a/gui/src/main/resources/fonts/JetBrainsMono[wght].ttf b/gui/src/main/resources/joos/gui/fonts/JetBrainsMono[wght].ttf similarity index 100% rename from gui/src/main/resources/fonts/JetBrainsMono[wght].ttf rename to gui/src/main/resources/joos/gui/fonts/JetBrainsMono[wght].ttf diff --git a/gui/src/main/resources/joos/gui/logo.png b/gui/src/main/resources/joos/gui/logo.png new file mode 100644 index 0000000..6d00118 Binary files /dev/null and b/gui/src/main/resources/joos/gui/logo.png differ diff --git a/navigation/build.gradle b/navigation/build.gradle index 94de141..574b853 100644 --- a/navigation/build.gradle +++ b/navigation/build.gradle @@ -15,13 +15,13 @@ test { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_1_9 + targetCompatibility = JavaVersion.VERSION_1_9 } compileKotlin { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "9" apiVersion = "1.5" } } @@ -39,14 +39,14 @@ dependencies { implementation "org.apache.commons:commons-math3:3.6.1" - implementation "com.fasterxml.jackson.core:jackson-databind:2.12.5" - implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.12.5" - implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.12.5" + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.2.2' + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.2' + implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.13.2' - testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.2" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.2" + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' - testImplementation "org.knowm.xchart:xchart:3.8.0" + testImplementation 'org.knowm.xchart:xchart:3.8.1' } task sourcesJar(type: Jar) { @@ -85,4 +85,4 @@ publishing { tasks["clean"].doFirst { delete file("graphs") delete file("config") -} +} \ No newline at end of file diff --git a/navigation/src/main/kotlin/module-info.java b/navigation/src/main/kotlin/module-info.java new file mode 100644 index 0000000..5d98b3f --- /dev/null +++ b/navigation/src/main/kotlin/module-info.java @@ -0,0 +1,19 @@ +module joos.navigation { + requires com.fasterxml.jackson.dataformat.yaml; + requires transitive kotlin.stdlib; + requires com.fasterxml.jackson.kotlin; + requires com.fasterxml.jackson.databind; + requires commons.math3; + exports com.amarcolini.joos.control; + exports com.amarcolini.joos.drive; + exports com.amarcolini.joos.followers; + exports com.amarcolini.joos.geometry; + exports com.amarcolini.joos.kinematics; + exports com.amarcolini.joos.localization; + exports com.amarcolini.joos.path; + exports com.amarcolini.joos.profile; + exports com.amarcolini.joos.trajectory; + exports com.amarcolini.joos.trajectory.config; + exports com.amarcolini.joos.trajectory.constraints; + exports com.amarcolini.joos.util; +} \ No newline at end of file