diff --git a/.github/workflows/release_ci.yml b/.github/workflows/release_ci.yml index 0b8570d8..7de652cb 100644 --- a/.github/workflows/release_ci.yml +++ b/.github/workflows/release_ci.yml @@ -26,7 +26,7 @@ jobs: ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.JRELEASER_MAVENCENTRAL_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.JRELEASER_GPG_PASSPHRASE }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.JRELEASER_GPG_SECRET_KEY }} - run: ./gradlew -Penv=release :Common:publishToMavenCentral :Vision:publishToMavenCentral :EOCV-Sim:publishToMavenCentral -x test :EOCV-Sim:shadowJar + run: ./gradlew -Penv=release :Common:publishToMavenCentral :Vision:publishToMavenCentral :EOCV-Sim:publishToMavenCentral -x test -x :EOCV-Sim:shadowJar if: ${{ startsWith(github.ref, 'refs/tags/v') }} - name: Build release shadow jar with Gradle diff --git a/Common/build.gradle b/Common/build.gradle index 3b624965..979d82f6 100644 --- a/Common/build.gradle +++ b/Common/build.gradle @@ -1,14 +1,31 @@ plugins { id 'kotlin' id 'signing' + id 'com.github.johnrengelman.shadow' id "com.vanniktech.maven.publish" version "0.30.0" } apply from: '../build.common.gradle' +components.java { + tasks.named("shadowJar").configure { + // only run shadowJar when explicitly specified by the user + // check if user invoked gradle with :shadowJar + enabled = project.gradle.startParameter.taskNames.contains("shadowJar") + } +} + +shadowJar { + dependencies { + exclude "nu/pattern/*" + } +} + dependencies { api "org.openpnp:opencv:$opencv_version" + implementation "com.moandjiezana.toml:toml4j:$toml4j_version" + implementation "info.picocli:picocli:$picocli_version" implementation "org.slf4j:slf4j-api:$slf4j_version" implementation 'org.jetbrains.kotlin:kotlin-stdlib' } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt similarity index 74% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt rename to Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt index 8ef35ac0..fa6010bd 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt @@ -23,10 +23,11 @@ package com.github.serivesmejia.eocvsim.util.extension -import com.github.serivesmejia.eocvsim.util.SysUtil import java.io.File import java.security.MessageDigest +val appData: File = File(System.getProperty("user.home") + File.separator) + /** * Operator function to concatenate a string to a file path */ @@ -34,9 +35,19 @@ operator fun File.plus(str: String): File { return File(this.absolutePath, str) } - fun File.fileHash(algorithm: String = "SHA-256"): String { val messageDigest = MessageDigest.getInstance(algorithm) messageDigest.update(readBytes()) - return SysUtil.byteArray2Hex(messageDigest.digest()) + return byteArrayToHex(messageDigest.digest()) +} + +private val hex = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') + +fun byteArrayToHex(bytes: ByteArray): String { + val sb = StringBuilder(bytes.size * 2) + for (b in bytes) { + sb.append(hex[(b.toInt() and 0xF0) shr 4]) + sb.append(hex[b.toInt() and 0x0F]) + } + return sb.toString() } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/extension/NumberExt.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/NumberExt.kt similarity index 100% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/extension/NumberExt.kt rename to Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/NumberExt.kt diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/extension/StrExt.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/StrExt.kt similarity index 84% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/extension/StrExt.kt rename to Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/StrExt.kt index 34f4e756..55bbbd52 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/extension/StrExt.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/StrExt.kt @@ -1,6 +1,5 @@ package com.github.serivesmejia.eocvsim.util.extension -import com.github.serivesmejia.eocvsim.util.SysUtil import java.security.MessageDigest /** @@ -20,5 +19,5 @@ val Any.hashString get() = Integer.toHexString(hashCode())!! val String.hashString: String get() { val messageDigest = MessageDigest.getInstance("SHA-256") val hash = messageDigest.digest(toByteArray()) - return SysUtil.byteArray2Hex(hash) + return byteArrayToHex(hash) } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt similarity index 87% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt rename to Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt index 71607f32..68c72411 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt @@ -1,6 +1,6 @@ package com.github.serivesmejia.eocvsim.util.io -import com.github.serivesmejia.eocvsim.util.SysUtil +import com.github.serivesmejia.eocvsim.util.extension.appData import com.github.serivesmejia.eocvsim.util.loggerForThis import java.io.File @@ -10,7 +10,7 @@ import java.io.File * Also handles locking the folder to prevent multiple instances * from running at the same time (which could cause issues). */ -object EOCVSimFolder : File(SysUtil.getAppData().absolutePath + separator + ".eocvsim") { +object EOCVSimFolder : File(appData.absolutePath + separator + ".eocvsim") { val logger by loggerForThis() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt similarity index 86% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt rename to Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt index b914e2c7..e0ce00b4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt @@ -1,6 +1,5 @@ package com.github.serivesmejia.eocvsim.util.io -import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.loggerForThis import java.io.File import java.io.RandomAccessFile @@ -12,14 +11,6 @@ import java.nio.channels.FileLock */ class LockFile(pathname: String) : File(pathname) { - companion object { - val POR_UNA_NOCHE = try { - SysUtil.loadResStr("/.lock") - } catch(ex: Exception) { - "lock" - } - } - private var raf = RandomAccessFile(this, "rw") var lock: FileLock? = null @@ -45,7 +36,7 @@ class LockFile(pathname: String) : File(pathname) { throw IllegalArgumentException("Lock file cannot be a directory") if(!exists()) - SysUtil.saveFileStr(this, POR_UNA_NOCHE) + createNewFile() } /** diff --git a/Common/src/main/java/io/github/deltacv/common/util/ParsedVersion.kt b/Common/src/main/java/io/github/deltacv/common/util/ParsedVersion.kt index 53dc0374..b6b6cc06 100644 --- a/Common/src/main/java/io/github/deltacv/common/util/ParsedVersion.kt +++ b/Common/src/main/java/io/github/deltacv/common/util/ParsedVersion.kt @@ -24,6 +24,8 @@ class ParsedVersion(val version: String) { */ val patch = splitVersion.getOrNull(2)?.toIntOrNull() ?: 0 + constructor(major: Int, minor: Int, patch: Int = 0) : this("$major.$minor.$patch") + /** * Compare this version to another ParsedVersion * @param o the other ParsedVersion to compare to diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt new file mode 100644 index 00000000..295da957 --- /dev/null +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt @@ -0,0 +1,9 @@ +package io.github.deltacv.eocvsim.plugin + +import com.github.serivesmejia.eocvsim.util.extension.plus +import com.github.serivesmejia.eocvsim.util.io.EOCVSimFolder +import java.io.File + +val PLUGIN_FOLDER = (EOCVSimFolder + File.separator + "plugins").apply { mkdir() } +val PLUGIN_CACHING_FOLDER = (PLUGIN_FOLDER + File.separator + "caching").apply { mkdir() } +val FILESYSTEMS_FOLDER = (PLUGIN_FOLDER + File.separator + "filesystems").apply { mkdir() } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt similarity index 100% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt rename to Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt similarity index 87% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt rename to Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt index 81cdf8d4..235c636c 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt @@ -23,13 +23,10 @@ package io.github.deltacv.eocvsim.plugin.security -import com.github.serivesmejia.eocvsim.util.SysUtil -import com.github.serivesmejia.eocvsim.util.loggerForThis import com.github.serivesmejia.eocvsim.util.extension.plus import com.github.serivesmejia.eocvsim.util.io.LockFile -import com.github.serivesmejia.eocvsim.util.io.lockDirectory -import com.moandjiezana.toml.Toml -import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import com.github.serivesmejia.eocvsim.util.loggerForThis +import io.github.deltacv.eocvsim.plugin.PLUGIN_CACHING_FOLDER import java.io.File import java.net.URL import java.security.KeyFactory @@ -59,8 +56,8 @@ object AuthorityFetcher { const val AUTHORITY_SERVER_URL = "https://raw.githubusercontent.com/deltacv/Authorities/refs/heads/master" - private val AUTHORITIES_FILE = PluginManager.PLUGIN_CACHING_FOLDER + File.separator + "authorities.toml" - private val AUTHORITIES_LOCK_FILE = LockFile(PluginManager.PLUGIN_CACHING_FOLDER + File.separator + "authorities.lock") + private val AUTHORITIES_FILE = PLUGIN_CACHING_FOLDER + File.separator + "authorities.toml" + private val AUTHORITIES_LOCK_FILE = LockFile(PLUGIN_CACHING_FOLDER + File.separator + "authorities.lock") private val AUTHORITIES_LOCK_FILE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(3) @@ -81,7 +78,7 @@ object AuthorityFetcher { // Load authorities from file if it exists if (AUTHORITIES_FILE.exists() && tryLockAuthoritiesFile()) { try { - val authoritiesToml = Toml().read(AUTHORITIES_FILE) + val authoritiesToml = com.moandjiezana.toml.Toml().read(AUTHORITIES_FILE) val timestamp = authoritiesToml.getLong("timestamp") if(System.currentTimeMillis() - timestamp > TTL_DURATION_MS) { @@ -138,13 +135,13 @@ object AuthorityFetcher { val currentTime = System.currentTimeMillis() if(!AUTHORITIES_FILE.exists()) { - SysUtil.saveFileStr(AUTHORITIES_FILE, "timestamp = $currentTime\n") + AUTHORITIES_FILE.writeText("timestamp = $currentTime\n") } - val authoritiesToml = Toml().read(AUTHORITIES_FILE) + val authoritiesToml = com.moandjiezana.toml.Toml().read(AUTHORITIES_FILE) val timestamp = authoritiesToml.getLong("timestamp") - if(currentTime - timestamp > TTL_DURATION_MS) { + if(timestamp != null && currentTime - timestamp > TTL_DURATION_MS) { AUTHORITIES_FILE.delete() logger.info("Authorities file has expired, clearing cache") cache.clear() @@ -175,7 +172,7 @@ object AuthorityFetcher { sb.appendLine("timestamp = ${System.currentTimeMillis()}") // Write the updated content to the file - SysUtil.saveFileStr(AUTHORITIES_FILE, sb.toString()) + AUTHORITIES_FILE.writeText(sb.toString()) } catch (e: Exception) { logger.error("Failed to save authority to file", e) } finally { diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt similarity index 80% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt rename to Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt index 97a6f916..adbaf8ef 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt @@ -30,15 +30,18 @@ import java.security.KeyPairGenerator import java.security.PrivateKey import java.util.Base64 -fun main() { - // Generate RSA key pair - val keyPair: KeyPair = generateKeyPair() +object KeyGeneratorTool { + @JvmStatic + fun main(args: Array) { + // Generate RSA key pair + val keyPair: KeyPair = generateKeyPair() - // Save keys to files - saveKeyToFile("private_key.pem", keyPair.private) - saveKeyToFile("public_key.pem", keyPair.public) + // Save keys to files + saveKeyToFile("private_key.pem", keyPair.private) + saveKeyToFile("public_key.pem", keyPair.public) - println("Keys generated and saved to files.") + println("Keys generated and saved to files 'private_key.pem' and 'public_key.pem'") + } } fun generateKeyPair(): KeyPair { diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt similarity index 100% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt rename to Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt similarity index 71% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt rename to Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt index b6a4ee74..181b525a 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt @@ -23,11 +23,7 @@ package io.github.deltacv.eocvsim.plugin.security -import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput import com.github.serivesmejia.eocvsim.util.extension.hashString -import com.github.serivesmejia.eocvsim.util.loggerForThis -import com.moandjiezana.toml.Toml -import io.github.deltacv.eocvsim.plugin.loader.PluginParser import picocli.CommandLine import java.io.File import java.security.KeyFactory @@ -43,49 +39,60 @@ import java.util.zip.ZipEntry import java.io.FileOutputStream import java.io.IOException import java.util.zip.ZipOutputStream -import kotlin.math.log import kotlin.system.exitProcess class PluginSigningTool : Runnable { - val logger by loggerForThis() - - @CommandLine.Option(names = ["-p", "--plugin"], description = ["The plugin JAR file to sign"], required = true) + @picocli.CommandLine.Option(names = ["-p", "--plugin"], description = ["The plugin JAR file to sign"], required = true) var pluginFile: String? = null @CommandLine.Option(names = ["-a", "--authority"], description = ["The authority to sign the plugin with"], required = true) var authority: String? = null - @CommandLine.Option(names = ["-k", "--key"], description = ["The private key file to sign the plugin with"], required = true) + @CommandLine.Option(names = ["-k", "--key"], description = ["The private key to sign the plugin with, can be both a file or a base64-encoded string"], required = true) var privateKeyFile: String? = null override fun run() { - logger.info("Signing plugin $pluginFile with authority $authority") + println("Signing plugin $pluginFile with authority $authority") val authority = AuthorityFetcher.fetchAuthority(authority ?: throw IllegalArgumentException("Authority is required")) if (authority == null) { + println("Failed to fetch authority ${this.authority}") exitProcess(1) } // Load the private key - val privateKey = loadPrivateKey(privateKeyFile ?: throw IllegalArgumentException("Private key file is required")) + val privateKey = loadPrivateKey(privateKeyFile ?: throw IllegalArgumentException("Private key is required")) + + val publicKey = authority.publicKey - val publicKey = authority.publicKey // Assuming Authority has a publicKey property if (!isPrivateKeyMatchingAuthority(publicKey, privateKey)) { - logger.error("Private key does not match to the authority's public key.") + println("Private key does not match to the authority's public key.") exitProcess(1) } else { - logger.info("Private key matches to the authority's public key.") + println("Private key matches to the authority's public key.") + } + + if(pluginFile == null) { + println("Plugin file is required") + exitProcess(1) } - signPlugin(File(pluginFile), privateKey, authority) + signPlugin(File(pluginFile!!), privateKey, authority) - logger.info("Plugin signed successfully and saved") + println("Plugin signed successfully and saved") } - private fun loadPrivateKey(privateKeyPath: String): PrivateKey { - val keyBytes = File(privateKeyPath).readBytes() - val keyString = String(keyBytes) + private fun loadPrivateKey(privateKey: String): PrivateKey { + val privateKeyFile = File(privateKey) + + val keyBytes = if(!privateKeyFile.exists()) { + Base64.getDecoder().decode(privateKey) + } else { + privateKeyFile.readBytes() + } + + val keyString = String(keyBytes, Charsets.UTF_8) val decodedKey = Base64.getDecoder().decode(AuthorityFetcher.parsePem(keyString)) val keySpec = PKCS8EncodedKeySpec(decodedKey) @@ -110,11 +117,11 @@ class PluginSigningTool : Runnable { val signature = signClass(classData, privateKey) signatures[className] = signature - logger.info("Signed class $className") + println("Signed class $className") } } - logger.info("Signed all classes, creating signature.toml in jar") + println("Signed all classes, creating signature.toml in jar") // Create signature.toml createSignatureToml(signatures, authority, jarFile) @@ -185,7 +192,7 @@ class PluginSigningTool : Runnable { private fun isPrivateKeyMatchingAuthority(publicKey: PublicKey, privateKey: PrivateKey): Boolean { // encrypt and decrypt a sample message to check if the keys match - val sampleMessage = PluginOutput.SPECIAL + val sampleMessage = "Hello, world!" val cipher = Cipher.getInstance("RSA") cipher.init(Cipher.ENCRYPT_MODE, publicKey) @@ -196,24 +203,32 @@ class PluginSigningTool : Runnable { return sampleMessage == String(decrypted) } -} -fun main(args: Array) { - if(args.isEmpty()) { - val scanner = Scanner(System.`in`) + companion object { + @JvmStatic + fun main(args: Array) { + if(args.isEmpty()) { + println("This tool provides a command line interface, but no arguments were provided.") + println("If you want to use command line arguments, please provide them in the format:") + println("java ... --plugin --authority --key ") + println("\nWe'll now ask for the required information interactively.") - val tool = PluginSigningTool() - println("Enter the plugin JAR file path:") - tool.pluginFile = scanner.next() + val scanner = Scanner(System.`in`) - println("Enter the authority to sign the plugin with:") - tool.authority = scanner.next() + val tool = PluginSigningTool() + println("Enter the plugin JAR file path:") + tool.pluginFile = scanner.next() - println("Enter the private key file path:") - tool.privateKeyFile = scanner.next() + println("Enter the authority to sign the plugin with:") + tool.authority = scanner.next() - tool.run() - } else { - CommandLine(PluginSigningTool()).execute(*args) + println("Enter the private key file path, or encoded in base64:") + tool.privateKeyFile = scanner.next() + + tool.run() + } else { + CommandLine(PluginSigningTool()).execute(*args) + } + } } } \ No newline at end of file diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index 4072996e..5b6e86bf 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -18,6 +18,14 @@ apply from: '../build.common.gradle' ext.kotest_version = '5.7.2' +components.java { + tasks.named("shadowJar").configure { + // only run shadowJar when explicitly specified by the user + // check if user invoked gradle with :shadowJar + enabled = project.gradle.startParameter.taskNames.contains("shadowJar") + } +} + shadowJar { mergeServiceFiles() } @@ -70,7 +78,7 @@ dependencies { implementation "org.deltacv.steve:core:1.1.3" implementation "org.deltacv.steve:backend-openpnp:1.1.3" - implementation 'info.picocli:picocli:4.6.1' + implementation "info.picocli:picocli:$picocli_version" implementation 'com.google.code.gson:gson:2.8.9' implementation "io.github.classgraph:classgraph:$classgraph_version" @@ -88,7 +96,7 @@ dependencies { testImplementation "io.kotest:kotest-runner-junit5:$kotest_version" testImplementation "io.kotest:kotest-assertions-core:$kotest_version" - implementation 'com.moandjiezana.toml:toml4j:0.7.2' + implementation "com.moandjiezana.toml:toml4j:$toml4j_version" implementation 'org.ow2.asm:asm:9.7' implementation 'org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-depchain:3.3.2' diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/EOCVSim.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/EOCVSim.kt index 2ba19617..87ef799c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/EOCVSim.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/EOCVSim.kt @@ -49,8 +49,8 @@ import com.qualcomm.robotcore.eventloop.opmode.OpMode import com.qualcomm.robotcore.eventloop.opmode.OpModePipelineHandler import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator import io.github.deltacv.common.util.ParsedVersion -import io.github.deltacv.eocvsim.plugin.security.AuthorityFetcher import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import io.github.deltacv.eocvsim.plugin.papervision.PaperVisionChecker import io.github.deltacv.vision.external.PipelineRenderHook import nu.pattern.OpenCV import org.opencv.core.Mat @@ -291,6 +291,27 @@ class EOCVSim(val params: Parameters = Parameters()) { workspaceManager.init() + visualizer.onInitFinished { + // SHOW WELCOME DIALOGS TO NEW USERS + + if(!config.flags.contains("hasShownIamA") || config.flags["hasShownIamA"] == false) { + // Initial dialog to introduce our cool stuff to the user + DialogFactory.createIAmA(visualizer) + } else if(!config.flags.contains("hasShownIamPaperVision") || config.flags["hasShownIamPaperVision"] == false) { + // sometimes the users might miss the PaperVision dialog after the IAmA dialog + // so we show it here if the user hasn't seen it yet + DialogFactory.createIAmAPaperVision(visualizer, false) + } else if(config.flags["prefersPaperVision"] == true) { + // if the user prefers PaperVision, switch to it upon start up + val indexOfTab = visualizer.pipelineOpModeSwitchablePanel.indexOfTab("PaperVision") + if(indexOfTab >= 0) { + visualizer.pipelineOpModeSwitchablePanel.selectedIndex = indexOfTab + } + } + + // END OF WELCOME DIALOGS + } + visualizer.initAsync(configManager.config.simTheme) //create gui in the EDT inputSourceManager.init() //loading user created input sources @@ -406,6 +427,8 @@ class EOCVSim(val params: Parameters = Parameters()) { throw IllegalStateException("start() must be called from the EOCVSim thread") } + PaperVisionChecker.check(this) + logger.info("-- Begin EOCVSim loop ($hexCode) --") while (!eocvSimThread.isInterrupted && !destroying) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Main.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Main.kt index 4bb6415c..9c629e7d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Main.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Main.kt @@ -19,6 +19,8 @@ object Main { @JvmStatic fun main(args: Array) { System.setProperty("sun.java2d.d3d", "false") + System.setProperty("apple.awt.application.appearance", "system") + System.setProperty("apple.awt.application.name", "EasyOpenCV Simulator") val result = CommandLine( EOCVSimCommandInterface() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/Config.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/Config.java index 0bc7f0d6..7064d338 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/Config.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/Config.java @@ -38,7 +38,7 @@ import java.util.List; public class Config { - public volatile Theme simTheme = Theme.Light; + public volatile Theme simTheme = Theme.Dark; @Deprecated public volatile double zoom = 1; @@ -67,6 +67,8 @@ public class Config { @Deprecated public volatile List superAccessPluginHashes = new ArrayList<>(); + public volatile boolean autoAcceptSuperAccessOnTrusted = true; + public volatile HashMap flags = new HashMap<>(); public boolean hasFlag(String flagName) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.java index a1cd2f17..3095bfe3 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.java @@ -24,8 +24,11 @@ package com.github.serivesmejia.eocvsim.gui; import com.github.serivesmejia.eocvsim.EOCVSim; +import com.github.serivesmejia.eocvsim.config.Config; import com.github.serivesmejia.eocvsim.gui.dialog.*; import com.github.serivesmejia.eocvsim.gui.dialog.SplashScreen; +import com.github.serivesmejia.eocvsim.gui.dialog.iama.IAmA; +import com.github.serivesmejia.eocvsim.gui.dialog.iama.IAmAPaperVision; import com.github.serivesmejia.eocvsim.gui.dialog.source.CreateCameraSource; import com.github.serivesmejia.eocvsim.gui.dialog.source.CreateImageSource; import com.github.serivesmejia.eocvsim.gui.dialog.source.CreateSource; @@ -160,6 +163,18 @@ public static void createSplashScreen(EventHandler closeHandler) { invokeLater(() -> new SplashScreen(closeHandler)); } + public static void createIAmA(Visualizer visualizer) { + invokeLater(() -> new IAmA(visualizer.frame, visualizer)); + } + + public static void createIAmAPaperVision(Visualizer visualizer, boolean showWorkspacesButton) { + invokeLater(() -> new IAmAPaperVision(visualizer.frame, visualizer, false, showWorkspacesButton)); + } + + public static void createWorkspace(Visualizer visualizer) { + invokeLater(() -> new CreateWorkspace(visualizer.frame, visualizer)); + } + public static FileAlreadyExists.UserChoice createFileAlreadyExistsDialog(EOCVSim eocvSim) { return new FileAlreadyExists(eocvSim.visualizer.frame, eocvSim).run(); } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/EOCVSimIconLibrary.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/EOCVSimIconLibrary.kt index 80131f9f..91061658 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/EOCVSimIconLibrary.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/EOCVSimIconLibrary.kt @@ -1,28 +1,40 @@ -package com.github.serivesmejia.eocvsim.gui - -object EOCVSimIconLibrary { - - val icoEOCVSim by icon("ico_eocvsim", "/images/icon/ico_eocvsim.png", false) - - val icoImg by icon("ico_img", "/images/icon/ico_img.png") - val icoCam by icon("ico_cam", "/images/icon/ico_cam.png") - val icoVid by icon("ico_vid", "/images/icon/ico_vid.png") - - val icoConfig by icon("ico_config", "/images/icon/ico_config.png") - val icoSlider by icon("ico_slider", "/images/icon/ico_slider.png") - val icoTextbox by icon("ico_textbox", "/images/icon/ico_textbox.png") - val icoColorPick by icon("ico_colorpick", "/images/icon/ico_colorpick.png") - - val icoArrowDropdown by icon("ico_arrow_dropdown", "/images/icon/ico_arrow_dropdown.png") - - val icoFlag by icon("ico_flag", "/images/icon/ico_flag.png") - val icoNotStarted by icon("ico_not_started", "/images/icon/ico_not_started.png") - val icoPlay by icon("ico_play", "/images/icon/ico_play.png") - val icoStop by icon("ico_stop", "/images/icon/ico_stop.png") - - val icoGears by icon("ico_gears", "/images/icon/ico_gears.png") - val icoHammer by icon("ico_hammer", "/images/icon/ico_hammer.png") - - val icoColorPickPointer by icon("ico_colorpick_pointer", "/images/icon/ico_colorpick_pointer.png") - +package com.github.serivesmejia.eocvsim.gui + +object EOCVSimIconLibrary { + + val icoEOCVSim by icon("ico_eocvsim", "/images/icon/ico_eocvsim_new.png", false) + + val icoEOCVSim128 by icon("ico_eocvsim_128", "/images/icon/ico_eocvsim_new_128.png", false) + val icoEOCVSim64 by icon("ico_eocvsim_128", "/images/icon/ico_eocvsim_new_64.png", false) + val icoEOCVSim32 by icon("ico_eocvsim_32", "/images/icon/ico_eocvsim_new_32.png", false) + val icoEOCVSim16 by icon("ico_eocvsim_16", "/images/icon/ico_eocvsim_new_16.png", false) + + val icoImg by icon("ico_img", "/images/icon/ico_img.png") + val icoCam by icon("ico_cam", "/images/icon/ico_cam.png") + val icoVid by icon("ico_vid", "/images/icon/ico_vid.png") + + val icoFolder by icon("ico_folder", "/images/icon/ico_folder.png", false) + + val icoConfig by icon("ico_config", "/images/icon/ico_config.png") + val icoSlider by icon("ico_slider", "/images/icon/ico_slider.png") + val icoTextbox by icon("ico_textbox", "/images/icon/ico_textbox.png") + val icoColorPick by icon("ico_colorpick", "/images/icon/ico_colorpick.png") + + val icoArrowDropdown by icon("ico_arrow_dropdown", "/images/icon/ico_arrow_dropdown.png") + + val icoFlag by icon("ico_flag", "/images/icon/ico_flag.png") + val icoNotStarted by icon("ico_not_started", "/images/icon/ico_not_started.png") + val icoPlay by icon("ico_play", "/images/icon/ico_play.png") + val icoStop by icon("ico_stop", "/images/icon/ico_stop.png") + + val icoGears by icon("ico_gears", "/images/icon/ico_gears.png") + val icoHammer by icon("ico_hammer", "/images/icon/ico_hammer.png") + + val icoColorPickPointer by icon("ico_colorpick_pointer", "/images/icon/ico_colorpick_pointer.png") + + val icoFirstRobotics by icon("ico_first_robotics", "/images/icon/ico_firstrobotics.png", false) + val icoPaperVision by icon("ico_paper_vision", "/images/icon/ico_ezv.png", false) + val icoVsCode by icon("ico_vscode", "/images/icon/ico_vscode.png", false) + val icoUser by icon("ico_user", "/images/icon/ico_user.png") + } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java index 3b9f6512..8e5a3221 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java @@ -61,7 +61,7 @@ public class Visualizer { public final ArrayList childFrames = new ArrayList<>(); public final ArrayList childDialogs = new ArrayList<>(); - private final EOCVSim eocvSim; + public final EOCVSim eocvSim; public JFrame frame; public SwingOpenCvViewport viewport = null; @@ -101,7 +101,7 @@ public void init(Theme theme) { if(Taskbar.isTaskbarSupported()){ try { //set icon for mac os (and other systems which do support this method) - Taskbar.getTaskbar().setIconImage(Icons.INSTANCE.getImage("ico_eocvsim").getImage()); + Taskbar.getTaskbar().setIconImage(EOCVSimIconLibrary.INSTANCE.getIcoEOCVSim128().getImage()); } catch (final UnsupportedOperationException e) { logger.warn("Setting the Taskbar icon image is not supported on this platform"); } catch (final SecurityException e) { @@ -156,7 +156,7 @@ public void init(Theme theme) { /* * TOP MENU BAR */ - + frame.setJMenuBar(menuBar); /* @@ -209,7 +209,12 @@ public void init(Theme theme) { frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - frame.setIconImage(Icons.INSTANCE.getImage("ico_eocvsim").getImage()); + frame.setIconImages(List.of( + EOCVSimIconLibrary.INSTANCE.getIcoEOCVSim128().getImage(), + EOCVSimIconLibrary.INSTANCE.getIcoEOCVSim64().getImage(), + EOCVSimIconLibrary.INSTANCE.getIcoEOCVSim32().getImage(), + EOCVSimIconLibrary.INSTANCE.getIcoEOCVSim16().getImage() + )); frame.setLocationRelativeTo(null); frame.setExtendedState(JFrame.MAXIMIZED_BOTH); @@ -385,7 +390,6 @@ public void createVSCodeWorkspace() { if(selectedFile.isDirectory() && selectedFile.listFiles().length == 0) { eocvSim.workspaceManager.createWorkspaceWithTemplateAsync( selectedFile, GradleWorkspaceTemplate.INSTANCE, - () -> { askOpenVSCode(); return Unit.INSTANCE; // weird kotlin interop @@ -405,6 +409,11 @@ public void askOpenVSCode() { DialogFactory.createYesOrNo(frame, "A new workspace was created. Do you want to open VS Code?", "", (result) -> { if(result == 0) { + JOptionPane.showMessageDialog( + frame, + "After opening VS Code, you will need to install the Extension Pack for Java, for proper autocompletion support. Ensure you do so when asked by the editor !" + ); + VSCodeLauncher.INSTANCE.asyncLaunch(eocvSim.workspaceManager.getWorkspaceFile()); } } @@ -471,7 +480,7 @@ public boolean pleaseWaitDialog(JDialog diag, String message, String subMessage, apwd.subMsg = subMsg; apwd.cancelBtt = cancelBtt; } - + if(size == null) size = new Dimension(400, 200); dialog.setSize(size); diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt index 85699db9..2a0d7a41 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt @@ -26,6 +26,7 @@ package com.github.serivesmejia.eocvsim.gui.component import java.awt.Color import java.awt.Dimension +import java.awt.Font import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import javax.swing.BorderFactory @@ -55,6 +56,7 @@ class CollapsiblePanelX @JvmOverloads constructor( border = TitledBorder(titleAndDescriptor) border.titleColor = titleCol + border.titleFont = border.titleFont.deriveFont(Font.BOLD) border.border = BorderFactory.createMatteBorder(1, 1, 1, 1, borderCol) setBorder(border) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelOptions.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelOptions.kt index e0ce3d79..9f9ed086 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelOptions.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelOptions.kt @@ -25,7 +25,6 @@ package com.github.serivesmejia.eocvsim.gui.component.tuner import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary -import com.github.serivesmejia.eocvsim.gui.Icons import com.github.serivesmejia.eocvsim.gui.component.PopupX import io.github.deltacv.vision.external.util.extension.cvtColor import com.github.serivesmejia.eocvsim.util.extension.clipUpperZero diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TopMenuBar.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TopMenuBar.kt index 81af4fa4..3ad7b23c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TopMenuBar.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TopMenuBar.kt @@ -30,6 +30,7 @@ import com.github.serivesmejia.eocvsim.gui.dialog.Output import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput import com.github.serivesmejia.eocvsim.gui.util.GuiUtil import com.github.serivesmejia.eocvsim.input.SourceType +import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager import com.github.serivesmejia.eocvsim.util.FileFilters import com.github.serivesmejia.eocvsim.util.exception.handling.CrashReport import com.github.serivesmejia.eocvsim.workspace.util.VSCodeLauncher @@ -46,18 +47,18 @@ import javax.swing.JMenuItem class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { companion object { - val docsUrl = URI("https://deltacv.gitbook.io/eocv-sim/") + val docsUrl = URI("https://docs.deltacv.org/eocv-sim/") } @JvmField val mFileMenu = JMenu("File") @JvmField val mWorkspMenu = JMenu("Workspace") @JvmField val mHelpMenu = JMenu("Help") - @JvmField val workspCompile = JMenuItem("Build java files") + @JvmField val workspCompile = JMenuItem("Build Java Files") init { + val desktop = Desktop.getDesktop() // FILE - val fileNew = JMenu("New") mFileMenu.add(fileNew) @@ -77,8 +78,7 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { fileNewInputSourceSubmenu.add(fileNewInputSourceItem) } - val fileSaveMat = JMenuItem("Screenshot pipeline") - + val fileSaveMat = JMenuItem("Screenshot Pipeline") fileSaveMat.addActionListener { val mat = Mat() @@ -97,10 +97,14 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { mFileMenu.addSeparator() - val editSettings = JMenuItem("Settings") - editSettings.addActionListener { DialogFactory.createConfigDialog(eocvSim) } - - mFileMenu.add(editSettings) + if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) { + desktop.setPreferencesHandler { DialogFactory.createConfigDialog(eocvSim) } + } + else { + val editSettings = JMenuItem("Settings") + editSettings.addActionListener { DialogFactory.createConfigDialog(eocvSim) } + mFileMenu.add(editSettings) + } val filePlugins = JMenuItem("Manage Plugins") filePlugins.addActionListener { eocvSim.pluginManager.appender.append(PluginOutput.SPECIAL_OPEN_MGR)} @@ -118,11 +122,22 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { //WORKSPACE - val workspSetWorkspace = JMenuItem("Select workspace") + val workspSetWorkspace = JMenuItem("Select Workspace") - workspSetWorkspace.addActionListener { visualizer.selectPipelinesWorkspace() } + workspSetWorkspace.addActionListener { DialogFactory.createWorkspace(visualizer) } mWorkspMenu.add(workspSetWorkspace) + val workspClose = JMenuItem("Close Current Workspace") + + workspClose.addActionListener { + eocvSim.onMainUpdate.doOnce { + eocvSim.workspaceManager.workspaceFile = CompiledPipelineManager.DEF_WORKSPACE_FOLDER + } + } + mWorkspMenu.add(workspClose) + + mWorkspMenu.addSeparator() + workspCompile.addActionListener { visualizer.asyncCompilePipelines() } mWorkspMenu.add(workspCompile) @@ -138,14 +153,14 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { val workspVSCode = JMenu("External") - val workspVSCodeCreate = JMenuItem("Create Gradle workspace") + val workspVSCodeCreate = JMenuItem("Create Gradle Workspace") workspVSCodeCreate.addActionListener { visualizer.createVSCodeWorkspace() } workspVSCode.add(workspVSCodeCreate) workspVSCode.addSeparator() - val workspVSCodeOpen = JMenuItem("Open VS Code here") + val workspVSCodeOpen = JMenuItem("Open VS Code Here") workspVSCodeOpen.addActionListener { VSCodeLauncher.asyncLaunch(eocvSim.workspaceManager.workspaceFile) @@ -160,7 +175,7 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { val helpUsage = JMenuItem("Documentation") helpUsage.addActionListener { - Desktop.getDesktop().browse(docsUrl) + desktop.browse(docsUrl) } helpUsage.isEnabled = Desktop.isDesktopSupported() @@ -198,10 +213,21 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { mHelpMenu.add(helpExportLogs) - val helpAbout = JMenuItem("About") - helpAbout.addActionListener { DialogFactory.createAboutDialog(eocvSim) } + mHelpMenu.addSeparator() + + val helpIAmA = JMenuItem("I am a...") + helpIAmA.addActionListener { DialogFactory.createIAmA(eocvSim.visualizer) } + + mHelpMenu.add(helpIAmA) - mHelpMenu.add(helpAbout) + if (desktop.isSupported(Desktop.Action.APP_ABOUT)) { + desktop.setAboutHandler { DialogFactory.createAboutDialog(eocvSim) } + } + else { + val helpAbout = JMenuItem("About") + helpAbout.addActionListener { DialogFactory.createAboutDialog(eocvSim) } + mHelpMenu.add(helpAbout) + } add(mHelpMenu) } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorButtonsPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorButtonsPanel.kt index 45f8ba0b..bb526223 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorButtonsPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorButtonsPanel.kt @@ -29,6 +29,8 @@ import com.github.serivesmejia.eocvsim.gui.component.PopupX import java.awt.GridBagConstraints import java.awt.GridBagLayout import java.awt.Insets +import javax.swing.BorderFactory +import javax.swing.Box import javax.swing.JButton import javax.swing.JPanel import javax.swing.JToggleButton @@ -42,7 +44,7 @@ class PipelineSelectorButtonsPanel(eocvSim: EOCVSim) : JPanel(GridBagLayout()) { val pipelineWorkspaceBtt = JButton("Workspace") val workspaceButtonsPanel = JPanel(GridBagLayout()) - val pipelineCompileBtt = JButton("Build java files") + val pipelineCompileBtt = JButton("Build Java Files") private var lastWorkspacePopup: PopupX? = null @@ -100,27 +102,32 @@ class PipelineSelectorButtonsPanel(eocvSim: EOCVSim) : JPanel(GridBagLayout()) { // WORKSPACE BUTTONS POPUP - pipelineCompileBtt.addActionListener { eocvSim.visualizer.asyncCompilePipelines() } - workspaceButtonsPanel.add(pipelineCompileBtt, GridBagConstraints().apply { + val selectWorkspBtt = JButton("Select Workspace") + + selectWorkspBtt.addActionListener { DialogFactory.createWorkspace(eocvSim.visualizer) } + workspaceButtonsPanel.add(selectWorkspBtt, GridBagConstraints().apply { gridx = 0 gridy = 0 }) - val selectWorkspBtt = JButton("Select workspace") - - selectWorkspBtt.addActionListener { eocvSim.visualizer.selectPipelinesWorkspace() } - workspaceButtonsPanel.add(selectWorkspBtt, GridBagConstraints().apply { + workspaceButtonsPanel.add(Box.createHorizontalStrut(5), GridBagConstraints().apply { gridx = 1 gridy = 0 }) + pipelineCompileBtt.addActionListener { eocvSim.visualizer.asyncCompilePipelines() } + workspaceButtonsPanel.add(pipelineCompileBtt, GridBagConstraints().apply { + gridx = 2 + gridy = 0 + }) + val outputBtt = JButton("Pipeline Output") outputBtt.addActionListener { DialogFactory.createPipelineOutput(eocvSim) } workspaceButtonsPanel.add(outputBtt, GridBagConstraints().apply { gridy = 1 weightx = 1.0 - gridwidth = 2 + gridwidth = 3 fill = GridBagConstraints.HORIZONTAL anchor = GridBagConstraints.CENTER diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.java index 958a6670..6f0b1846 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.java @@ -24,6 +24,7 @@ package com.github.serivesmejia.eocvsim.gui.dialog; import com.github.serivesmejia.eocvsim.EOCVSim; +import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary; import com.github.serivesmejia.eocvsim.gui.Icons; import io.github.deltacv.vision.external.gui.component.ImageX; import com.github.serivesmejia.eocvsim.gui.util.GuiUtil; @@ -70,7 +71,7 @@ private void initAbout() { JPanel contents = new JPanel(new GridLayout(2, 1)); contents.setAlignmentX(Component.CENTER_ALIGNMENT); - ImageX icon = new ImageX(Icons.INSTANCE.getImage("ico_eocvsim")); + ImageX icon = new ImageX(EOCVSimIconLibrary.INSTANCE.getIcoEOCVSim64()); icon.setSize(50, 50); icon.setAlignmentX(Component.CENTER_ALIGNMENT); diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.java index 90b6f0f6..9b5a92e2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.java @@ -44,10 +44,12 @@ public class Configuration { private final EOCVSim eocvSim; public JPanel contents = new JPanel(new GridBagLayout()); public JComboBox themeComboBox = new JComboBox<>(); + public JCheckBox superAccessCheckBox = new JCheckBox("Auto Accept SuperAccess on Trusted Plugins"); + public JCheckBox prefersPaperVisionCheckbox = new JCheckBox("Focus on PaperVision Upon Startup"); public JButton acceptButton = new JButton("Accept"); - public JCheckBox pauseOnImageCheckBox = new JCheckBox(); + public JCheckBox pauseOnImageCheckBox = new JCheckBox("Pause with Image Sources"); public EnumComboBox pipelineTimeoutComboBox = null; public EnumComboBox pipelineFpsComboBox = null; @@ -81,7 +83,7 @@ private void initConfiguration() { UI TAB */ - JPanel uiPanel = new JPanel(new GridLayout(1, 1, 1, 8)); + JPanel uiPanel = new JPanel(new GridLayout(3, 1, 1, 8)); /* THEME SETTING */ JPanel themePanel = new JPanel(new FlowLayout()); @@ -98,6 +100,27 @@ private void initConfiguration() { themePanel.add(this.themeComboBox); uiPanel.add(themePanel); + /* AUTO ACCEPT SUPERACCESS ON TRUSTED PLUGINS */ + + JPanel superAccessPanel = new JPanel(new FlowLayout()); + + superAccessCheckBox.setSelected(config.autoAcceptSuperAccessOnTrusted); + superAccessPanel.add(superAccessCheckBox); + uiPanel.add(superAccessPanel); + + /* FOCUS ON PAPERVISION UPON STARTUP */ + + JPanel prefersPaperVisionPanel = new JPanel(new FlowLayout()); + + boolean prefersPaperVision = false; + if(config.flags.containsKey("prefersPaperVision")) { + prefersPaperVision = config.flags.get("prefersPaperVision"); + } + + prefersPaperVisionCheckbox.setSelected(prefersPaperVision); + prefersPaperVisionPanel.add(prefersPaperVisionCheckbox); + uiPanel.add(prefersPaperVisionPanel); + tabbedPane.addTab("Interface", uiPanel); /* @@ -107,12 +130,10 @@ private void initConfiguration() { /* PAUSE WITH IMAGE SOURCES OPTION */ JPanel pauseOnImagePanel = new JPanel(new FlowLayout()); - JLabel pauseOnImageLabel = new JLabel("Pause with Image Sources"); pauseOnImageCheckBox.setSelected(config.pauseOnImages); pauseOnImagePanel.add(pauseOnImageCheckBox); - pauseOnImagePanel.add(pauseOnImageLabel); inputSourcesPanel.add(pauseOnImagePanel); @@ -215,6 +236,9 @@ private void applyChanges() { config.pipelineMaxFps = pipelineFpsComboBox.getSelectedEnum(); config.videoRecordingSize = videoRecordingSize.getCurrentSize(); config.videoRecordingFps = videoRecordingFpsComboBox.getSelectedEnum(); + config.autoAcceptSuperAccessOnTrusted = superAccessCheckBox.isSelected(); + + config.flags.put("prefersPaperVision", prefersPaperVisionCheckbox.isSelected()); eocvSim.configManager.saveToFile(); //update config file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/CreateWorkspace.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/CreateWorkspace.kt new file mode 100644 index 00000000..d519d3f6 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/CreateWorkspace.kt @@ -0,0 +1,100 @@ +package com.github.serivesmejia.eocvsim.gui.dialog + +import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary +import com.github.serivesmejia.eocvsim.gui.Visualizer +import java.awt.Dimension +import java.awt.GridBagConstraints +import java.awt.GridBagLayout +import java.awt.GridLayout +import javax.swing.JButton +import javax.swing.JDialog +import javax.swing.JFrame +import javax.swing.JLabel +import javax.swing.JOptionPane +import javax.swing.JPanel + +class CreateWorkspace( + val parent: JFrame, + val visualizer: Visualizer +) { + + val dialog = JDialog(parent) + + init { + dialog.isModal = true + dialog.title = "Create a Workspace" + + dialog.contentPane.layout = GridBagLayout() + + val text = """ + Start visualizing your OpenCV pipelines now

+ A workspace contains the Java source code of the pipelines you
+ wish to run and build. Opt for the coding environment of your choice. + """.trimIndent() + + dialog.contentPane.add(JLabel("
$text
").apply { + font = font.deriveFont(14f) + }, GridBagConstraints().apply { + gridx = 0 + gridy = 0 + weightx = 1.0 + weighty = 1.0 + }) + + val buttonsPanel = JPanel().apply { + layout = GridLayout(1, 2, 10, 10) + } + + buttonsPanel.add(JButton( + "
Create a VS Code Workspace
", + EOCVSimIconLibrary.icoVsCode.scaleToFit(60, 60) + ).apply { + font = font.deriveFont(14f) + horizontalTextPosition = JButton.CENTER + verticalTextPosition = JButton.BOTTOM + + addActionListener { + dialog.dispose() + + JOptionPane.showConfirmDialog( + this@CreateWorkspace.parent, + "This feature prefers that you have Visual Studio Code already installed. You can opt to use IntelliJ IDEA instead, but you will have to do so manually.\n\n", + "VS Code Workspace", + JOptionPane.DEFAULT_OPTION, + JOptionPane.INFORMATION_MESSAGE + ) + + visualizer.createVSCodeWorkspace() + } + }) + + buttonsPanel.add(JButton( + "
Open an Existing Folder
", + EOCVSimIconLibrary.icoFolder.scaleToFit(60, 60) + ).apply { + font = font.deriveFont(14f) + horizontalTextPosition = JButton.CENTER + verticalTextPosition = JButton.BOTTOM + + addActionListener { + dialog.dispose() + visualizer.selectPipelinesWorkspace() + } + }) + + dialog.contentPane.add(buttonsPanel, GridBagConstraints().apply { + gridx = 0 + gridy = 1 + weightx = 1.0 + weighty = 1.0 + }) + + dialog.size = Dimension(520, 250) + dialog.isResizable = false + + dialog.defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE + dialog.setLocationRelativeTo(null) + dialog.isVisible = true + } + +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/PluginOutput.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/PluginOutput.kt index 148c9fcd..7df8e825 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/PluginOutput.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/PluginOutput.kt @@ -39,7 +39,6 @@ import java.awt.Dimension import java.awt.GridBagConstraints import java.awt.GridBagLayout import java.awt.Toolkit -import java.awt.Color import java.awt.datatransfer.StringSelection import javax.swing.* import java.awt.Desktop diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/SplashScreen.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/SplashScreen.kt index 9ae95f79..bb7651ff 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/SplashScreen.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/SplashScreen.kt @@ -52,7 +52,7 @@ class SplashScreen(closeHandler: EventHandler? = null) : JDialog() { g.drawImage(img.image, 0, 0, width, height, this) } - override fun getPreferredSize() = Dimension(img.iconWidth / 2, img.iconHeight / 2) + override fun getPreferredSize() = Dimension(img.iconWidth / 6, img.iconHeight / 6) } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmA.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmA.kt new file mode 100644 index 00000000..9761d89a --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmA.kt @@ -0,0 +1,109 @@ +package com.github.serivesmejia.eocvsim.gui.dialog.iama + +import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary +import com.github.serivesmejia.eocvsim.gui.Visualizer +import java.awt.Dimension +import java.awt.GridBagConstraints +import java.awt.GridBagLayout +import java.awt.GridLayout +import javax.swing.BorderFactory +import javax.swing.JButton +import javax.swing.JDialog +import javax.swing.JFrame +import javax.swing.JLabel +import javax.swing.JPanel + +class IAmA( + val parent: JFrame, + val visualizer: Visualizer +) { + + val dialog = JDialog(parent) + + init { + dialog.isModal = true + dialog.title = "Welcome !" + + dialog.contentPane.layout = GridBagLayout() + + val text = """ + Welcome to EOCV-Sim! We'll start with a walkthrough.
+ Please select the option that best describes you.

+ I am.. + """.trimIndent() + + dialog.contentPane.add(JLabel("
$text
").apply { + font = font.deriveFont(14f) + }, GridBagConstraints().apply { + gridx = 0 + gridy = 0 + weightx = 1.0 + weighty = 1.0 + }) + + val buttonsPanel = JPanel().apply { + layout = GridLayout(1, 3, 10, 10) + } + + buttonsPanel.add(JButton( + "
A FIRST Robotics Team
", + EOCVSimIconLibrary.icoFirstRobotics.scaleToFit(60, 60) + ).apply { + font = font.deriveFont(14f) + horizontalTextPosition = JButton.CENTER + verticalTextPosition = JButton.BOTTOM + + addActionListener { + dialog.dispose() + IAmAFirstRobotics(this@IAmA.parent, visualizer) + } + }) + + buttonsPanel.add(JButton( + "
A General Public User
", + EOCVSimIconLibrary.icoUser.scaleToFit(60, 60) + ).apply { + font = font.deriveFont(14f) + horizontalTextPosition = JButton.CENTER + verticalTextPosition = JButton.BOTTOM + + addActionListener { + dialog.dispose() + IAmAGeneralPublic(this@IAmA.parent, visualizer) + } + }) + + buttonsPanel.add(JButton( + "
Specifically Interested
in PaperVision
", + EOCVSimIconLibrary.icoPaperVision.scaleToFit(60, 60) + ).apply { + font = font.deriveFont(14f) + horizontalTextPosition = JButton.CENTER + verticalTextPosition = JButton.BOTTOM + + addActionListener { + dialog.dispose() + IAmAPaperVision(this@IAmA.parent, visualizer, specificallyInterested = true) + } + }) + + buttonsPanel.border = BorderFactory.createEmptyBorder(0, 0, 10, 0) + + dialog.contentPane.add(buttonsPanel, GridBagConstraints().apply { + gridx = 0 + gridy = 1 + weightx = 1.0 + weighty = 1.0 + }) + + dialog.minimumSize = Dimension(620, 250) + dialog.isResizable = false + + dialog.defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE + dialog.setLocationRelativeTo(null) + + visualizer.eocvSim.config.flags["hasShownIamA"] = true + dialog.isVisible = true + } + +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmAFirstRobotics.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmAFirstRobotics.kt new file mode 100644 index 00000000..ea996310 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmAFirstRobotics.kt @@ -0,0 +1,115 @@ +package com.github.serivesmejia.eocvsim.gui.dialog.iama + +import com.github.serivesmejia.eocvsim.gui.Visualizer +import java.awt.Desktop +import java.awt.GridBagConstraints +import java.awt.GridBagLayout +import java.net.URI +import javax.swing.BorderFactory +import javax.swing.Box +import javax.swing.BoxLayout +import javax.swing.JButton +import javax.swing.JDialog +import javax.swing.JFrame +import javax.swing.JLabel +import javax.swing.JPanel + +class IAmAFirstRobotics( + parent: JFrame, + visualizer: Visualizer +) { + + private val dialog = JDialog(parent).apply { + isModal = true + title = "Welcome !" + contentPane.layout = GridBagLayout() + + isResizable = false + setSize(520, 250) + setLocationRelativeTo(null) + } + + init { + // Create the contents panel + val contentsPanel = JPanel(GridBagLayout()) + + contentsPanel.border = BorderFactory.createEmptyBorder(10, 0, 0, 0) + + val contentText = """ + Hey FTC teams! EOCV-Sim helps you develop EasyOpenCV pipelines
+ without needing to always have the robot hardware at hand.


+ Work with code identical to the FtcRobotController, iterate with tools like the
+ variable tuner, and quickly visualize your code changes using workspaces.

+ Click on "Open Docs" to learn more about EOCV-Sim and workspaces.
+ """.trimIndent() + + // Add a descriptive JLabel to the contents panel + contentsPanel.add( + JLabel("
$contentText
").apply { + font = font.deriveFont(14f) + horizontalAlignment = JLabel.CENTER + }, + GridBagConstraints().apply { + gridx = 0 + gridy = 0 + weightx = 1.0 + weighty = 1.0 + fill = GridBagConstraints.BOTH + } + ) + + // Create the buttons panel + val buttonsPanel = JPanel().apply { + layout = BoxLayout(this, BoxLayout.X_AXIS) + + add(Box.createHorizontalGlue()) // Align the button to the right + + add(JButton("Open Docs").apply { + addActionListener { + // open webpage + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + Desktop.getDesktop().browse(URI("https://docs.deltacv.org/eocv-sim")); + } + } + }) + + add(Box.createHorizontalStrut(10)) // Add some space between the buttons + + add(JButton("Next").apply { + addActionListener { + dialog.dispose() // Close the dialog on click + IAmAPaperVision(parent, visualizer) + } + }) + + border = BorderFactory.createEmptyBorder(0, 10, 10, 10) + } + + // Add the contents panel to the dialog + dialog.contentPane.add( + contentsPanel, + GridBagConstraints().apply { + gridx = 0 + gridy = 0 + weightx = 1.0 + weighty = 1.0 + fill = GridBagConstraints.BOTH + } + ) + + // Add the buttons panel to the dialog + dialog.contentPane.add( + buttonsPanel, + GridBagConstraints().apply { + gridx = 0 + gridy = 1 + weightx = 1.0 + weighty = 0.0 + fill = GridBagConstraints.HORIZONTAL + } + ) + + // Make the dialog visible + dialog.isVisible = true + } +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmAGeneralPublic.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmAGeneralPublic.kt new file mode 100644 index 00000000..2556370b --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmAGeneralPublic.kt @@ -0,0 +1,115 @@ +package com.github.serivesmejia.eocvsim.gui.dialog.iama + +import com.github.serivesmejia.eocvsim.gui.Visualizer +import java.awt.Desktop +import java.awt.GridBagConstraints +import java.awt.GridBagLayout +import java.net.URI +import javax.swing.BorderFactory +import javax.swing.Box +import javax.swing.BoxLayout +import javax.swing.JButton +import javax.swing.JDialog +import javax.swing.JFrame +import javax.swing.JLabel +import javax.swing.JPanel + +class IAmAGeneralPublic( + parent: JFrame, + visualizer: Visualizer +) { + + private val dialog = JDialog(parent).apply { + isModal = true + title = "Welcome !" + contentPane.layout = GridBagLayout() + + isResizable = false + setSize(540, 250) + setLocationRelativeTo(null) + } + + init { + // Create the contents panel + val contentsPanel = JPanel(GridBagLayout()) + + contentsPanel.border = BorderFactory.createEmptyBorder(10, 0, 0, 0) + + val contentText = """ + Hey there! EOCV-Sim is a tool that acts as a vision development platform.

+ Built from the ground up to use the OpenCV interfaces with Java bindings,
+ EOCV-Sim allows you to develop vision pipelines, with tools that help you iterate
+ and visualize your code changes quickly, in an easy-to-use interface.

+ Click on "Open Docs" to learn more about EOCV-Sim and its features.
+ """.trimIndent() + + // Add a descriptive JLabel to the contents panel + contentsPanel.add( + JLabel("
$contentText
").apply { + font = font.deriveFont(14f) + horizontalAlignment = JLabel.CENTER + }, + GridBagConstraints().apply { + gridx = 0 + gridy = 0 + weightx = 1.0 + weighty = 1.0 + fill = GridBagConstraints.BOTH + } + ) + + // Create the buttons panel + val buttonsPanel = JPanel().apply { + layout = BoxLayout(this, BoxLayout.X_AXIS) + + add(Box.createHorizontalGlue()) // Align the button to the right + + add(JButton("Open Docs").apply { + addActionListener { + // open webpage + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + Desktop.getDesktop().browse(URI("https://docs.deltacv.org/eocv-sim")); + } + } + }) + + add(Box.createHorizontalStrut(10)) // Add some space between the buttons + + add(JButton("Next").apply { + addActionListener { + dialog.dispose() // Close the dialog on click + IAmAPaperVision(parent, visualizer) + } + }) + + border = BorderFactory.createEmptyBorder(0, 10, 10, 10) + } + + // Add the contents panel to the dialog + dialog.contentPane.add( + contentsPanel, + GridBagConstraints().apply { + gridx = 0 + gridy = 0 + weightx = 1.0 + weighty = 1.0 + fill = GridBagConstraints.BOTH + } + ) + + // Add the buttons panel to the dialog + dialog.contentPane.add( + buttonsPanel, + GridBagConstraints().apply { + gridx = 0 + gridy = 1 + weightx = 1.0 + weighty = 0.0 + fill = GridBagConstraints.HORIZONTAL + } + ) + + // Make the dialog visible + dialog.isVisible = true + } +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmAPaperVision.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmAPaperVision.kt new file mode 100644 index 00000000..2ae49dbf --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/iama/IAmAPaperVision.kt @@ -0,0 +1,168 @@ +package com.github.serivesmejia.eocvsim.gui.dialog.iama + +import com.github.serivesmejia.eocvsim.gui.DialogFactory +import com.github.serivesmejia.eocvsim.gui.Visualizer +import java.awt.BorderLayout +import java.awt.Desktop +import java.awt.Dimension +import java.awt.FlowLayout +import java.net.URI +import javax.swing.BorderFactory +import javax.swing.Box +import javax.swing.BoxLayout +import javax.swing.ImageIcon +import javax.swing.JButton +import javax.swing.JDialog +import javax.swing.JFrame +import javax.swing.JLabel +import javax.swing.JOptionPane +import javax.swing.JPanel +import javax.swing.SwingConstants + +class IAmAPaperVision( + parent: JFrame, + visualizer: Visualizer, + specificallyInterested: Boolean = false, + showWorkspacesButton: Boolean = true +) { + + companion object { + val papervisionGif = ImageIcon(this::class.java.getResource("/images/papervision.gif")) + } + + val dialog = JDialog(parent).apply { + isModal = true + title = "Welcome !" + + layout = BoxLayout(contentPane, BoxLayout.Y_AXIS) + + size = Dimension(820, 550) + isResizable = false + setLocationRelativeTo(null) + } + + init { + val title = JLabel("
Introducing PaperVision
") + + title.font = title.font.deriveFont(20f) + title.horizontalAlignment = SwingConstants.CENTER + title.alignmentX = JPanel.CENTER_ALIGNMENT // Align horizontally in the BoxLayout + dialog.contentPane.add(title) + + dialog.contentPane.add(Box.createVerticalStrut(5)) + + val gifPanel = JPanel(BorderLayout()) + val label = JLabel(papervisionGif) + gifPanel.add(label, BorderLayout.CENTER) + + gifPanel.alignmentX = JPanel.CENTER_ALIGNMENT // Align the panel in the BoxLayout + dialog.contentPane.add(gifPanel) + + val text = """ + +
+ PaperVision is a new pipeline development tool that allows you to create
+ your OpenCV algorithms with a visual programming interface, easier than ever before. +
+ + """.trimIndent() + + dialog.contentPane.add(JLabel(text).apply { + font = font.deriveFont(18f) + horizontalAlignment = SwingConstants.CENTER + alignmentX = JPanel.CENTER_ALIGNMENT // Align horizontally in the BoxLayout + }) + + dialog.contentPane.add(Box.createVerticalStrut(10)) + + // Create the buttons panel + val buttonsPanel = JPanel().apply { + layout = BoxLayout(this, BoxLayout.X_AXIS) + + add(Box.createHorizontalGlue()) // Align the button to the right + + + add(JButton("Close").apply { + addActionListener { + // Handle the next button click here + dialog.dispose() // Close the dialog on click + } + }) + + add(Box.createHorizontalStrut(10)) // Add some space between the buttons + + if(!specificallyInterested && showWorkspacesButton) { + add(JButton("Use Workspaces Instead").apply { + addActionListener { + dialog.dispose() // Close the dialog on click + DialogFactory.createWorkspace(visualizer) + } + }) + + add(Box.createHorizontalStrut(10)) // Add some space between the buttons + } + + add(JButton("Use PaperVision").apply { + addActionListener { + dialog.dispose() // Close the dialog on click + + val indexOfTab = visualizer.pipelineOpModeSwitchablePanel.indexOfTab("PaperVision") + if(indexOfTab >= 0) { + visualizer.pipelineOpModeSwitchablePanel.selectedIndex = indexOfTab + } else { + JOptionPane.showMessageDialog( + parent, + "PaperVision is not currently available, please check your plugin settings.", + "Warning", + JOptionPane.ERROR_MESSAGE + ) + return@addActionListener + } + + fun openPaperVisionByDefault() { + visualizer.eocvSim.config.flags["prefersPaperVision"] = true + } + + if(specificallyInterested) { + openPaperVisionByDefault() + + JOptionPane.showConfirmDialog( + parent, + "From now on, EOCV-Sim will focus on PaperVision upon startup.\nYou can change this in the settings.", + "PaperVision", + JOptionPane.DEFAULT_OPTION, + JOptionPane.INFORMATION_MESSAGE + ) + } else { + JOptionPane.showOptionDialog( + parent, + "Would you like to focus on PaperVision by default?\nThis is useful if you're not interested on the other tools.\nYou can change this in the settings.", + "PaperVision", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + arrayOf("Yes", "No"), + "No" + ).let { + if(it == JOptionPane.YES_OPTION) { + openPaperVisionByDefault() + } else { + visualizer.eocvSim.config.flags["prefersPaperVision"] = false + } + } + } + } + }) + + border = BorderFactory.createEmptyBorder(0, 10, 10, 10) + } + + buttonsPanel.alignmentX = JPanel.CENTER_ALIGNMENT // Align the panel in the BoxLayout + dialog.contentPane.add(buttonsPanel) + + + visualizer.eocvSim.config.flags["hasShownIamPaperVision"] = true + dialog.isVisible = true + } + +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/SysUtil.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/SysUtil.java index 08338f5c..2bf8e812 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/SysUtil.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/SysUtil.java @@ -168,17 +168,6 @@ public static boolean saveFileStr(File f, String contents) { } } - private static final char[] hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - - public static String byteArray2Hex(byte[] bytes) { - StringBuffer sb = new StringBuffer(bytes.length * 2); - for(final byte b : bytes) { - sb.append(hex[(b & 0xF0) >> 4]); - sb.append(hex[b & 0x0F]); - } - return sb.toString(); - } - public static void download(String url, String fileName) throws Exception { try (InputStream in = URI.create(url).toURL().openStream()) { Files.copy(in, Paths.get(fileName)); diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt index 1b5b083e..ec563cb6 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt @@ -139,7 +139,12 @@ class SuperAccessRequest(sourceName: String, reason: String, val untrusted: Bool } }) - frame.setIconImage(EOCVSimIconLibrary.icoEOCVSim.image) + frame.setIconImages(listOf( + EOCVSimIconLibrary.icoEOCVSim128.image, + EOCVSimIconLibrary.icoEOCVSim64.image, + EOCVSimIconLibrary.icoEOCVSim32.image, + EOCVSimIconLibrary.icoEOCVSim16.image + )); frame.pack() diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt index 0fc66726..3f43c496 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt @@ -218,7 +218,11 @@ class PluginLoader( } pluginClass = pluginClassLoader.loadClassStrict(pluginToml.getString("main")) - plugin = pluginClass.getConstructor().newInstance() as EOCVSimPlugin + plugin = try { + pluginClass.getConstructor().newInstance() as EOCVSimPlugin + } catch(e: Throwable) { + throw InvalidPluginException("Failed to instantiate plugin class ${pluginClass.name}. Make sure your plugin class has a public no-args constructor.") + } plugin.onLoad() diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginManager.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginManager.kt index 521fa830..56e98dc7 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginManager.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginManager.kt @@ -47,9 +47,9 @@ import kotlin.properties.Delegates class PluginManager(val eocvSim: EOCVSim) { companion object { - val PLUGIN_FOLDER = (EOCVSimFolder + File.separator + "plugins").apply { mkdir() } - val PLUGIN_CACHING_FOLDER = (PLUGIN_FOLDER + File.separator + "caching").apply { mkdir() } - val FILESYSTEMS_FOLDER = (PLUGIN_FOLDER + File.separator + "filesystems").apply { mkdir() } + val PLUGIN_FOLDER = io.github.deltacv.eocvsim.plugin.PLUGIN_FOLDER + val PLUGIN_CACHING_FOLDER = io.github.deltacv.eocvsim.plugin.PLUGIN_CACHING_FOLDER + val FILESYSTEMS_FOLDER = io.github.deltacv.eocvsim.plugin.FILESYSTEMS_FOLDER const val GENERIC_SUPERACCESS_WARN = "Plugins run in a restricted environment by default. SuperAccess will grant full system access. Ensure you trust the authors before accepting." const val GENERIC_LAWYER_YEET = "

By accepting, you acknowledge that deltacv is not responsible for damages caused by third-party plugins." @@ -57,7 +57,11 @@ class PluginManager(val eocvSim: EOCVSim) { val logger by loggerForThis() - val superAccessDaemonClient = SuperAccessDaemonClient() + val superAccessDaemonClient by lazy { + SuperAccessDaemonClient( + autoacceptOnTrusted = eocvSim.config.autoAcceptSuperAccessOnTrusted + ) + } private val _loadedPluginHashes = mutableListOf() val loadedPluginHashes get() = _loadedPluginHashes.toList() @@ -270,6 +274,8 @@ class PluginManager(val eocvSim: EOCVSim) { haltCondition.await() } + appender.appendln(PluginOutput.SPECIAL_SILENT + "Super access for ${loader.pluginName} v${loader.pluginVersion} was ${if(access) "granted" else "denied"}") + return access } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/papervision/PaperVisionChecker.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/papervision/PaperVisionChecker.kt new file mode 100644 index 00000000..da1217f1 --- /dev/null +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/papervision/PaperVisionChecker.kt @@ -0,0 +1,126 @@ +package io.github.deltacv.eocvsim.plugin.papervision + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.util.extension.hashString +import com.github.serivesmejia.eocvsim.util.loggerForThis +import io.github.deltacv.common.util.ParsedVersion +import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import io.github.deltacv.eocvsim.plugin.loader.PluginSource +import io.github.deltacv.eocvsim.plugin.repository.PluginRepositoryManager +import javax.swing.JOptionPane +import javax.swing.SwingUtilities +import kotlin.math.log + +object PaperVisionChecker { + + val LATEST_PAPERVISION = ParsedVersion(1, 0, 3) + + const val RESET_QUESTION = "o you wish to fix this by resetting back to the default settings? Please note this will wipe your plugins folder!" + + val logger by loggerForThis() + + fun check( + eocvSim: EOCVSim + ) { + fun startFresh() { + eocvSim.onMainUpdate.doOnce { + eocvSim.config.flags["startFresh"] = true + PluginRepositoryManager.REPOSITORY_FILE.delete() + PluginRepositoryManager.CACHE_FILE.delete() + } + + val result = JOptionPane.showOptionDialog( + eocvSim.visualizer.frame, + "You need to restart to apply the latest changes, Restart now?", + "Restart Now", + JOptionPane.WARNING_MESSAGE, + JOptionPane.YES_NO_OPTION, + null, arrayOf("Restart", "Ignore"), null + ) + + if(result == JOptionPane.YES_OPTION) { + eocvSim.onMainUpdate.doOnce { + eocvSim.restart() + } + } + } + + val paperVisionPlugin = eocvSim.pluginManager.loaders.values.find { it.pluginName == "PaperVision" && it.pluginAuthor == "deltacv" } + val hash = paperVisionPlugin?.pluginFile?.absolutePath?.hashString + + logger.info("hash_check = ${eocvSim.config.flags["${hash}_check"]}") + logger.info("null_check = ${eocvSim.config.flags["null_check"]}") + + if(eocvSim.config.flags["${hash}_check"] == true) { + return + } else { + eocvSim.config.flags["${hash}_check"] = true + } + + val parsedVersion = try { + ParsedVersion(paperVisionPlugin!!.pluginVersion).apply { + logger.info("Parsed PaperVision version: $this") + } + } catch(e: Exception) { + logger.warn("Failed to parse PaperVision version", e) + null + } + + if(paperVisionPlugin == null) { + SwingUtilities.invokeLater { + val result = JOptionPane.showOptionDialog( + eocvSim.visualizer.frame, + "The PaperVision plugin is not present.\nD$RESET_QUESTION", + "PaperVision Missing", + JOptionPane.WARNING_MESSAGE, + JOptionPane.YES_NO_OPTION, + null, arrayOf("Reset and Fix", "Ignore and Continue"), null + ) + + if(result == JOptionPane.YES_OPTION) { + startFresh() + } + } + + logger.warn("PaperVision plugin not present") + } else if(paperVisionPlugin.pluginSource == PluginSource.FILE) { + SwingUtilities.invokeLater { + val result = JOptionPane.showOptionDialog( + eocvSim.visualizer.frame, + "PaperVision was loaded from a file. You can ignore this message ONLY IF you did this intentionally and intend to test development versions.\nIf that's not the case, d$RESET_QUESTION", + "PaperVision Source", + JOptionPane.WARNING_MESSAGE, + JOptionPane.YES_NO_OPTION, + null, arrayOf("Reset and Fix", "Ignore and Continue"), null + ) + + if(result == JOptionPane.YES_OPTION) { + startFresh() + } + } + + eocvSim.config.flags["null_check"] = false + logger.warn("PaperVision plugin loaded from file") + } else if(parsedVersion == null || parsedVersion < LATEST_PAPERVISION) { + SwingUtilities.invokeLater { + val result = JOptionPane.showOptionDialog( + eocvSim.visualizer.frame, + "The PaperVision plugin is outdated.\nD$RESET_QUESTION", + "PaperVision Outdated", + JOptionPane.WARNING_MESSAGE, + JOptionPane.YES_NO_OPTION, + null, arrayOf("Reset and Fix", "Ignore and Continue"), null + ) + + if(result == JOptionPane.YES_OPTION) { + startFresh() + } + } + + eocvSim.config.flags["null_check"] = false + logger.warn("PaperVision plugin outdated") + } + } + +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt index 3df7ab79..bcf3b655 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt @@ -37,16 +37,13 @@ import io.github.deltacv.eocvsim.plugin.loader.PluginParser import io.github.deltacv.eocvsim.plugin.security.Authority import io.github.deltacv.eocvsim.plugin.security.AuthorityFetcher import io.github.deltacv.eocvsim.plugin.security.MutablePluginSignature -import org.apache.logging.log4j.LogManager -import org.apache.logging.log4j.core.LoggerContext -import org.apache.logging.log4j.core.appender.FileAppender import org.java_websocket.client.WebSocketClient import org.java_websocket.handshake.ServerHandshake import java.io.File import java.lang.Exception +import java.lang.Thread.sleep import java.net.URI -import java.nio.file.Files -import java.nio.file.Paths +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executors import java.util.zip.ZipFile import javax.swing.SwingUtilities @@ -78,29 +75,32 @@ object SuperAccessDaemon { val SUPERACCESS_FILE = PluginManager.PLUGIN_CACHING_FOLDER + File.separator + "superaccess.txt" - private val access = mutableMapOf() + private val access = ConcurrentHashMap() @JvmStatic fun main(args: Array) { - if(args.isEmpty()) { - logger.error("Port is required.") - return + if(args.size < 2) { + logger.error("Usage: ") + exitProcess(-1) } - if(args.size > 1) { + if(args.size > 2) { logger.warn("Ignoring extra arguments.") } System.setProperty("sun.java2d.d3d", "false") + System.setProperty("apple.awt.application.appearance", "system") + System.setProperty("apple.awt.application.name", "EasyOpenCV Simulator - SuperUserAccess") - WsClient(args[0].toIntOrNull() ?: throw IllegalArgumentException("Port is not a valid int")).connect() + WsClient(args[0].toIntOrNull() ?: throw IllegalArgumentException("Port is not a valid int"), args[1].toBoolean()).connect() } - class WsClient(port: Int) : WebSocketClient(URI("ws://localhost:$port")) { + class WsClient(port: Int, val autoacceptTrusted: Boolean) : WebSocketClient(URI("ws://localhost:$port")) { private val executor = Executors.newFixedThreadPool(4) override fun onOpen(p0: ServerHandshake?) { - logger.info("SuperAccessDaemon connection opened") + logger.info("SuperAccessDaemon connection opened.") + logger.info("Autoaccept on trusted: $autoacceptTrusted") } override fun onMessage(msg: String) { @@ -169,21 +169,32 @@ object SuperAccessDaemon { warning += "

$reason" } + // helper function to grant access, avoid code duplication + fun grant() { + SUPERACCESS_FILE.appendText(pluginFile.fileHash() + "\n") + accessGranted(message.id, message.pluginPath) + } + warning += if(validAuthority != null) { - "

This plugin has been digitally signed by ${validAuthority.name}, ensuring its integrity and authenticity.
${validAuthority.name} is a trusted authority in the EOCV-Sim ecosystem." + "

This plugin has been digitally signed by ${validAuthority.name}.
It is a trusted authority in the EOCV-Sim ecosystem." } else if(untrusted) { - "

This plugin claims to be made by trusted authority ${parser.pluginAuthor}, but it has not been digitally signed by them.

Beware of potential security risks.

" + "

This plugin claims to be made by ${parser.pluginAuthor}, but it has not been digitally signed by them.

Beware of potential security risks.

" } else { GENERIC_LAWYER_YEET } warning += "" + if(validAuthority != null && autoacceptTrusted) { + grant() + logger.info("Granted automatic SuperAccess to $name. The plugin has been signed by ${validAuthority.name}") + return + } + SwingUtilities.invokeLater { - SuperAccessRequest(name, warning, validAuthority == null || untrusted) { granted -> + SuperAccessRequest(name, warning, validAuthority == null) { granted -> if(granted) { - SUPERACCESS_FILE.appendText(pluginFile.fileHash() + "\n") - accessGranted(message.id, message.pluginPath) + grant() } else { accessDenied(message.id, message.pluginPath) } diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt index c4c990ab..70541aa5 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt @@ -24,7 +24,6 @@ package io.github.deltacv.eocvsim.plugin.security.superaccess import com.github.serivesmejia.eocvsim.util.JavaProcess -import com.github.serivesmejia.eocvsim.util.extension.hashString import com.github.serivesmejia.eocvsim.util.loggerForThis import org.java_websocket.WebSocket import org.java_websocket.handshake.ClientHandshake @@ -32,7 +31,6 @@ import org.java_websocket.server.WebSocketServer import java.io.File import java.lang.Exception import java.net.InetSocketAddress -import java.util.Random import java.util.concurrent.Executors import java.util.concurrent.locks.Condition import java.util.concurrent.locks.ReentrantLock @@ -45,20 +43,11 @@ private data class AccessCache( val file: String, val hasAccess: Boolean, val timestamp: Long -) { - init { - // get calling class - val stackTrace = Thread.currentThread().stackTrace - val callingClass = stackTrace[2].className - - if(callingClass != SuperAccessDaemonClient::class.java.name) { - throw IllegalAccessException("AccessCache should only be created from SuperAccessDaemonClient") - } - } -} +) class SuperAccessDaemonClient( - val cacheTTLMillis: Long = 3_000 + val cacheTTLMillis: Long = 3_000, + autoacceptOnTrusted: Boolean ) { val logger by loggerForThis() @@ -70,7 +59,7 @@ class SuperAccessDaemonClient( private val accessCache = mutableMapOf() // create a new WebSocket server - private val server = WsServer(startLock, startCondition) + private val server = WsServer(startLock, startCondition, autoacceptOnTrusted) fun init() { server.start() @@ -91,10 +80,19 @@ class SuperAccessDaemonClient( server.broadcast(SuperAccessDaemon.gson.toJson(request)) server.addResponseReceiver(request.id) { response -> - if(response is SuperAccessDaemon.SuperAccessResponse.Success) { + var result = if(response is SuperAccessDaemon.SuperAccessResponse.Success) { onResponse(true) + true } else if(response is SuperAccessDaemon.SuperAccessResponse.Failure) { onResponse(false) + false + } else null + + if(result != null) { + synchronized(cacheLock) { + val newCache = AccessCache(request.pluginPath, result, System.currentTimeMillis()) + accessCache[request.pluginPath] = newCache + } } } } @@ -149,6 +147,7 @@ class SuperAccessDaemonClient( private class WsServer( val startLock: ReentrantLock, val startCondition: Condition, + val autoacceptOnTrusted: Boolean ) : WebSocketServer(InetSocketAddress("127.0.0.1", 0)) { // create an executor with 1 thread private val executor = Executors.newSingleThreadExecutor() @@ -222,7 +221,7 @@ class SuperAccessDaemonClient( SuperAccessDaemon::class.java, JavaProcess.SLF4JIOReceiver(logger), listOf("-Dlog4j.configurationFile=log4j2_nofile.xml"), - listOf(port.toString()) + listOf(port.toString(), autoacceptOnTrusted.toString()) ) if(processRestarts == 6) { diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt index 1844c2be..fd66636d 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt @@ -36,9 +36,10 @@ class SandboxFileSystem(loader: PluginLoader) : FileSystem() { init { logger.info("Loading filesystem ${loader.hash()}") + Runtime.getRuntime().addShutdownHook(Thread { if(isOpen) { - logger.info("Unloading filesystem ${loader.hash()} on shutdown") + logger.info("SHUTDOWN - Unloading filesystem ${loader.hash()}") close() } }) diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new.png b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new.png new file mode 100644 index 00000000..03889692 Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new.png differ diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_128.png b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_128.png new file mode 100644 index 00000000..8866b673 Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_128.png differ diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_16.png b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_16.png new file mode 100644 index 00000000..6fd14904 Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_16.png differ diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_32.png b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_32.png new file mode 100644 index 00000000..16062b3c Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_32.png differ diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_64.png b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_64.png new file mode 100644 index 00000000..619b4856 Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_eocvsim_new_64.png differ diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_ezv.png b/EOCV-Sim/src/main/resources/images/icon/ico_ezv.png new file mode 100644 index 00000000..dad239db Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_ezv.png differ diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_firstrobotics.png b/EOCV-Sim/src/main/resources/images/icon/ico_firstrobotics.png new file mode 100644 index 00000000..69c88c03 Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_firstrobotics.png differ diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_folder.png b/EOCV-Sim/src/main/resources/images/icon/ico_folder.png new file mode 100644 index 00000000..5da52f6e Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_folder.png differ diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_user.png b/EOCV-Sim/src/main/resources/images/icon/ico_user.png new file mode 100644 index 00000000..c4756348 Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_user.png differ diff --git a/EOCV-Sim/src/main/resources/images/icon/ico_vscode.png b/EOCV-Sim/src/main/resources/images/icon/ico_vscode.png new file mode 100644 index 00000000..8135be5a Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/icon/ico_vscode.png differ diff --git a/EOCV-Sim/src/main/resources/images/papervision.gif b/EOCV-Sim/src/main/resources/images/papervision.gif new file mode 100644 index 00000000..01bdc40e Binary files /dev/null and b/EOCV-Sim/src/main/resources/images/papervision.gif differ diff --git a/EOCV-Sim/src/main/resources/repository.toml b/EOCV-Sim/src/main/resources/repository.toml index 79e1b2ae..4cd34d81 100644 --- a/EOCV-Sim/src/main/resources/repository.toml +++ b/EOCV-Sim/src/main/resources/repository.toml @@ -1,10 +1,11 @@ [repositories] -# Declare the URL of the Maven repositories to use, with a friendly name. +# Declare the URL of the Maven repositories to use, with a key name. # The URL must preferably end with a slash to be handled correctly. central = "https://repo.maven.apache.org/maven2/" jitpack = "https://jitpack.io/" [plugins] -# Declare the plugin ID and the Maven coordinates of the plugin, similar to how you do it in Gradle. -# (group:artifact:version) which will be used to download the plugin from one of Maven repositories. -# Any dependency that does not have a plugin.toml in its jar will not be considered after download. \ No newline at end of file +# Declare the ID and the Maven coordinates of the plugin (group:artifact:version) +# which will be used to download the plugin from one of repositories specified above. +# Any dependency that is not a valid EOCV-Sim plugin will not be considered after download. +PaperVision = "org.deltacv.PaperVision:EOCVSimPlugin:1.0.3" \ No newline at end of file diff --git a/EOCV-Sim/src/main/resources/templates/gradle_workspace.zip b/EOCV-Sim/src/main/resources/templates/gradle_workspace.zip index e69fb559..bcc1f1ea 100644 Binary files a/EOCV-Sim/src/main/resources/templates/gradle_workspace.zip and b/EOCV-Sim/src/main/resources/templates/gradle_workspace.zip differ diff --git a/README.md b/README.md index 5db3baa9..40d69d7e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ - + ![Java CI with Gradle](https://github.com/deltacv/EOCV-Sim/workflows/Build%20and%20test%20with%20Gradle/badge.svg) -[![](https://jitpack.io/v/deltacv/EOCV-Sim.svg)](https://jitpack.io/#deltacv/EOCV-Sim) - -[![Run on Repl.it](https://repl.it/badge/github/deltacv/EOCV-Sim)](https://repl.it/github/deltacv/EOCV-Sim) +[![Maven Publish & Create GitHub Release(s)](https://github.com/deltacv/EOCV-Sim/actions/workflows/release_ci.yml/badge.svg)](https://github.com/deltacv/EOCV-Sim/actions/workflows/release_ci.yml) # Welcome! @@ -14,8 +12,17 @@ transfer it onto your robot! -# Learn how to install and use the simulator in the [documentation here](https://deltacv.gitbook.io/eocv-sim/) -### (Common users won't need to scroll down any further from here - the gitbook has everything you need) +## Introducing PaperVision + +Starting at v4.0.0, EOCV-Sim comes with a new tool that will make it even easier to work on your computer vision pipelines. +PaperVision takes inspiration in Blender and Unreal Engine's blueprint system, allowing you to create pipelines by connecting nodes together.

+PaperVision is still in its early stages, but it's already a powerful tool that will make it easier to work on your pipelines.

+Find it in the "PaperVision" tab in the simulator ! + + + +# Learn how to install and use the simulator in the [documentation here](https://docs.deltacv.org/eocv-sim) +### (Common users won't need to scroll down any further from here - the docs have everything you need) # [Buy me a coffee](https://buymeacoffee.com/serivesmejia) @@ -46,7 +53,7 @@ Since OpenCV in Java uses a native library, which is platform specific, the simu ## Downloading and documentation -Follow the steps in [this page](https://deltacv.gitbook.io/eocv-sim/basics/downloading-eocv-sim) to download the sim. The rest of the documentation can also be found [there](https://deltacv.gitbook.io/eocv-sim/). +Follow the steps in [this page](https://docs.deltacv.org/eocv-sim/downloading-eocv-sim) to download the sim. The rest of the documentation can also be found [there](https://deltacv.gitbook.io/eocv-sim/). ## Adding EOCV-Sim as a dependency for plugin development @@ -55,7 +62,8 @@ Follow the steps in [this page](https://deltacv.gitbook.io/eocv-sim/basics/downl ### Gradle ```groovy repositories { - maven { url 'https://jitpack.com' } //add jitpack as a maven repo + maven { url 'https://jitpack.com' } //add jitpack as a maven repo + mavenCentral() //add maven central as a maven repo } dependencies { @@ -93,6 +101,13 @@ Join the [deltacv discord server](https://discord.gg/A3RMYzf6DA) ! ### Formerly, EOCV-Sim was hosted on a [personal account repo](https://github.com/serivesmejia/EOCV-Sim/). Released prior to 3.0.0 can be found there for historic purposes. +## [v4.0.0 - PaperVision is here !](https://github.com/deltacv/EOCV-Sim/releases/tag/v4.0.0) +- This is the 30th release for EOCV-Sim + - Introducing PaperVision ! A tool bundled in the simulator that will make it even easier to work on your computer vision pipelines. + - Auto-accepts superaccess on trusted plugins, this reduces the UI clutter, especially for PaperVision + - Improved several dialogs, adds a better "create workspace" dialog & a welcome dialog that is shown when the sim is opened for the first time + - New modernized application icon + - @qwertychouskie made their first contribution: "macOS: Add application name, use system light/dark mode in titlebar" and "macOS: Use standard Settings & About entries in menu". Thank you ! ## [v3.8.4 - Maven Central Migration](https://github.com/deltacv/EOCV-Sim/releases/tag/v3.8.4) - This is the 29th release for EOCV-Sim - Migrates all of EOCV-Sim's artifacts to the maven central repository (bye bye JitPack) diff --git a/TeamCode/build.gradle b/TeamCode/build.gradle index 264ec071..3b292b31 100644 --- a/TeamCode/build.gradle +++ b/TeamCode/build.gradle @@ -5,7 +5,7 @@ plugins { apply from: '../build.common.gradle' dependencies { - implementation project(':EOCV-Sim') + api project(':EOCV-Sim') implementation "org.jetbrains.kotlin:kotlin-stdlib" } diff --git a/Vision/build.gradle b/Vision/build.gradle index 4dc1a07c..36a427c9 100644 --- a/Vision/build.gradle +++ b/Vision/build.gradle @@ -6,16 +6,10 @@ plugins { apply from: '../build.common.gradle' -configurations.all { - resolutionStrategy { - cacheChangingModulesFor 0, 'seconds' - } -} - dependencies { implementation project(':Common') - api("com.github.deltacv:AprilTagDesktop:$apriltag_plugin_version") { + api("org.deltacv:AprilTagDesktop:$apriltag_plugin_version") { transitive = false } diff --git a/build.gradle b/build.gradle index d123a1ca..13210ce7 100644 --- a/build.gradle +++ b/build.gradle @@ -9,12 +9,14 @@ buildscript { slf4j_version = "2.0.16" log4j_version = "2.24.1" opencv_version = "4.7.0-0" - apriltag_plugin_version = "2.1.0-C" + apriltag_plugin_version = "2.1.0-D" skiko_version = "0.8.15" classgraph_version = "4.8.112" opencsv_version = "5.5.2" + toml4j_version = "0.7.2" + picocli_version = "4.6.1" Penv = findProperty('env') if(Penv != null && (Penv != 'dev' && Penv != 'release')) { @@ -44,15 +46,10 @@ plugins { allprojects { group 'org.deltacv.EOCV-Sim' - version '3.8.4' + version '4.0.0' apply plugin: 'java' - configurations { - // do not pull in EOCV-Sim's Common module from a repository, it's already in the project - runtime.exclude group: "com.github.deltacv.EOCV-Sim", module: "Common" - } - ext { standardVersion = version } @@ -67,7 +64,7 @@ allprojects { maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" } } - tasks.withType(Jar) { + tasks.withType(Jar).configureEach { manifest { attributes['Main-Class'] = 'com.github.serivesmejia.eocvsim.Main' } diff --git a/doc/images/eocvsim_screenshot_1.png b/doc/images/eocvsim_screenshot_1.png index aa0c1e02..9628f4d4 100644 Binary files a/doc/images/eocvsim_screenshot_1.png and b/doc/images/eocvsim_screenshot_1.png differ diff --git a/gradle.properties b/gradle.properties index eadc428e..7ffa4ca7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ RELEASE_SIGNING_ENABLED=true POM_DESCRIPTION=Develop, test, and tune your EasyOpenCV pipelines directly on your computer with a simple GUI ! POM_INCEPTION_YEAR=2024 -POM_URL=https://github.com/deltacv/EOCV-Sim/ +POM_URL=https://docs.deltacv.org/eocv-sim POM_LICENSE_NAME=MIT License POM_LICENSE_URL=https://opensource.org/license/mit @@ -16,4 +16,4 @@ POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/deltacv/EOCV-Sim.git POM_DEVELOPER_ID=serivesmejia POM_DEVELOPER_NAME=Sebastian Erives -POM_DEVELOPER_URL=https://github.com/serivesmejia/ +POM_DEVELOPER_URL=https://github.com/serivesmejia/ \ No newline at end of file